The Go Programming Language

Source file src/pkg/go/ast/filter.go

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

release.r60.3. Except as noted, this content is licensed under a Creative Commons Attribution 3.0 License.