]> Cypherpunks repositories - gostls13.git/commitdiff
- ast.FilterExports: strips all non-exported nodes from an AST
authorRobert Griesemer <gri@golang.org>
Mon, 6 Jul 2009 17:37:33 +0000 (10:37 -0700)
committerRobert Griesemer <gri@golang.org>
Mon, 6 Jul 2009 17:37:33 +0000 (10:37 -0700)
- use FilterExports instead of the various predicates in printer.go and doc.go
  which simplifies a lot of code and makes it easier to deal with complex cases

R=rsc
DELTA=445  (197 added, 190 deleted, 58 changed)
OCL=31110
CL=31196

src/cmd/godoc/godoc.go
src/cmd/gofmt/gofmt.go
src/pkg/go/ast/Makefile
src/pkg/go/ast/filter.go [new file with mode: 0644]
src/pkg/go/doc/doc.go
src/pkg/go/printer/printer.go

index ac5beb5da4d44538517d7d66b1061ca21567aece..ab95b519f35c8241369124f7d7c641474386153f 100644 (file)
@@ -198,9 +198,13 @@ func parse(path string, mode uint) (*ast.Program, *parseErrors) {
 // Templates
 
 // Return text for an AST node.
-func nodeText(node interface{}, mode uint) []byte {
+func nodeText(node interface{}) []byte {
        var buf bytes.Buffer;
        tw := makeTabwriter(&buf);
+       mode := uint(0);
+       if _, isProgram := node.(*ast.Program); isProgram {
+               mode = printer.DocComments;
+       }
        printer.Fprint(tw, node, mode);
        tw.Flush();
        return buf.Data();
@@ -219,9 +223,9 @@ func toText(x interface{}) []byte {
        case String:
                return strings.Bytes(v.String());
        case ast.Decl:
-               return nodeText(v, printer.ExportsOnly);
+               return nodeText(v);
        case ast.Expr:
-               return nodeText(v, printer.ExportsOnly);
+               return nodeText(v);
        }
        var buf bytes.Buffer;
        fmt.Fprint(&buf, x);
@@ -331,7 +335,7 @@ func serveGoSource(c *http.Conn, name string) {
 
        var buf bytes.Buffer;
        fmt.Fprintln(&buf, "<pre>");
-       template.HtmlEscape(&buf, nodeText(prog, printer.DocComments));
+       template.HtmlEscape(&buf, nodeText(prog));
        fmt.Fprintln(&buf, "</pre>");
 
        servePage(c, name + " - Go source", buf.Data());
@@ -491,6 +495,7 @@ func (p *pakDesc) Doc() (*doc.PackageDoc, *parseErrors) {
                        r.Init(prog.Name.Value, p.importpath);
                }
                i++;
+               ast.FilterExports(prog);  // we only care about exports
                r.AddProgram(prog);
        }
 
index 73f9d8e23c82d15c3565aced8674d8a3652ea54f..569e948b58095d670ed18aad4cd0ca2b26f1de08 100644 (file)
@@ -7,6 +7,7 @@ package main
 import (
        "flag";
        "fmt";
+       "go/ast";
        "go/parser";
        "go/printer";
        "io";
@@ -48,9 +49,6 @@ func parserMode() uint {
 
 func printerMode() uint {
        mode := uint(0);
-       if *exports {
-               mode |= printer.ExportsOnly;
-       }
        if *optcommas {
                mode |= printer.OptCommas;
        }
@@ -100,6 +98,9 @@ func main() {
        }
 
        if !*silent {
+               if *exports {
+                       ast.FilterExports(prog);  // ignore result
+               }
                w := makeTabwriter(os.Stdout);
                printer.Fprint(w, prog, printerMode());
                w.Flush();
index ead35dead149aa17ebe957aa0c391620660c55ac..cf37bba4c5a44012d83b1736a8da4a4c62ebd415 100644 (file)
@@ -2,8 +2,9 @@
 # Use of this source code is governed by a BSD-style
 # license that can be found in the LICENSE file.
 
+
 # DO NOT EDIT.  Automatically generated by gobuild.
-# gobuild -m >Makefile
+# gobuild -m ast.go filter.go >Makefile
 
 D=/go/
 
@@ -20,7 +21,7 @@ test: packages
 
 coverage: packages
        gotest
-       6cov -g `pwd` | grep -v '_test\.go:'
+       6cov -g $$(pwd) | grep -v '_test\.go:'
 
 %.$O: %.go
        $(GC) -I_obj $*.go
@@ -34,14 +35,21 @@ coverage: packages
 O1=\
        ast.$O\
 
+O2=\
+       filter.$O\
+
 
-phases: a1
+phases: a1 a2
 _obj$D/ast.a: phases
 
 a1: $(O1)
        $(AR) grc _obj$D/ast.a ast.$O
        rm -f $(O1)
 
+a2: $(O2)
+       $(AR) grc _obj$D/ast.a filter.$O
+       rm -f $(O2)
+
 
 newpkg: clean
        mkdir -p _obj$D
@@ -49,6 +57,7 @@ newpkg: clean
 
 $(O1): newpkg
 $(O2): a1
+$(O3): a2
 
 nuke: clean
        rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/ast.a
diff --git a/src/pkg/go/ast/filter.go b/src/pkg/go/ast/filter.go
new file mode 100644 (file)
index 0000000..cf65f4a
--- /dev/null
@@ -0,0 +1,134 @@
+// Copyright 2009 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.
+
+package ast
+
+import "go/ast"
+
+
+func filterIdentList(list []*Ident) []*Ident {
+       j := 0;
+       for _, x := range list {
+               if x.IsExported() {
+                       list[j] = x;
+                       j++;
+               }
+       }
+       return list[0 : j];
+}
+
+
+func filterType(typ Expr)
+
+func filterFieldList(list []*Field) []*Field {
+       j := 0;
+       for _, f := range list {
+               exported := false;
+               if len(f.Names) == 0 {
+                       // anonymous field
+                       // TODO(gri) check if the type is exported for anonymous field
+                       exported = true;
+               } else {
+                       f.Names = filterIdentList(f.Names);
+                       exported = len(f.Names) > 0;
+               }
+               if exported {
+                       filterType(f.Type);
+                       list[j] = f;
+                       j++;
+               }
+       }
+       return list[0 : j];
+}
+
+
+func filterType(typ Expr) {
+       switch t := typ.(type) {
+       case *ArrayType:
+               filterType(t.Elt);
+       case *StructType:
+               t.Fields = filterFieldList(t.Fields);
+       case *FuncType:
+               t.Params = filterFieldList(t.Params);
+               t.Results = filterFieldList(t.Results);
+       case *InterfaceType:
+               t.Methods = filterFieldList(t.Methods);
+       case *MapType:
+               filterType(t.Key);
+               filterType(t.Value);
+       case *ChanType:
+               filterType(t.Value);
+       }
+}
+
+
+func filterSpec(spec Spec) bool {
+       switch s := spec.(type) {
+       case *ValueSpec:
+               s.Names = filterIdentList(s.Names);
+               if len(s.Names) > 0 {
+                       filterType(s.Type);
+                       return true;
+               }
+       case *TypeSpec:
+               // TODO(gri) consider stripping forward declarations
+               //           of structs, interfaces, functions, and methods
+               if s.Name.IsExported() {
+                       filterType(s.Type);
+                       return true;
+               }
+       }
+       return false;
+}
+
+
+func filterSpecList(list []Spec) []Spec {
+       j := 0;
+       for _, s := range list {
+               if filterSpec(s) {
+                       list[j] = s;
+                       j++;
+               }
+       }
+       return list[0 : j];
+}
+
+
+func filterDecl(decl Decl) bool {
+       switch d := decl.(type) {
+       case *GenDecl:
+               d.Specs = filterSpecList(d.Specs);
+               return len(d.Specs) > 0;
+       case *FuncDecl:
+               // TODO consider removing function declaration altogether if
+               //      forward declaration (i.e., if d.Body == nil) because
+               //      in that case the actual declaration will come later.
+               d.Body = nil;  // strip body
+               return d.Name.IsExported();
+       }
+       return false;
+}
+
+
+// FilterExports trims an AST in place such that only exported nodes remain:
+// all top-level identififiers 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.
+//
+// FilterExports returns true if there is an exported declaration; it returns
+// false otherwise.
+//
+func FilterExports(prog *Program) bool {
+       j := 0;
+       for _, d := range prog.Decls {
+               if filterDecl(d) {
+                       prog.Decls[j] = d;
+                       j++;
+               }
+       }
+       prog.Decls = prog.Decls[0 : j];
+       prog.Comments = nil;  // remove unassociated comments
+       return j > 0;
+}
index 03872fd1483a4449b0e9687028d07a479d9574be..bbae654a50412a0c32073e07738d37cdfb73eec8 100644 (file)
@@ -17,28 +17,6 @@ import (
 )
 
 
-// ----------------------------------------------------------------------------
-// Elementary support
-
-func hasExportedNames(names []*ast.Ident) bool {
-       for i, name := range names {
-               if name.IsExported() {
-                       return true;
-               }
-       }
-       return false;
-}
-
-
-func hasExportedSpecs(specs []ast.Spec) bool {
-       for i, s := range specs {
-               // only called for []astSpec lists of *ast.ValueSpec
-               return hasExportedNames(s.(*ast.ValueSpec).Names);
-       }
-       return false;
-}
-
-
 // ----------------------------------------------------------------------------
 
 type typeDoc struct {
@@ -149,33 +127,25 @@ func (doc *DocReader) addDecl(decl ast.Decl) {
                                // ignore
                        case token.CONST:
                                // constants are always handled as a group
-                               if hasExportedSpecs(d.Specs) {
-                                       doc.consts.Push(d);
-                               }
+                               doc.consts.Push(d);
                        case token.TYPE:
                                // types are handled individually
+                               var noPos token.Position;
                                for i, spec := range d.Specs {
+                                       // make a (fake) GenDecl node for this TypeSpec
+                                       // (we need to do this here - as opposed to just
+                                       // for printing - so we don't lose the GenDecl
+                                       // documentation)
                                        s := spec.(*ast.TypeSpec);
-                                       if s.Name.IsExported() {
-                                               // make a (fake) GenDecl node for this TypeSpec
-                                               // (we need to do this here - as opposed to just
-                                               // for printing - so we don't loose the GenDecl
-                                               // documentation)
-                                               var noPos token.Position;
-                                               doc.addType(&ast.GenDecl{d.Doc, d.Pos(), token.TYPE, noPos, []ast.Spec{s}, noPos});
-                                       }
+                                       doc.addType(&ast.GenDecl{d.Doc, d.Pos(), token.TYPE, noPos, []ast.Spec{s}, noPos});
                                }
                        case token.VAR:
                                // variables are always handled as a group
-                               if hasExportedSpecs(d.Specs) {
-                                       doc.vars.Push(d);
-                               }
+                               doc.vars.Push(d);
                        }
                }
        case *ast.FuncDecl:
-               if d.Name.IsExported() {
-                       doc.addFunc(d);
-               }
+               doc.addFunc(d);
        }
 }
 
@@ -194,7 +164,7 @@ func (doc *DocReader) AddProgram(prog *ast.Program) {
                doc.doc = prog.Doc
        }
 
-       // add all exported declarations
+       // add all declarations
        for i, decl := range prog.Decls {
                doc.addDecl(decl);
        }
index 19cb4d108250f62d1b370c64a8e8caadf80ac440..4c5daefbf977f8b0da6620d372f5784768c3f6bf 100644 (file)
@@ -20,8 +20,7 @@ import (
 // to Fprint via the mode parameter.
 //
 const (
-       ExportsOnly uint = 1 << iota;  // print exported code only
-       DocComments;  // print documentation comments
+       DocComments uint = 1 << iota;  // print documentation comments
        OptCommas;  // print optional commas
        OptSemis;  // print optional semicolons
 )
@@ -181,97 +180,13 @@ func (p *printer) print(args ...) {
 
 
 // ----------------------------------------------------------------------------
-// Predicates
+// Printing of common AST nodes.
 
 func (p *printer) optSemis() bool {
        return p.mode & OptSemis != 0;
 }
 
 
-func (p *printer) exportsOnly() bool {
-       return p.mode & ExportsOnly != 0;
-}
-
-
-// The isVisibleX predicates return true if X should produce any output
-// given the printing mode and depending on whether X contains exported
-// names.
-
-func (p *printer) isVisibleIdent(x *ast.Ident) bool {
-       // identifiers in local scopes (p.level > 0) are always visible
-       // if the surrounding code is printed in the first place
-       return !p.exportsOnly() || x.IsExported() || p.level > 0;
-}
-
-
-func (p *printer) isVisibleIdentList(list []*ast.Ident) bool {
-       for _, x := range list {
-               if p.isVisibleIdent(x) {
-                       return true;
-               }
-       }
-       return false;
-}
-
-
-func (p *printer) isVisibleFieldList(list []*ast.Field) bool {
-       for _, f := range list {
-               if len(f.Names) == 0 {
-                       // anonymous field
-                       // TODO should only return true if the anonymous field
-                       //      type is visible (for now be conservative and
-                       //      print it so that the generated code is valid)
-                       return true;
-               }
-               if p.isVisibleIdentList(f.Names) {
-                       return true;
-               }
-       }
-       return false;
-}
-
-
-func (p *printer) isVisibleSpec(spec ast.Spec) bool {
-       switch s := spec.(type) {
-       case *ast.ImportSpec:
-               return !p.exportsOnly();
-       case *ast.ValueSpec:
-               return p.isVisibleIdentList(s.Names);
-       case *ast.TypeSpec:
-               return p.isVisibleIdent(s.Name);
-       }
-       panic("unreachable");
-       return false;
-}
-
-
-func (p *printer) isVisibleSpecList(list []ast.Spec) bool {
-       for _, s := range list {
-               if p.isVisibleSpec(s) {
-                       return true;
-               }
-       }
-       return false;
-}
-
-
-func (p *printer) isVisibleDecl(decl ast.Decl) bool {
-       switch d := decl.(type) {
-       case *ast.BadDecl:
-               return false;
-       case *ast.GenDecl:
-               return p.isVisibleSpecList(d.Specs);
-       case *ast.FuncDecl:
-               return p.isVisibleIdent(d.Name);
-       }
-       panic("unreachable");
-       return false;
-}
-
-
-// ----------------------------------------------------------------------------
-// Printing of common AST nodes.
-
 func (p *printer) comment(c *ast.Comment) {
        if c != nil {
                text := c.Text;
@@ -297,15 +212,11 @@ func (p *printer) doc(d ast.Comments) {
 func (p *printer) expr(x ast.Expr) bool
 
 func (p *printer) identList(list []*ast.Ident) {
-       needsComma := false;
        for i, x := range list {
-               if p.isVisibleIdent(x) {
-                       if needsComma {
-                               p.print(token.COMMA, blank);
-                       }
-                       p.expr(x);
-                       needsComma = true;
+               if i > 0 {
+                       p.print(token.COMMA, blank);
                }
+               p.expr(x);
        }
 }
 
@@ -362,83 +273,73 @@ func (p *printer) signature(params, result []*ast.Field) {
 
 // Returns true if the field list ends in a closing brace.
 func (p *printer) fieldList(lbrace token.Position, list []*ast.Field, rbrace token.Position, isInterface bool) bool {
-       hasBody := p.isVisibleFieldList(list);
-       if !lbrace.IsValid() || p.exportsOnly() && !hasBody {
-               // forward declaration without {}'s or no visible exported fields
-               // (in all other cases, the {}'s must be printed even if there are
-               // no fields, otherwise the type is incorrect)
+       if !lbrace.IsValid() {
+               // forward declaration without {}'s
                return false;  // no {}'s
        }
 
-       p.print(blank, lbrace, token.LBRACE);
+       if len(list) == 0 {
+               p.print(blank, lbrace, token.LBRACE, rbrace, token.RBRACE);
+               return true;  // empty list with {}'s
+       }
 
-       if hasBody {
-               p.print(+1, newline);
+       p.print(blank, lbrace, token.LBRACE, +1, newline);
 
-               var needsSemi bool;
-               var lastWasAnon bool;  // true if the previous line was an anonymous field
-               var lastComment *ast.Comment;  // the comment from the previous line
-               for _, f := range list {
-                       hasNames := p.isVisibleIdentList(f.Names);
-                       isAnon := len(f.Names) == 0;
-
-                       if hasNames || isAnon {
-                               // at least one visible identifier or anonymous field
-                               // TODO this is conservative - see isVisibleFieldList
-                               if needsSemi {
-                                       p.print(token.SEMICOLON);
-                                       p.comment(lastComment);
-                                       if lastWasAnon == isAnon {
-                                               // previous and current line have same structure;
-                                               // continue with existing columns
-                                               p.print(newline);
-                                       } else {
-                                               // previous and current line have different structure;
-                                               // flush tabwriter and start new columns (the "type
-                                               // column" on a line with named fields may line up
-                                               // with the "trailing comment column" on a line with
-                                               // an anonymous field, leading to bad alignment)
-                                               p.print(formfeed);
-                                       }
-                               }
-
-                               p.doc(f.Doc);
-                               if hasNames {
-                                       p.identList(f.Names);
-                                       p.print(tab);
-                               }
+       var lastWasAnon bool;  // true if the previous line was an anonymous field
+       var lastComment *ast.Comment;  // the comment from the previous line
+       for i, f := range list {
+               // at least one visible identifier or anonymous field
+               isAnon := len(f.Names) == 0;
+               if i > 0 {
+                       p.print(token.SEMICOLON);
+                       p.comment(lastComment);
+                       if lastWasAnon == isAnon {
+                               // previous and current line have same structure;
+                               // continue with existing columns
+                               p.print(newline);
+                       } else {
+                               // previous and current line have different structure;
+                               // flush tabwriter and start new columns (the "type
+                               // column" on a line with named fields may line up
+                               // with the "trailing comment column" on a line with
+                               // an anonymous field, leading to bad alignment)
+                               p.print(formfeed);
+                       }
+               }
 
-                               if isInterface {
-                                       if ftyp, isFtyp := f.Type.(*ast.FuncType); isFtyp {
-                                               // methods
-                                               p.signature(ftyp.Params, ftyp.Results);
-                                       } else {
-                                               // embedded interface
-                                               p.expr(f.Type);
-                                       }
-                               } else {
-                                       p.expr(f.Type);
-                                       if f.Tag != nil && !p.exportsOnly() {
-                                               p.print(tab);
-                                               p.expr(&ast.StringList{f.Tag});
-                                       }
-                               }
+               p.doc(f.Doc);
+               if !isAnon {
+                       p.identList(f.Names);
+                       p.print(tab);
+               }
 
-                               needsSemi = true;
-                               lastWasAnon = isAnon;
-                               lastComment = f.Comment;
+               if isInterface {
+                       if ftyp, isFtyp := f.Type.(*ast.FuncType); isFtyp {
+                               // methods
+                               p.signature(ftyp.Params, ftyp.Results);
+                       } else {
+                               // embedded interface
+                               p.expr(f.Type);
+                       }
+               } else {
+                       p.expr(f.Type);
+                       if f.Tag != nil {
+                               p.print(tab);
+                               p.expr(&ast.StringList{f.Tag});
                        }
                }
 
-               if p.optSemis() {
-                       p.print(token.SEMICOLON);
-               }
+               lastWasAnon = isAnon;
+               lastComment = f.Comment;
+       }
 
-               p.comment(lastComment);
-               p.print(-1, newline);
+       if p.optSemis() {
+               p.print(token.SEMICOLON);
        }
+       p.comment(lastComment);
+
+       p.print(-1, newline, rbrace, token.RBRACE);
 
-       p.print(rbrace, token.RBRACE);
        return true;  // field list with {}'s
 }
 
@@ -905,25 +806,17 @@ func (p *printer) decl(decl ast.Decl) (optSemi bool) {
 
                if d.Lparen.IsValid() {
                        // group of parenthesized declarations
-                       p.print(d.Lparen, token.LPAREN);
-                       if p.isVisibleSpecList(d.Specs) {
-                               p.print(+1, newline);
-                               semi := false;
-                               for _, s := range d.Specs {
-                                       if p.isVisibleSpec(s) {
-                                               if semi {
-                                                       p.print(token.SEMICOLON, newline);
-                                               }
-                                               p.spec(s);
-                                               semi = true;
-                                       }
-                               }
-                               if p.optSemis() {
-                                       p.print(token.SEMICOLON);
+                       p.print(d.Lparen, token.LPAREN, +1, newline);
+                       for i, s := range d.Specs {
+                               if i > 0 {
+                                       p.print(token.SEMICOLON, newline);
                                }
-                               p.print(-1, newline);
+                               p.spec(s);
                        }
-                       p.print(d.Rparen, token.RPAREN);
+                       if p.optSemis() {
+                               p.print(token.SEMICOLON);
+                       }
+                       p.print(-1, newline, d.Rparen, token.RPAREN);
                        optSemi = true;
 
                } else {
@@ -946,7 +839,7 @@ func (p *printer) decl(decl ast.Decl) (optSemi bool) {
                }
                p.expr(d.Name);
                p.signature(d.Type.Params, d.Type.Results);
-               if !p.exportsOnly() && d.Body != nil {
+               if d.Body != nil {
                        p.print(blank);
                        p.level++;  // adjust nesting level for function body
                        p.stmt(d.Body);
@@ -965,23 +858,19 @@ func (p *printer) decl(decl ast.Decl) (optSemi bool) {
 // Programs
 
 func (p *printer) program(prog *ast.Program) {
-       // set unassociated comments if all code is printed
-       if !p.exportsOnly() {
-               // TODO enable this once comments are properly interspersed
-               //p.setComments(prog.Comments);
-       }
+       // set unassociated comments
+       // TODO enable this once comments are properly interspersed
+       // p.setComments(prog.Comments);
 
        p.doc(prog.Doc);
        p.print(prog.Pos(), token.PACKAGE, blank);
        p.expr(prog.Name);
 
        for _, d := range prog.Decls {
-               if p.isVisibleDecl(d) {
-                       p.print(newline, newline);
-                       p.decl(d);
-                       if p.optSemis() {
-                               p.print(token.SEMICOLON);
-                       }
+               p.print(newline, newline);
+               p.decl(d);
+               if p.optSemis() {
+                       p.print(token.SEMICOLON);
                }
        }