// Copyright 2009 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.package astimport ()// ----------------------------------------------------------------------------// Export filtering// exportFilter is a special filter function to extract exported nodes.func exportFilter( string) bool {returnIsExported()}// FileExports trims the AST for a Go source file in place such that// only exported nodes remain: all top-level identifiers which are not exported// and their associated information (such as type, initial value, or function// body) are removed. Non-exported fields and methods of exported types are// stripped. The [File.Comments] list is not changed.//// FileExports reports whether there are exported declarations.func ( *File) bool {returnfilterFile(, exportFilter, true)}// PackageExports trims the AST for a Go package in place such that// only exported nodes remain. The pkg.Files list is not changed, so that// file names and top-level package comments don't get lost.//// PackageExports reports whether there are exported declarations;// it returns false otherwise.func ( *Package) bool {returnfilterPackage(, exportFilter, true)}// ----------------------------------------------------------------------------// General filteringtypeFilterfunc(string) boolfunc filterIdentList( []*Ident, Filter) []*Ident { := 0for , := range {if (.Name) { [] = ++ } }return [0:]}// fieldName assumes that x is the type of an anonymous field and// returns the corresponding field name. If x is not an acceptable// anonymous field, the result is nil.func fieldName( Expr) *Ident {switch t := .(type) {case *Ident:returncase *SelectorExpr:if , := .X.(*Ident); {return .Sel }case *StarExpr:return (.X) }returnnil}func filterFieldList( *FieldList, Filter, bool) ( bool) {if == nil {returnfalse } := .List := 0for , := range { := falseiflen(.Names) == 0 {// anonymous field := fieldName(.Type) = != nil && (.Name) } else { := len(.Names) .Names = filterIdentList(.Names, )iflen(.Names) < { = true } = len(.Names) > 0 }if {if {filterType(.Type, , ) } [] = ++ } }if < len() { = true } .List = [0:]return}func filterCompositeLit( *CompositeLit, Filter, bool) { := len(.Elts) .Elts = filterExprList(.Elts, , )iflen(.Elts) < { .Incomplete = true }}func filterExprList( []Expr, Filter, bool) []Expr { := 0for , := range {switch x := .(type) {case *CompositeLit:filterCompositeLit(, , )case *KeyValueExpr:if , := .Key.(*Ident); && !(.Name) {continue }if , := .Value.(*CompositeLit); {filterCompositeLit(, , ) } } [] = ++ }return [0:]}func filterParamList( *FieldList, Filter, bool) bool {if == nil {returnfalse }varboolfor , := range .List {iffilterType(.Type, , ) { = true } }return}func filterType( Expr, Filter, bool) bool {switch t := .(type) {case *Ident:return (.Name)case *ParenExpr:return (.X, , )case *ArrayType:return (.Elt, , )case *StructType:iffilterFieldList(.Fields, , ) { .Incomplete = true }returnlen(.Fields.List) > 0case *FuncType: := filterParamList(.Params, , ) := filterParamList(.Results, , )return || case *InterfaceType:iffilterFieldList(.Methods, , ) { .Incomplete = true }returnlen(.Methods.List) > 0case *MapType: := (.Key, , ) := (.Value, , )return || case *ChanType:return (.Value, , ) }returnfalse}func filterSpec( Spec, Filter, bool) bool {switch s := .(type) {case *ValueSpec: .Names = filterIdentList(.Names, ) .Values = filterExprList(.Values, , )iflen(.Names) > 0 {if {filterType(.Type, , ) }returntrue }case *TypeSpec:if (.Name.Name) {if {filterType(.Type, , ) }returntrue }if ! {// For general filtering (not just exports), // filter type even if name is not filtered // out. // If the type contains filtered elements, // keep the declaration.returnfilterType(.Type, , ) } }returnfalse}func filterSpecList( []Spec, Filter, bool) []Spec { := 0for , := range {iffilterSpec(, , ) { [] = ++ } }return [0:]}// FilterDecl trims the AST for a Go declaration in place by removing// all names (including struct field and interface method names, but// not from parameter lists) that don't pass through the filter f.//// FilterDecl reports whether there are any declared names left after// filtering.func ( Decl, Filter) bool {returnfilterDecl(, , false)}func filterDecl( Decl, Filter, bool) bool {switch d := .(type) {case *GenDecl: .Specs = filterSpecList(.Specs, , )returnlen(.Specs) > 0case *FuncDecl:return (.Name.Name) }returnfalse}// FilterFile trims the AST for a Go file in place by removing all// names from top-level declarations (including struct field and// interface method names, but not from parameter lists) that don't// pass through the filter f. If the declaration is empty afterwards,// the declaration is removed from the AST. Import declarations are// always removed. The [File.Comments] list is not changed.//// FilterFile reports whether there are any top-level declarations// left after filtering.func ( *File, Filter) bool {returnfilterFile(, , false)}func filterFile( *File, Filter, bool) bool { := 0for , := range .Decls {iffilterDecl(, , ) { .Decls[] = ++ } } .Decls = .Decls[0:]return > 0}// FilterPackage trims the AST for a Go package in place by removing// all names from top-level declarations (including struct field and// interface method names, but not from parameter lists) that don't// pass through the filter f. If the declaration is empty afterwards,// the declaration is removed from the AST. The pkg.Files list is not// changed, so that file names and top-level package comments don't get// lost.//// FilterPackage reports whether there are any top-level declarations// left after filtering.func ( *Package, Filter) bool {returnfilterPackage(, , false)}func filterPackage( *Package, Filter, bool) bool { := falsefor , := range .Files {iffilterFile(, , ) { = true } }return}// ----------------------------------------------------------------------------// Merging of package files// The MergeMode flags control the behavior of [MergePackageFiles].typeMergeModeuintconst (// If set, duplicate function declarations are excluded.FilterFuncDuplicatesMergeMode = 1 << iota// If set, comments that are not associated with a specific // AST node (as Doc or Comment) are excluded.FilterUnassociatedComments// If set, duplicate import declarations are excluded.FilterImportDuplicates)// nameOf returns the function (foo) or method name (foo.bar) for// the given function declaration. If the AST is incorrect for the// receiver, it assumes a function instead.func nameOf( *FuncDecl) string {if := .Recv; != nil && len(.List) == 1 {// looks like a correct receiver declaration := .List[0].Type// dereference pointer receiver typesif , := .(*StarExpr); != nil { = .X }// the receiver type must be a type nameif , := .(*Ident); != nil {return .Name + "." + .Name.Name }// otherwise assume a function instead }return .Name.Name}// separator is an empty //-style comment that is interspersed between// different comment groups when they are concatenated into a single groupvar separator = &Comment{token.NoPos, "//"}// MergePackageFiles creates a file AST by merging the ASTs of the// files belonging to a package. The mode flags control merging behavior.func ( *Package, MergeMode) *File {// Count the number of package docs, comments and declarations across // all package files. Also, compute sorted list of filenames, so that // subsequent iterations can always iterate in the same order. := 0 := 0 := 0 := make([]string, len(.Files))var , token.Pos := 0for , := range .Files { [] = ++if .Doc != nil { += len(.Doc.List) + 1// +1 for separator } += len(.Comments) += len(.Decls)if == 0 || .FileStart < { = .FileStart }if == 0 || .FileEnd > { = .FileEnd } }slices.Sort()// Collect package comments from all package files into a single // CommentGroup - the collected package documentation. In general // there should be only one file with a package comment; but it's // better to collect extra comments than drop them on the floor.var *CommentGroupvartoken.Posif > 0 { := make([]*Comment, -1) // -1: no separator before first group := 0for , := range { := .Files[]if .Doc != nil {if > 0 {// not the first group - add separator [] = separator ++ }for , := range .Doc.List { [] = ++ }if .Package > {// Keep the maximum package clause position as // position for the package clause of the merged // files. = .Package } } } = &CommentGroup{} }// Collect declarations from all package files.var []Declif > 0 { = make([]Decl, ) := make(map[string]int) // map of func name -> decls index := 0// current index := 0// number of filtered entriesfor , := range { := .Files[]for , := range .Decls {if &FilterFuncDuplicates != 0 {// A language entity may be declared multiple // times in different package files; only at // build time declarations must be unique. // For now, exclude multiple declarations of // functions - keep the one with documentation. // // TODO(gri): Expand this filtering to other // entities (const, type, vars) if // multiple declarations are common.if , := .(*FuncDecl); { := nameOf()if , := []; {// function declared alreadyif [] != nil && [].(*FuncDecl).Doc == nil {// existing declaration has no documentation; // ignore the existing declaration [] = nil } else {// ignore the new declaration = nil } ++ // filtered an entry } else { [] = } } } [] = ++ } }// Eliminate nil entries from the decls list if entries were // filtered. We do this using a 2nd pass in order to not disturb // the original declaration order in the source (otherwise, this // would also invalidate the monotonically increasing position // info within a single file).if > 0 { = 0for , := range {if != nil { [] = ++ } } = [0:] } }// Collect import specs from all package files.var []*ImportSpecif &FilterImportDuplicates != 0 { := make(map[string]bool)for , := range { := .Files[]for , := range .Imports {if := .Path.Value; ![] {// TODO: consider handling cases where: // - 2 imports exist with the same import path but // have different local names (one should probably // keep both of them) // - 2 imports exist but only one has a comment // - 2 imports exist and they both have (possibly // different) comments = append(, ) [] = true } } } } else {// Iterate over filenames for deterministic order.for , := range { := .Files[] = append(, .Imports...) } }// Collect comments from all package files.var []*CommentGroupif &FilterUnassociatedComments == 0 { = make([]*CommentGroup, ) := 0for , := range { := .Files[] += copy([:], .Comments) } }// TODO(gri) need to compute unresolved identifiers!return &File{, , NewIdent(.Name), , , , .Scope, , nil, , ""}}
The pages are generated with Goldsv0.7.0-preview. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.