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