diff options
Diffstat (limited to 'src/pkg/go/printer')
-rw-r--r-- | src/pkg/go/printer/nodes.go | 72 | ||||
-rw-r--r-- | src/pkg/go/printer/printer.go | 125 | ||||
-rw-r--r-- | src/pkg/go/printer/printer_test.go | 6 | ||||
-rw-r--r-- | src/pkg/go/printer/testdata/comments.golden | 13 | ||||
-rw-r--r-- | src/pkg/go/printer/testdata/comments.input | 12 | ||||
-rw-r--r-- | src/pkg/go/printer/testdata/comments2.golden | 26 | ||||
-rw-r--r-- | src/pkg/go/printer/testdata/comments2.input | 28 | ||||
-rw-r--r-- | src/pkg/go/printer/testdata/declarations.golden | 18 | ||||
-rw-r--r-- | src/pkg/go/printer/testdata/declarations.input | 21 |
9 files changed, 248 insertions, 73 deletions
diff --git a/src/pkg/go/printer/nodes.go b/src/pkg/go/printer/nodes.go index 583c6c370..04b5f1a76 100644 --- a/src/pkg/go/printer/nodes.go +++ b/src/pkg/go/printer/nodes.go @@ -378,10 +378,6 @@ func (p *printer) setLineComment(text string) { p.setComment(&ast.CommentGroup{List: []*ast.Comment{{Slash: token.NoPos, Text: text}}}) } -func (p *printer) isMultiLine(n ast.Node) bool { - return p.lineFor(n.End())-p.lineFor(n.Pos()) > 0 -} - func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool) { lbrace := fields.Opening list := fields.List @@ -428,13 +424,14 @@ func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool) if len(list) == 1 { sep = blank } - newSection := false + var line int for i, f := range list { if i > 0 { - p.linebreak(p.lineFor(f.Pos()), 1, ignore, newSection) + p.linebreak(p.lineFor(f.Pos()), 1, ignore, p.linesFrom(line) > 0) } extraTabs := 0 p.setComment(f.Doc) + p.recordLine(&line) if len(f.Names) > 0 { // named fields p.identList(f.Names, false) @@ -460,7 +457,6 @@ func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool) } p.setComment(f.Comment) } - newSection = p.isMultiLine(f) } if isIncomplete { if len(list) > 0 { @@ -472,12 +468,13 @@ func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool) } else { // interface - newSection := false + var line int for i, f := range list { if i > 0 { - p.linebreak(p.lineFor(f.Pos()), 1, ignore, newSection) + p.linebreak(p.lineFor(f.Pos()), 1, ignore, p.linesFrom(line) > 0) } p.setComment(f.Doc) + p.recordLine(&line) if ftyp, isFtyp := f.Type.(*ast.FuncType); isFtyp { // method p.expr(f.Names[0]) @@ -487,7 +484,6 @@ func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool) p.expr(f.Type) } p.setComment(f.Comment) - newSection = p.isMultiLine(f) } if isIncomplete { if len(list) > 0 { @@ -826,10 +822,16 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) { } p.print(x.Lbrace, token.LBRACE) p.exprList(x.Lbrace, x.Elts, 1, commaTerm, x.Rbrace) - // do not insert extra line breaks because of comments before - // the closing '}' as it might break the code if there is no - // trailing ',' - p.print(noExtraLinebreak, x.Rbrace, token.RBRACE, noExtraLinebreak) + // do not insert extra line break following a /*-style comment + // before the closing '}' as it might break the code if there + // is no trailing ',' + mode := noExtraLinebreak + // do not insert extra blank following a /*-style comment + // before the closing '}' unless the literal is empty + if len(x.Elts) > 0 { + mode |= noExtraBlank + } + p.print(mode, x.Rbrace, token.RBRACE, mode) case *ast.Ellipsis: p.print(token.ELLIPSIS) @@ -901,20 +903,31 @@ func (p *printer) stmtList(list []ast.Stmt, nindent int, nextIsRBrace bool) { if nindent > 0 { p.print(indent) } - multiLine := false + var line int i := 0 for _, s := range list { // ignore empty statements (was issue 3466) if _, isEmpty := s.(*ast.EmptyStmt); !isEmpty { - // _indent == 0 only for lists of switch/select case clauses; + // nindent == 0 only for lists of switch/select case clauses; // in those cases each clause is a new section if len(p.output) > 0 { // only print line break if we are not at the beginning of the output // (i.e., we are not printing only a partial program) - p.linebreak(p.lineFor(s.Pos()), 1, ignore, i == 0 || nindent == 0 || multiLine) + p.linebreak(p.lineFor(s.Pos()), 1, ignore, i == 0 || nindent == 0 || p.linesFrom(line) > 0) } + p.recordLine(&line) p.stmt(s, nextIsRBrace && i == len(list)-1) - multiLine = p.isMultiLine(s) + // labeled statements put labels on a separate line, but here + // we only care about the start line of the actual statement + // without label - correct line for each label + for t := s; ; { + lt, _ := t.(*ast.LabeledStmt) + if lt == nil { + break + } + line++ + t = lt.Stmt + } i++ } } @@ -1375,22 +1388,22 @@ func (p *printer) genDecl(d *ast.GenDecl) { // two or more grouped const/var declarations: // determine if the type column must be kept keepType := keepTypeColumn(d.Specs) - newSection := false + var line int for i, s := range d.Specs { if i > 0 { - p.linebreak(p.lineFor(s.Pos()), 1, ignore, newSection) + p.linebreak(p.lineFor(s.Pos()), 1, ignore, p.linesFrom(line) > 0) } + p.recordLine(&line) p.valueSpec(s.(*ast.ValueSpec), keepType[i]) - newSection = p.isMultiLine(s) } } else { - newSection := false + var line int for i, s := range d.Specs { if i > 0 { - p.linebreak(p.lineFor(s.Pos()), 1, ignore, newSection) + p.linebreak(p.lineFor(s.Pos()), 1, ignore, p.linesFrom(line) > 0) } + p.recordLine(&line) p.spec(s, n, false) - newSection = p.isMultiLine(s) } } p.print(unindent, formfeed) @@ -1448,13 +1461,16 @@ func (p *printer) bodySize(b *ast.BlockStmt, maxSize int) int { // opening and closing brace are on different lines - don't make it a one-liner return maxSize + 1 } - if len(b.List) > 5 || p.commentBefore(p.posFor(pos2)) { - // too many statements or there is a comment inside - don't make it a one-liner + if len(b.List) > 5 { + // too many statements - don't make it a one-liner return maxSize + 1 } // otherwise, estimate body size - bodySize := 0 + bodySize := p.commentSizeBefore(p.posFor(pos2)) for i, s := range b.List { + if bodySize > maxSize { + break // no need to continue + } if i > 0 { bodySize += 2 // space for a semicolon and blank } @@ -1488,7 +1504,7 @@ func (p *printer) adjBlock(headerSize int, sep whiteSpace, b *ast.BlockStmt) { } p.print(blank) } - p.print(b.Rbrace, token.RBRACE) + p.print(noExtraLinebreak, b.Rbrace, token.RBRACE, noExtraLinebreak) return } diff --git a/src/pkg/go/printer/printer.go b/src/pkg/go/printer/printer.go index e06d2edfb..280c697a0 100644 --- a/src/pkg/go/printer/printer.go +++ b/src/pkg/go/printer/printer.go @@ -39,9 +39,17 @@ const ( type pmode int const ( - noExtraLinebreak pmode = 1 << iota + noExtraBlank pmode = 1 << iota // disables extra blank after /*-style comment + noExtraLinebreak // disables extra line break after /*-style comment ) +type commentInfo struct { + cindex int // current comment index + comment *ast.CommentGroup // = printer.comments[cindex]; or nil + commentOffset int // = printer.posFor(printer.comments[cindex].List[0].Pos()).Offset; or infinity + commentNewline bool // true if the comment group contains newlines +} + type printer struct { // Configuration (does not change after initialization) Config @@ -52,7 +60,8 @@ type printer struct { indent int // current indentation mode pmode // current printer mode impliedSemi bool // if set, a linebreak implies a semicolon - lastTok token.Token // the last token printed (token.ILLEGAL if it's whitespace) + lastTok token.Token // last token printed (token.ILLEGAL if it's whitespace) + prevOpen token.Token // previous non-brace "open" token (, [, or token.ILLEGAL wsbuf []whiteSpace // delayed white space // Positions @@ -61,19 +70,17 @@ type printer struct { // white space). If there's a difference and SourcePos is set in // ConfigMode, //line comments are used in the output to restore // original source positions for a reader. - pos token.Position // current position in AST (source) space - out token.Position // current position in output space - last token.Position // value of pos after calling writeString + pos token.Position // current position in AST (source) space + out token.Position // current position in output space + last token.Position // value of pos after calling writeString + linePtr *int // if set, record out.Line for the next token in *linePtr // The list of all source comments, in order of appearance. comments []*ast.CommentGroup // may be nil - cindex int // current comment index useNodeComments bool // if not set, ignore lead and line comments of nodes // Information about p.comments[p.cindex]; set up by nextComment. - comment *ast.CommentGroup // = p.comments[p.cindex]; or nil - commentOffset int // = p.posFor(p.comments[p.cindex].List[0].Pos()).Offset; or infinity - commentNewline bool // true if the comment group contains newlines + commentInfo // Cache of already computed node sizes. nodeSizes map[ast.Node]int @@ -93,6 +100,14 @@ func (p *printer) init(cfg *Config, fset *token.FileSet, nodeSizes map[ast.Node] p.cachedPos = -1 } +func (p *printer) internalError(msg ...interface{}) { + if debug { + fmt.Print(p.pos.String() + ": ") + fmt.Println(msg...) + panic("go/printer") + } +} + // commentsHaveNewline reports whether a list of comments belonging to // an *ast.CommentGroup contains newlines. Because the position information // may only be partially correct, we also have to read the comment text. @@ -129,12 +144,49 @@ func (p *printer) nextComment() { p.commentOffset = infinity } -func (p *printer) internalError(msg ...interface{}) { - if debug { - fmt.Print(p.pos.String() + ": ") - fmt.Println(msg...) - panic("go/printer") +// commentBefore returns true iff the current comment group occurs +// before the next position in the source code and printing it does +// not introduce implicit semicolons. +// +func (p *printer) commentBefore(next token.Position) bool { + return p.commentOffset < next.Offset && (!p.impliedSemi || !p.commentNewline) +} + +// commentSizeBefore returns the estimated size of the +// comments on the same line before the next position. +// +func (p *printer) commentSizeBefore(next token.Position) int { + // save/restore current p.commentInfo (p.nextComment() modifies it) + defer func(info commentInfo) { + p.commentInfo = info + }(p.commentInfo) + + size := 0 + for p.commentBefore(next) { + for _, c := range p.comment.List { + size += len(c.Text) + } + p.nextComment() } + return size +} + +// recordLine records the output line number for the next non-whitespace +// token in *linePtr. It is used to compute an accurate line number for a +// formatted construct, independent of pending (not yet emitted) whitespace +// or comments. +// +func (p *printer) recordLine(linePtr *int) { + p.linePtr = linePtr +} + +// linesFrom returns the number of output lines between the current +// output line and the line argument, ignoring any pending (not yet +// emitted) whitespace or comments. It is used to compute an accurate +// size (in number of lines) for a formatted construct. +// +func (p *printer) linesFrom(line int) int { + return p.out.Line - line } func (p *printer) posFor(pos token.Pos) token.Position { @@ -675,10 +727,14 @@ func (p *printer) intersperseComments(next token.Position, tok token.Token) (wro if last != nil { // if the last comment is a /*-style comment and the next item - // follows on the same line but is not a comma or a "closing" - // token, add an extra blank for separation - if last.Text[1] == '*' && p.lineFor(last.Pos()) == next.Line && tok != token.COMMA && - tok != token.RPAREN && tok != token.RBRACK && tok != token.RBRACE { + // follows on the same line but is not a comma, and not a "closing" + // token immediately following its corresponding "opening" token, + // add an extra blank for separation unless explicitly disabled + if p.mode&noExtraBlank == 0 && + last.Text[1] == '*' && p.lineFor(last.Pos()) == next.Line && + tok != token.COMMA && + (tok != token.RPAREN || p.prevOpen == token.LPAREN) && + (tok != token.RBRACK || p.prevOpen == token.LBRACK) { p.writeByte(' ', 1) } // ensure that there is a line break after a //-style comment, @@ -735,12 +791,8 @@ func (p *printer) writeWhitespace(n int) { } // shift remaining entries down - i := 0 - for ; n < len(p.wsbuf); n++ { - p.wsbuf[i] = p.wsbuf[n] - i++ - } - p.wsbuf = p.wsbuf[0:i] + l := copy(p.wsbuf, p.wsbuf[n:]) + p.wsbuf = p.wsbuf[:l] } // ---------------------------------------------------------------------------- @@ -790,6 +842,17 @@ func (p *printer) print(args ...interface{}) { var isLit bool var impliedSemi bool // value for p.impliedSemi after this arg + // record previous opening token, if any + switch p.lastTok { + case token.ILLEGAL: + // ignore (white space) + case token.LPAREN, token.LBRACK: + p.prevOpen = p.lastTok + default: + // other tokens followed any opening token + p.prevOpen = token.ILLEGAL + } + switch x := arg.(type) { case pmode: // toggle printer mode @@ -899,19 +962,17 @@ func (p *printer) print(args ...interface{}) { } } + // the next token starts now - record its line number if requested + if p.linePtr != nil { + *p.linePtr = p.out.Line + p.linePtr = nil + } + p.writeString(next, data, isLit) p.impliedSemi = impliedSemi } } -// commentBefore returns true iff the current comment group occurs -// before the next position in the source code and printing it does -// not introduce implicit semicolons. -// -func (p *printer) commentBefore(next token.Position) (result bool) { - return p.commentOffset < next.Offset && (!p.impliedSemi || !p.commentNewline) -} - // flush prints any pending comments and whitespace occurring textually // before the position of the next token tok. The flush result indicates // if a newline was written or if a formfeed was dropped from the whitespace diff --git a/src/pkg/go/printer/printer_test.go b/src/pkg/go/printer/printer_test.go index 8454ac12b..306928a69 100644 --- a/src/pkg/go/printer/printer_test.go +++ b/src/pkg/go/printer/printer_test.go @@ -63,7 +63,7 @@ func format(src []byte, mode checkMode) ([]byte, error) { return nil, fmt.Errorf("print: %s", err) } - // make sure formated output is syntactically correct + // make sure formatted output is syntactically correct res := buf.Bytes() if _, err := parser.ParseFile(fset, "", res, 0); err != nil { return nil, fmt.Errorf("re-parse: %s\n%s", err, buf.Bytes()) @@ -179,7 +179,7 @@ func check(t *testing.T, source, golden string, mode checkMode) { // test running past time out t.Errorf("%s: running too slowly", source) case <-cc: - // test finished within alloted time margin + // test finished within allotted time margin } } @@ -212,7 +212,7 @@ func TestFiles(t *testing.T) { } } -// TestLineComments, using a simple test case, checks that consequtive line +// TestLineComments, using a simple test case, checks that consecutive line // comments are properly terminated with a newline even if the AST position // information is incorrect. // diff --git a/src/pkg/go/printer/testdata/comments.golden b/src/pkg/go/printer/testdata/comments.golden index 610a42a68..b1af7958a 100644 --- a/src/pkg/go/printer/testdata/comments.golden +++ b/src/pkg/go/printer/testdata/comments.golden @@ -494,16 +494,21 @@ func _() { func _( /* this */ x /* is */ /* an */ int) { } -func _( /* no params */) {} +func _( /* no params - extra blank before and after comment */ ) {} +func _(a, b int /* params - no extra blank after comment */) {} + +func _() { f( /* no args - extra blank before and after comment */ ) } +func _() { f(a, b /* args - no extra blank after comment */) } func _() { - f( /* no args */) + f( /* no args - extra blank before and after comment */ ) + f(a, b /* args - no extra blank after comment */) } func ( /* comment1 */ T /* comment2 */) _() {} -func _() { /* one-line functions with comments are formatted as multi-line functions */ -} +func _() { /* "short-ish one-line functions with comments are formatted as multi-line functions */ } +func _() { x := 0; /* comment */ y = x /* comment */ } func _() { _ = 0 diff --git a/src/pkg/go/printer/testdata/comments.input b/src/pkg/go/printer/testdata/comments.input index d121dd4be..983e2b2c9 100644 --- a/src/pkg/go/printer/testdata/comments.input +++ b/src/pkg/go/printer/testdata/comments.input @@ -500,15 +500,21 @@ func _() { func _(/* this */x/* is *//* an */ int) { } -func _(/* no params */) {} +func _(/* no params - extra blank before and after comment */) {} +func _(a, b int /* params - no extra blank after comment */) {} + +func _() { f(/* no args - extra blank before and after comment */) } +func _() { f(a, b /* args - no extra blank after comment */) } func _() { - f(/* no args */) + f(/* no args - extra blank before and after comment */) + f(a, b /* args - no extra blank after comment */) } func (/* comment1 */ T /* comment2 */) _() {} -func _() { /* one-line functions with comments are formatted as multi-line functions */ } +func _() { /* "short-ish one-line functions with comments are formatted as multi-line functions */ } +func _() { x := 0; /* comment */ y = x /* comment */ } func _() { _ = 0 diff --git a/src/pkg/go/printer/testdata/comments2.golden b/src/pkg/go/printer/testdata/comments2.golden index d3b50bf3e..7676a26c1 100644 --- a/src/pkg/go/printer/testdata/comments2.golden +++ b/src/pkg/go/printer/testdata/comments2.golden @@ -77,3 +77,29 @@ func main() { println("test") } } + +func issue5623() { +L: + _ = yyyyyyyyyyyyyyyy // comment - should be aligned + _ = xxxxxxxxxxxxxxxxxxxxxxxxxxxx /* comment */ + + _ = yyyyyyyyyyyyyyyy /* comment - should be aligned */ + _ = xxxxxxxxxxxxxxxxxxxxxxxxxxxx // comment + +LLLLLLL: + _ = yyyyyyyyyyyyyyyy // comment - should be aligned + _ = xxxxxxxxxxxxxxxxxxxxxxxxxxxx // comment + +LL: +LLLLL: + _ = xxxxxxxxxxxxxxxxxxxxxxxxxxxx /* comment */ + _ = yyyyyyyyyyyyyyyy /* comment - should be aligned */ + + _ = xxxxxxxxxxxxxxxxxxxxxxxxxxxx // comment + _ = yyyyyyyyyyyyyyyy // comment - should be aligned + + // test case from issue +label: + mask := uint64(1)<<c - 1 // Allocation mask + used := atomic.LoadUint64(&h.used) // Current allocations +} diff --git a/src/pkg/go/printer/testdata/comments2.input b/src/pkg/go/printer/testdata/comments2.input index 6f8c85c94..4a055c827 100644 --- a/src/pkg/go/printer/testdata/comments2.input +++ b/src/pkg/go/printer/testdata/comments2.input @@ -76,4 +76,30 @@ prints test 5 times for i := 0; i < 5; i++ { println("test") } -}
\ No newline at end of file +} + +func issue5623() { +L: + _ = yyyyyyyyyyyyyyyy // comment - should be aligned + _ = xxxxxxxxxxxxxxxxxxxxxxxxxxxx /* comment */ + + _ = yyyyyyyyyyyyyyyy /* comment - should be aligned */ + _ = xxxxxxxxxxxxxxxxxxxxxxxxxxxx // comment + +LLLLLLL: + _ = yyyyyyyyyyyyyyyy // comment - should be aligned + _ = xxxxxxxxxxxxxxxxxxxxxxxxxxxx // comment + +LL: +LLLLL: + _ = xxxxxxxxxxxxxxxxxxxxxxxxxxxx /* comment */ + _ = yyyyyyyyyyyyyyyy /* comment - should be aligned */ + + _ = xxxxxxxxxxxxxxxxxxxxxxxxxxxx // comment + _ = yyyyyyyyyyyyyyyy // comment - should be aligned + +// test case from issue +label: + mask := uint64(1)<<c - 1 // Allocation mask + used := atomic.LoadUint64(&h.used) // Current allocations +} diff --git a/src/pkg/go/printer/testdata/declarations.golden b/src/pkg/go/printer/testdata/declarations.golden index 0331615e5..a27f21fc8 100644 --- a/src/pkg/go/printer/testdata/declarations.golden +++ b/src/pkg/go/printer/testdata/declarations.golden @@ -397,6 +397,21 @@ func _() { } } +// use the formatted output rather than the input to decide when to align +// (was issue 4505) +const ( + short = 2 * (1 + 2) + aMuchLongerName = 3 +) + +var ( + short = X{} + aMuchLongerName = X{} + + x1 = X{} // foo + x2 = X{} // foo +) + func _() { type ( xxxxxx int @@ -723,7 +738,8 @@ func _() int { } // making function declarations safe for new semicolon rules -func _() { /* multi-line func because of comment */ +func _() { /* single-line function because of "short-ish" comment */ } +func _() { /* multi-line function because of "long-ish" comment - much more comment text is following here */ /* and more */ } func _() { diff --git a/src/pkg/go/printer/testdata/declarations.input b/src/pkg/go/printer/testdata/declarations.input index dbdbdfe74..d9951d386 100644 --- a/src/pkg/go/printer/testdata/declarations.input +++ b/src/pkg/go/printer/testdata/declarations.input @@ -409,6 +409,24 @@ func _() { } } +// use the formatted output rather than the input to decide when to align +// (was issue 4505) +const ( + short = 2 * ( + 1 + 2) + aMuchLongerName = 3 +) + +var ( + short = X{ + } + aMuchLongerName = X{} + + x1 = X{} // foo + x2 = X{ + } // foo +) + func _() { type ( xxxxxx int @@ -737,7 +755,8 @@ func _() int { // making function declarations safe for new semicolon rules -func _() { /* multi-line func because of comment */ } +func _() { /* single-line function because of "short-ish" comment */ } +func _() { /* multi-line function because of "long-ish" comment - much more comment text is following here */ /* and more */ } func _() { /* multi-line func because block is on multiple lines */ } |