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