--- /dev/null
+// 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;
+}
)
-// ----------------------------------------------------------------------------
-// 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 {
// 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);
}
}
doc.doc = prog.Doc
}
- // add all exported declarations
+ // add all declarations
for i, decl := range prog.Decls {
doc.addDecl(decl);
}
// 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
)
// ----------------------------------------------------------------------------
-// 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;
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);
}
}
// 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
}
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 {
}
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);
// 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);
}
}