1
0
mirror of https://github.com/golang/go synced 2024-11-21 21:24:45 -07:00

go/ast cleanup: base File/PackageExports on FilterFile/FilterPackage code

R=r, rsc
CC=golang-dev
https://golang.org/cl/4927046
This commit is contained in:
Robert Griesemer 2011-08-23 16:03:42 -07:00
parent cde06f541f
commit e656a184cb
2 changed files with 14 additions and 267 deletions

View File

@ -9,164 +9,34 @@ import "go/token"
// ----------------------------------------------------------------------------
// Export filtering
func identListExports(list []*Ident) []*Ident {
j := 0
for _, x := range list {
if x.IsExported() {
list[j] = x
j++
}
}
return list[0:j]
// exportFilter is a special filter function to extract exported nodes.
func exportFilter(name string) bool {
return IsExported(name)
}
func fieldListExports(fields *FieldList) (removedFields bool) {
if fields == nil {
return
}
list := fields.List
j := 0
for _, f := range list {
exported := false
if len(f.Names) == 0 {
// anonymous field
// (Note that a non-exported anonymous field
// may still refer to a type with exported
// fields, so this is not absolutely correct.
// However, this cannot be done w/o complete
// type information.)
name := fieldName(f.Type)
exported = name != nil && name.IsExported()
} else {
n := len(f.Names)
f.Names = identListExports(f.Names)
if len(f.Names) < n {
removedFields = true
}
exported = len(f.Names) > 0
}
if exported {
typeExports(f.Type)
list[j] = f
j++
}
}
if j < len(list) {
removedFields = true
}
fields.List = list[0:j]
return
}
func paramListExports(fields *FieldList) {
if fields == nil {
return
}
for _, f := range fields.List {
typeExports(f.Type)
}
}
func typeExports(typ Expr) {
switch t := typ.(type) {
case *ArrayType:
typeExports(t.Elt)
case *StructType:
if fieldListExports(t.Fields) {
t.Incomplete = true
}
case *FuncType:
paramListExports(t.Params)
paramListExports(t.Results)
case *InterfaceType:
if fieldListExports(t.Methods) {
t.Incomplete = true
}
case *MapType:
typeExports(t.Key)
typeExports(t.Value)
case *ChanType:
typeExports(t.Value)
}
}
func specExports(spec Spec) bool {
switch s := spec.(type) {
case *ValueSpec:
s.Names = identListExports(s.Names)
if len(s.Names) > 0 {
typeExports(s.Type)
return true
}
case *TypeSpec:
if s.Name.IsExported() {
typeExports(s.Type)
return true
}
}
return false
}
func specListExports(list []Spec) []Spec {
j := 0
for _, s := range list {
if specExports(s) {
list[j] = s
j++
}
}
return list[0:j]
}
func declExports(decl Decl) bool {
switch d := decl.(type) {
case *GenDecl:
d.Specs = specListExports(d.Specs)
return len(d.Specs) > 0
case *FuncDecl:
d.Body = nil // strip body
return d.Name.IsExported()
}
return false
}
// FileExports trims the AST for a Go source file in place such that only
// exported nodes remain: all top-level identifiers which are not exported
// FileExports trims the AST for a Go source file in place such that
// only exported nodes remain: all top-level identifiers which are not exported
// and their associated information (such as type, initial value, or function
// body) are removed. Non-exported fields and methods of exported types are
// stripped, and the function bodies of exported functions are set to nil.
// The File.Comments list is not changed.
//
// FileExports returns true if there is an exported declaration; it returns
// false otherwise.
// FileExports returns true if there are exported declarationa;
// it returns false otherwise.
//
func FileExports(src *File) bool {
j := 0
for _, d := range src.Decls {
if declExports(d) {
src.Decls[j] = d
j++
}
}
src.Decls = src.Decls[0:j]
return j > 0
return FilterFile(src, exportFilter)
}
// PackageExports trims the AST for a Go package in place such that only
// exported nodes remain. The pkg.Files list is not changed, so that file
// names and top-level package comments don't get lost.
// PackageExports trims the AST for a Go package in place such that
// only exported nodes remain. The pkg.Files list is not changed, so that
// file names and top-level package comments don't get lost.
//
// PackageExports returns true if there is an exported declaration; it
// returns false otherwise.
// PackageExports returns true if there are exported declarations;
// it returns false otherwise.
//
func PackageExports(pkg *Package) bool {
hasExports := false
for _, f := range pkg.Files {
if FileExports(f) {
hasExports = true
}
}
return hasExports
return FilterPackage(pkg, exportFilter)
}
// ----------------------------------------------------------------------------
@ -387,38 +257,6 @@ func FilterPackage(pkg *Package, f Filter) bool {
return hasDecls
}
// exportFilter is a special filter function to extract exported nodes.
func exportFilter(name string) bool {
return IsExported(name)
}
// TODO(gri): Remove the FileExports and PackageExports (above).
// FilterFileExports trims the AST for a Go source file in place such that
// only exported nodes remain: all top-level identifiers which are not exported
// and their associated information (such as type, initial value, or function
// body) are removed. Non-exported fields and methods of exported types are
// stripped, and the function bodies of exported functions are set to nil.
// The File.Comments list is not changed.
//
// FilterFileExports returns true if there are exported declarationa;
// it returns false otherwise.
//
func FilterFileExports(src *File) bool {
return FilterFile(src, exportFilter)
}
// FilterPackageExports trims the AST for a Go package in place such that
// only exported nodes remain. The pkg.Files list is not changed, so that
// file names and top-level package comments don't get lost.
//
// FilterPackageExports returns true if there are exported declarations;
// it returns false otherwise.
//
func FilterPackageExports(pkg *Package) bool {
return FilterPackage(pkg, exportFilter)
}
// ----------------------------------------------------------------------------
// Merging of package files

View File

@ -1,91 +0,0 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// A test that ensures that ast.FileExports(file) and
// ast.FilterFile(file, ast.IsExported) produce the
// same trimmed AST given the same input file for all
// files under runtime.GOROOT().
//
// The test is here because it requires parsing, and the
// parser imports AST already (avoid import cycle).
package parser
import (
"bytes"
"go/ast"
"go/printer"
"go/token"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
)
// For a short test run, limit the number of files to a few.
// Set to a large value to test all files under GOROOT.
const maxFiles = 10000
type visitor struct {
t *testing.T
n int
}
func (v *visitor) VisitDir(path string, f *os.FileInfo) bool {
return true
}
func str(f *ast.File, fset *token.FileSet) string {
var buf bytes.Buffer
printer.Fprint(&buf, fset, f)
return buf.String()
}
func (v *visitor) VisitFile(path string, f *os.FileInfo) {
// exclude files that clearly don't make it
if !f.IsRegular() || len(f.Name) > 0 && f.Name[0] == '.' || !strings.HasSuffix(f.Name, ".go") {
return
}
// should we stop early for quick test runs?
if v.n <= 0 {
return
}
v.n--
fset := token.NewFileSet()
// get two ASTs f1, f2 of identical structure;
// parsing twice is easiest
f1, err := ParseFile(fset, path, nil, ParseComments)
if err != nil {
v.t.Logf("parse error (1): %s", err)
return
}
f2, err := ParseFile(fset, path, nil, ParseComments)
if err != nil {
v.t.Logf("parse error (2): %s", err)
return
}
b1 := ast.FileExports(f1)
b2 := ast.FilterFileExports(f2)
if b1 != b2 {
v.t.Errorf("filtering failed (a): %s", path)
return
}
s1 := str(f1, fset)
s2 := str(f2, fset)
if s1 != s2 {
v.t.Errorf("filtering failed (b): %s", path)
return
}
}
func TestFilter(t *testing.T) {
filepath.Walk(runtime.GOROOT(), &visitor{t, maxFiles}, nil)
}