Commit 1401151a authored by Robert Griesemer's avatar Robert Griesemer

- don't add "..." anonymous field to structs/interfaces if entries are stripped

- don't print any optional semicolons after declarations inside functions
- indicate non-exported fields/methods in exported types with a comment
  so that the "exported source" is legal Go code
- more tests

R=rsc
DELTA=300  (227 added, 25 deleted, 48 changed)
OCL=34697
CL=34730
parent 75f72e7b
...@@ -83,12 +83,12 @@ type CommentGroup struct { ...@@ -83,12 +83,12 @@ type CommentGroup struct {
// Expressions and types // Expressions and types
// A Field represents a Field declaration list in a struct type, // A Field represents a Field declaration list in a struct type,
// a method in an interface type, or a parameter/result declaration // a method list in an interface type, or a parameter/result declaration
// in a signature. // in a signature.
// //
type Field struct { type Field struct {
Doc *CommentGroup; // associated documentation; or nil Doc *CommentGroup; // associated documentation; or nil
Names []*Ident; // field/method/parameter names; nil if anonymous field Names []*Ident; // field/method/parameter names; or nil if anonymous field
Type Expr; // field/method/parameter type Type Expr; // field/method/parameter type
Tag []*BasicLit; // field tag; or nil Tag []*BasicLit; // field tag; or nil
Comment *CommentGroup; // line comments; or nil Comment *CommentGroup; // line comments; or nil
...@@ -249,8 +249,9 @@ type ( ...@@ -249,8 +249,9 @@ type (
StructType struct { StructType struct {
token.Position; // position of "struct" keyword token.Position; // position of "struct" keyword
Lbrace token.Position; // position of "{" Lbrace token.Position; // position of "{"
Fields []*Field; // list of field declarations; nil if forward declaration Fields []*Field; // list of field declarations
Rbrace token.Position; // position of "}" Rbrace token.Position; // position of "}"
Incomplete bool; // true if (source) fields are missing in the Fields list
}; };
// Pointer types are represented via StarExpr nodes. // Pointer types are represented via StarExpr nodes.
...@@ -266,8 +267,9 @@ type ( ...@@ -266,8 +267,9 @@ type (
InterfaceType struct { InterfaceType struct {
token.Position; // position of "interface" keyword token.Position; // position of "interface" keyword
Lbrace token.Position; // position of "{" Lbrace token.Position; // position of "{"
Methods []*Field; // list of methods; nil if forward declaration Methods []*Field; // list of methods
Rbrace token.Position; // position of "}" Rbrace token.Position; // position of "}"
Incomplete bool; // true if (source) methods are missing in the Methods list
}; };
// A MapType node represents a map type. // A MapType node represents a map type.
......
...@@ -4,9 +4,7 @@ ...@@ -4,9 +4,7 @@
package ast package ast
import ( import "go/token";
"go/token";
)
func filterIdentList(list []*Ident) []*Ident { func filterIdentList(list []*Ident) []*Ident {
...@@ -38,7 +36,7 @@ func isExportedType(typ Expr) bool { ...@@ -38,7 +36,7 @@ func isExportedType(typ Expr) bool {
} }
func filterFieldList(list []*Field) []*Field { func filterFieldList(list []*Field, incomplete *bool) []*Field {
j := 0; j := 0;
for _, f := range list { for _, f := range list {
exported := false; exported := false;
...@@ -51,7 +49,11 @@ func filterFieldList(list []*Field) []*Field { ...@@ -51,7 +49,11 @@ func filterFieldList(list []*Field) []*Field {
// type information.) // type information.)
exported = isExportedType(f.Type); exported = isExportedType(f.Type);
} else { } else {
n := len(f.Names);
f.Names = filterIdentList(f.Names); f.Names = filterIdentList(f.Names);
if len(f.Names) < n {
*incomplete = true;
}
exported = len(f.Names) > 0; exported = len(f.Names) > 0;
} }
if exported { if exported {
...@@ -60,11 +62,8 @@ func filterFieldList(list []*Field) []*Field { ...@@ -60,11 +62,8 @@ func filterFieldList(list []*Field) []*Field {
j++; j++;
} }
} }
if j > 0 && j < len(list) { if j < len(list) {
// fields have been stripped but there is at least one left; *incomplete = true;
// add a '...' anonymous field instead
list[j] = &Field{nil, nil, &Ellipsis{}, nil, nil};
j++;
} }
return list[0 : j]; return list[0 : j];
} }
...@@ -84,30 +83,12 @@ func filterType(typ Expr) { ...@@ -84,30 +83,12 @@ func filterType(typ Expr) {
case *ArrayType: case *ArrayType:
filterType(t.Elt); filterType(t.Elt);
case *StructType: case *StructType:
// don't change if empty struct t.Fields = filterFieldList(t.Fields, &t.Incomplete);
if len(t.Fields) > 0 {
t.Fields = filterFieldList(t.Fields);
if len(t.Fields) == 0 {
// all fields have been stripped - make look like forward-decl
t.Lbrace = noPos;
t.Fields = nil;
t.Rbrace = noPos;
}
}
case *FuncType: case *FuncType:
filterParamList(t.Params); filterParamList(t.Params);
filterParamList(t.Results); filterParamList(t.Results);
case *InterfaceType: case *InterfaceType:
// don't change if empty interface t.Methods = filterFieldList(t.Methods, &t.Incomplete);
if len(t.Methods) > 0 {
t.Methods = filterFieldList(t.Methods);
if len(t.Methods) == 0 {
// all methods have been stripped - make look like forward-decl
t.Lbrace = noPos;
t.Methods = nil;
t.Rbrace = noPos;
}
}
case *MapType: case *MapType:
filterType(t.Key); filterType(t.Key);
filterType(t.Value); filterType(t.Value);
......
...@@ -512,7 +512,7 @@ func (p *parser) parseStructType() *ast.StructType { ...@@ -512,7 +512,7 @@ func (p *parser) parseStructType() *ast.StructType {
fields[i] = list.At(i).(*ast.Field); fields[i] = list.At(i).(*ast.Field);
} }
return &ast.StructType{pos, lbrace, fields, rbrace}; return &ast.StructType{pos, lbrace, fields, rbrace, false};
} }
...@@ -720,7 +720,7 @@ func (p *parser) parseInterfaceType() *ast.InterfaceType { ...@@ -720,7 +720,7 @@ func (p *parser) parseInterfaceType() *ast.InterfaceType {
methods[i] = list.At(i).(*ast.Field); methods[i] = list.At(i).(*ast.Field);
} }
return &ast.InterfaceType{pos, lbrace, methods, rbrace}; return &ast.InterfaceType{pos, lbrace, methods, rbrace, false};
} }
......
...@@ -550,7 +550,8 @@ func (p *printer) parameters(list []*ast.Field) { ...@@ -550,7 +550,8 @@ func (p *printer) parameters(list []*ast.Field) {
} }
func (p *printer) signature(params, result []*ast.Field) { // Returns true if a separating semicolon is optional.
func (p *printer) signature(params, result []*ast.Field) (optSemi bool) {
p.parameters(params); p.parameters(params);
if result != nil { if result != nil {
p.print(blank); p.print(blank);
...@@ -559,29 +560,35 @@ func (p *printer) signature(params, result []*ast.Field) { ...@@ -559,29 +560,35 @@ func (p *printer) signature(params, result []*ast.Field) {
// single anonymous result; no ()'s unless it's a function type // single anonymous result; no ()'s unless it's a function type
f := result[0]; f := result[0];
if _, isFtyp := f.Type.(*ast.FuncType); !isFtyp { if _, isFtyp := f.Type.(*ast.FuncType); !isFtyp {
p.expr(f.Type); optSemi = p.expr(f.Type);
return; return;
} }
} }
p.parameters(result); p.parameters(result);
} }
return;
} }
// Returns true if the field list ends in a closing brace. func incompleteMsg(isInterface bool) string {
func (p *printer) fieldList(lbrace token.Position, list []*ast.Field, rbrace token.Position, isInterface bool) bool { if isInterface {
if list == nil { return "// contains unexported methods";
// forward declaration
// TODO(gri) remove this logic once godoc doesn't produce field
// lists that resemble forward declarations anymore
return false; // no {}'s
} }
return "// contains unexported fields";
}
func (p *printer) fieldList(lbrace token.Position, list []*ast.Field, rbrace token.Position, isIncomplete, isInterface bool) {
if len(list) == 0 { if len(list) == 0 {
if isIncomplete {
// all entries were stripped
p.print(blank, lbrace, token.LBRACE, +1, newline, incompleteMsg(isInterface), -1, newline, rbrace, token.RBRACE);
} else {
// no blank between keyword and {} in this case // no blank between keyword and {} in this case
p.print(lbrace, token.LBRACE, rbrace, token.RBRACE); p.print(lbrace, token.LBRACE, rbrace, token.RBRACE);
return true; // empty list with {}'s }
return;
} }
p.print(blank, lbrace, token.LBRACE, +1, newline); p.print(blank, lbrace, token.LBRACE, +1, newline);
...@@ -636,9 +643,13 @@ func (p *printer) fieldList(lbrace token.Position, list []*ast.Field, rbrace tok ...@@ -636,9 +643,13 @@ func (p *printer) fieldList(lbrace token.Position, list []*ast.Field, rbrace tok
p.print(token.SEMICOLON); p.print(token.SEMICOLON);
p.lineComment(lastComment); p.lineComment(lastComment);
p.print(-1, formfeed, rbrace, token.RBRACE);
return true; // field list with {}'s if isIncomplete {
// at least one entry printed, but some entries were stripped
p.print(newline, incompleteMsg(isInterface));
}
p.print(-1, formfeed, rbrace, token.RBRACE);
} }
...@@ -715,6 +726,7 @@ func (p *printer) binaryExpr(x *ast.BinaryExpr, prec1 int) { ...@@ -715,6 +726,7 @@ func (p *printer) binaryExpr(x *ast.BinaryExpr, prec1 int) {
} }
// Returns true if a separating semicolon is optional.
func (p *printer) expr1(expr ast.Expr, prec1 int) (optSemi bool) { func (p *printer) expr1(expr ast.Expr, prec1 int) (optSemi bool) {
p.print(expr.Pos()); p.print(expr.Pos());
...@@ -735,7 +747,7 @@ func (p *printer) expr1(expr ast.Expr, prec1 int) (optSemi bool) { ...@@ -735,7 +747,7 @@ func (p *printer) expr1(expr ast.Expr, prec1 int) (optSemi bool) {
case *ast.StarExpr: case *ast.StarExpr:
p.print(token.MUL); p.print(token.MUL);
p.expr(x.X); optSemi = p.expr(x.X);
case *ast.UnaryExpr: case *ast.UnaryExpr:
const prec = token.UnaryPrec; const prec = token.UnaryPrec;
...@@ -823,25 +835,27 @@ func (p *printer) expr1(expr ast.Expr, prec1 int) (optSemi bool) { ...@@ -823,25 +835,27 @@ func (p *printer) expr1(expr ast.Expr, prec1 int) (optSemi bool) {
p.expr(x.Len); p.expr(x.Len);
} }
p.print(token.RBRACK); p.print(token.RBRACK);
p.expr(x.Elt); optSemi = p.expr(x.Elt);
case *ast.StructType: case *ast.StructType:
p.print(token.STRUCT); p.print(token.STRUCT);
optSemi = p.fieldList(x.Lbrace, x.Fields, x.Rbrace, false); p.fieldList(x.Lbrace, x.Fields, x.Rbrace, x.Incomplete, false);
optSemi = true;
case *ast.FuncType: case *ast.FuncType:
p.print(token.FUNC); p.print(token.FUNC);
p.signature(x.Params, x.Results); optSemi = p.signature(x.Params, x.Results);
case *ast.InterfaceType: case *ast.InterfaceType:
p.print(token.INTERFACE); p.print(token.INTERFACE);
optSemi = p.fieldList(x.Lbrace, x.Methods, x.Rbrace, true); p.fieldList(x.Lbrace, x.Methods, x.Rbrace, x.Incomplete, true);
optSemi = true;
case *ast.MapType: case *ast.MapType:
p.print(token.MAP, token.LBRACK); p.print(token.MAP, token.LBRACK);
p.expr(x.Key); p.expr(x.Key);
p.print(token.RBRACK); p.print(token.RBRACK);
p.expr(x.Value); optSemi = p.expr(x.Value);
case *ast.ChanType: case *ast.ChanType:
switch x.Dir { switch x.Dir {
...@@ -853,7 +867,7 @@ func (p *printer) expr1(expr ast.Expr, prec1 int) (optSemi bool) { ...@@ -853,7 +867,7 @@ func (p *printer) expr1(expr ast.Expr, prec1 int) (optSemi bool) {
p.print(token.CHAN, token.ARROW); p.print(token.CHAN, token.ARROW);
} }
p.print(blank); p.print(blank);
p.expr(x.Value); optSemi = p.expr(x.Value);
default: default:
panic("unreachable"); panic("unreachable");
......
...@@ -15,9 +15,47 @@ const ( ...@@ -15,9 +15,47 @@ const (
) )
// The T type. // The SZ struct; it is empty.
type T struct { type SZ struct {}
a, b, c int // 3 fields
// The S0 struct; no field is exported.
type S0 struct {
int;
x, y, z int; // 3 unexported fields
}
// The S1 struct; some fields are not exported.
type S1 struct {
S0;
A, B, C float; // 3 exported fields
D, b, c int; // 2 unexported fields
}
// The S2 struct; all fields are exported.
type S2 struct {
S1;
A, B, C float; // 3 exported fields
}
// The IZ interface; it is empty.
type SZ interface {}
// The I0 interface; no method is exported.
type I0 interface {
f, g (x int) int; // 2 unexported methods
}
// The I1 interface; some methods are not exported.
type I1 interface {
I0;
F, G (x float) float; // 2 exported methods
H, g (x int) int; // 1 unexported method
}
// The I2 interface; all methods are exported.
type I1 interface {
I0;
F, G (x float) float; // 2 exported methods
} }
// This comment group should be separated // This comment group should be separated
......
...@@ -15,9 +15,47 @@ const ( ...@@ -15,9 +15,47 @@ const (
) )
// The T type. // The SZ struct; it is empty.
type T struct { type SZ struct{}
a, b, c int; // 3 fields
// The S0 struct; no field is exported.
type S0 struct {
int;
x, y, z int; // 3 unexported fields
}
// The S1 struct; some fields are not exported.
type S1 struct {
S0;
A, B, C float; // 3 exported fields
D, b, c int; // 2 unexported fields
}
// The S2 struct; all fields are exported.
type S2 struct {
S1;
A, B, C float; // 3 exported fields
}
// The IZ interface; it is empty.
type SZ interface{}
// The I0 interface; no method is exported.
type I0 interface {
f, g (x int) int; // 2 unexported methods
}
// The I1 interface; some methods are not exported.
type I1 interface {
I0;
F, G (x float) float; // 2 exported methods
H, g (x int) int; // 1 unexported method
}
// The I2 interface; all methods are exported.
type I1 interface {
I0;
F, G (x float) float; // 2 exported methods
} }
// This comment group should be separated // This comment group should be separated
......
...@@ -2,5 +2,46 @@ ...@@ -2,5 +2,46 @@
// //
package main package main
// The T type. // The SZ struct; it is empty.
type T struct type SZ struct{}
// The S0 struct; no field is exported.
type S0 struct {
// contains unexported fields
}
// The S1 struct; some fields are not exported.
type S1 struct {
S0;
A, B, C float; // 3 exported fields
D int; // 2 unexported fields
// contains unexported fields
}
// The S2 struct; all fields are exported.
type S2 struct {
S1;
A, B, C float; // 3 exported fields
}
// The IZ interface; it is empty.
type SZ interface{}
// The I0 interface; no method is exported.
type I0 interface {
// contains unexported methods
}
// The I1 interface; some methods are not exported.
type I1 interface {
I0;
F, G (x float) float;
H (x int) int;
// contains unexported methods
}
// The I2 interface; all methods are exported.
type I1 interface {
I0;
F, G (x float) float;
}
...@@ -7,10 +7,10 @@ package imports ...@@ -7,10 +7,10 @@ package imports
import "io" import "io"
import ( import (
a "io" _ "io"
) )
import a "io" import _ "io"
import ( import (
"io"; "io";
...@@ -25,4 +25,50 @@ import ( ...@@ -25,4 +25,50 @@ import (
c "i" "o"; c "i" "o";
) )
func _() {
// the following decls need a semicolon at the end
type _ int;
type _ *int;
type _ []int;
type _ map[string]int;
type _ chan int;
type _ func() int;
var _ int;
var _ *int;
var _ []int;
var _ map[string]int;
var _ chan int;
var _ func() int;
// the following decls don't need a semicolon at the end
type _ struct{}
type _ *struct{}
type _ []struct{}
type _ map[string]struct{}
type _ chan struct{}
type _ func() struct{}
type _ interface{}
type _ *interface{}
type _ []interface{}
type _ map[string]interface{}
type _ chan interface{}
type _ func() interface{}
var _ struct{}
var _ *struct{}
var _ []struct{}
var _ map[string]struct{}
var _ chan struct{}
var _ func() struct{}
var _ interface{}
var _ *interface{}
var _ []interface{}
var _ map[string]interface{}
var _ chan interface{}
var _ func() interface{}
}
// TODO(gri) add more test cases // TODO(gri) add more test cases
...@@ -7,10 +7,10 @@ package imports ...@@ -7,10 +7,10 @@ package imports
import "io" import "io"
import ( import (
a "io"; _ "io";
) )
import a "io" import _ "io"
import ( import (
"io"; "io";
...@@ -25,4 +25,46 @@ import ( ...@@ -25,4 +25,46 @@ import (
c "i" "o"; c "i" "o";
) )
func _() {
// the following decls need a semicolon at the end
type _ int;
type _ *int;
type _ []int;
type _ map[string]int;
type _ chan int;
type _ func() int;
var _ int;
var _ *int;
var _ []int;
var _ map[string]int;
var _ chan int;
var _ func() int;
// the following decls don't need a semicolon at the end
type _ struct{}
type _ *struct{}
type _ []struct{}
type _ map[string]struct{}
type _ chan struct{}
type _ func() struct{}
type _ interface{}
type _ *interface{}
type _ []interface{}
type _ map[string]interface{}
type _ chan interface{}
type _ func() interface{}
var _ struct{}
var _ *struct{}
var _ []struct{}
var _ map[string]struct{}
var _ chan struct{}
var _ func() struct{}
var _ interface{}
var _ *interface{}
var _ []interface{}
var _ map[string]interface{}
var _ chan interface{}
var _ func() interface{}
}
// TODO(gri) add more test cases // TODO(gri) add more test cases
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment