github.com/shijuvar/go@v0.0.0-20141209052335-e8f13700b70c/src/go/ast/filter.go (about)

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package ast
     6  
     7  import (
     8  	"go/token"
     9  	"sort"
    10  )
    11  
    12  // ----------------------------------------------------------------------------
    13  // Export filtering
    14  
    15  // exportFilter is a special filter function to extract exported nodes.
    16  func exportFilter(name string) bool {
    17  	return IsExported(name)
    18  }
    19  
    20  // FileExports trims the AST for a Go source file in place such that
    21  // only exported nodes remain: all top-level identifiers which are not exported
    22  // and their associated information (such as type, initial value, or function
    23  // body) are removed. Non-exported fields and methods of exported types are
    24  // stripped. The File.Comments list is not changed.
    25  //
    26  // FileExports returns true if there are exported declarations;
    27  // it returns false otherwise.
    28  //
    29  func FileExports(src *File) bool {
    30  	return filterFile(src, exportFilter, true)
    31  }
    32  
    33  // PackageExports trims the AST for a Go package in place such that
    34  // only exported nodes remain. The pkg.Files list is not changed, so that
    35  // file names and top-level package comments don't get lost.
    36  //
    37  // PackageExports returns true if there are exported declarations;
    38  // it returns false otherwise.
    39  //
    40  func PackageExports(pkg *Package) bool {
    41  	return filterPackage(pkg, exportFilter, true)
    42  }
    43  
    44  // ----------------------------------------------------------------------------
    45  // General filtering
    46  
    47  type Filter func(string) bool
    48  
    49  func filterIdentList(list []*Ident, f Filter) []*Ident {
    50  	j := 0
    51  	for _, x := range list {
    52  		if f(x.Name) {
    53  			list[j] = x
    54  			j++
    55  		}
    56  	}
    57  	return list[0:j]
    58  }
    59  
    60  // fieldName assumes that x is the type of an anonymous field and
    61  // returns the corresponding field name. If x is not an acceptable
    62  // anonymous field, the result is nil.
    63  //
    64  func fieldName(x Expr) *Ident {
    65  	switch t := x.(type) {
    66  	case *Ident:
    67  		return t
    68  	case *SelectorExpr:
    69  		if _, ok := t.X.(*Ident); ok {
    70  			return t.Sel
    71  		}
    72  	case *StarExpr:
    73  		return fieldName(t.X)
    74  	}
    75  	return nil
    76  }
    77  
    78  func filterFieldList(fields *FieldList, filter Filter, export bool) (removedFields bool) {
    79  	if fields == nil {
    80  		return false
    81  	}
    82  	list := fields.List
    83  	j := 0
    84  	for _, f := range list {
    85  		keepField := false
    86  		if len(f.Names) == 0 {
    87  			// anonymous field
    88  			name := fieldName(f.Type)
    89  			keepField = name != nil && filter(name.Name)
    90  		} else {
    91  			n := len(f.Names)
    92  			f.Names = filterIdentList(f.Names, filter)
    93  			if len(f.Names) < n {
    94  				removedFields = true
    95  			}
    96  			keepField = len(f.Names) > 0
    97  		}
    98  		if keepField {
    99  			if export {
   100  				filterType(f.Type, filter, export)
   101  			}
   102  			list[j] = f
   103  			j++
   104  		}
   105  	}
   106  	if j < len(list) {
   107  		removedFields = true
   108  	}
   109  	fields.List = list[0:j]
   110  	return
   111  }
   112  
   113  func filterParamList(fields *FieldList, filter Filter, export bool) bool {
   114  	if fields == nil {
   115  		return false
   116  	}
   117  	var b bool
   118  	for _, f := range fields.List {
   119  		if filterType(f.Type, filter, export) {
   120  			b = true
   121  		}
   122  	}
   123  	return b
   124  }
   125  
   126  func filterType(typ Expr, f Filter, export bool) bool {
   127  	switch t := typ.(type) {
   128  	case *Ident:
   129  		return f(t.Name)
   130  	case *ParenExpr:
   131  		return filterType(t.X, f, export)
   132  	case *ArrayType:
   133  		return filterType(t.Elt, f, export)
   134  	case *StructType:
   135  		if filterFieldList(t.Fields, f, export) {
   136  			t.Incomplete = true
   137  		}
   138  		return len(t.Fields.List) > 0
   139  	case *FuncType:
   140  		b1 := filterParamList(t.Params, f, export)
   141  		b2 := filterParamList(t.Results, f, export)
   142  		return b1 || b2
   143  	case *InterfaceType:
   144  		if filterFieldList(t.Methods, f, export) {
   145  			t.Incomplete = true
   146  		}
   147  		return len(t.Methods.List) > 0
   148  	case *MapType:
   149  		b1 := filterType(t.Key, f, export)
   150  		b2 := filterType(t.Value, f, export)
   151  		return b1 || b2
   152  	case *ChanType:
   153  		return filterType(t.Value, f, export)
   154  	}
   155  	return false
   156  }
   157  
   158  func filterSpec(spec Spec, f Filter, export bool) bool {
   159  	switch s := spec.(type) {
   160  	case *ValueSpec:
   161  		s.Names = filterIdentList(s.Names, f)
   162  		if len(s.Names) > 0 {
   163  			if export {
   164  				filterType(s.Type, f, export)
   165  			}
   166  			return true
   167  		}
   168  	case *TypeSpec:
   169  		if f(s.Name.Name) {
   170  			if export {
   171  				filterType(s.Type, f, export)
   172  			}
   173  			return true
   174  		}
   175  		if !export {
   176  			// For general filtering (not just exports),
   177  			// filter type even if name is not filtered
   178  			// out.
   179  			// If the type contains filtered elements,
   180  			// keep the declaration.
   181  			return filterType(s.Type, f, export)
   182  		}
   183  	}
   184  	return false
   185  }
   186  
   187  func filterSpecList(list []Spec, f Filter, export bool) []Spec {
   188  	j := 0
   189  	for _, s := range list {
   190  		if filterSpec(s, f, export) {
   191  			list[j] = s
   192  			j++
   193  		}
   194  	}
   195  	return list[0:j]
   196  }
   197  
   198  // FilterDecl trims the AST for a Go declaration in place by removing
   199  // all names (including struct field and interface method names, but
   200  // not from parameter lists) that don't pass through the filter f.
   201  //
   202  // FilterDecl returns true if there are any declared names left after
   203  // filtering; it returns false otherwise.
   204  //
   205  func FilterDecl(decl Decl, f Filter) bool {
   206  	return filterDecl(decl, f, false)
   207  }
   208  
   209  func filterDecl(decl Decl, f Filter, export bool) bool {
   210  	switch d := decl.(type) {
   211  	case *GenDecl:
   212  		d.Specs = filterSpecList(d.Specs, f, export)
   213  		return len(d.Specs) > 0
   214  	case *FuncDecl:
   215  		return f(d.Name.Name)
   216  	}
   217  	return false
   218  }
   219  
   220  // FilterFile trims the AST for a Go file in place by removing all
   221  // names from top-level declarations (including struct field and
   222  // interface method names, but not from parameter lists) that don't
   223  // pass through the filter f. If the declaration is empty afterwards,
   224  // the declaration is removed from the AST. The File.Comments list
   225  // is not changed.
   226  //
   227  // FilterFile returns true if there are any top-level declarations
   228  // left after filtering; it returns false otherwise.
   229  //
   230  func FilterFile(src *File, f Filter) bool {
   231  	return filterFile(src, f, false)
   232  }
   233  
   234  func filterFile(src *File, f Filter, export bool) bool {
   235  	j := 0
   236  	for _, d := range src.Decls {
   237  		if filterDecl(d, f, export) {
   238  			src.Decls[j] = d
   239  			j++
   240  		}
   241  	}
   242  	src.Decls = src.Decls[0:j]
   243  	return j > 0
   244  }
   245  
   246  // FilterPackage trims the AST for a Go package in place by removing
   247  // all names from top-level declarations (including struct field and
   248  // interface method names, but not from parameter lists) that don't
   249  // pass through the filter f. If the declaration is empty afterwards,
   250  // the declaration is removed from the AST. The pkg.Files list is not
   251  // changed, so that file names and top-level package comments don't get
   252  // lost.
   253  //
   254  // FilterPackage returns true if there are any top-level declarations
   255  // left after filtering; it returns false otherwise.
   256  //
   257  func FilterPackage(pkg *Package, f Filter) bool {
   258  	return filterPackage(pkg, f, false)
   259  }
   260  
   261  func filterPackage(pkg *Package, f Filter, export bool) bool {
   262  	hasDecls := false
   263  	for _, src := range pkg.Files {
   264  		if filterFile(src, f, export) {
   265  			hasDecls = true
   266  		}
   267  	}
   268  	return hasDecls
   269  }
   270  
   271  // ----------------------------------------------------------------------------
   272  // Merging of package files
   273  
   274  // The MergeMode flags control the behavior of MergePackageFiles.
   275  type MergeMode uint
   276  
   277  const (
   278  	// If set, duplicate function declarations are excluded.
   279  	FilterFuncDuplicates MergeMode = 1 << iota
   280  	// If set, comments that are not associated with a specific
   281  	// AST node (as Doc or Comment) are excluded.
   282  	FilterUnassociatedComments
   283  	// If set, duplicate import declarations are excluded.
   284  	FilterImportDuplicates
   285  )
   286  
   287  // nameOf returns the function (foo) or method name (foo.bar) for
   288  // the given function declaration. If the AST is incorrect for the
   289  // receiver, it assumes a function instead.
   290  //
   291  func nameOf(f *FuncDecl) string {
   292  	if r := f.Recv; r != nil && len(r.List) == 1 {
   293  		// looks like a correct receiver declaration
   294  		t := r.List[0].Type
   295  		// dereference pointer receiver types
   296  		if p, _ := t.(*StarExpr); p != nil {
   297  			t = p.X
   298  		}
   299  		// the receiver type must be a type name
   300  		if p, _ := t.(*Ident); p != nil {
   301  			return p.Name + "." + f.Name.Name
   302  		}
   303  		// otherwise assume a function instead
   304  	}
   305  	return f.Name.Name
   306  }
   307  
   308  // separator is an empty //-style comment that is interspersed between
   309  // different comment groups when they are concatenated into a single group
   310  //
   311  var separator = &Comment{token.NoPos, "//"}
   312  
   313  // MergePackageFiles creates a file AST by merging the ASTs of the
   314  // files belonging to a package. The mode flags control merging behavior.
   315  //
   316  func MergePackageFiles(pkg *Package, mode MergeMode) *File {
   317  	// Count the number of package docs, comments and declarations across
   318  	// all package files. Also, compute sorted list of filenames, so that
   319  	// subsequent iterations can always iterate in the same order.
   320  	ndocs := 0
   321  	ncomments := 0
   322  	ndecls := 0
   323  	filenames := make([]string, len(pkg.Files))
   324  	i := 0
   325  	for filename, f := range pkg.Files {
   326  		filenames[i] = filename
   327  		i++
   328  		if f.Doc != nil {
   329  			ndocs += len(f.Doc.List) + 1 // +1 for separator
   330  		}
   331  		ncomments += len(f.Comments)
   332  		ndecls += len(f.Decls)
   333  	}
   334  	sort.Strings(filenames)
   335  
   336  	// Collect package comments from all package files into a single
   337  	// CommentGroup - the collected package documentation. In general
   338  	// there should be only one file with a package comment; but it's
   339  	// better to collect extra comments than drop them on the floor.
   340  	var doc *CommentGroup
   341  	var pos token.Pos
   342  	if ndocs > 0 {
   343  		list := make([]*Comment, ndocs-1) // -1: no separator before first group
   344  		i := 0
   345  		for _, filename := range filenames {
   346  			f := pkg.Files[filename]
   347  			if f.Doc != nil {
   348  				if i > 0 {
   349  					// not the first group - add separator
   350  					list[i] = separator
   351  					i++
   352  				}
   353  				for _, c := range f.Doc.List {
   354  					list[i] = c
   355  					i++
   356  				}
   357  				if f.Package > pos {
   358  					// Keep the maximum package clause position as
   359  					// position for the package clause of the merged
   360  					// files.
   361  					pos = f.Package
   362  				}
   363  			}
   364  		}
   365  		doc = &CommentGroup{list}
   366  	}
   367  
   368  	// Collect declarations from all package files.
   369  	var decls []Decl
   370  	if ndecls > 0 {
   371  		decls = make([]Decl, ndecls)
   372  		funcs := make(map[string]int) // map of func name -> decls index
   373  		i := 0                        // current index
   374  		n := 0                        // number of filtered entries
   375  		for _, filename := range filenames {
   376  			f := pkg.Files[filename]
   377  			for _, d := range f.Decls {
   378  				if mode&FilterFuncDuplicates != 0 {
   379  					// A language entity may be declared multiple
   380  					// times in different package files; only at
   381  					// build time declarations must be unique.
   382  					// For now, exclude multiple declarations of
   383  					// functions - keep the one with documentation.
   384  					//
   385  					// TODO(gri): Expand this filtering to other
   386  					//            entities (const, type, vars) if
   387  					//            multiple declarations are common.
   388  					if f, isFun := d.(*FuncDecl); isFun {
   389  						name := nameOf(f)
   390  						if j, exists := funcs[name]; exists {
   391  							// function declared already
   392  							if decls[j] != nil && decls[j].(*FuncDecl).Doc == nil {
   393  								// existing declaration has no documentation;
   394  								// ignore the existing declaration
   395  								decls[j] = nil
   396  							} else {
   397  								// ignore the new declaration
   398  								d = nil
   399  							}
   400  							n++ // filtered an entry
   401  						} else {
   402  							funcs[name] = i
   403  						}
   404  					}
   405  				}
   406  				decls[i] = d
   407  				i++
   408  			}
   409  		}
   410  
   411  		// Eliminate nil entries from the decls list if entries were
   412  		// filtered. We do this using a 2nd pass in order to not disturb
   413  		// the original declaration order in the source (otherwise, this
   414  		// would also invalidate the monotonically increasing position
   415  		// info within a single file).
   416  		if n > 0 {
   417  			i = 0
   418  			for _, d := range decls {
   419  				if d != nil {
   420  					decls[i] = d
   421  					i++
   422  				}
   423  			}
   424  			decls = decls[0:i]
   425  		}
   426  	}
   427  
   428  	// Collect import specs from all package files.
   429  	var imports []*ImportSpec
   430  	if mode&FilterImportDuplicates != 0 {
   431  		seen := make(map[string]bool)
   432  		for _, filename := range filenames {
   433  			f := pkg.Files[filename]
   434  			for _, imp := range f.Imports {
   435  				if path := imp.Path.Value; !seen[path] {
   436  					// TODO: consider handling cases where:
   437  					// - 2 imports exist with the same import path but
   438  					//   have different local names (one should probably
   439  					//   keep both of them)
   440  					// - 2 imports exist but only one has a comment
   441  					// - 2 imports exist and they both have (possibly
   442  					//   different) comments
   443  					imports = append(imports, imp)
   444  					seen[path] = true
   445  				}
   446  			}
   447  		}
   448  	} else {
   449  		for _, f := range pkg.Files {
   450  			imports = append(imports, f.Imports...)
   451  		}
   452  	}
   453  
   454  	// Collect comments from all package files.
   455  	var comments []*CommentGroup
   456  	if mode&FilterUnassociatedComments == 0 {
   457  		comments = make([]*CommentGroup, ncomments)
   458  		i := 0
   459  		for _, f := range pkg.Files {
   460  			i += copy(comments[i:], f.Comments)
   461  		}
   462  	}
   463  
   464  	// TODO(gri) need to compute unresolved identifiers!
   465  	return &File{doc, pos, NewIdent(pkg.Name), decls, pkg.Scope, imports, nil, comments}
   466  }