* gcc.dg/guality/guality.exp: Skip on AIX.
[official-gcc.git] / libgo / go / go / printer / nodes.go
blob7cd068e22ef45f754a9bec7a31c154b9715a248a
1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // This file implements printing of AST nodes; specifically
6 // expressions, statements, declarations, and files. It uses
7 // the print functionality implemented in printer.go.
9 package printer
11 import (
12 "bytes"
13 "go/ast"
14 "go/token"
15 "unicode/utf8"
18 // Formatting issues:
19 // - better comment formatting for /*-style comments at the end of a line (e.g. a declaration)
20 // when the comment spans multiple lines; if such a comment is just two lines, formatting is
21 // not idempotent
22 // - formatting of expression lists
23 // - should use blank instead of tab to separate one-line function bodies from
24 // the function header unless there is a group of consecutive one-liners
26 // ----------------------------------------------------------------------------
27 // Common AST nodes.
29 // Print as many newlines as necessary (but at least min newlines) to get to
30 // the current line. ws is printed before the first line break. If newSection
31 // is set, the first line break is printed as formfeed. Returns true if any
32 // line break was printed; returns false otherwise.
34 // TODO(gri): linebreak may add too many lines if the next statement at "line"
35 // is preceded by comments because the computation of n assumes
36 // the current position before the comment and the target position
37 // after the comment. Thus, after interspersing such comments, the
38 // space taken up by them is not considered to reduce the number of
39 // linebreaks. At the moment there is no easy way to know about
40 // future (not yet interspersed) comments in this function.
42 func (p *printer) linebreak(line, min int, ws whiteSpace, newSection bool) (printedBreak bool) {
43 n := nlimit(line - p.pos.Line)
44 if n < min {
45 n = min
47 if n > 0 {
48 p.print(ws)
49 if newSection {
50 p.print(formfeed)
51 n--
53 for ; n > 0; n-- {
54 p.print(newline)
56 printedBreak = true
58 return
61 // setComment sets g as the next comment if g != nil and if node comments
62 // are enabled - this mode is used when printing source code fragments such
63 // as exports only. It assumes that there is no pending comment in p.comments
64 // and at most one pending comment in the p.comment cache.
65 func (p *printer) setComment(g *ast.CommentGroup) {
66 if g == nil || !p.useNodeComments {
67 return
69 if p.comments == nil {
70 // initialize p.comments lazily
71 p.comments = make([]*ast.CommentGroup, 1)
72 } else if p.cindex < len(p.comments) {
73 // for some reason there are pending comments; this
74 // should never happen - handle gracefully and flush
75 // all comments up to g, ignore anything after that
76 p.flush(p.posFor(g.List[0].Pos()), token.ILLEGAL)
77 p.comments = p.comments[0:1]
78 // in debug mode, report error
79 p.internalError("setComment found pending comments")
81 p.comments[0] = g
82 p.cindex = 0
83 // don't overwrite any pending comment in the p.comment cache
84 // (there may be a pending comment when a line comment is
85 // immediately followed by a lead comment with no other
86 // tokens between)
87 if p.commentOffset == infinity {
88 p.nextComment() // get comment ready for use
92 type exprListMode uint
94 const (
95 commaTerm exprListMode = 1 << iota // list is optionally terminated by a comma
96 noIndent // no extra indentation in multi-line lists
99 // If indent is set, a multi-line identifier list is indented after the
100 // first linebreak encountered.
101 func (p *printer) identList(list []*ast.Ident, indent bool) {
102 // convert into an expression list so we can re-use exprList formatting
103 xlist := make([]ast.Expr, len(list))
104 for i, x := range list {
105 xlist[i] = x
107 var mode exprListMode
108 if !indent {
109 mode = noIndent
111 p.exprList(token.NoPos, xlist, 1, mode, token.NoPos)
114 // Print a list of expressions. If the list spans multiple
115 // source lines, the original line breaks are respected between
116 // expressions.
118 // TODO(gri) Consider rewriting this to be independent of []ast.Expr
119 // so that we can use the algorithm for any kind of list
120 // (e.g., pass list via a channel over which to range).
121 func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exprListMode, next0 token.Pos) {
122 if len(list) == 0 {
123 return
126 prev := p.posFor(prev0)
127 next := p.posFor(next0)
128 line := p.lineFor(list[0].Pos())
129 endLine := p.lineFor(list[len(list)-1].End())
131 if prev.IsValid() && prev.Line == line && line == endLine {
132 // all list entries on a single line
133 for i, x := range list {
134 if i > 0 {
135 // use position of expression following the comma as
136 // comma position for correct comment placement
137 p.print(x.Pos(), token.COMMA, blank)
139 p.expr0(x, depth)
141 return
144 // list entries span multiple lines;
145 // use source code positions to guide line breaks
147 // don't add extra indentation if noIndent is set;
148 // i.e., pretend that the first line is already indented
149 ws := ignore
150 if mode&noIndent == 0 {
151 ws = indent
154 // the first linebreak is always a formfeed since this section must not
155 // depend on any previous formatting
156 prevBreak := -1 // index of last expression that was followed by a linebreak
157 if prev.IsValid() && prev.Line < line && p.linebreak(line, 0, ws, true) {
158 ws = ignore
159 prevBreak = 0
162 // initialize expression/key size: a zero value indicates expr/key doesn't fit on a single line
163 size := 0
165 // print all list elements
166 for i, x := range list {
167 prevLine := line
168 line = p.lineFor(x.Pos())
170 // determine if the next linebreak, if any, needs to use formfeed:
171 // in general, use the entire node size to make the decision; for
172 // key:value expressions, use the key size
173 // TODO(gri) for a better result, should probably incorporate both
174 // the key and the node size into the decision process
175 useFF := true
177 // determine element size: all bets are off if we don't have
178 // position information for the previous and next token (likely
179 // generated code - simply ignore the size in this case by setting
180 // it to 0)
181 prevSize := size
182 const infinity = 1e6 // larger than any source line
183 size = p.nodeSize(x, infinity)
184 pair, isPair := x.(*ast.KeyValueExpr)
185 if size <= infinity && prev.IsValid() && next.IsValid() {
186 // x fits on a single line
187 if isPair {
188 size = p.nodeSize(pair.Key, infinity) // size <= infinity
190 } else {
191 // size too large or we don't have good layout information
192 size = 0
195 // if the previous line and the current line had single-
196 // line-expressions and the key sizes are small or the
197 // the ratio between the key sizes does not exceed a
198 // threshold, align columns and do not use formfeed
199 if prevSize > 0 && size > 0 {
200 const smallSize = 20
201 if prevSize <= smallSize && size <= smallSize {
202 useFF = false
203 } else {
204 const r = 4 // threshold
205 ratio := float64(size) / float64(prevSize)
206 useFF = ratio <= 1.0/r || r <= ratio
210 if i > 0 {
211 needsLinebreak := prevLine < line && prevLine > 0 && line > 0
212 // use position of expression following the comma as
213 // comma position for correct comment placement, but
214 // only if the expression is on the same line
215 if !needsLinebreak {
216 p.print(x.Pos())
218 p.print(token.COMMA)
219 needsBlank := true
220 if needsLinebreak {
221 // lines are broken using newlines so comments remain aligned
222 // unless forceFF is set or there are multiple expressions on
223 // the same line in which case formfeed is used
224 if p.linebreak(line, 0, ws, useFF || prevBreak+1 < i) {
225 ws = ignore
226 prevBreak = i
227 needsBlank = false // we got a line break instead
230 if needsBlank {
231 p.print(blank)
235 if isPair && size > 0 && len(list) > 1 {
236 // we have a key:value expression that fits onto one line and
237 // is in a list with more then one entry: use a column for the
238 // key such that consecutive entries can align if possible
239 p.expr(pair.Key)
240 p.print(pair.Colon, token.COLON, vtab)
241 p.expr(pair.Value)
242 } else {
243 p.expr0(x, depth)
247 if mode&commaTerm != 0 && next.IsValid() && p.pos.Line < next.Line {
248 // print a terminating comma if the next token is on a new line
249 p.print(token.COMMA)
250 if ws == ignore && mode&noIndent == 0 {
251 // unindent if we indented
252 p.print(unindent)
254 p.print(formfeed) // terminating comma needs a line break to look good
255 return
258 if ws == ignore && mode&noIndent == 0 {
259 // unindent if we indented
260 p.print(unindent)
264 func (p *printer) parameters(fields *ast.FieldList) {
265 p.print(fields.Opening, token.LPAREN)
266 if len(fields.List) > 0 {
267 prevLine := p.lineFor(fields.Opening)
268 ws := indent
269 for i, par := range fields.List {
270 // determine par begin and end line (may be different
271 // if there are multiple parameter names for this par
272 // or the type is on a separate line)
273 var parLineBeg int
274 if len(par.Names) > 0 {
275 parLineBeg = p.lineFor(par.Names[0].Pos())
276 } else {
277 parLineBeg = p.lineFor(par.Type.Pos())
279 var parLineEnd = p.lineFor(par.Type.End())
280 // separating "," if needed
281 needsLinebreak := 0 < prevLine && prevLine < parLineBeg
282 if i > 0 {
283 // use position of parameter following the comma as
284 // comma position for correct comma placement, but
285 // only if the next parameter is on the same line
286 if !needsLinebreak {
287 p.print(par.Pos())
289 p.print(token.COMMA)
291 // separator if needed (linebreak or blank)
292 if needsLinebreak && p.linebreak(parLineBeg, 0, ws, true) {
293 // break line if the opening "(" or previous parameter ended on a different line
294 ws = ignore
295 } else if i > 0 {
296 p.print(blank)
298 // parameter names
299 if len(par.Names) > 0 {
300 // Very subtle: If we indented before (ws == ignore), identList
301 // won't indent again. If we didn't (ws == indent), identList will
302 // indent if the identList spans multiple lines, and it will outdent
303 // again at the end (and still ws == indent). Thus, a subsequent indent
304 // by a linebreak call after a type, or in the next multi-line identList
305 // will do the right thing.
306 p.identList(par.Names, ws == indent)
307 p.print(blank)
309 // parameter type
310 p.expr(stripParensAlways(par.Type))
311 prevLine = parLineEnd
313 // if the closing ")" is on a separate line from the last parameter,
314 // print an additional "," and line break
315 if closing := p.lineFor(fields.Closing); 0 < prevLine && prevLine < closing {
316 p.print(token.COMMA)
317 p.linebreak(closing, 0, ignore, true)
319 // unindent if we indented
320 if ws == ignore {
321 p.print(unindent)
324 p.print(fields.Closing, token.RPAREN)
327 func (p *printer) signature(params, result *ast.FieldList) {
328 if params != nil {
329 p.parameters(params)
330 } else {
331 p.print(token.LPAREN, token.RPAREN)
333 n := result.NumFields()
334 if n > 0 {
335 // result != nil
336 p.print(blank)
337 if n == 1 && result.List[0].Names == nil {
338 // single anonymous result; no ()'s
339 p.expr(stripParensAlways(result.List[0].Type))
340 return
342 p.parameters(result)
346 func identListSize(list []*ast.Ident, maxSize int) (size int) {
347 for i, x := range list {
348 if i > 0 {
349 size += len(", ")
351 size += utf8.RuneCountInString(x.Name)
352 if size >= maxSize {
353 break
356 return
359 func (p *printer) isOneLineFieldList(list []*ast.Field) bool {
360 if len(list) != 1 {
361 return false // allow only one field
363 f := list[0]
364 if f.Tag != nil || f.Comment != nil {
365 return false // don't allow tags or comments
367 // only name(s) and type
368 const maxSize = 30 // adjust as appropriate, this is an approximate value
369 namesSize := identListSize(f.Names, maxSize)
370 if namesSize > 0 {
371 namesSize = 1 // blank between names and types
373 typeSize := p.nodeSize(f.Type, maxSize)
374 return namesSize+typeSize <= maxSize
377 func (p *printer) setLineComment(text string) {
378 p.setComment(&ast.CommentGroup{List: []*ast.Comment{{Slash: token.NoPos, Text: text}}})
381 func (p *printer) isMultiLine(n ast.Node) bool {
382 return p.lineFor(n.End())-p.lineFor(n.Pos()) > 0
385 func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool) {
386 lbrace := fields.Opening
387 list := fields.List
388 rbrace := fields.Closing
389 hasComments := isIncomplete || p.commentBefore(p.posFor(rbrace))
390 srcIsOneLine := lbrace.IsValid() && rbrace.IsValid() && p.lineFor(lbrace) == p.lineFor(rbrace)
392 if !hasComments && srcIsOneLine {
393 // possibly a one-line struct/interface
394 if len(list) == 0 {
395 // no blank between keyword and {} in this case
396 p.print(lbrace, token.LBRACE, rbrace, token.RBRACE)
397 return
398 } else if isStruct && p.isOneLineFieldList(list) { // for now ignore interfaces
399 // small enough - print on one line
400 // (don't use identList and ignore source line breaks)
401 p.print(lbrace, token.LBRACE, blank)
402 f := list[0]
403 for i, x := range f.Names {
404 if i > 0 {
405 // no comments so no need for comma position
406 p.print(token.COMMA, blank)
408 p.expr(x)
410 if len(f.Names) > 0 {
411 p.print(blank)
413 p.expr(f.Type)
414 p.print(blank, rbrace, token.RBRACE)
415 return
418 // hasComments || !srcIsOneLine
420 p.print(blank, lbrace, token.LBRACE, indent)
421 if hasComments || len(list) > 0 {
422 p.print(formfeed)
425 if isStruct {
427 sep := vtab
428 if len(list) == 1 {
429 sep = blank
431 newSection := false
432 for i, f := range list {
433 if i > 0 {
434 p.linebreak(p.lineFor(f.Pos()), 1, ignore, newSection)
436 extraTabs := 0
437 p.setComment(f.Doc)
438 if len(f.Names) > 0 {
439 // named fields
440 p.identList(f.Names, false)
441 p.print(sep)
442 p.expr(f.Type)
443 extraTabs = 1
444 } else {
445 // anonymous field
446 p.expr(f.Type)
447 extraTabs = 2
449 if f.Tag != nil {
450 if len(f.Names) > 0 && sep == vtab {
451 p.print(sep)
453 p.print(sep)
454 p.expr(f.Tag)
455 extraTabs = 0
457 if f.Comment != nil {
458 for ; extraTabs > 0; extraTabs-- {
459 p.print(sep)
461 p.setComment(f.Comment)
463 newSection = p.isMultiLine(f)
465 if isIncomplete {
466 if len(list) > 0 {
467 p.print(formfeed)
469 p.flush(p.posFor(rbrace), token.RBRACE) // make sure we don't lose the last line comment
470 p.setLineComment("// contains filtered or unexported fields")
473 } else { // interface
475 newSection := false
476 for i, f := range list {
477 if i > 0 {
478 p.linebreak(p.lineFor(f.Pos()), 1, ignore, newSection)
480 p.setComment(f.Doc)
481 if ftyp, isFtyp := f.Type.(*ast.FuncType); isFtyp {
482 // method
483 p.expr(f.Names[0])
484 p.signature(ftyp.Params, ftyp.Results)
485 } else {
486 // embedded interface
487 p.expr(f.Type)
489 p.setComment(f.Comment)
490 newSection = p.isMultiLine(f)
492 if isIncomplete {
493 if len(list) > 0 {
494 p.print(formfeed)
496 p.flush(p.posFor(rbrace), token.RBRACE) // make sure we don't lose the last line comment
497 p.setLineComment("// contains filtered or unexported methods")
501 p.print(unindent, formfeed, rbrace, token.RBRACE)
504 // ----------------------------------------------------------------------------
505 // Expressions
507 func walkBinary(e *ast.BinaryExpr) (has4, has5 bool, maxProblem int) {
508 switch e.Op.Precedence() {
509 case 4:
510 has4 = true
511 case 5:
512 has5 = true
515 switch l := e.X.(type) {
516 case *ast.BinaryExpr:
517 if l.Op.Precedence() < e.Op.Precedence() {
518 // parens will be inserted.
519 // pretend this is an *ast.ParenExpr and do nothing.
520 break
522 h4, h5, mp := walkBinary(l)
523 has4 = has4 || h4
524 has5 = has5 || h5
525 if maxProblem < mp {
526 maxProblem = mp
530 switch r := e.Y.(type) {
531 case *ast.BinaryExpr:
532 if r.Op.Precedence() <= e.Op.Precedence() {
533 // parens will be inserted.
534 // pretend this is an *ast.ParenExpr and do nothing.
535 break
537 h4, h5, mp := walkBinary(r)
538 has4 = has4 || h4
539 has5 = has5 || h5
540 if maxProblem < mp {
541 maxProblem = mp
544 case *ast.StarExpr:
545 if e.Op == token.QUO { // `*/`
546 maxProblem = 5
549 case *ast.UnaryExpr:
550 switch e.Op.String() + r.Op.String() {
551 case "/*", "&&", "&^":
552 maxProblem = 5
553 case "++", "--":
554 if maxProblem < 4 {
555 maxProblem = 4
559 return
562 func cutoff(e *ast.BinaryExpr, depth int) int {
563 has4, has5, maxProblem := walkBinary(e)
564 if maxProblem > 0 {
565 return maxProblem + 1
567 if has4 && has5 {
568 if depth == 1 {
569 return 5
571 return 4
573 if depth == 1 {
574 return 6
576 return 4
579 func diffPrec(expr ast.Expr, prec int) int {
580 x, ok := expr.(*ast.BinaryExpr)
581 if !ok || prec != x.Op.Precedence() {
582 return 1
584 return 0
587 func reduceDepth(depth int) int {
588 depth--
589 if depth < 1 {
590 depth = 1
592 return depth
595 // Format the binary expression: decide the cutoff and then format.
596 // Let's call depth == 1 Normal mode, and depth > 1 Compact mode.
597 // (Algorithm suggestion by Russ Cox.)
599 // The precedences are:
600 // 5 * / % << >> & &^
601 // 4 + - | ^
602 // 3 == != < <= > >=
603 // 2 &&
604 // 1 ||
606 // The only decision is whether there will be spaces around levels 4 and 5.
607 // There are never spaces at level 6 (unary), and always spaces at levels 3 and below.
609 // To choose the cutoff, look at the whole expression but excluding primary
610 // expressions (function calls, parenthesized exprs), and apply these rules:
612 // 1) If there is a binary operator with a right side unary operand
613 // that would clash without a space, the cutoff must be (in order):
615 // /* 6
616 // && 6
617 // &^ 6
618 // ++ 5
619 // -- 5
621 // (Comparison operators always have spaces around them.)
623 // 2) If there is a mix of level 5 and level 4 operators, then the cutoff
624 // is 5 (use spaces to distinguish precedence) in Normal mode
625 // and 4 (never use spaces) in Compact mode.
627 // 3) If there are no level 4 operators or no level 5 operators, then the
628 // cutoff is 6 (always use spaces) in Normal mode
629 // and 4 (never use spaces) in Compact mode.
631 func (p *printer) binaryExpr(x *ast.BinaryExpr, prec1, cutoff, depth int) {
632 prec := x.Op.Precedence()
633 if prec < prec1 {
634 // parenthesis needed
635 // Note: The parser inserts an ast.ParenExpr node; thus this case
636 // can only occur if the AST is created in a different way.
637 p.print(token.LPAREN)
638 p.expr0(x, reduceDepth(depth)) // parentheses undo one level of depth
639 p.print(token.RPAREN)
640 return
643 printBlank := prec < cutoff
645 ws := indent
646 p.expr1(x.X, prec, depth+diffPrec(x.X, prec))
647 if printBlank {
648 p.print(blank)
650 xline := p.pos.Line // before the operator (it may be on the next line!)
651 yline := p.lineFor(x.Y.Pos())
652 p.print(x.OpPos, x.Op)
653 if xline != yline && xline > 0 && yline > 0 {
654 // at least one line break, but respect an extra empty line
655 // in the source
656 if p.linebreak(yline, 1, ws, true) {
657 ws = ignore
658 printBlank = false // no blank after line break
661 if printBlank {
662 p.print(blank)
664 p.expr1(x.Y, prec+1, depth+1)
665 if ws == ignore {
666 p.print(unindent)
670 func isBinary(expr ast.Expr) bool {
671 _, ok := expr.(*ast.BinaryExpr)
672 return ok
675 func (p *printer) expr1(expr ast.Expr, prec1, depth int) {
676 p.print(expr.Pos())
678 switch x := expr.(type) {
679 case *ast.BadExpr:
680 p.print("BadExpr")
682 case *ast.Ident:
683 p.print(x)
685 case *ast.BinaryExpr:
686 if depth < 1 {
687 p.internalError("depth < 1:", depth)
688 depth = 1
690 p.binaryExpr(x, prec1, cutoff(x, depth), depth)
692 case *ast.KeyValueExpr:
693 p.expr(x.Key)
694 p.print(x.Colon, token.COLON, blank)
695 p.expr(x.Value)
697 case *ast.StarExpr:
698 const prec = token.UnaryPrec
699 if prec < prec1 {
700 // parenthesis needed
701 p.print(token.LPAREN)
702 p.print(token.MUL)
703 p.expr(x.X)
704 p.print(token.RPAREN)
705 } else {
706 // no parenthesis needed
707 p.print(token.MUL)
708 p.expr(x.X)
711 case *ast.UnaryExpr:
712 const prec = token.UnaryPrec
713 if prec < prec1 {
714 // parenthesis needed
715 p.print(token.LPAREN)
716 p.expr(x)
717 p.print(token.RPAREN)
718 } else {
719 // no parenthesis needed
720 p.print(x.Op)
721 if x.Op == token.RANGE {
722 // TODO(gri) Remove this code if it cannot be reached.
723 p.print(blank)
725 p.expr1(x.X, prec, depth)
728 case *ast.BasicLit:
729 p.print(x)
731 case *ast.FuncLit:
732 p.expr(x.Type)
733 p.adjBlock(p.distanceFrom(x.Type.Pos()), blank, x.Body)
735 case *ast.ParenExpr:
736 if _, hasParens := x.X.(*ast.ParenExpr); hasParens {
737 // don't print parentheses around an already parenthesized expression
738 // TODO(gri) consider making this more general and incorporate precedence levels
739 p.expr0(x.X, reduceDepth(depth)) // parentheses undo one level of depth
740 } else {
741 p.print(token.LPAREN)
742 p.expr0(x.X, reduceDepth(depth)) // parentheses undo one level of depth
743 p.print(x.Rparen, token.RPAREN)
746 case *ast.SelectorExpr:
747 p.expr1(x.X, token.HighestPrec, depth)
748 p.print(token.PERIOD)
749 if line := p.lineFor(x.Sel.Pos()); p.pos.IsValid() && p.pos.Line < line {
750 p.print(indent, newline, x.Sel.Pos(), x.Sel, unindent)
751 } else {
752 p.print(x.Sel.Pos(), x.Sel)
755 case *ast.TypeAssertExpr:
756 p.expr1(x.X, token.HighestPrec, depth)
757 p.print(token.PERIOD, token.LPAREN)
758 if x.Type != nil {
759 p.expr(x.Type)
760 } else {
761 p.print(token.TYPE)
763 p.print(token.RPAREN)
765 case *ast.IndexExpr:
766 // TODO(gri): should treat[] like parentheses and undo one level of depth
767 p.expr1(x.X, token.HighestPrec, 1)
768 p.print(x.Lbrack, token.LBRACK)
769 p.expr0(x.Index, depth+1)
770 p.print(x.Rbrack, token.RBRACK)
772 case *ast.SliceExpr:
773 // TODO(gri): should treat[] like parentheses and undo one level of depth
774 p.expr1(x.X, token.HighestPrec, 1)
775 p.print(x.Lbrack, token.LBRACK)
776 if x.Low != nil {
777 p.expr0(x.Low, depth+1)
779 // blanks around ":" if both sides exist and either side is a binary expression
780 if depth <= 1 && x.Low != nil && x.High != nil && (isBinary(x.Low) || isBinary(x.High)) {
781 p.print(blank, token.COLON, blank)
782 } else {
783 p.print(token.COLON)
785 if x.High != nil {
786 p.expr0(x.High, depth+1)
788 p.print(x.Rbrack, token.RBRACK)
790 case *ast.CallExpr:
791 if len(x.Args) > 1 {
792 depth++
794 if _, ok := x.Fun.(*ast.FuncType); ok {
795 // conversions to literal function types require parentheses around the type
796 p.print(token.LPAREN)
797 p.expr1(x.Fun, token.HighestPrec, depth)
798 p.print(token.RPAREN)
799 } else {
800 p.expr1(x.Fun, token.HighestPrec, depth)
802 p.print(x.Lparen, token.LPAREN)
803 if x.Ellipsis.IsValid() {
804 p.exprList(x.Lparen, x.Args, depth, 0, x.Ellipsis)
805 p.print(x.Ellipsis, token.ELLIPSIS)
806 if x.Rparen.IsValid() && p.lineFor(x.Ellipsis) < p.lineFor(x.Rparen) {
807 p.print(token.COMMA, formfeed)
809 } else {
810 p.exprList(x.Lparen, x.Args, depth, commaTerm, x.Rparen)
812 p.print(x.Rparen, token.RPAREN)
814 case *ast.CompositeLit:
815 // composite literal elements that are composite literals themselves may have the type omitted
816 if x.Type != nil {
817 p.expr1(x.Type, token.HighestPrec, depth)
819 p.print(x.Lbrace, token.LBRACE)
820 p.exprList(x.Lbrace, x.Elts, 1, commaTerm, x.Rbrace)
821 // do not insert extra line breaks because of comments before
822 // the closing '}' as it might break the code if there is no
823 // trailing ','
824 p.print(noExtraLinebreak, x.Rbrace, token.RBRACE, noExtraLinebreak)
826 case *ast.Ellipsis:
827 p.print(token.ELLIPSIS)
828 if x.Elt != nil {
829 p.expr(x.Elt)
832 case *ast.ArrayType:
833 p.print(token.LBRACK)
834 if x.Len != nil {
835 p.expr(x.Len)
837 p.print(token.RBRACK)
838 p.expr(x.Elt)
840 case *ast.StructType:
841 p.print(token.STRUCT)
842 p.fieldList(x.Fields, true, x.Incomplete)
844 case *ast.FuncType:
845 p.print(token.FUNC)
846 p.signature(x.Params, x.Results)
848 case *ast.InterfaceType:
849 p.print(token.INTERFACE)
850 p.fieldList(x.Methods, false, x.Incomplete)
852 case *ast.MapType:
853 p.print(token.MAP, token.LBRACK)
854 p.expr(x.Key)
855 p.print(token.RBRACK)
856 p.expr(x.Value)
858 case *ast.ChanType:
859 switch x.Dir {
860 case ast.SEND | ast.RECV:
861 p.print(token.CHAN)
862 case ast.RECV:
863 p.print(token.ARROW, token.CHAN) // x.Arrow and x.Pos() are the same
864 case ast.SEND:
865 p.print(token.CHAN, x.Arrow, token.ARROW)
867 p.print(blank)
868 p.expr(x.Value)
870 default:
871 panic("unreachable")
874 return
877 func (p *printer) expr0(x ast.Expr, depth int) {
878 p.expr1(x, token.LowestPrec, depth)
881 func (p *printer) expr(x ast.Expr) {
882 const depth = 1
883 p.expr1(x, token.LowestPrec, depth)
886 // ----------------------------------------------------------------------------
887 // Statements
889 // Print the statement list indented, but without a newline after the last statement.
890 // Extra line breaks between statements in the source are respected but at most one
891 // empty line is printed between statements.
892 func (p *printer) stmtList(list []ast.Stmt, nindent int, nextIsRBrace bool) {
893 if nindent > 0 {
894 p.print(indent)
896 multiLine := false
897 i := 0
898 for _, s := range list {
899 // ignore empty statements (was issue 3466)
900 if _, isEmpty := s.(*ast.EmptyStmt); !isEmpty {
901 // _indent == 0 only for lists of switch/select case clauses;
902 // in those cases each clause is a new section
903 if len(p.output) > 0 {
904 // only print line break if we are not at the beginning of the output
905 // (i.e., we are not printing only a partial program)
906 p.linebreak(p.lineFor(s.Pos()), 1, ignore, i == 0 || nindent == 0 || multiLine)
908 p.stmt(s, nextIsRBrace && i == len(list)-1)
909 multiLine = p.isMultiLine(s)
913 if nindent > 0 {
914 p.print(unindent)
918 // block prints an *ast.BlockStmt; it always spans at least two lines.
919 func (p *printer) block(b *ast.BlockStmt, nindent int) {
920 p.print(b.Lbrace, token.LBRACE)
921 p.stmtList(b.List, nindent, true)
922 p.linebreak(p.lineFor(b.Rbrace), 1, ignore, true)
923 p.print(b.Rbrace, token.RBRACE)
926 func isTypeName(x ast.Expr) bool {
927 switch t := x.(type) {
928 case *ast.Ident:
929 return true
930 case *ast.SelectorExpr:
931 return isTypeName(t.X)
933 return false
936 func stripParens(x ast.Expr) ast.Expr {
937 if px, strip := x.(*ast.ParenExpr); strip {
938 // parentheses must not be stripped if there are any
939 // unparenthesized composite literals starting with
940 // a type name
941 ast.Inspect(px.X, func(node ast.Node) bool {
942 switch x := node.(type) {
943 case *ast.ParenExpr:
944 // parentheses protect enclosed composite literals
945 return false
946 case *ast.CompositeLit:
947 if isTypeName(x.Type) {
948 strip = false // do not strip parentheses
950 return false
952 // in all other cases, keep inspecting
953 return true
955 if strip {
956 return stripParens(px.X)
959 return x
962 func stripParensAlways(x ast.Expr) ast.Expr {
963 if x, ok := x.(*ast.ParenExpr); ok {
964 return stripParensAlways(x.X)
966 return x
969 func (p *printer) controlClause(isForStmt bool, init ast.Stmt, expr ast.Expr, post ast.Stmt) {
970 p.print(blank)
971 needsBlank := false
972 if init == nil && post == nil {
973 // no semicolons required
974 if expr != nil {
975 p.expr(stripParens(expr))
976 needsBlank = true
978 } else {
979 // all semicolons required
980 // (they are not separators, print them explicitly)
981 if init != nil {
982 p.stmt(init, false)
984 p.print(token.SEMICOLON, blank)
985 if expr != nil {
986 p.expr(stripParens(expr))
987 needsBlank = true
989 if isForStmt {
990 p.print(token.SEMICOLON, blank)
991 needsBlank = false
992 if post != nil {
993 p.stmt(post, false)
994 needsBlank = true
998 if needsBlank {
999 p.print(blank)
1003 // indentList reports whether an expression list would look better if it
1004 // were indented wholesale (starting with the very first element, rather
1005 // than starting at the first line break).
1007 func (p *printer) indentList(list []ast.Expr) bool {
1008 // Heuristic: indentList returns true if there are more than one multi-
1009 // line element in the list, or if there is any element that is not
1010 // starting on the same line as the previous one ends.
1011 if len(list) >= 2 {
1012 var b = p.lineFor(list[0].Pos())
1013 var e = p.lineFor(list[len(list)-1].End())
1014 if 0 < b && b < e {
1015 // list spans multiple lines
1016 n := 0 // multi-line element count
1017 line := b
1018 for _, x := range list {
1019 xb := p.lineFor(x.Pos())
1020 xe := p.lineFor(x.End())
1021 if line < xb {
1022 // x is not starting on the same
1023 // line as the previous one ended
1024 return true
1026 if xb < xe {
1027 // x is a multi-line element
1030 line = xe
1032 return n > 1
1035 return false
1038 func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool) {
1039 p.print(stmt.Pos())
1041 switch s := stmt.(type) {
1042 case *ast.BadStmt:
1043 p.print("BadStmt")
1045 case *ast.DeclStmt:
1046 p.decl(s.Decl)
1048 case *ast.EmptyStmt:
1049 // nothing to do
1051 case *ast.LabeledStmt:
1052 // a "correcting" unindent immediately following a line break
1053 // is applied before the line break if there is no comment
1054 // between (see writeWhitespace)
1055 p.print(unindent)
1056 p.expr(s.Label)
1057 p.print(s.Colon, token.COLON, indent)
1058 if e, isEmpty := s.Stmt.(*ast.EmptyStmt); isEmpty {
1059 if !nextIsRBrace {
1060 p.print(newline, e.Pos(), token.SEMICOLON)
1061 break
1063 } else {
1064 p.linebreak(p.lineFor(s.Stmt.Pos()), 1, ignore, true)
1066 p.stmt(s.Stmt, nextIsRBrace)
1068 case *ast.ExprStmt:
1069 const depth = 1
1070 p.expr0(s.X, depth)
1072 case *ast.SendStmt:
1073 const depth = 1
1074 p.expr0(s.Chan, depth)
1075 p.print(blank, s.Arrow, token.ARROW, blank)
1076 p.expr0(s.Value, depth)
1078 case *ast.IncDecStmt:
1079 const depth = 1
1080 p.expr0(s.X, depth+1)
1081 p.print(s.TokPos, s.Tok)
1083 case *ast.AssignStmt:
1084 var depth = 1
1085 if len(s.Lhs) > 1 && len(s.Rhs) > 1 {
1086 depth++
1088 p.exprList(s.Pos(), s.Lhs, depth, 0, s.TokPos)
1089 p.print(blank, s.TokPos, s.Tok, blank)
1090 p.exprList(s.TokPos, s.Rhs, depth, 0, token.NoPos)
1092 case *ast.GoStmt:
1093 p.print(token.GO, blank)
1094 p.expr(s.Call)
1096 case *ast.DeferStmt:
1097 p.print(token.DEFER, blank)
1098 p.expr(s.Call)
1100 case *ast.ReturnStmt:
1101 p.print(token.RETURN)
1102 if s.Results != nil {
1103 p.print(blank)
1104 // Use indentList heuristic to make corner cases look
1105 // better (issue 1207). A more systematic approach would
1106 // always indent, but this would cause significant
1107 // reformatting of the code base and not necessarily
1108 // lead to more nicely formatted code in general.
1109 if p.indentList(s.Results) {
1110 p.print(indent)
1111 p.exprList(s.Pos(), s.Results, 1, noIndent, token.NoPos)
1112 p.print(unindent)
1113 } else {
1114 p.exprList(s.Pos(), s.Results, 1, 0, token.NoPos)
1118 case *ast.BranchStmt:
1119 p.print(s.Tok)
1120 if s.Label != nil {
1121 p.print(blank)
1122 p.expr(s.Label)
1125 case *ast.BlockStmt:
1126 p.block(s, 1)
1128 case *ast.IfStmt:
1129 p.print(token.IF)
1130 p.controlClause(false, s.Init, s.Cond, nil)
1131 p.block(s.Body, 1)
1132 if s.Else != nil {
1133 p.print(blank, token.ELSE, blank)
1134 switch s.Else.(type) {
1135 case *ast.BlockStmt, *ast.IfStmt:
1136 p.stmt(s.Else, nextIsRBrace)
1137 default:
1138 p.print(token.LBRACE, indent, formfeed)
1139 p.stmt(s.Else, true)
1140 p.print(unindent, formfeed, token.RBRACE)
1144 case *ast.CaseClause:
1145 if s.List != nil {
1146 p.print(token.CASE, blank)
1147 p.exprList(s.Pos(), s.List, 1, 0, s.Colon)
1148 } else {
1149 p.print(token.DEFAULT)
1151 p.print(s.Colon, token.COLON)
1152 p.stmtList(s.Body, 1, nextIsRBrace)
1154 case *ast.SwitchStmt:
1155 p.print(token.SWITCH)
1156 p.controlClause(false, s.Init, s.Tag, nil)
1157 p.block(s.Body, 0)
1159 case *ast.TypeSwitchStmt:
1160 p.print(token.SWITCH)
1161 if s.Init != nil {
1162 p.print(blank)
1163 p.stmt(s.Init, false)
1164 p.print(token.SEMICOLON)
1166 p.print(blank)
1167 p.stmt(s.Assign, false)
1168 p.print(blank)
1169 p.block(s.Body, 0)
1171 case *ast.CommClause:
1172 if s.Comm != nil {
1173 p.print(token.CASE, blank)
1174 p.stmt(s.Comm, false)
1175 } else {
1176 p.print(token.DEFAULT)
1178 p.print(s.Colon, token.COLON)
1179 p.stmtList(s.Body, 1, nextIsRBrace)
1181 case *ast.SelectStmt:
1182 p.print(token.SELECT, blank)
1183 body := s.Body
1184 if len(body.List) == 0 && !p.commentBefore(p.posFor(body.Rbrace)) {
1185 // print empty select statement w/o comments on one line
1186 p.print(body.Lbrace, token.LBRACE, body.Rbrace, token.RBRACE)
1187 } else {
1188 p.block(body, 0)
1191 case *ast.ForStmt:
1192 p.print(token.FOR)
1193 p.controlClause(true, s.Init, s.Cond, s.Post)
1194 p.block(s.Body, 1)
1196 case *ast.RangeStmt:
1197 p.print(token.FOR, blank)
1198 p.expr(s.Key)
1199 if s.Value != nil {
1200 // use position of value following the comma as
1201 // comma position for correct comment placement
1202 p.print(s.Value.Pos(), token.COMMA, blank)
1203 p.expr(s.Value)
1205 p.print(blank, s.TokPos, s.Tok, blank, token.RANGE, blank)
1206 p.expr(stripParens(s.X))
1207 p.print(blank)
1208 p.block(s.Body, 1)
1210 default:
1211 panic("unreachable")
1214 return
1217 // ----------------------------------------------------------------------------
1218 // Declarations
1220 // The keepTypeColumn function determines if the type column of a series of
1221 // consecutive const or var declarations must be kept, or if initialization
1222 // values (V) can be placed in the type column (T) instead. The i'th entry
1223 // in the result slice is true if the type column in spec[i] must be kept.
1225 // For example, the declaration:
1227 // const (
1228 // foobar int = 42 // comment
1229 // x = 7 // comment
1230 // foo
1231 // bar = 991
1232 // )
1234 // leads to the type/values matrix below. A run of value columns (V) can
1235 // be moved into the type column if there is no type for any of the values
1236 // in that column (we only move entire columns so that they align properly).
1238 // matrix formatted result
1239 // matrix
1240 // T V -> T V -> true there is a T and so the type
1241 // - V - V true column must be kept
1242 // - - - - false
1243 // - V V - false V is moved into T column
1245 func keepTypeColumn(specs []ast.Spec) []bool {
1246 m := make([]bool, len(specs))
1248 populate := func(i, j int, keepType bool) {
1249 if keepType {
1250 for ; i < j; i++ {
1251 m[i] = true
1256 i0 := -1 // if i0 >= 0 we are in a run and i0 is the start of the run
1257 var keepType bool
1258 for i, s := range specs {
1259 t := s.(*ast.ValueSpec)
1260 if t.Values != nil {
1261 if i0 < 0 {
1262 // start of a run of ValueSpecs with non-nil Values
1263 i0 = i
1264 keepType = false
1266 } else {
1267 if i0 >= 0 {
1268 // end of a run
1269 populate(i0, i, keepType)
1270 i0 = -1
1273 if t.Type != nil {
1274 keepType = true
1277 if i0 >= 0 {
1278 // end of a run
1279 populate(i0, len(specs), keepType)
1282 return m
1285 func (p *printer) valueSpec(s *ast.ValueSpec, keepType bool) {
1286 p.setComment(s.Doc)
1287 p.identList(s.Names, false) // always present
1288 extraTabs := 3
1289 if s.Type != nil || keepType {
1290 p.print(vtab)
1291 extraTabs--
1293 if s.Type != nil {
1294 p.expr(s.Type)
1296 if s.Values != nil {
1297 p.print(vtab, token.ASSIGN, blank)
1298 p.exprList(token.NoPos, s.Values, 1, 0, token.NoPos)
1299 extraTabs--
1301 if s.Comment != nil {
1302 for ; extraTabs > 0; extraTabs-- {
1303 p.print(vtab)
1305 p.setComment(s.Comment)
1309 // The parameter n is the number of specs in the group. If doIndent is set,
1310 // multi-line identifier lists in the spec are indented when the first
1311 // linebreak is encountered.
1313 func (p *printer) spec(spec ast.Spec, n int, doIndent bool) {
1314 switch s := spec.(type) {
1315 case *ast.ImportSpec:
1316 p.setComment(s.Doc)
1317 if s.Name != nil {
1318 p.expr(s.Name)
1319 p.print(blank)
1321 p.expr(s.Path)
1322 p.setComment(s.Comment)
1323 p.print(s.EndPos)
1325 case *ast.ValueSpec:
1326 if n != 1 {
1327 p.internalError("expected n = 1; got", n)
1329 p.setComment(s.Doc)
1330 p.identList(s.Names, doIndent) // always present
1331 if s.Type != nil {
1332 p.print(blank)
1333 p.expr(s.Type)
1335 if s.Values != nil {
1336 p.print(blank, token.ASSIGN, blank)
1337 p.exprList(token.NoPos, s.Values, 1, 0, token.NoPos)
1339 p.setComment(s.Comment)
1341 case *ast.TypeSpec:
1342 p.setComment(s.Doc)
1343 p.expr(s.Name)
1344 if n == 1 {
1345 p.print(blank)
1346 } else {
1347 p.print(vtab)
1349 p.expr(s.Type)
1350 p.setComment(s.Comment)
1352 default:
1353 panic("unreachable")
1357 func (p *printer) genDecl(d *ast.GenDecl) {
1358 p.setComment(d.Doc)
1359 p.print(d.Pos(), d.Tok, blank)
1361 if d.Lparen.IsValid() {
1362 // group of parenthesized declarations
1363 p.print(d.Lparen, token.LPAREN)
1364 if n := len(d.Specs); n > 0 {
1365 p.print(indent, formfeed)
1366 if n > 1 && (d.Tok == token.CONST || d.Tok == token.VAR) {
1367 // two or more grouped const/var declarations:
1368 // determine if the type column must be kept
1369 keepType := keepTypeColumn(d.Specs)
1370 newSection := false
1371 for i, s := range d.Specs {
1372 if i > 0 {
1373 p.linebreak(p.lineFor(s.Pos()), 1, ignore, newSection)
1375 p.valueSpec(s.(*ast.ValueSpec), keepType[i])
1376 newSection = p.isMultiLine(s)
1378 } else {
1379 newSection := false
1380 for i, s := range d.Specs {
1381 if i > 0 {
1382 p.linebreak(p.lineFor(s.Pos()), 1, ignore, newSection)
1384 p.spec(s, n, false)
1385 newSection = p.isMultiLine(s)
1388 p.print(unindent, formfeed)
1390 p.print(d.Rparen, token.RPAREN)
1392 } else {
1393 // single declaration
1394 p.spec(d.Specs[0], 1, true)
1398 // nodeSize determines the size of n in chars after formatting.
1399 // The result is <= maxSize if the node fits on one line with at
1400 // most maxSize chars and the formatted output doesn't contain
1401 // any control chars. Otherwise, the result is > maxSize.
1403 func (p *printer) nodeSize(n ast.Node, maxSize int) (size int) {
1404 // nodeSize invokes the printer, which may invoke nodeSize
1405 // recursively. For deep composite literal nests, this can
1406 // lead to an exponential algorithm. Remember previous
1407 // results to prune the recursion (was issue 1628).
1408 if size, found := p.nodeSizes[n]; found {
1409 return size
1412 size = maxSize + 1 // assume n doesn't fit
1413 p.nodeSizes[n] = size
1415 // nodeSize computation must be independent of particular
1416 // style so that we always get the same decision; print
1417 // in RawFormat
1418 cfg := Config{Mode: RawFormat}
1419 var buf bytes.Buffer
1420 if err := cfg.fprint(&buf, p.fset, n, p.nodeSizes); err != nil {
1421 return
1423 if buf.Len() <= maxSize {
1424 for _, ch := range buf.Bytes() {
1425 if ch < ' ' {
1426 return
1429 size = buf.Len() // n fits
1430 p.nodeSizes[n] = size
1432 return
1435 // bodySize is like nodeSize but it is specialized for *ast.BlockStmt's.
1436 func (p *printer) bodySize(b *ast.BlockStmt, maxSize int) int {
1437 pos1 := b.Pos()
1438 pos2 := b.Rbrace
1439 if pos1.IsValid() && pos2.IsValid() && p.lineFor(pos1) != p.lineFor(pos2) {
1440 // opening and closing brace are on different lines - don't make it a one-liner
1441 return maxSize + 1
1443 if len(b.List) > 5 || p.commentBefore(p.posFor(pos2)) {
1444 // too many statements or there is a comment inside - don't make it a one-liner
1445 return maxSize + 1
1447 // otherwise, estimate body size
1448 bodySize := 0
1449 for i, s := range b.List {
1450 if i > 0 {
1451 bodySize += 2 // space for a semicolon and blank
1453 bodySize += p.nodeSize(s, maxSize)
1455 return bodySize
1458 // adjBlock prints an "adjacent" block (e.g., a for-loop or function body) following
1459 // a header (e.g., a for-loop control clause or function signature) of given headerSize.
1460 // If the header's and block's size are "small enough" and the block is "simple enough",
1461 // the block is printed on the current line, without line breaks, spaced from the header
1462 // by sep. Otherwise the block's opening "{" is printed on the current line, followed by
1463 // lines for the block's statements and its closing "}".
1465 func (p *printer) adjBlock(headerSize int, sep whiteSpace, b *ast.BlockStmt) {
1466 if b == nil {
1467 return
1470 const maxSize = 100
1471 if headerSize+p.bodySize(b, maxSize) <= maxSize {
1472 p.print(sep, b.Lbrace, token.LBRACE)
1473 if len(b.List) > 0 {
1474 p.print(blank)
1475 for i, s := range b.List {
1476 if i > 0 {
1477 p.print(token.SEMICOLON, blank)
1479 p.stmt(s, i == len(b.List)-1)
1481 p.print(blank)
1483 p.print(b.Rbrace, token.RBRACE)
1484 return
1487 if sep != ignore {
1488 p.print(blank) // always use blank
1490 p.block(b, 1)
1493 // distanceFrom returns the column difference between from and p.pos (the current
1494 // estimated position) if both are on the same line; if they are on different lines
1495 // (or unknown) the result is infinity.
1496 func (p *printer) distanceFrom(from token.Pos) int {
1497 if from.IsValid() && p.pos.IsValid() {
1498 if f := p.posFor(from); f.Line == p.pos.Line {
1499 return p.pos.Column - f.Column
1502 return infinity
1505 func (p *printer) funcDecl(d *ast.FuncDecl) {
1506 p.setComment(d.Doc)
1507 p.print(d.Pos(), token.FUNC, blank)
1508 if d.Recv != nil {
1509 p.parameters(d.Recv) // method: print receiver
1510 p.print(blank)
1512 p.expr(d.Name)
1513 p.signature(d.Type.Params, d.Type.Results)
1514 p.adjBlock(p.distanceFrom(d.Pos()), vtab, d.Body)
1517 func (p *printer) decl(decl ast.Decl) {
1518 switch d := decl.(type) {
1519 case *ast.BadDecl:
1520 p.print(d.Pos(), "BadDecl")
1521 case *ast.GenDecl:
1522 p.genDecl(d)
1523 case *ast.FuncDecl:
1524 p.funcDecl(d)
1525 default:
1526 panic("unreachable")
1530 // ----------------------------------------------------------------------------
1531 // Files
1533 func declToken(decl ast.Decl) (tok token.Token) {
1534 tok = token.ILLEGAL
1535 switch d := decl.(type) {
1536 case *ast.GenDecl:
1537 tok = d.Tok
1538 case *ast.FuncDecl:
1539 tok = token.FUNC
1541 return
1544 func (p *printer) declList(list []ast.Decl) {
1545 tok := token.ILLEGAL
1546 for _, d := range list {
1547 prev := tok
1548 tok = declToken(d)
1549 // If the declaration token changed (e.g., from CONST to TYPE)
1550 // or the next declaration has documentation associated with it,
1551 // print an empty line between top-level declarations.
1552 // (because p.linebreak is called with the position of d, which
1553 // is past any documentation, the minimum requirement is satisfied
1554 // even w/o the extra getDoc(d) nil-check - leave it in case the
1555 // linebreak logic improves - there's already a TODO).
1556 if len(p.output) > 0 {
1557 // only print line break if we are not at the beginning of the output
1558 // (i.e., we are not printing only a partial program)
1559 min := 1
1560 if prev != tok || getDoc(d) != nil {
1561 min = 2
1563 p.linebreak(p.lineFor(d.Pos()), min, ignore, false)
1565 p.decl(d)
1569 func (p *printer) file(src *ast.File) {
1570 p.setComment(src.Doc)
1571 p.print(src.Pos(), token.PACKAGE, blank)
1572 p.expr(src.Name)
1573 p.declList(src.Decls)
1574 p.print(newline)