1
0
mirror of https://github.com/golang/go synced 2024-11-13 19:00:25 -07:00

various go printer fixes:

- better handling of line breaks in expression lists
- fixed line breaks around label decls
- remove ()'s around if, for, switch expressions
- simple index expressions don't require blanks
- better line breaks around declarations of different kind

R=rsc
DELTA=404  (369 added, 8 deleted, 27 changed)
OCL=35354
CL=35359
This commit is contained in:
Robert Griesemer 2009-10-05 19:37:34 -07:00
parent c915bc54ba
commit e8210824f6
9 changed files with 394 additions and 33 deletions

View File

@ -410,25 +410,29 @@ func (p *printer) flush(next token.Position) {
// Printing of common AST nodes. // Printing of common AST nodes.
// Print as many newlines as necessary (at least one and and at most // Print as many newlines as necessary (but at least min and and at most
// max newlines) to get to the current line. If newSection is set, the // max newlines) to get to the current line. If newSection is set, the
// first newline is printed as a formfeed. // first newline is printed as a formfeed. Returns true if any linebreak
// was printed; returns false otherwise.
// //
// TODO(gri): Reconsider signature (provide position instead of line) // TODO(gri): Reconsider signature (provide position instead of line)
// //
func (p *printer) linebreak(line, max int, newSection bool) { func (p *printer) linebreak(line, min, max int, newSection bool) (printedBreak bool) {
n := line - p.last.Line; n := line - p.pos.Line;
switch { switch {
case n < 1: n = 1; case n < min: n = min;
case n > max: n = max; case n > max: n = max;
} }
if newSection { if n > 0 && newSection {
p.print(formfeed); p.print(formfeed);
n--; n--;
printedBreak = true;
} }
for ; n > 0; n-- { for ; n > 0; n-- {
p.print(newline); p.print(newline);
printedBreak = true;
} }
return;
} }
@ -506,11 +510,12 @@ func (p *printer) exprList(list []ast.Expr, mode exprListMode) {
return; return;
} }
if mode & blankStart != 0 {
p.print(blank);
}
if list[0].Pos().Line == list[len(list)-1].Pos().Line { if list[0].Pos().Line == list[len(list)-1].Pos().Line {
// all list entries on a single line // all list entries on a single line
if mode & blankStart != 0 {
p.print(blank);
}
for i, x := range list { for i, x := range list {
if i > 0 { if i > 0 {
if mode & commaSep != 0 { if mode & commaSep != 0 {
@ -525,8 +530,14 @@ func (p *printer) exprList(list []ast.Expr, mode exprListMode) {
// list entries span multiple lines; // list entries span multiple lines;
// use source code positions to guide line breaks // use source code positions to guide line breaks
p.print(+1, formfeed);
line := list[0].Pos().Line; line := list[0].Pos().Line;
indented := false;
// there may or may not be a linebreak before the first list
// element; in any case indent once after the first linebreak
if p.linebreak(line, 0, 2, true) {
p.print(+1);
indented = true;
}
for i, x := range list { for i, x := range list {
prev := line; prev := line;
line = x.Pos().Line; line = x.Pos().Line;
@ -535,7 +546,12 @@ func (p *printer) exprList(list []ast.Expr, mode exprListMode) {
p.print(token.COMMA); p.print(token.COMMA);
} }
if prev < line { if prev < line {
p.print(newline); // at least one linebreak, but respect an extra empty line
// in the source
if p.linebreak(x.Pos().Line, 1, 2, true) && !indented {
p.print(+1);
indented = true;
}
} else { } else {
p.print(blank); p.print(blank);
} }
@ -544,8 +560,15 @@ func (p *printer) exprList(list []ast.Expr, mode exprListMode) {
} }
if mode & commaTerm != 0 { if mode & commaTerm != 0 {
p.print(token.COMMA); p.print(token.COMMA);
if indented {
// should always be indented here since we have a multi-line
// expression list - be conservative and check anyway
p.print(-1);
}
p.print(formfeed); // terminating comma needs a line break to look good
} else if indented {
p.print(-1);
} }
p.print(-1, formfeed);
} }
@ -668,6 +691,9 @@ func needsBlanks(expr ast.Expr) bool {
case *ast.ParenExpr: case *ast.ParenExpr:
// parenthesized expressions don't need blanks around them // parenthesized expressions don't need blanks around them
return false; return false;
case *ast.IndexExpr:
// index expressions don't need blanks if the indexed expressions are simple
return needsBlanks(x.X)
case *ast.CallExpr: case *ast.CallExpr:
// call expressions need blanks if they have more than one // call expressions need blanks if they have more than one
// argument or if the function or the argument need blanks // argument or if the function or the argument need blanks
@ -893,7 +919,9 @@ const maxStmtNewlines = 2 // maximum number of newlines between statements
func (p *printer) stmtList(list []ast.Stmt, indent int) { func (p *printer) stmtList(list []ast.Stmt, indent int) {
p.print(+indent); p.print(+indent);
for i, s := range list { for i, s := range list {
p.linebreak(s.Pos().Line, maxStmtNewlines, i == 0); // indent == 0 only for lists of switch/select case clauses;
// in those cases each clause is a new section
p.linebreak(s.Pos().Line, 1, maxStmtNewlines, i == 0 || indent == 0);
if !p.stmt(s) { if !p.stmt(s) {
p.print(token.SEMICOLON); p.print(token.SEMICOLON);
} }
@ -906,19 +934,30 @@ func (p *printer) block(s *ast.BlockStmt, indent int) {
p.print(s.Pos(), token.LBRACE); p.print(s.Pos(), token.LBRACE);
if len(s.List) > 0 { if len(s.List) > 0 {
p.stmtList(s.List, indent); p.stmtList(s.List, indent);
p.linebreak(s.Rbrace.Line, maxStmtNewlines, true); p.linebreak(s.Rbrace.Line, 1, maxStmtNewlines, true);
} }
p.print(s.Rbrace, token.RBRACE); p.print(s.Rbrace, token.RBRACE);
} }
// TODO(gri): Decide if this should be used more broadly. The printing code
// knows when to insert parentheses for precedence reasons, but
// need to be careful to keep them around type expressions.
func stripParens(x ast.Expr) ast.Expr {
if px, hasParens := x.(*ast.ParenExpr); hasParens {
return stripParens(px.X);
}
return x;
}
func (p *printer) controlClause(isForStmt bool, init ast.Stmt, expr ast.Expr, post ast.Stmt) { func (p *printer) controlClause(isForStmt bool, init ast.Stmt, expr ast.Expr, post ast.Stmt) {
p.print(blank); p.print(blank);
needsBlank := false; needsBlank := false;
if init == nil && post == nil { if init == nil && post == nil {
// no semicolons required // no semicolons required
if expr != nil { if expr != nil {
p.expr(expr); p.expr(stripParens(expr));
needsBlank = true; needsBlank = true;
} }
} else { } else {
@ -929,7 +968,7 @@ func (p *printer) controlClause(isForStmt bool, init ast.Stmt, expr ast.Expr, po
} }
p.print(token.SEMICOLON, blank); p.print(token.SEMICOLON, blank);
if expr != nil { if expr != nil {
p.expr(expr); p.expr(stripParens(expr));
needsBlank = true; needsBlank = true;
} }
if isForStmt { if isForStmt {
@ -962,9 +1001,12 @@ func (p *printer) stmt(stmt ast.Stmt) (optSemi bool) {
// nothing to do // nothing to do
case *ast.LabeledStmt: case *ast.LabeledStmt:
p.print(-1, formfeed); // whitespace printing is delayed, thus indentation adjustments
// take place before the previous newline/formfeed is printed
p.print(-1);
p.expr(s.Label); p.expr(s.Label);
p.print(token.COLON, tab, +1, formfeed); p.print(token.COLON, tab, +1);
p.linebreak(s.Stmt.Pos().Line, 0, 1, true);
optSemi = p.stmt(s.Stmt); optSemi = p.stmt(s.Stmt);
case *ast.ExprStmt: case *ast.ExprStmt:
@ -1011,7 +1053,14 @@ func (p *printer) stmt(stmt ast.Stmt) (optSemi bool) {
optSemi = true; optSemi = true;
if s.Else != nil { if s.Else != nil {
p.print(blank, token.ELSE, blank); p.print(blank, token.ELSE, blank);
optSemi = p.stmt(s.Else); switch s.Else.(type) {
case *ast.BlockStmt, *ast.IfStmt:
optSemi = p.stmt(s.Else);
default:
p.print(token.LBRACE, +1, formfeed);
p.stmt(s.Else);
p.print(-1, formfeed, token.RBRACE);
}
} }
case *ast.CaseClause: case *ast.CaseClause:
@ -1267,14 +1316,35 @@ func (p *printer) decl(decl ast.Decl) (optSemi bool) {
const maxDeclNewlines = 3 // maximum number of newlines between declarations const maxDeclNewlines = 3 // maximum number of newlines between declarations
func declToken(decl ast.Decl) (tok token.Token) {
tok = token.ILLEGAL;
switch d := decl.(type) {
case *ast.GenDecl:
tok = d.Tok;
case *ast.FuncDecl:
tok = token.FUNC;
}
return;
}
func (p *printer) file(src *ast.File) { func (p *printer) file(src *ast.File) {
p.leadComment(src.Doc); p.leadComment(src.Doc);
p.print(src.Pos(), token.PACKAGE, blank); p.print(src.Pos(), token.PACKAGE, blank);
p.expr(src.Name); p.expr(src.Name);
if len(src.Decls) > 0 { if len(src.Decls) > 0 {
tok := token.ILLEGAL;
for _, d := range src.Decls { for _, d := range src.Decls {
p.linebreak(d.Pos().Line, maxDeclNewlines, false); prev := tok;
tok = declToken(d);
// if the declaration token changed (e.g., from CONST to TYPE)
// print an empty line between top-level declarations
min := 1;
if prev != tok {
min = 2;
}
p.linebreak(d.Pos().Line, min, maxDeclNewlines, false);
p.decl(d); p.decl(d);
} }
} }

View File

@ -252,3 +252,27 @@ type _ interface { // this comment must not change indentation
fffff(); // no blank between identifier and () fffff(); // no blank between identifier and ()
gggggggggggg(x, y, z int) (); // hurray gggggggggggg(x, y, z int) (); // hurray
} }
// formatting of variable declarations
func _() {
type day struct { n int; short, long string };
var (
Sunday = day{ 0, "SUN", "Sunday" };
Monday = day{ 1, "MON", "Monday" };
Tuesday = day{ 2, "TUE", "Tuesday" };
Wednesday = day{ 3, "WED", "Wednesday" };
Thursday = day{ 4, "THU", "Thursday" };
Friday = day{ 5, "FRI", "Friday" };
Saturday = day{ 6, "SAT", "Saturday" };
)
}
// formatting of consecutive single-line functions
func _() {}
func _() {}
func _() {}
func _() {} // an empty line before this function
func _() {}
func _() {}

View File

@ -43,6 +43,7 @@ import _ "fmt"
// at least one empty line between declarations of different kind // at least one empty line between declarations of different kind
import _ "io" import _ "io"
var _ int var _ int
@ -250,3 +251,30 @@ type _ interface { // this comment must not change indentation
fffff(); // no blank between identifier and () fffff(); // no blank between identifier and ()
gggggggggggg(x, y, z int); // hurray gggggggggggg(x, y, z int); // hurray
} }
// formatting of variable declarations
func _() {
type day struct {
n int;
short, long string;
}
var (
Sunday = day{0, "SUN", "Sunday"};
Monday = day{1, "MON", "Monday"};
Tuesday = day{2, "TUE", "Tuesday"};
Wednesday = day{3, "WED", "Wednesday"};
Thursday = day{4, "THU", "Thursday"};
Friday = day{5, "FRI", "Friday"};
Saturday = day{6, "SAT", "Saturday"};
)
}
// formatting of consecutive single-line functions
func _() {}
func _() {}
func _() {}
func _() {} // an empty line before this function
func _() {}
func _() {}

View File

@ -34,6 +34,9 @@ func _() {
_ = s[1:2]; _ = s[1:2];
_ = s[a:b]; _ = s[a:b];
_ = s[0:len(s)]; _ = s[0:len(s)];
_ = s[0]<<1;
_ = (s[0]<<1)&0xf;
_ = s[0] << 2 | s[1] >> 4;
// spaces around expressions of different precedence or expressions containing spaces // spaces around expressions of different precedence or expressions containing spaces
_ = a + -b; _ = a + -b;
@ -49,6 +52,7 @@ func _() {
_ = s[a+b : len(s)]; _ = s[a+b : len(s)];
_ = s[len(s) : -a]; _ = s[len(s) : -a];
_ = s[a : len(s)+1]; _ = s[a : len(s)+1];
_ = s[a : len(s)+1]+s;
// spaces around operators with equal or lower precedence than comparisons // spaces around operators with equal or lower precedence than comparisons
_ = a == b; _ = a == b;
@ -91,3 +95,12 @@ func _() {
_ = ([]T){}; _ = ([]T){};
_ = (map[int]T){}; _ = (map[int]T){};
} }
func _() {
// TODO respect source line breaks in multi-line expressions
_ = a < b ||
b < a;
// TODO(gri): add more test cases
// TODO(gri): these comments should be indented
}

View File

@ -34,6 +34,9 @@ func _() {
_ = s[1:2]; _ = s[1:2];
_ = s[a:b]; _ = s[a:b];
_ = s[0:len(s)]; _ = s[0:len(s)];
_ = s[0]<<1;
_ = (s[0]<<1)&0xf;
_ = s[0]<<2 | s[1]>>4;
// spaces around expressions of different precedence or expressions containing spaces // spaces around expressions of different precedence or expressions containing spaces
_ = a + -b; _ = a + -b;
@ -49,6 +52,7 @@ func _() {
_ = s[a+b : len(s)]; _ = s[a+b : len(s)];
_ = s[len(s) : -a]; _ = s[len(s) : -a];
_ = s[a : len(s)+1]; _ = s[a : len(s)+1];
_ = s[a : len(s)+1]+s;
// spaces around operators with equal or lower precedence than comparisons // spaces around operators with equal or lower precedence than comparisons
_ = a == b; _ = a == b;
@ -91,3 +95,11 @@ func _() {
_ = ([]T){}; _ = ([]T){};
_ = (map[int]T){}; _ = (map[int]T){};
} }
func _() {
// TODO respect source line breaks in multi-line expressions
_ = a < b || b < a;
// TODO(gri): add more test cases
// TODO(gri): these comments should be indented
}

View File

@ -14,6 +14,74 @@ import (
"testing"; "testing";
) )
type writerTestEntry struct {
header *Header;
contents string;
}
type writerTest struct {
file string; // filename of expected output
entries []*writerTestEntry;
}
var writerTests = []*writerTest{
&writerTest{
file: "testdata/writer.tar",
entries: []*writerTestEntry{
&writerTestEntry{
header: &Header{
Name: "small.txt",
Mode: 0640,
Uid: 73025,
Gid: 5000,
Size: 5,
Mtime: 1246508266,
Typeflag: '0',
Uname: "dsymonds",
Gname: "eng",
},
contents: "Kilts",
},
&writerTestEntry{
header: &Header{
Name: "small2.txt",
Mode: 0640,
Uid: 73025,
Gid: 5000,
Size: 11,
Mtime: 1245217492,
Typeflag: '0',
Uname: "dsymonds",
Gname: "eng",
},
contents: "Google.com\n",
},
}
},
// The truncated test file was produced using these commands:
// dd if=/dev/zero bs=1048576 count=16384 > /tmp/16gig.txt
// tar -b 1 -c -f- /tmp/16gig.txt | dd bs=512 count=8 > writer-big.tar
&writerTest{
file: "testdata/writer-big.tar",
entries: []*writerTestEntry{
&writerTestEntry{
header: &Header{
Name: "tmp/16gig.txt",
Mode: 0640,
Uid: 73025,
Gid: 5000,
Size: 16 << 30,
Mtime: 1254699560,
Typeflag: '0',
Uname: "dsymonds",
Gname: "eng",
},
// no contents
},
},
},
}
type untarTest struct { type untarTest struct {
file string; file string;
headers []*Header; headers []*Header;
@ -114,6 +182,16 @@ var facts = map[int] string {
"51185210916864000000000000000000000000", "51185210916864000000000000000000000000",
} }
func usage() {
fmt.Fprintf(os.Stderr,
// TODO(gri): the 2nd string of this string list should not be indented
"usage: godoc package [name ...]\n"
" godoc -http=:6060\n"
);
flag.PrintDefaults();
os.Exit(2);
}
func TestReader(t *testing.T) { func TestReader(t *testing.T) {
testLoop: testLoop:
for i, test := range untarTests { for i, test := range untarTests {

View File

@ -14,6 +14,71 @@ import (
"testing"; "testing";
) )
type writerTestEntry struct {
header *Header;
contents string;
}
type writerTest struct {
file string; // filename of expected output
entries []*writerTestEntry;
}
var writerTests = []*writerTest{
&writerTest{
file: "testdata/writer.tar",
entries: []*writerTestEntry{
&writerTestEntry{
header: &Header{
Name: "small.txt",
Mode: 0640,
Uid: 73025,
Gid: 5000,
Size: 5,
Mtime: 1246508266,
Typeflag: '0',
Uname: "dsymonds",
Gname: "eng",
},
contents: "Kilts",
},
&writerTestEntry{
header: &Header{
Name: "small2.txt",
Mode: 0640,
Uid: 73025,
Gid: 5000,
Size: 11,
Mtime: 1245217492,
Typeflag: '0',
Uname: "dsymonds",
Gname: "eng",
},
contents: "Google.com\n",
},
},
},
// The truncated test file was produced using these commands:
// dd if=/dev/zero bs=1048576 count=16384 > /tmp/16gig.txt
// tar -b 1 -c -f- /tmp/16gig.txt | dd bs=512 count=8 > writer-big.tar
&writerTest{
file: "testdata/writer-big.tar",
entries: []*writerTestEntry{&writerTestEntry{header: &Header{
Name: "tmp/16gig.txt",
Mode: 0640,
Uid: 73025,
Gid: 5000,
Size: 16<<30,
Mtime: 1254699560,
Typeflag: '0',
Uname: "dsymonds",
Gname: "eng",
}
// no contents
}},
},
}
type untarTest struct { type untarTest struct {
file string; file string;
headers []*Header; headers []*Header;
@ -109,15 +174,21 @@ var facts = map[int]string{
2: "2", 2: "2",
10: "3628800", 10: "3628800",
20: "2432902008176640000", 20: "2432902008176640000",
100: 100: "933262154439441526816992388562667004907159682643816214685929"
"933262154439441526816992388562667004907159682643816214685929"
"638952175999932299156089414639761565182862536979208272237582" "638952175999932299156089414639761565182862536979208272237582"
"51185210916864000000000000000000000000" "51185210916864000000000000000000000000",
, }
func usage() {
fmt.Fprintf(os.Stderr,
// TODO(gri): the 2nd string of this string list should not be indented
"usage: godoc package [name ...]\n"
" godoc -http=:6060\n");
flag.PrintDefaults();
os.Exit(2);
} }
func TestReader(t *testing.T) { func TestReader(t *testing.T) {
testLoop: testLoop:
for i, test := range untarTests { for i, test := range untarTests {
f, err := os.Open(test.file, os.O_RDONLY, 0444); f, err := os.Open(test.file, os.O_RDONLY, 0444);
@ -134,10 +205,8 @@ testLoop:
continue testLoop; continue testLoop;
} }
if !reflect.DeepEqual(hdr, header) { if !reflect.DeepEqual(hdr, header) {
t.Errorf( t.Errorf("test %d, entry %d: Incorrect header:\nhave %+v\nwant %+v",
"test %d, entry %d: Incorrect header:\nhave %+v\nwant %+v", i, j, *hdr, *header);
i, j, *hdr, *header
);
} }
} }
hdr, err := tr.Next(); hdr, err := tr.Next();

View File

@ -14,6 +14,8 @@ func _() {
if;{} // no semicolon printed if;{} // no semicolon printed
if expr{} if expr{}
if;expr{} // no semicolon printed if;expr{} // no semicolon printed
if (expr){} // no parens printed
if;((expr)){} // no semicolon and parens printed
if x:=expr;{ if x:=expr;{
use(x)} use(x)}
if x:=expr; expr {use(x)} if x:=expr; expr {use(x)}
@ -26,6 +28,8 @@ func _() {
switch;{} // no semicolon printed switch;{} // no semicolon printed
switch expr {} switch expr {}
switch;expr{} // no semicolon printed switch;expr{} // no semicolon printed
switch (expr) {} // no parens printed
switch;((expr)){} // no semicolon and parens printed
switch x := expr; { default:use( switch x := expr; { default:use(
x) x)
} }
@ -51,6 +55,13 @@ func _() {
use(x); use(x);
use(x); use(x);
} }
switch x {
case 0:
use(x);
case 1: // this comment should have no effect on the previous or next line
use(x);
}
} }
@ -58,9 +69,11 @@ func _() {
func _() { func _() {
for{} for{}
for expr {} for expr {}
for;;{} // no semicolon printed for (expr) {} // no parens printed
for;;{} // no semicolons printed
for x :=expr;; {use( x)} for x :=expr;; {use( x)}
for; expr;{} // no semicolon printed for; expr;{} // no semicolons printed
for; ((expr));{} // no semicolons and parens printed
for; ; expr = false {} for; ; expr = false {}
for x :=expr; expr; {use(x)} for x :=expr; expr; {use(x)}
for x := expr;; expr=false {use(x)} for x := expr;; expr=false {use(x)}
@ -101,3 +114,23 @@ func _() {
} }
} }
// Formatting around labels.
func _() {
L:
}
func _() {
L: _ = 0;
}
func _() {
for {
L1: _ = 0;
L2:
_ = 0;
}
}

View File

@ -14,6 +14,8 @@ func _() {
if {} // no semicolon printed if {} // no semicolon printed
if expr {} if expr {}
if expr {} // no semicolon printed if expr {} // no semicolon printed
if expr {} // no parens printed
if expr {} // no semicolon and parens printed
if x := expr; { if x := expr; {
use(x); use(x);
} }
@ -29,6 +31,8 @@ func _() {
switch {} // no semicolon printed switch {} // no semicolon printed
switch expr {} switch expr {}
switch expr {} // no semicolon printed switch expr {} // no semicolon printed
switch expr {} // no parens printed
switch expr {} // no semicolon and parens printed
switch x := expr; { switch x := expr; {
default: default:
use(x); use(x);
@ -57,6 +61,13 @@ func _() {
use(x); use(x);
use(x); use(x);
} }
switch x {
case 0:
use(x);
case 1: // this comment should have no effect on the previous or next line
use(x);
}
} }
@ -64,11 +75,13 @@ func _() {
func _() { func _() {
for {} for {}
for expr {} for expr {}
for {} // no semicolon printed for expr {} // no parens printed
for {} // no semicolons printed
for x := expr; ; { for x := expr; ; {
use(x); use(x);
} }
for expr {} // no semicolon printed for expr {} // no semicolons printed
for expr {} // no semicolons and parens printed
for ; ; expr = false {} for ; ; expr = false {}
for x := expr; expr; { for x := expr; expr; {
use(x); use(x);
@ -116,3 +129,24 @@ func _() {
} }
} }
// Formatting around labels.
func _() {
L:
;
}
func _() {
L: _ = 0;
}
func _() {
for {
L1: _ = 0;
L2:
_ = 0;
}
}