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