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 {
// Expressions and types
// 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.
//
type Field struct {
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
Tag []*BasicLit; // field tag; or nil
Comment *CommentGroup; // line comments; or nil
......@@ -249,8 +249,9 @@ type (
StructType struct {
token.Position; // position of "struct" keyword
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 "}"
Incomplete bool; // true if (source) fields are missing in the Fields list
};
// Pointer types are represented via StarExpr nodes.
......@@ -266,8 +267,9 @@ type (
InterfaceType struct {
token.Position; // position of "interface" keyword
Lbrace token.Position; // position of "{"
Methods []*Field; // list of methods; nil if forward declaration
Methods []*Field; // list of methods
Rbrace token.Position; // position of "}"
Incomplete bool; // true if (source) methods are missing in the Methods list
};
// A MapType node represents a map type.
......
......@@ -4,9 +4,7 @@
package ast
import (
"go/token";
)
import "go/token";
func filterIdentList(list []*Ident) []*Ident {
......@@ -38,7 +36,7 @@ func isExportedType(typ Expr) bool {
}
func filterFieldList(list []*Field) []*Field {
func filterFieldList(list []*Field, incomplete *bool) []*Field {
j := 0;
for _, f := range list {
exported := false;
......@@ -51,7 +49,11 @@ func filterFieldList(list []*Field) []*Field {
// type information.)
exported = isExportedType(f.Type);
} else {
n := len(f.Names);
f.Names = filterIdentList(f.Names);
if len(f.Names) < n {
*incomplete = true;
}
exported = len(f.Names) > 0;
}
if exported {
......@@ -60,11 +62,8 @@ func filterFieldList(list []*Field) []*Field {
j++;
}
}
if j > 0 && j < len(list) {
// fields have been stripped but there is at least one left;
// add a '...' anonymous field instead
list[j] = &Field{nil, nil, &Ellipsis{}, nil, nil};
j++;
if j < len(list) {
*incomplete = true;
}
return list[0 : j];
}
......@@ -84,30 +83,12 @@ func filterType(typ Expr) {
case *ArrayType:
filterType(t.Elt);
case *StructType:
// don't change if empty struct
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;
}
}
t.Fields = filterFieldList(t.Fields, &t.Incomplete);
case *FuncType:
filterParamList(t.Params);
filterParamList(t.Results);
case *InterfaceType:
// don't change if empty interface
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;
}
}
t.Methods = filterFieldList(t.Methods, &t.Incomplete);
case *MapType:
filterType(t.Key);
filterType(t.Value);
......
......@@ -512,7 +512,7 @@ func (p *parser) parseStructType() *ast.StructType {
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 {
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) {
}
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);
if result != nil {
p.print(blank);
......@@ -559,29 +560,35 @@ func (p *printer) signature(params, result []*ast.Field) {
// single anonymous result; no ()'s unless it's a function type
f := result[0];
if _, isFtyp := f.Type.(*ast.FuncType); !isFtyp {
p.expr(f.Type);
optSemi = p.expr(f.Type);
return;
}
}
p.parameters(result);
}
return;
}
// 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 {
if list == nil {
// forward declaration
// TODO(gri) remove this logic once godoc doesn't produce field
// lists that resemble forward declarations anymore
return false; // no {}'s
func incompleteMsg(isInterface bool) string {
if isInterface {
return "// contains unexported methods";
}
return "// contains unexported fields";
}
func (p *printer) fieldList(lbrace token.Position, list []*ast.Field, rbrace token.Position, isIncomplete, isInterface bool) {
if len(list) == 0 {
// no blank between keyword and {} in this case
p.print(lbrace, token.LBRACE, rbrace, token.RBRACE);
return true; // empty list with {}'s
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
p.print(lbrace, token.LBRACE, rbrace, token.RBRACE);
}
return;
}
p.print(blank, lbrace, token.LBRACE, +1, newline);
......@@ -636,9 +643,13 @@ func (p *printer) fieldList(lbrace token.Position, list []*ast.Field, rbrace tok
p.print(token.SEMICOLON);
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) {
}
// Returns true if a separating semicolon is optional.
func (p *printer) expr1(expr ast.Expr, prec1 int) (optSemi bool) {
p.print(expr.Pos());
......@@ -735,7 +747,7 @@ func (p *printer) expr1(expr ast.Expr, prec1 int) (optSemi bool) {
case *ast.StarExpr:
p.print(token.MUL);
p.expr(x.X);
optSemi = p.expr(x.X);
case *ast.UnaryExpr:
const prec = token.UnaryPrec;
......@@ -823,25 +835,27 @@ func (p *printer) expr1(expr ast.Expr, prec1 int) (optSemi bool) {
p.expr(x.Len);
}
p.print(token.RBRACK);
p.expr(x.Elt);
optSemi = p.expr(x.Elt);
case *ast.StructType:
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:
p.print(token.FUNC);
p.signature(x.Params, x.Results);
optSemi = p.signature(x.Params, x.Results);
case *ast.InterfaceType:
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:
p.print(token.MAP, token.LBRACK);
p.expr(x.Key);
p.print(token.RBRACK);
p.expr(x.Value);
optSemi = p.expr(x.Value);
case *ast.ChanType:
switch x.Dir {
......@@ -853,7 +867,7 @@ func (p *printer) expr1(expr ast.Expr, prec1 int) (optSemi bool) {
p.print(token.CHAN, token.ARROW);
}
p.print(blank);
p.expr(x.Value);
optSemi = p.expr(x.Value);
default:
panic("unreachable");
......
......@@ -15,9 +15,47 @@ const (
)
// The T type.
type T struct {
a, b, c int // 3 fields
// The SZ struct; it is empty.
type SZ struct {}
// 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
......
......@@ -15,9 +15,47 @@ const (
)
// The T type.
type T struct {
a, b, c int; // 3 fields
// The SZ struct; it is empty.
type SZ struct{}
// 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
......
......@@ -2,5 +2,46 @@
//
package main
// The T type.
type T struct
// The SZ struct; it is empty.
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
import "io"
import (
a "io"
_ "io"
)
import a "io"
import _ "io"
import (
"io";
......@@ -25,4 +25,50 @@ import (
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
......@@ -7,10 +7,10 @@ package imports
import "io"
import (
a "io";
_ "io";
)
import a "io"
import _ "io"
import (
"io";
......@@ -25,4 +25,46 @@ import (
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
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