summaryrefslogtreecommitdiff
path: root/src/pkg/go/printer
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/go/printer')
-rw-r--r--src/pkg/go/printer/nodes.go72
-rw-r--r--src/pkg/go/printer/printer.go125
-rw-r--r--src/pkg/go/printer/printer_test.go6
-rw-r--r--src/pkg/go/printer/testdata/comments.golden13
-rw-r--r--src/pkg/go/printer/testdata/comments.input12
-rw-r--r--src/pkg/go/printer/testdata/comments2.golden26
-rw-r--r--src/pkg/go/printer/testdata/comments2.input28
-rw-r--r--src/pkg/go/printer/testdata/declarations.golden18
-rw-r--r--src/pkg/go/printer/testdata/declarations.input21
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 */ }