github.com/dannin/go@v0.0.0-20161031215817-d35dfd405eaa/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 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 *AliasSpec: 160 if f(s.Name.Name) { 161 return true 162 } 163 case *ValueSpec: 164 s.Names = filterIdentList(s.Names, f) 165 if len(s.Names) > 0 { 166 if export { 167 filterType(s.Type, f, export) 168 } 169 return true 170 } 171 case *TypeSpec: 172 if f(s.Name.Name) { 173 if export { 174 filterType(s.Type, f, export) 175 } 176 return true 177 } 178 if !export { 179 // For general filtering (not just exports), 180 // filter type even if name is not filtered 181 // out. 182 // If the type contains filtered elements, 183 // keep the declaration. 184 return filterType(s.Type, f, export) 185 } 186 } 187 return false 188 } 189 190 func filterSpecList(list []Spec, f Filter, export bool) []Spec { 191 j := 0 192 for _, s := range list { 193 if filterSpec(s, f, export) { 194 list[j] = s 195 j++ 196 } 197 } 198 return list[0:j] 199 } 200 201 // FilterDecl trims the AST for a Go declaration in place by removing 202 // all names (including struct field and interface method names, but 203 // not from parameter lists) that don't pass through the filter f. 204 // 205 // FilterDecl reports whether there are any declared names left after 206 // filtering. 207 // 208 func FilterDecl(decl Decl, f Filter) bool { 209 return filterDecl(decl, f, false) 210 } 211 212 func filterDecl(decl Decl, f Filter, export bool) bool { 213 switch d := decl.(type) { 214 case *GenDecl: 215 d.Specs = filterSpecList(d.Specs, f, export) 216 return len(d.Specs) > 0 217 case *FuncDecl: 218 return f(d.Name.Name) 219 } 220 return false 221 } 222 223 // FilterFile trims the AST for a Go file in place by removing all 224 // names from top-level declarations (including struct field and 225 // interface method names, but not from parameter lists) that don't 226 // pass through the filter f. If the declaration is empty afterwards, 227 // the declaration is removed from the AST. Import declarations are 228 // always removed. The File.Comments list is not changed. 229 // 230 // FilterFile reports whether there are any top-level declarations 231 // left after filtering. 232 // 233 func FilterFile(src *File, f Filter) bool { 234 return filterFile(src, f, false) 235 } 236 237 func filterFile(src *File, f Filter, export bool) bool { 238 j := 0 239 for _, d := range src.Decls { 240 if filterDecl(d, f, export) { 241 src.Decls[j] = d 242 j++ 243 } 244 } 245 src.Decls = src.Decls[0:j] 246 return j > 0 247 } 248 249 // FilterPackage trims the AST for a Go package in place by removing 250 // all names from top-level declarations (including struct field and 251 // interface method names, but not from parameter lists) that don't 252 // pass through the filter f. If the declaration is empty afterwards, 253 // the declaration is removed from the AST. The pkg.Files list is not 254 // changed, so that file names and top-level package comments don't get 255 // lost. 256 // 257 // FilterPackage reports whether there are any top-level declarations 258 // left after filtering. 259 // 260 func FilterPackage(pkg *Package, f Filter) bool { 261 return filterPackage(pkg, f, false) 262 } 263 264 func filterPackage(pkg *Package, f Filter, export bool) bool { 265 hasDecls := false 266 for _, src := range pkg.Files { 267 if filterFile(src, f, export) { 268 hasDecls = true 269 } 270 } 271 return hasDecls 272 } 273 274 // ---------------------------------------------------------------------------- 275 // Merging of package files 276 277 // The MergeMode flags control the behavior of MergePackageFiles. 278 type MergeMode uint 279 280 const ( 281 // If set, duplicate function declarations are excluded. 282 FilterFuncDuplicates MergeMode = 1 << iota 283 // If set, comments that are not associated with a specific 284 // AST node (as Doc or Comment) are excluded. 285 FilterUnassociatedComments 286 // If set, duplicate import declarations are excluded. 287 FilterImportDuplicates 288 ) 289 290 // nameOf returns the function (foo) or method name (foo.bar) for 291 // the given function declaration. If the AST is incorrect for the 292 // receiver, it assumes a function instead. 293 // 294 func nameOf(f *FuncDecl) string { 295 if r := f.Recv; r != nil && len(r.List) == 1 { 296 // looks like a correct receiver declaration 297 t := r.List[0].Type 298 // dereference pointer receiver types 299 if p, _ := t.(*StarExpr); p != nil { 300 t = p.X 301 } 302 // the receiver type must be a type name 303 if p, _ := t.(*Ident); p != nil { 304 return p.Name + "." + f.Name.Name 305 } 306 // otherwise assume a function instead 307 } 308 return f.Name.Name 309 } 310 311 // separator is an empty //-style comment that is interspersed between 312 // different comment groups when they are concatenated into a single group 313 // 314 var separator = &Comment{token.NoPos, "//"} 315 316 // MergePackageFiles creates a file AST by merging the ASTs of the 317 // files belonging to a package. The mode flags control merging behavior. 318 // 319 func MergePackageFiles(pkg *Package, mode MergeMode) *File { 320 // Count the number of package docs, comments and declarations across 321 // all package files. Also, compute sorted list of filenames, so that 322 // subsequent iterations can always iterate in the same order. 323 ndocs := 0 324 ncomments := 0 325 ndecls := 0 326 filenames := make([]string, len(pkg.Files)) 327 i := 0 328 for filename, f := range pkg.Files { 329 filenames[i] = filename 330 i++ 331 if f.Doc != nil { 332 ndocs += len(f.Doc.List) + 1 // +1 for separator 333 } 334 ncomments += len(f.Comments) 335 ndecls += len(f.Decls) 336 } 337 sort.Strings(filenames) 338 339 // Collect package comments from all package files into a single 340 // CommentGroup - the collected package documentation. In general 341 // there should be only one file with a package comment; but it's 342 // better to collect extra comments than drop them on the floor. 343 var doc *CommentGroup 344 var pos token.Pos 345 if ndocs > 0 { 346 list := make([]*Comment, ndocs-1) // -1: no separator before first group 347 i := 0 348 for _, filename := range filenames { 349 f := pkg.Files[filename] 350 if f.Doc != nil { 351 if i > 0 { 352 // not the first group - add separator 353 list[i] = separator 354 i++ 355 } 356 for _, c := range f.Doc.List { 357 list[i] = c 358 i++ 359 } 360 if f.Package > pos { 361 // Keep the maximum package clause position as 362 // position for the package clause of the merged 363 // files. 364 pos = f.Package 365 } 366 } 367 } 368 doc = &CommentGroup{list} 369 } 370 371 // Collect declarations from all package files. 372 var decls []Decl 373 if ndecls > 0 { 374 decls = make([]Decl, ndecls) 375 funcs := make(map[string]int) // map of func name -> decls index 376 i := 0 // current index 377 n := 0 // number of filtered entries 378 for _, filename := range filenames { 379 f := pkg.Files[filename] 380 for _, d := range f.Decls { 381 if mode&FilterFuncDuplicates != 0 { 382 // A language entity may be declared multiple 383 // times in different package files; only at 384 // build time declarations must be unique. 385 // For now, exclude multiple declarations of 386 // functions - keep the one with documentation. 387 // 388 // TODO(gri): Expand this filtering to other 389 // entities (const, type, vars) if 390 // multiple declarations are common. 391 if f, isFun := d.(*FuncDecl); isFun { 392 name := nameOf(f) 393 if j, exists := funcs[name]; exists { 394 // function declared already 395 if decls[j] != nil && decls[j].(*FuncDecl).Doc == nil { 396 // existing declaration has no documentation; 397 // ignore the existing declaration 398 decls[j] = nil 399 } else { 400 // ignore the new declaration 401 d = nil 402 } 403 n++ // filtered an entry 404 } else { 405 funcs[name] = i 406 } 407 } 408 } 409 decls[i] = d 410 i++ 411 } 412 } 413 414 // Eliminate nil entries from the decls list if entries were 415 // filtered. We do this using a 2nd pass in order to not disturb 416 // the original declaration order in the source (otherwise, this 417 // would also invalidate the monotonically increasing position 418 // info within a single file). 419 if n > 0 { 420 i = 0 421 for _, d := range decls { 422 if d != nil { 423 decls[i] = d 424 i++ 425 } 426 } 427 decls = decls[0:i] 428 } 429 } 430 431 // Collect import specs from all package files. 432 var imports []*ImportSpec 433 if mode&FilterImportDuplicates != 0 { 434 seen := make(map[string]bool) 435 for _, filename := range filenames { 436 f := pkg.Files[filename] 437 for _, imp := range f.Imports { 438 if path := imp.Path.Value; !seen[path] { 439 // TODO: consider handling cases where: 440 // - 2 imports exist with the same import path but 441 // have different local names (one should probably 442 // keep both of them) 443 // - 2 imports exist but only one has a comment 444 // - 2 imports exist and they both have (possibly 445 // different) comments 446 imports = append(imports, imp) 447 seen[path] = true 448 } 449 } 450 } 451 } else { 452 for _, f := range pkg.Files { 453 imports = append(imports, f.Imports...) 454 } 455 } 456 457 // Collect comments from all package files. 458 var comments []*CommentGroup 459 if mode&FilterUnassociatedComments == 0 { 460 comments = make([]*CommentGroup, ncomments) 461 i := 0 462 for _, f := range pkg.Files { 463 i += copy(comments[i:], f.Comments) 464 } 465 } 466 467 // TODO(gri) need to compute unresolved identifiers! 468 return &File{doc, pos, NewIdent(pkg.Name), decls, pkg.Scope, imports, nil, comments} 469 }