From b880543ffaa1835babcd615f607db56ffedf877c Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Mon, 29 Mar 2010 15:26:07 -0700 Subject: [PATCH] go/ast: generalized ast filtering R=rsc CC=golang-dev https://golang.org/cl/788041 --- src/pkg/go/ast/ast.go | 2 + src/pkg/go/ast/filter.go | 153 ++++++++++++++++++++++++++++++++------- 2 files changed, 129 insertions(+), 26 deletions(-) diff --git a/src/pkg/go/ast/ast.go b/src/pkg/go/ast/ast.go index 83a63dba44f..34d20ed0591 100644 --- a/src/pkg/go/ast/ast.go +++ b/src/pkg/go/ast/ast.go @@ -358,6 +358,8 @@ func (x *ChanType) exprNode() {} // ---------------------------------------------------------------------------- // Convenience functions for Idents +var noPos token.Position + // NewIdent creates a new Ident without position and minimal object // information. Useful for ASTs generated by code other than the Go // parser. diff --git a/src/pkg/go/ast/filter.go b/src/pkg/go/ast/filter.go index 4e2060303be..d1d096ffd1f 100644 --- a/src/pkg/go/ast/filter.go +++ b/src/pkg/go/ast/filter.go @@ -6,8 +6,10 @@ package ast import "go/token" +// ---------------------------------------------------------------------------- +// Export filtering -func filterIdentList(list []*Ident) []*Ident { +func identListExports(list []*Ident) []*Ident { j := 0 for _, x := range list { if x.IsExported() { @@ -36,7 +38,7 @@ func isExportedType(typ Expr) bool { } -func filterFieldList(fields *FieldList, incomplete *bool) { +func fieldListExports(fields *FieldList, incomplete *bool) { if fields == nil { return } @@ -54,14 +56,14 @@ func filterFieldList(fields *FieldList, incomplete *bool) { exported = isExportedType(f.Type) } else { n := len(f.Names) - f.Names = filterIdentList(f.Names) + f.Names = identListExports(f.Names) if len(f.Names) < n { *incomplete = true } exported = len(f.Names) > 0 } if exported { - filterType(f.Type) + typeExports(f.Type) list[j] = f j++ } @@ -73,49 +75,47 @@ func filterFieldList(fields *FieldList, incomplete *bool) { } -func filterParamList(fields *FieldList) { +func paramListExports(fields *FieldList) { if fields == nil { return } for _, f := range fields.List { - filterType(f.Type) + typeExports(f.Type) } } -var noPos token.Position - -func filterType(typ Expr) { +func typeExports(typ Expr) { switch t := typ.(type) { case *ArrayType: - filterType(t.Elt) + typeExports(t.Elt) case *StructType: - filterFieldList(t.Fields, &t.Incomplete) + fieldListExports(t.Fields, &t.Incomplete) case *FuncType: - filterParamList(t.Params) - filterParamList(t.Results) + paramListExports(t.Params) + paramListExports(t.Results) case *InterfaceType: - filterFieldList(t.Methods, &t.Incomplete) + fieldListExports(t.Methods, &t.Incomplete) case *MapType: - filterType(t.Key) - filterType(t.Value) + typeExports(t.Key) + typeExports(t.Value) case *ChanType: - filterType(t.Value) + typeExports(t.Value) } } -func filterSpec(spec Spec) bool { +func specExports(spec Spec) bool { switch s := spec.(type) { case *ValueSpec: - s.Names = filterIdentList(s.Names) + s.Names = identListExports(s.Names) if len(s.Names) > 0 { - filterType(s.Type) + typeExports(s.Type) return true } case *TypeSpec: if s.Name.IsExported() { - filterType(s.Type) + typeExports(s.Type) return true } } @@ -123,10 +123,10 @@ func filterSpec(spec Spec) bool { } -func filterSpecList(list []Spec) []Spec { +func specListExports(list []Spec) []Spec { j := 0 for _, s := range list { - if filterSpec(s) { + if specExports(s) { list[j] = s j++ } @@ -135,10 +135,10 @@ func filterSpecList(list []Spec) []Spec { } -func filterDecl(decl Decl) bool { +func declExports(decl Decl) bool { switch d := decl.(type) { case *GenDecl: - d.Specs = filterSpecList(d.Specs) + d.Specs = specListExports(d.Specs) return len(d.Specs) > 0 case *FuncDecl: d.Body = nil // strip body @@ -161,7 +161,7 @@ func filterDecl(decl Decl) bool { func FileExports(src *File) bool { j := 0 for _, d := range src.Decls { - if filterDecl(d) { + if declExports(d) { src.Decls[j] = d j++ } @@ -189,6 +189,107 @@ func PackageExports(pkg *Package) bool { } +// ---------------------------------------------------------------------------- +// General filtering + +type Filter func(string) bool + +func filterIdentList(list []*Ident, f Filter) []*Ident { + j := 0 + for _, x := range list { + if f(x.Name()) { + list[j] = x + j++ + } + } + return list[0:j] +} + + +func filterSpec(spec Spec, f Filter) bool { + switch s := spec.(type) { + case *ValueSpec: + s.Names = filterIdentList(s.Names, f) + return len(s.Names) > 0 + case *TypeSpec: + return f(s.Name.Name()) + } + return false +} + + +func filterSpecList(list []Spec, f Filter) []Spec { + j := 0 + for _, s := range list { + if filterSpec(s, f) { + list[j] = s + j++ + } + } + return list[0:j] +} + + +func filterDecl(decl Decl, f Filter) bool { + switch d := decl.(type) { + case *GenDecl: + d.Specs = filterSpecList(d.Specs, f) + return len(d.Specs) > 0 + case *FuncDecl: + return f(d.Name.Name()) + } + return false +} + + +// FilterFile trims the AST for a Go file in place by removing all +// names from top-level declarations (but not from parameter lists +// or inside types) that don't pass through the filter f. If the +// declaration is empty afterwards, the declaration is removed from +// the AST. +// The File.comments list is not changed. +// +// FilterFile returns true if there are any top-level declarations +// left after filtering; it returns false otherwise. +// +func FilterFile(src *File, f Filter) bool { + j := 0 + for _, d := range src.Decls { + if filterDecl(d, f) { + src.Decls[j] = d + j++ + } + } + src.Decls = src.Decls[0:j] + return j > 0 +} + + +// FilterPackage trims the AST for a Go package in place by removing all +// names from top-level declarations (but not from parameter lists +// or inside types) that don't pass through the filter f. If the +// declaration is empty afterwards, the declaration is removed from +// the AST. +// The pkg.Files list is not changed, so that file names and top-level +// package comments don't get lost. +// +// FilterPackage returns true if there are any top-level declarations +// left after filtering; it returns false otherwise. +// +func FilterPackage(pkg *Package, f Filter) bool { + hasDecls := false + for _, src := range pkg.Files { + if FilterFile(src, f) { + hasDecls = true + } + } + return hasDecls +} + + +// ---------------------------------------------------------------------------- +// Merging of package files + // separator is an empty //-style comment that is interspersed between // different comment groups when they are concatenated into a single group //