github.com/goplus/gop@v1.2.6/ast/filter.go (about)

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