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.
17 // Other formatting issues:
18 // - better comment formatting for /*-style comments at the end of a line (e.g. a declaration)
19 // when the comment spans multiple lines; if such a comment is just two lines, formatting is
21 // - formatting of expression lists
22 // - should use blank instead of tab to separate one-line function bodies from
23 // the function header unless there is a group of consecutive one-liners
25 // ----------------------------------------------------------------------------
28 // Print as many newlines as necessary (but at least min newlines) to get to
29 // the current line. ws is printed before the first line break. If newSection
30 // is set, the first line break is printed as formfeed. Returns true if any
31 // line break was printed; returns false otherwise.
33 // TODO(gri): linebreak may add too many lines if the next statement at "line"
34 // is preceded by comments because the computation of n assumes
35 // the current position before the comment and the target position
36 // after the comment. Thus, after interspersing such comments, the
37 // space taken up by them is not considered to reduce the number of
38 // linebreaks. At the moment there is no easy way to know about
39 // future (not yet interspersed) comments in this function.
41 func (p *printer) linebreak(line, min int, ws whiteSpace, newSection bool) (printedBreak bool) {
42 n := p.nlines(line-p.pos.Line, min)
57 // setComment sets g as the next comment if g != nil and if node comments
58 // are enabled - this mode is used when printing source code fragments such
59 // as exports only. It assumes that there are no other pending comments to
61 func (p *printer) setComment(g *ast.CommentGroup) {
62 if g == nil || !p.useNodeComments {
65 if p.comments == nil {
66 // initialize p.comments lazily
67 p.comments = make([]*ast.CommentGroup, 1)
68 } else if p.cindex < len(p.comments) {
69 // for some reason there are pending comments; this
70 // should never happen - handle gracefully and flush
71 // all comments up to g, ignore anything after that
72 p.flush(p.fset.Position(g.List[0].Pos()), token.ILLEGAL)
78 type exprListMode uint
81 blankStart exprListMode = 1 << iota // print a blank before a non-empty list
82 blankEnd // print a blank after a non-empty list
83 commaSep // elements are separated by commas
84 commaTerm // list is optionally terminated by a comma
85 noIndent // no extra indentation in multi-line lists
86 periodSep // elements are separated by periods
89 // Sets multiLine to true if the identifier list spans multiple lines.
90 // If indent is set, a multi-line identifier list is indented after the
91 // first linebreak encountered.
92 func (p *printer) identList(list []*ast.Ident, indent bool, multiLine *bool) {
93 // convert into an expression list so we can re-use exprList formatting
94 xlist := make([]ast.Expr, len(list))
95 for i, x := range list {
102 p.exprList(token.NoPos, xlist, 1, mode, multiLine, token.NoPos)
105 // Print a list of expressions. If the list spans multiple
106 // source lines, the original line breaks are respected between
107 // expressions. Sets multiLine to true if the list spans multiple
110 // TODO(gri) Consider rewriting this to be independent of []ast.Expr
111 // so that we can use the algorithm for any kind of list
112 // (e.g., pass list via a channel over which to range).
113 func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exprListMode, multiLine *bool, next0 token.Pos) {
118 if mode&blankStart != 0 {
122 prev := p.fset.Position(prev0)
123 next := p.fset.Position(next0)
124 line := p.fset.Position(list[0].Pos()).Line
125 endLine := p.fset.Position(list[len(list)-1].End()).Line
127 if prev.IsValid() && prev.Line == line && line == endLine {
128 // all list entries on a single line
129 for i, x := range list {
131 if mode&commaSep != 0 {
136 p.expr0(x, depth, multiLine)
138 if mode&blankEnd != 0 {
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
150 if mode&noIndent == 0 {
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) {
163 // initialize expression/key size: a zero value indicates expr/key doesn't fit on a single line
166 // print all list elements
167 for i, x := range list {
169 line = p.fset.Position(x.Pos()).Line
171 // determine if the next linebreak, if any, needs to use formfeed:
172 // in general, use the entire node size to make the decision; for
173 // key:value expressions, use the key size
174 // TODO(gri) for a better result, should probably incorporate both
175 // the key and the node size into the decision process
178 // determine element size: all bets are off if we don't have
179 // position information for the previous and next token (likely
180 // generated code - simply ignore the size in this case by setting
183 const infinity = 1e6 // larger than any source line
184 size = p.nodeSize(x, infinity)
185 pair, isPair := x.(*ast.KeyValueExpr)
186 if size <= infinity && prev.IsValid() && next.IsValid() {
187 // x fits on a single line
189 size = p.nodeSize(pair.Key, infinity) // size <= infinity
192 // size too large or we don't have good layout information
196 // if the previous line and the current line had single-
197 // line-expressions and the key sizes are small or the
198 // the ratio between the key sizes does not exceed a
199 // threshold, align columns and do not use formfeed
200 if prevSize > 0 && size > 0 {
202 if prevSize <= smallSize && size <= smallSize {
205 const r = 4 // threshold
206 ratio := float64(size) / float64(prevSize)
207 useFF = ratio <= 1/r || r <= ratio
213 case mode&commaSep != 0:
215 case mode&periodSep != 0:
216 p.print(token.PERIOD)
218 needsBlank := mode&periodSep == 0 // period-separated list elements don't need a blank
219 if prevLine < line && prevLine > 0 && line > 0 {
220 // lines are broken using newlines so comments remain aligned
221 // unless forceFF is set or there are multiple expressions on
222 // the same line in which case formfeed is used
223 if p.linebreak(line, 0, ws, useFF || prevBreak+1 < i) {
227 needsBlank = false // we got a line break instead
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, multiLine)
240 p.print(pair.Colon, token.COLON, vtab)
241 p.expr(pair.Value, multiLine)
243 p.expr0(x, depth, multiLine)
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
250 if ws == ignore && mode&noIndent == 0 {
251 // unindent if we indented
254 p.print(formfeed) // terminating comma needs a line break to look good
258 if mode&blankEnd != 0 {
262 if ws == ignore && mode&noIndent == 0 {
263 // unindent if we indented
268 // Sets multiLine to true if the the parameter list spans multiple lines.
269 func (p *printer) parameters(fields *ast.FieldList, multiLine *bool) {
270 p.print(fields.Opening, token.LPAREN)
271 if len(fields.List) > 0 {
273 var prevLine, line int
274 for i, par := range fields.List {
277 if len(par.Names) > 0 {
278 line = p.fset.Position(par.Names[0].Pos()).Line
280 line = p.fset.Position(par.Type.Pos()).Line
282 if 0 < prevLine && prevLine < line && p.linebreak(line, 0, ws, true) {
289 if len(par.Names) > 0 {
290 // Very subtle: If we indented before (ws == ignore), identList
291 // won't indent again. If we didn't (ws == indent), identList will
292 // indent if the identList spans multiple lines, and it will outdent
293 // again at the end (and still ws == indent). Thus, a subsequent indent
294 // by a linebreak call after a type, or in the next multi-line identList
295 // will do the right thing.
296 p.identList(par.Names, ws == indent, multiLine)
299 p.expr(par.Type, multiLine)
300 prevLine = p.fset.Position(par.Type.Pos()).Line
303 // unindent if we indented
307 p.print(fields.Closing, token.RPAREN)
310 // Sets multiLine to true if the signature spans multiple lines.
311 func (p *printer) signature(params, result *ast.FieldList, multiLine *bool) {
312 p.parameters(params, multiLine)
313 n := result.NumFields()
316 if n == 1 && result.List[0].Names == nil {
317 // single anonymous result; no ()'s
318 p.expr(result.List[0].Type, multiLine)
321 p.parameters(result, multiLine)
325 func identListSize(list []*ast.Ident, maxSize int) (size int) {
326 for i, x := range list {
338 func (p *printer) isOneLineFieldList(list []*ast.Field) bool {
340 return false // allow only one field
343 if f.Tag != nil || f.Comment != nil {
344 return false // don't allow tags or comments
346 // only name(s) and type
347 const maxSize = 30 // adjust as appropriate, this is an approximate value
348 namesSize := identListSize(f.Names, maxSize)
350 namesSize = 1 // blank between names and types
352 typeSize := p.nodeSize(f.Type, maxSize)
353 return namesSize+typeSize <= maxSize
356 func (p *printer) setLineComment(text string) {
357 p.setComment(&ast.CommentGroup{[]*ast.Comment{{token.NoPos, text}}})
360 func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool) {
361 lbrace := fields.Opening
363 rbrace := fields.Closing
364 srcIsOneLine := lbrace.IsValid() && rbrace.IsValid() && p.fset.Position(lbrace).Line == p.fset.Position(rbrace).Line
366 if !isIncomplete && !p.commentBefore(p.fset.Position(rbrace)) && srcIsOneLine {
367 // possibly a one-line struct/interface
369 // no blank between keyword and {} in this case
370 p.print(lbrace, token.LBRACE, rbrace, token.RBRACE)
372 } else if isStruct && p.isOneLineFieldList(list) { // for now ignore interfaces
373 // small enough - print on one line
374 // (don't use identList and ignore source line breaks)
375 p.print(lbrace, token.LBRACE, blank)
377 for i, x := range f.Names {
379 p.print(token.COMMA, blank)
381 p.expr(x, ignoreMultiLine)
383 if len(f.Names) > 0 {
386 p.expr(f.Type, ignoreMultiLine)
387 p.print(blank, rbrace, token.RBRACE)
392 // at least one entry or incomplete
393 p.print(blank, lbrace, token.LBRACE, indent, formfeed)
401 for i, f := range list {
403 p.linebreak(p.fset.Position(f.Pos()).Line, 1, ignore, ml)
408 if len(f.Names) > 0 {
410 p.identList(f.Names, false, &ml)
420 if len(f.Names) > 0 && sep == vtab {
427 if f.Comment != nil {
428 for ; extraTabs > 0; extraTabs-- {
431 p.setComment(f.Comment)
438 p.flush(p.fset.Position(rbrace), token.RBRACE) // make sure we don't lose the last line comment
439 p.setLineComment("// contains filtered or unexported fields")
442 } else { // interface
445 for i, f := range list {
447 p.linebreak(p.fset.Position(f.Pos()).Line, 1, ignore, ml)
451 if ftyp, isFtyp := f.Type.(*ast.FuncType); isFtyp {
453 p.expr(f.Names[0], &ml)
454 p.signature(ftyp.Params, ftyp.Results, &ml)
456 // embedded interface
459 p.setComment(f.Comment)
465 p.flush(p.fset.Position(rbrace), token.RBRACE) // make sure we don't lose the last line comment
466 p.setLineComment("// contains filtered or unexported methods")
470 p.print(unindent, formfeed, rbrace, token.RBRACE)
473 // ----------------------------------------------------------------------------
476 func walkBinary(e *ast.BinaryExpr) (has4, has5 bool, maxProblem int) {
477 switch e.Op.Precedence() {
484 switch l := e.X.(type) {
485 case *ast.BinaryExpr:
486 if l.Op.Precedence() < e.Op.Precedence() {
487 // parens will be inserted.
488 // pretend this is an *ast.ParenExpr and do nothing.
491 h4, h5, mp := walkBinary(l)
499 switch r := e.Y.(type) {
500 case *ast.BinaryExpr:
501 if r.Op.Precedence() <= e.Op.Precedence() {
502 // parens will be inserted.
503 // pretend this is an *ast.ParenExpr and do nothing.
506 h4, h5, mp := walkBinary(r)
514 if e.Op == token.QUO { // `*/`
519 switch e.Op.String() + r.Op.String() {
520 case "/*", "&&", "&^":
531 func cutoff(e *ast.BinaryExpr, depth int) int {
532 has4, has5, maxProblem := walkBinary(e)
534 return maxProblem + 1
548 func diffPrec(expr ast.Expr, prec int) int {
549 x, ok := expr.(*ast.BinaryExpr)
550 if !ok || prec != x.Op.Precedence() {
556 func reduceDepth(depth int) int {
564 // Format the binary expression: decide the cutoff and then format.
565 // Let's call depth == 1 Normal mode, and depth > 1 Compact mode.
566 // (Algorithm suggestion by Russ Cox.)
568 // The precedences are:
569 // 5 * / % << >> & &^
575 // The only decision is whether there will be spaces around levels 4 and 5.
576 // There are never spaces at level 6 (unary), and always spaces at levels 3 and below.
578 // To choose the cutoff, look at the whole expression but excluding primary
579 // expressions (function calls, parenthesized exprs), and apply these rules:
581 // 1) If there is a binary operator with a right side unary operand
582 // that would clash without a space, the cutoff must be (in order):
590 // (Comparison operators always have spaces around them.)
592 // 2) If there is a mix of level 5 and level 4 operators, then the cutoff
593 // is 5 (use spaces to distinguish precedence) in Normal mode
594 // and 4 (never use spaces) in Compact mode.
596 // 3) If there are no level 4 operators or no level 5 operators, then the
597 // cutoff is 6 (always use spaces) in Normal mode
598 // and 4 (never use spaces) in Compact mode.
600 // Sets multiLine to true if the binary expression spans multiple lines.
601 func (p *printer) binaryExpr(x *ast.BinaryExpr, prec1, cutoff, depth int, multiLine *bool) {
602 prec := x.Op.Precedence()
604 // parenthesis needed
605 // Note: The parser inserts an ast.ParenExpr node; thus this case
606 // can only occur if the AST is created in a different way.
607 p.print(token.LPAREN)
608 p.expr0(x, reduceDepth(depth), multiLine) // parentheses undo one level of depth
609 p.print(token.RPAREN)
613 printBlank := prec < cutoff
616 p.expr1(x.X, prec, depth+diffPrec(x.X, prec), multiLine)
620 xline := p.pos.Line // before the operator (it may be on the next line!)
621 yline := p.fset.Position(x.Y.Pos()).Line
622 p.print(x.OpPos, x.Op)
623 if xline != yline && xline > 0 && yline > 0 {
624 // at least one line break, but respect an extra empty line
626 if p.linebreak(yline, 1, ws, true) {
629 printBlank = false // no blank after line break
635 p.expr1(x.Y, prec+1, depth+1, multiLine)
641 func isBinary(expr ast.Expr) bool {
642 _, ok := expr.(*ast.BinaryExpr)
646 // If the expression contains one or more selector expressions, splits it into
647 // two expressions at the rightmost period. Writes entire expr to suffix when
648 // selector isn't found. Rewrites AST nodes for calls, index expressions and
649 // type assertions, all of which may be found in selector chains, to make them
650 // parts of the chain.
651 func splitSelector(expr ast.Expr) (body, suffix ast.Expr) {
652 switch x := expr.(type) {
653 case *ast.SelectorExpr:
654 body, suffix = x.X, x.Sel
657 body, suffix = splitSelector(x.Fun)
659 suffix = &ast.CallExpr{suffix, x.Lparen, x.Args, x.Ellipsis, x.Rparen}
663 body, suffix = splitSelector(x.X)
665 suffix = &ast.IndexExpr{suffix, x.Lbrack, x.Index, x.Rbrack}
669 body, suffix = splitSelector(x.X)
671 suffix = &ast.SliceExpr{suffix, x.Lbrack, x.Low, x.High, x.Rbrack}
674 case *ast.TypeAssertExpr:
675 body, suffix = splitSelector(x.X)
677 suffix = &ast.TypeAssertExpr{suffix, x.Type}
685 // Convert an expression into an expression list split at the periods of
686 // selector expressions.
687 func selectorExprList(expr ast.Expr) (list []ast.Expr) {
691 expr, suffix = splitSelector(expr)
692 list = append(list, suffix)
696 for i, j := 0, len(list)-1; i < j; i, j = i+1, j-1 {
697 list[i], list[j] = list[j], list[i]
703 // Sets multiLine to true if the expression spans multiple lines.
704 func (p *printer) expr1(expr ast.Expr, prec1, depth int, multiLine *bool) {
707 switch x := expr.(type) {
714 case *ast.BinaryExpr:
716 p.internalError("depth < 1:", depth)
719 p.binaryExpr(x, prec1, cutoff(x, depth), depth, multiLine)
721 case *ast.KeyValueExpr:
722 p.expr(x.Key, multiLine)
723 p.print(x.Colon, token.COLON, blank)
724 p.expr(x.Value, multiLine)
727 const prec = token.UnaryPrec
729 // parenthesis needed
730 p.print(token.LPAREN)
732 p.expr(x.X, multiLine)
733 p.print(token.RPAREN)
735 // no parenthesis needed
737 p.expr(x.X, multiLine)
741 const prec = token.UnaryPrec
743 // parenthesis needed
744 p.print(token.LPAREN)
746 p.print(token.RPAREN)
748 // no parenthesis needed
750 if x.Op == token.RANGE {
751 // TODO(gri) Remove this code if it cannot be reached.
754 p.expr1(x.X, prec, depth, multiLine)
761 p.expr(x.Type, multiLine)
762 p.funcBody(x.Body, p.distance(x.Type.Pos(), p.pos), true, multiLine)
765 if _, hasParens := x.X.(*ast.ParenExpr); hasParens {
766 // don't print parentheses around an already parenthesized expression
767 // TODO(gri) consider making this more general and incorporate precedence levels
768 p.expr0(x.X, reduceDepth(depth), multiLine) // parentheses undo one level of depth
770 p.print(token.LPAREN)
771 p.expr0(x.X, reduceDepth(depth), multiLine) // parentheses undo one level of depth
772 p.print(x.Rparen, token.RPAREN)
775 case *ast.SelectorExpr:
776 parts := selectorExprList(expr)
777 p.exprList(token.NoPos, parts, depth, periodSep, multiLine, token.NoPos)
779 case *ast.TypeAssertExpr:
780 p.expr1(x.X, token.HighestPrec, depth, multiLine)
781 p.print(token.PERIOD, token.LPAREN)
783 p.expr(x.Type, multiLine)
787 p.print(token.RPAREN)
790 // TODO(gri): should treat[] like parentheses and undo one level of depth
791 p.expr1(x.X, token.HighestPrec, 1, multiLine)
792 p.print(x.Lbrack, token.LBRACK)
793 p.expr0(x.Index, depth+1, multiLine)
794 p.print(x.Rbrack, token.RBRACK)
797 // TODO(gri): should treat[] like parentheses and undo one level of depth
798 p.expr1(x.X, token.HighestPrec, 1, multiLine)
799 p.print(x.Lbrack, token.LBRACK)
801 p.expr0(x.Low, depth+1, multiLine)
803 // blanks around ":" if both sides exist and either side is a binary expression
804 if depth <= 1 && x.Low != nil && x.High != nil && (isBinary(x.Low) || isBinary(x.High)) {
805 p.print(blank, token.COLON, blank)
810 p.expr0(x.High, depth+1, multiLine)
812 p.print(x.Rbrack, token.RBRACK)
818 p.expr1(x.Fun, token.HighestPrec, depth, multiLine)
819 p.print(x.Lparen, token.LPAREN)
820 p.exprList(x.Lparen, x.Args, depth, commaSep|commaTerm, multiLine, x.Rparen)
821 if x.Ellipsis.IsValid() {
822 p.print(x.Ellipsis, token.ELLIPSIS)
824 p.print(x.Rparen, token.RPAREN)
826 case *ast.CompositeLit:
827 // composite literal elements that are composite literals themselves may have the type omitted
829 p.expr1(x.Type, token.HighestPrec, depth, multiLine)
831 p.print(x.Lbrace, token.LBRACE)
832 p.exprList(x.Lbrace, x.Elts, 1, commaSep|commaTerm, multiLine, x.Rbrace)
833 // do not insert extra line breaks because of comments before
834 // the closing '}' as it might break the code if there is no
836 p.print(noExtraLinebreak, x.Rbrace, token.RBRACE, noExtraLinebreak)
839 p.print(token.ELLIPSIS)
841 p.expr(x.Elt, multiLine)
845 p.print(token.LBRACK)
847 p.expr(x.Len, multiLine)
849 p.print(token.RBRACK)
850 p.expr(x.Elt, multiLine)
852 case *ast.StructType:
853 p.print(token.STRUCT)
854 p.fieldList(x.Fields, true, x.Incomplete)
858 p.signature(x.Params, x.Results, multiLine)
860 case *ast.InterfaceType:
861 p.print(token.INTERFACE)
862 p.fieldList(x.Methods, false, x.Incomplete)
865 p.print(token.MAP, token.LBRACK)
866 p.expr(x.Key, multiLine)
867 p.print(token.RBRACK)
868 p.expr(x.Value, multiLine)
872 case ast.SEND | ast.RECV:
875 p.print(token.ARROW, token.CHAN)
877 p.print(token.CHAN, token.ARROW)
880 p.expr(x.Value, multiLine)
889 func (p *printer) expr0(x ast.Expr, depth int, multiLine *bool) {
890 p.expr1(x, token.LowestPrec, depth, multiLine)
893 // Sets multiLine to true if the expression spans multiple lines.
894 func (p *printer) expr(x ast.Expr, multiLine *bool) {
896 p.expr1(x, token.LowestPrec, depth, multiLine)
899 // ----------------------------------------------------------------------------
902 // Print the statement list indented, but without a newline after the last statement.
903 // Extra line breaks between statements in the source are respected but at most one
904 // empty line is printed between statements.
905 func (p *printer) stmtList(list []ast.Stmt, _indent int, nextIsRBrace bool) {
906 // TODO(gri): fix _indent code
911 for i, s := range list {
912 // _indent == 0 only for lists of switch/select case clauses;
913 // in those cases each clause is a new section
914 p.linebreak(p.fset.Position(s.Pos()).Line, 1, ignore, i == 0 || _indent == 0 || multiLine)
916 p.stmt(s, nextIsRBrace && i == len(list)-1, &multiLine)
923 // block prints an *ast.BlockStmt; it always spans at least two lines.
924 func (p *printer) block(s *ast.BlockStmt, indent int) {
925 p.print(s.Pos(), token.LBRACE)
926 p.stmtList(s.List, indent, true)
927 p.linebreak(p.fset.Position(s.Rbrace).Line, 1, ignore, true)
928 p.print(s.Rbrace, token.RBRACE)
931 func isTypeName(x ast.Expr) bool {
932 switch t := x.(type) {
935 case *ast.SelectorExpr:
936 return isTypeName(t.X)
941 func stripParens(x ast.Expr) ast.Expr {
942 if px, strip := x.(*ast.ParenExpr); strip {
943 // parentheses must not be stripped if there are any
944 // unparenthesized composite literals starting with
946 ast.Inspect(px.X, func(node ast.Node) bool {
947 switch x := node.(type) {
949 // parentheses protect enclosed composite literals
951 case *ast.CompositeLit:
952 if isTypeName(x.Type) {
953 strip = false // do not strip parentheses
957 // in all other cases, keep inspecting
961 return stripParens(px.X)
967 func (p *printer) controlClause(isForStmt bool, init ast.Stmt, expr ast.Expr, post ast.Stmt) {
970 if init == nil && post == nil {
971 // no semicolons required
973 p.expr(stripParens(expr), ignoreMultiLine)
977 // all semicolons required
978 // (they are not separators, print them explicitly)
980 p.stmt(init, false, ignoreMultiLine)
982 p.print(token.SEMICOLON, blank)
984 p.expr(stripParens(expr), ignoreMultiLine)
988 p.print(token.SEMICOLON, blank)
991 p.stmt(post, false, ignoreMultiLine)
1001 // Sets multiLine to true if the statements spans multiple lines.
1002 func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool, multiLine *bool) {
1005 switch s := stmt.(type) {
1010 p.decl(s.Decl, multiLine)
1012 case *ast.EmptyStmt:
1015 case *ast.LabeledStmt:
1016 // a "correcting" unindent immediately following a line break
1017 // is applied before the line break if there is no comment
1018 // between (see writeWhitespace)
1020 p.expr(s.Label, multiLine)
1021 p.print(s.Colon, token.COLON, indent)
1022 if e, isEmpty := s.Stmt.(*ast.EmptyStmt); isEmpty {
1024 p.print(newline, e.Pos(), token.SEMICOLON)
1028 p.linebreak(p.fset.Position(s.Stmt.Pos()).Line, 1, ignore, true)
1030 p.stmt(s.Stmt, nextIsRBrace, multiLine)
1034 p.expr0(s.X, depth, multiLine)
1038 p.expr0(s.Chan, depth, multiLine)
1039 p.print(blank, s.Arrow, token.ARROW, blank)
1040 p.expr0(s.Value, depth, multiLine)
1042 case *ast.IncDecStmt:
1044 p.expr0(s.X, depth+1, multiLine)
1045 p.print(s.TokPos, s.Tok)
1047 case *ast.AssignStmt:
1049 if len(s.Lhs) > 1 && len(s.Rhs) > 1 {
1052 p.exprList(s.Pos(), s.Lhs, depth, commaSep, multiLine, s.TokPos)
1053 p.print(blank, s.TokPos, s.Tok)
1054 p.exprList(s.TokPos, s.Rhs, depth, blankStart|commaSep, multiLine, token.NoPos)
1057 p.print(token.GO, blank)
1058 p.expr(s.Call, multiLine)
1060 case *ast.DeferStmt:
1061 p.print(token.DEFER, blank)
1062 p.expr(s.Call, multiLine)
1064 case *ast.ReturnStmt:
1065 p.print(token.RETURN)
1066 if s.Results != nil {
1067 p.exprList(s.Pos(), s.Results, 1, blankStart|commaSep, multiLine, token.NoPos)
1070 case *ast.BranchStmt:
1074 p.expr(s.Label, multiLine)
1077 case *ast.BlockStmt:
1083 p.controlClause(false, s.Init, s.Cond, nil)
1087 p.print(blank, token.ELSE, blank)
1088 switch s.Else.(type) {
1089 case *ast.BlockStmt, *ast.IfStmt:
1090 p.stmt(s.Else, nextIsRBrace, ignoreMultiLine)
1092 p.print(token.LBRACE, indent, formfeed)
1093 p.stmt(s.Else, true, ignoreMultiLine)
1094 p.print(unindent, formfeed, token.RBRACE)
1098 case *ast.CaseClause:
1101 p.exprList(s.Pos(), s.List, 1, blankStart|commaSep, multiLine, s.Colon)
1103 p.print(token.DEFAULT)
1105 p.print(s.Colon, token.COLON)
1106 p.stmtList(s.Body, 1, nextIsRBrace)
1108 case *ast.SwitchStmt:
1109 p.print(token.SWITCH)
1110 p.controlClause(false, s.Init, s.Tag, nil)
1114 case *ast.TypeSwitchStmt:
1115 p.print(token.SWITCH)
1118 p.stmt(s.Init, false, ignoreMultiLine)
1119 p.print(token.SEMICOLON)
1122 p.stmt(s.Assign, false, ignoreMultiLine)
1127 case *ast.CommClause:
1129 p.print(token.CASE, blank)
1130 p.stmt(s.Comm, false, ignoreMultiLine)
1132 p.print(token.DEFAULT)
1134 p.print(s.Colon, token.COLON)
1135 p.stmtList(s.Body, 1, nextIsRBrace)
1137 case *ast.SelectStmt:
1138 p.print(token.SELECT, blank)
1140 if len(body.List) == 0 && !p.commentBefore(p.fset.Position(body.Rbrace)) {
1141 // print empty select statement w/o comments on one line
1142 p.print(body.Lbrace, token.LBRACE, body.Rbrace, token.RBRACE)
1150 p.controlClause(true, s.Init, s.Cond, s.Post)
1154 case *ast.RangeStmt:
1155 p.print(token.FOR, blank)
1156 p.expr(s.Key, multiLine)
1158 p.print(token.COMMA, blank)
1159 p.expr(s.Value, multiLine)
1161 p.print(blank, s.TokPos, s.Tok, blank, token.RANGE, blank)
1162 p.expr(stripParens(s.X), multiLine)
1168 panic("unreachable")
1174 // ----------------------------------------------------------------------------
1177 // The keepTypeColumn function determines if the type column of a series of
1178 // consecutive const or var declarations must be kept, or if initialization
1179 // values (V) can be placed in the type column (T) instead. The i'th entry
1180 // in the result slice is true if the type column in spec[i] must be kept.
1182 // For example, the declaration:
1185 // foobar int = 42 // comment
1191 // leads to the type/values matrix below. A run of value columns (V) can
1192 // be moved into the type column if there is no type for any of the values
1193 // in that column (we only move entire columns so that they align properly).
1195 // matrix formatted result
1197 // T V -> T V -> true there is a T and so the type
1198 // - V - V true column must be kept
1200 // - V V - false V is moved into T column
1202 func keepTypeColumn(specs []ast.Spec) []bool {
1203 m := make([]bool, len(specs))
1205 populate := func(i, j int, keepType bool) {
1213 i0 := -1 // if i0 >= 0 we are in a run and i0 is the start of the run
1215 for i, s := range specs {
1216 t := s.(*ast.ValueSpec)
1217 if t.Values != nil {
1219 // start of a run of ValueSpecs with non-nil Values
1226 populate(i0, i, keepType)
1236 populate(i0, len(specs), keepType)
1242 func (p *printer) valueSpec(s *ast.ValueSpec, keepType, doIndent bool, multiLine *bool) {
1244 p.identList(s.Names, doIndent, multiLine) // always present
1246 if s.Type != nil || keepType {
1251 p.expr(s.Type, multiLine)
1253 if s.Values != nil {
1254 p.print(vtab, token.ASSIGN)
1255 p.exprList(token.NoPos, s.Values, 1, blankStart|commaSep, multiLine, token.NoPos)
1258 if s.Comment != nil {
1259 for ; extraTabs > 0; extraTabs-- {
1262 p.setComment(s.Comment)
1266 // The parameter n is the number of specs in the group. If doIndent is set,
1267 // multi-line identifier lists in the spec are indented when the first
1268 // linebreak is encountered.
1269 // Sets multiLine to true if the spec spans multiple lines.
1271 func (p *printer) spec(spec ast.Spec, n int, doIndent bool, multiLine *bool) {
1272 switch s := spec.(type) {
1273 case *ast.ImportSpec:
1276 p.expr(s.Name, multiLine)
1279 p.expr(s.Path, multiLine)
1280 p.setComment(s.Comment)
1283 case *ast.ValueSpec:
1285 p.internalError("expected n = 1; got", n)
1288 p.identList(s.Names, doIndent, multiLine) // always present
1291 p.expr(s.Type, multiLine)
1293 if s.Values != nil {
1294 p.print(blank, token.ASSIGN)
1295 p.exprList(token.NoPos, s.Values, 1, blankStart|commaSep, multiLine, token.NoPos)
1297 p.setComment(s.Comment)
1301 p.expr(s.Name, multiLine)
1307 p.expr(s.Type, multiLine)
1308 p.setComment(s.Comment)
1311 panic("unreachable")
1315 // Sets multiLine to true if the declaration spans multiple lines.
1316 func (p *printer) genDecl(d *ast.GenDecl, multiLine *bool) {
1318 p.print(d.Pos(), d.Tok, blank)
1320 if d.Lparen.IsValid() {
1321 // group of parenthesized declarations
1322 p.print(d.Lparen, token.LPAREN)
1323 if n := len(d.Specs); n > 0 {
1324 p.print(indent, formfeed)
1325 if n > 1 && (d.Tok == token.CONST || d.Tok == token.VAR) {
1326 // two or more grouped const/var declarations:
1327 // determine if the type column must be kept
1328 keepType := keepTypeColumn(d.Specs)
1330 for i, s := range d.Specs {
1332 p.linebreak(p.fset.Position(s.Pos()).Line, 1, ignore, ml)
1335 p.valueSpec(s.(*ast.ValueSpec), keepType[i], false, &ml)
1339 for i, s := range d.Specs {
1341 p.linebreak(p.fset.Position(s.Pos()).Line, 1, ignore, ml)
1344 p.spec(s, n, false, &ml)
1347 p.print(unindent, formfeed)
1350 p.print(d.Rparen, token.RPAREN)
1353 // single declaration
1354 p.spec(d.Specs[0], 1, true, multiLine)
1358 // nodeSize determines the size of n in chars after formatting.
1359 // The result is <= maxSize if the node fits on one line with at
1360 // most maxSize chars and the formatted output doesn't contain
1361 // any control chars. Otherwise, the result is > maxSize.
1363 func (p *printer) nodeSize(n ast.Node, maxSize int) (size int) {
1364 // nodeSize invokes the printer, which may invoke nodeSize
1365 // recursively. For deep composite literal nests, this can
1366 // lead to an exponential algorithm. Remember previous
1367 // results to prune the recursion (was issue 1628).
1368 if size, found := p.nodeSizes[n]; found {
1372 size = maxSize + 1 // assume n doesn't fit
1373 p.nodeSizes[n] = size
1375 // nodeSize computation must be independent of particular
1376 // style so that we always get the same decision; print
1378 cfg := Config{Mode: RawFormat}
1379 var buf bytes.Buffer
1380 if err := cfg.fprint(&buf, p.fset, n, p.nodeSizes); err != nil {
1383 if buf.Len() <= maxSize {
1384 for _, ch := range buf.Bytes() {
1389 size = buf.Len() // n fits
1390 p.nodeSizes[n] = size
1395 func (p *printer) isOneLineFunc(b *ast.BlockStmt, headerSize int) bool {
1398 if pos1.IsValid() && pos2.IsValid() && p.fset.Position(pos1).Line != p.fset.Position(pos2).Line {
1399 // opening and closing brace are on different lines - don't make it a one-liner
1402 if len(b.List) > 5 || p.commentBefore(p.fset.Position(pos2)) {
1403 // too many statements or there is a comment inside - don't make it a one-liner
1406 // otherwise, estimate body size
1409 for i, s := range b.List {
1411 bodySize += 2 // space for a semicolon and blank
1413 bodySize += p.nodeSize(s, maxSize)
1415 return headerSize+bodySize <= maxSize
1418 // Sets multiLine to true if the function body spans multiple lines.
1419 func (p *printer) funcBody(b *ast.BlockStmt, headerSize int, isLit bool, multiLine *bool) {
1424 if p.isOneLineFunc(b, headerSize) {
1429 p.print(sep, b.Lbrace, token.LBRACE)
1430 if len(b.List) > 0 {
1432 for i, s := range b.List {
1434 p.print(token.SEMICOLON, blank)
1436 p.stmt(s, i == len(b.List)-1, ignoreMultiLine)
1440 p.print(b.Rbrace, token.RBRACE)
1449 // distance returns the column difference between from and to if both
1450 // are on the same line; if they are on different lines (or unknown)
1451 // the result is infinity.
1452 func (p *printer) distance(from0 token.Pos, to token.Position) int {
1453 from := p.fset.Position(from0)
1454 if from.IsValid() && to.IsValid() && from.Line == to.Line {
1455 return to.Column - from.Column
1460 // Sets multiLine to true if the declaration spans multiple lines.
1461 func (p *printer) funcDecl(d *ast.FuncDecl, multiLine *bool) {
1463 p.print(d.Pos(), token.FUNC, blank)
1465 p.parameters(d.Recv, multiLine) // method: print receiver
1468 p.expr(d.Name, multiLine)
1469 p.signature(d.Type.Params, d.Type.Results, multiLine)
1470 p.funcBody(d.Body, p.distance(d.Pos(), p.pos), false, multiLine)
1473 // Sets multiLine to true if the declaration spans multiple lines.
1474 func (p *printer) decl(decl ast.Decl, multiLine *bool) {
1475 switch d := decl.(type) {
1477 p.print(d.Pos(), "BadDecl")
1479 p.genDecl(d, multiLine)
1481 p.funcDecl(d, multiLine)
1483 panic("unreachable")
1487 // ----------------------------------------------------------------------------
1490 func declToken(decl ast.Decl) (tok token.Token) {
1492 switch d := decl.(type) {
1501 func (p *printer) file(src *ast.File) {
1502 p.setComment(src.Doc)
1503 p.print(src.Pos(), token.PACKAGE, blank)
1504 p.expr(src.Name, ignoreMultiLine)
1506 if len(src.Decls) > 0 {
1507 tok := token.ILLEGAL
1508 for _, d := range src.Decls {
1511 // if the declaration token changed (e.g., from CONST to TYPE)
1512 // print an empty line between top-level declarations
1517 p.linebreak(p.fset.Position(d.Pos()).Line, min, ignore, false)
1518 p.decl(d, ignoreMultiLine)