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