mirror of
https://github.com/golang/go
synced 2024-11-20 05:04:43 -07:00
- ast.FilterExports: strips all non-exported nodes from an AST
- 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
This commit is contained in:
parent
cd4aab62e3
commit
deb954772d
@ -198,9 +198,13 @@ func parse(path string, mode uint) (*ast.Program, *parseErrors) {
|
|||||||
// Templates
|
// Templates
|
||||||
|
|
||||||
// Return text for an AST node.
|
// Return text for an AST node.
|
||||||
func nodeText(node interface{}, mode uint) []byte {
|
func nodeText(node interface{}) []byte {
|
||||||
var buf bytes.Buffer;
|
var buf bytes.Buffer;
|
||||||
tw := makeTabwriter(&buf);
|
tw := makeTabwriter(&buf);
|
||||||
|
mode := uint(0);
|
||||||
|
if _, isProgram := node.(*ast.Program); isProgram {
|
||||||
|
mode = printer.DocComments;
|
||||||
|
}
|
||||||
printer.Fprint(tw, node, mode);
|
printer.Fprint(tw, node, mode);
|
||||||
tw.Flush();
|
tw.Flush();
|
||||||
return buf.Data();
|
return buf.Data();
|
||||||
@ -219,9 +223,9 @@ func toText(x interface{}) []byte {
|
|||||||
case String:
|
case String:
|
||||||
return strings.Bytes(v.String());
|
return strings.Bytes(v.String());
|
||||||
case ast.Decl:
|
case ast.Decl:
|
||||||
return nodeText(v, printer.ExportsOnly);
|
return nodeText(v);
|
||||||
case ast.Expr:
|
case ast.Expr:
|
||||||
return nodeText(v, printer.ExportsOnly);
|
return nodeText(v);
|
||||||
}
|
}
|
||||||
var buf bytes.Buffer;
|
var buf bytes.Buffer;
|
||||||
fmt.Fprint(&buf, x);
|
fmt.Fprint(&buf, x);
|
||||||
@ -331,7 +335,7 @@ func serveGoSource(c *http.Conn, name string) {
|
|||||||
|
|
||||||
var buf bytes.Buffer;
|
var buf bytes.Buffer;
|
||||||
fmt.Fprintln(&buf, "<pre>");
|
fmt.Fprintln(&buf, "<pre>");
|
||||||
template.HtmlEscape(&buf, nodeText(prog, printer.DocComments));
|
template.HtmlEscape(&buf, nodeText(prog));
|
||||||
fmt.Fprintln(&buf, "</pre>");
|
fmt.Fprintln(&buf, "</pre>");
|
||||||
|
|
||||||
servePage(c, name + " - Go source", buf.Data());
|
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);
|
r.Init(prog.Name.Value, p.importpath);
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
|
ast.FilterExports(prog); // we only care about exports
|
||||||
r.AddProgram(prog);
|
r.AddProgram(prog);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"flag";
|
"flag";
|
||||||
"fmt";
|
"fmt";
|
||||||
|
"go/ast";
|
||||||
"go/parser";
|
"go/parser";
|
||||||
"go/printer";
|
"go/printer";
|
||||||
"io";
|
"io";
|
||||||
@ -48,9 +49,6 @@ func parserMode() uint {
|
|||||||
|
|
||||||
func printerMode() uint {
|
func printerMode() uint {
|
||||||
mode := uint(0);
|
mode := uint(0);
|
||||||
if *exports {
|
|
||||||
mode |= printer.ExportsOnly;
|
|
||||||
}
|
|
||||||
if *optcommas {
|
if *optcommas {
|
||||||
mode |= printer.OptCommas;
|
mode |= printer.OptCommas;
|
||||||
}
|
}
|
||||||
@ -100,6 +98,9 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !*silent {
|
if !*silent {
|
||||||
|
if *exports {
|
||||||
|
ast.FilterExports(prog); // ignore result
|
||||||
|
}
|
||||||
w := makeTabwriter(os.Stdout);
|
w := makeTabwriter(os.Stdout);
|
||||||
printer.Fprint(w, prog, printerMode());
|
printer.Fprint(w, prog, printerMode());
|
||||||
w.Flush();
|
w.Flush();
|
||||||
|
@ -2,8 +2,9 @@
|
|||||||
# Use of this source code is governed by a BSD-style
|
# Use of this source code is governed by a BSD-style
|
||||||
# license that can be found in the LICENSE file.
|
# license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
|
||||||
# DO NOT EDIT. Automatically generated by gobuild.
|
# DO NOT EDIT. Automatically generated by gobuild.
|
||||||
# gobuild -m >Makefile
|
# gobuild -m ast.go filter.go >Makefile
|
||||||
|
|
||||||
D=/go/
|
D=/go/
|
||||||
|
|
||||||
@ -20,7 +21,7 @@ test: packages
|
|||||||
|
|
||||||
coverage: packages
|
coverage: packages
|
||||||
gotest
|
gotest
|
||||||
6cov -g `pwd` | grep -v '_test\.go:'
|
6cov -g $$(pwd) | grep -v '_test\.go:'
|
||||||
|
|
||||||
%.$O: %.go
|
%.$O: %.go
|
||||||
$(GC) -I_obj $*.go
|
$(GC) -I_obj $*.go
|
||||||
@ -34,14 +35,21 @@ coverage: packages
|
|||||||
O1=\
|
O1=\
|
||||||
ast.$O\
|
ast.$O\
|
||||||
|
|
||||||
|
O2=\
|
||||||
|
filter.$O\
|
||||||
|
|
||||||
phases: a1
|
|
||||||
|
phases: a1 a2
|
||||||
_obj$D/ast.a: phases
|
_obj$D/ast.a: phases
|
||||||
|
|
||||||
a1: $(O1)
|
a1: $(O1)
|
||||||
$(AR) grc _obj$D/ast.a ast.$O
|
$(AR) grc _obj$D/ast.a ast.$O
|
||||||
rm -f $(O1)
|
rm -f $(O1)
|
||||||
|
|
||||||
|
a2: $(O2)
|
||||||
|
$(AR) grc _obj$D/ast.a filter.$O
|
||||||
|
rm -f $(O2)
|
||||||
|
|
||||||
|
|
||||||
newpkg: clean
|
newpkg: clean
|
||||||
mkdir -p _obj$D
|
mkdir -p _obj$D
|
||||||
@ -49,6 +57,7 @@ newpkg: clean
|
|||||||
|
|
||||||
$(O1): newpkg
|
$(O1): newpkg
|
||||||
$(O2): a1
|
$(O2): a1
|
||||||
|
$(O3): a2
|
||||||
|
|
||||||
nuke: clean
|
nuke: clean
|
||||||
rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/ast.a
|
rm -f $(GOROOT)/pkg/$(GOOS)_$(GOARCH)$D/ast.a
|
||||||
|
134
src/pkg/go/ast/filter.go
Normal file
134
src/pkg/go/ast/filter.go
Normal file
@ -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;
|
||||||
|
}
|
@ -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 {
|
type typeDoc struct {
|
||||||
@ -149,33 +127,25 @@ func (doc *DocReader) addDecl(decl ast.Decl) {
|
|||||||
// ignore
|
// ignore
|
||||||
case token.CONST:
|
case token.CONST:
|
||||||
// constants are always handled as a group
|
// constants are always handled as a group
|
||||||
if hasExportedSpecs(d.Specs) {
|
doc.consts.Push(d);
|
||||||
doc.consts.Push(d);
|
|
||||||
}
|
|
||||||
case token.TYPE:
|
case token.TYPE:
|
||||||
// types are handled individually
|
// types are handled individually
|
||||||
|
var noPos token.Position;
|
||||||
for i, spec := range d.Specs {
|
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);
|
s := spec.(*ast.TypeSpec);
|
||||||
if s.Name.IsExported() {
|
doc.addType(&ast.GenDecl{d.Doc, d.Pos(), token.TYPE, noPos, []ast.Spec{s}, noPos});
|
||||||
// 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});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case token.VAR:
|
case token.VAR:
|
||||||
// variables are always handled as a group
|
// variables are always handled as a group
|
||||||
if hasExportedSpecs(d.Specs) {
|
doc.vars.Push(d);
|
||||||
doc.vars.Push(d);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case *ast.FuncDecl:
|
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
|
doc.doc = prog.Doc
|
||||||
}
|
}
|
||||||
|
|
||||||
// add all exported declarations
|
// add all declarations
|
||||||
for i, decl := range prog.Decls {
|
for i, decl := range prog.Decls {
|
||||||
doc.addDecl(decl);
|
doc.addDecl(decl);
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,7 @@ import (
|
|||||||
// to Fprint via the mode parameter.
|
// to Fprint via the mode parameter.
|
||||||
//
|
//
|
||||||
const (
|
const (
|
||||||
ExportsOnly uint = 1 << iota; // print exported code only
|
DocComments uint = 1 << iota; // print documentation comments
|
||||||
DocComments; // print documentation comments
|
|
||||||
OptCommas; // print optional commas
|
OptCommas; // print optional commas
|
||||||
OptSemis; // print optional semicolons
|
OptSemis; // print optional semicolons
|
||||||
)
|
)
|
||||||
@ -181,97 +180,13 @@ func (p *printer) print(args ...) {
|
|||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Predicates
|
// Printing of common AST nodes.
|
||||||
|
|
||||||
func (p *printer) optSemis() bool {
|
func (p *printer) optSemis() bool {
|
||||||
return p.mode & OptSemis != 0;
|
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) {
|
func (p *printer) comment(c *ast.Comment) {
|
||||||
if c != nil {
|
if c != nil {
|
||||||
text := c.Text;
|
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) expr(x ast.Expr) bool
|
||||||
|
|
||||||
func (p *printer) identList(list []*ast.Ident) {
|
func (p *printer) identList(list []*ast.Ident) {
|
||||||
needsComma := false;
|
|
||||||
for i, x := range list {
|
for i, x := range list {
|
||||||
if p.isVisibleIdent(x) {
|
if i > 0 {
|
||||||
if needsComma {
|
p.print(token.COMMA, blank);
|
||||||
p.print(token.COMMA, blank);
|
|
||||||
}
|
|
||||||
p.expr(x);
|
|
||||||
needsComma = true;
|
|
||||||
}
|
}
|
||||||
|
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.
|
// 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 {
|
func (p *printer) fieldList(lbrace token.Position, list []*ast.Field, rbrace token.Position, isInterface bool) bool {
|
||||||
hasBody := p.isVisibleFieldList(list);
|
if !lbrace.IsValid() {
|
||||||
if !lbrace.IsValid() || p.exportsOnly() && !hasBody {
|
// forward declaration without {}'s
|
||||||
// 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)
|
|
||||||
return false; // no {}'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(blank, lbrace, token.LBRACE, +1, newline);
|
||||||
p.print(+1, newline);
|
|
||||||
|
|
||||||
var needsSemi bool;
|
var lastWasAnon bool; // true if the previous line was an anonymous field
|
||||||
var lastWasAnon bool; // true if the previous line was an anonymous field
|
var lastComment *ast.Comment; // the comment from the previous line
|
||||||
var lastComment *ast.Comment; // the comment from the previous line
|
for i, f := range list {
|
||||||
for _, f := range list {
|
// at least one visible identifier or anonymous field
|
||||||
hasNames := p.isVisibleIdentList(f.Names);
|
isAnon := len(f.Names) == 0;
|
||||||
isAnon := len(f.Names) == 0;
|
if i > 0 {
|
||||||
|
p.print(token.SEMICOLON);
|
||||||
if hasNames || isAnon {
|
p.comment(lastComment);
|
||||||
// at least one visible identifier or anonymous field
|
if lastWasAnon == isAnon {
|
||||||
// TODO this is conservative - see isVisibleFieldList
|
// previous and current line have same structure;
|
||||||
if needsSemi {
|
// continue with existing columns
|
||||||
p.print(token.SEMICOLON);
|
p.print(newline);
|
||||||
p.comment(lastComment);
|
} else {
|
||||||
if lastWasAnon == isAnon {
|
// previous and current line have different structure;
|
||||||
// previous and current line have same structure;
|
// flush tabwriter and start new columns (the "type
|
||||||
// continue with existing columns
|
// column" on a line with named fields may line up
|
||||||
p.print(newline);
|
// with the "trailing comment column" on a line with
|
||||||
} else {
|
// an anonymous field, leading to bad alignment)
|
||||||
// previous and current line have different structure;
|
p.print(formfeed);
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.optSemis() {
|
p.doc(f.Doc);
|
||||||
p.print(token.SEMICOLON);
|
if !isAnon {
|
||||||
|
p.identList(f.Names);
|
||||||
|
p.print(tab);
|
||||||
}
|
}
|
||||||
|
|
||||||
p.comment(lastComment);
|
if isInterface {
|
||||||
p.print(-1, newline);
|
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
|
return true; // field list with {}'s
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -905,25 +806,17 @@ func (p *printer) decl(decl ast.Decl) (optSemi bool) {
|
|||||||
|
|
||||||
if d.Lparen.IsValid() {
|
if d.Lparen.IsValid() {
|
||||||
// group of parenthesized declarations
|
// group of parenthesized declarations
|
||||||
p.print(d.Lparen, token.LPAREN);
|
p.print(d.Lparen, token.LPAREN, +1, newline);
|
||||||
if p.isVisibleSpecList(d.Specs) {
|
for i, s := range d.Specs {
|
||||||
p.print(+1, newline);
|
if i > 0 {
|
||||||
semi := false;
|
p.print(token.SEMICOLON, newline);
|
||||||
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.spec(s);
|
||||||
p.print(token.SEMICOLON);
|
|
||||||
}
|
|
||||||
p.print(-1, newline);
|
|
||||||
}
|
}
|
||||||
p.print(d.Rparen, token.RPAREN);
|
if p.optSemis() {
|
||||||
|
p.print(token.SEMICOLON);
|
||||||
|
}
|
||||||
|
p.print(-1, newline, d.Rparen, token.RPAREN);
|
||||||
optSemi = true;
|
optSemi = true;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@ -946,7 +839,7 @@ func (p *printer) decl(decl ast.Decl) (optSemi bool) {
|
|||||||
}
|
}
|
||||||
p.expr(d.Name);
|
p.expr(d.Name);
|
||||||
p.signature(d.Type.Params, d.Type.Results);
|
p.signature(d.Type.Params, d.Type.Results);
|
||||||
if !p.exportsOnly() && d.Body != nil {
|
if d.Body != nil {
|
||||||
p.print(blank);
|
p.print(blank);
|
||||||
p.level++; // adjust nesting level for function body
|
p.level++; // adjust nesting level for function body
|
||||||
p.stmt(d.Body);
|
p.stmt(d.Body);
|
||||||
@ -965,23 +858,19 @@ func (p *printer) decl(decl ast.Decl) (optSemi bool) {
|
|||||||
// Programs
|
// Programs
|
||||||
|
|
||||||
func (p *printer) program(prog *ast.Program) {
|
func (p *printer) program(prog *ast.Program) {
|
||||||
// set unassociated comments if all code is printed
|
// set unassociated comments
|
||||||
if !p.exportsOnly() {
|
// TODO enable this once comments are properly interspersed
|
||||||
// TODO enable this once comments are properly interspersed
|
// p.setComments(prog.Comments);
|
||||||
//p.setComments(prog.Comments);
|
|
||||||
}
|
|
||||||
|
|
||||||
p.doc(prog.Doc);
|
p.doc(prog.Doc);
|
||||||
p.print(prog.Pos(), token.PACKAGE, blank);
|
p.print(prog.Pos(), token.PACKAGE, blank);
|
||||||
p.expr(prog.Name);
|
p.expr(prog.Name);
|
||||||
|
|
||||||
for _, d := range prog.Decls {
|
for _, d := range prog.Decls {
|
||||||
if p.isVisibleDecl(d) {
|
p.print(newline, newline);
|
||||||
p.print(newline, newline);
|
p.decl(d);
|
||||||
p.decl(d);
|
if p.optSemis() {
|
||||||
if p.optSemis() {
|
p.print(token.SEMICOLON);
|
||||||
p.print(token.SEMICOLON);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user