github.com/q45/go@v0.0.0-20151101211701-a4fb8c13db3f/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 *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 }