diff --git a/src/cmd/godoc/godoc.go b/src/cmd/godoc/godoc.go index ac5beb5da4..ab95b519f3 100644 --- a/src/cmd/godoc/godoc.go +++ b/src/cmd/godoc/godoc.go @@ -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, "
");
-	template.HtmlEscape(&buf, nodeText(prog, printer.DocComments));
+	template.HtmlEscape(&buf, nodeText(prog));
 	fmt.Fprintln(&buf, "
"); 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); } diff --git a/src/cmd/gofmt/gofmt.go b/src/cmd/gofmt/gofmt.go index 73f9d8e23c..569e948b58 100644 --- a/src/cmd/gofmt/gofmt.go +++ b/src/cmd/gofmt/gofmt.go @@ -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(); diff --git a/src/pkg/go/ast/Makefile b/src/pkg/go/ast/Makefile index ead35dead1..cf37bba4c5 100644 --- a/src/pkg/go/ast/Makefile +++ b/src/pkg/go/ast/Makefile @@ -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 index 0000000000..cf65f4ae65 --- /dev/null +++ b/src/pkg/go/ast/filter.go @@ -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; +} diff --git a/src/pkg/go/doc/doc.go b/src/pkg/go/doc/doc.go index 03872fd148..bbae654a50 100644 --- a/src/pkg/go/doc/doc.go +++ b/src/pkg/go/doc/doc.go @@ -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); } diff --git a/src/pkg/go/printer/printer.go b/src/pkg/go/printer/printer.go index 19cb4d1082..4c5daefbf9 100644 --- a/src/pkg/go/printer/printer.go +++ b/src/pkg/go/printer/printer.go @@ -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); - } - - 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}); - } - } - - needsSemi = true; - lastWasAnon = isAnon; - lastComment = f.Comment; + 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 p.optSemis() { - p.print(token.SEMICOLON); + p.doc(f.Doc); + if !isAnon { + p.identList(f.Names); + p.print(tab); } - p.comment(lastComment); - p.print(-1, newline); + 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}); + } + } + + lastWasAnon = isAnon; + lastComment = f.Comment; } - p.print(rbrace, token.RBRACE); + if p.optSemis() { + p.print(token.SEMICOLON); + } + p.comment(lastComment); + + p.print(-1, newline, 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; - } + p.print(d.Lparen, token.LPAREN, +1, newline); + for i, s := range d.Specs { + if i > 0 { + p.print(token.SEMICOLON, newline); } - if p.optSemis() { - p.print(token.SEMICOLON); - } - 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); } }