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.
18 // Other 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
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
27 // ----------------------------------------------------------------------------
30 // Print as many newlines as necessary (but at least min newlines) to get to
31 // the current line. ws is printed before the first line break. If newSection
32 // is set, the first line break is printed as formfeed. Returns true if any
33 // line break was printed; returns false otherwise.
35 // TODO(gri): linebreak may add too many lines if the next statement at "line"
36 // is preceeded by comments because the computation of n assumes
37 // the current position before the comment and the target position
38 // after the comment. Thus, after interspersing such comments, the
39 // space taken up by them is not considered to reduce the number of
40 // linebreaks. At the moment there is no easy way to know about
41 // future (not yet interspersed) comments in this function.
43 func (p *printer) linebreak(line, min int, ws whiteSpace, newSection bool) (printedBreak bool) {
44 n := p.nlines(line-p.pos.Line, min)
60 // setComment sets g as the next comment if g != nil and if node comments
61 // are enabled - this mode is used when printing source code fragments such
62 // as exports only. It assumes that there are no other pending comments to
64 func (p *printer) setComment(g *ast.CommentGroup) {
65 if g == nil || !p.useNodeComments {
68 if p.comments == nil {
69 // initialize p.comments lazily
70 p.comments = make([]*ast.CommentGroup, 1)
71 } else if p.cindex < len(p.comments) {
72 // for some reason there are pending comments; this
73 // should never happen - handle gracefully and flush
74 // all comments up to g, ignore anything after that
75 p.flush(p.fset.Position(g.List[0].Pos()), token.ILLEGAL)
82 type exprListMode uint
85 blankStart exprListMode = 1 << iota // print a blank before a non-empty list
86 blankEnd // print a blank after a non-empty list
87 commaSep // elements are separated by commas
88 commaTerm // list is optionally terminated by a comma
89 noIndent // no extra indentation in multi-line lists
90 periodSep // elements are separated by periods
94 // Sets multiLine to true if the identifier list spans multiple lines.
95 // If indent is set, a multi-line identifier list is indented after the
96 // first linebreak encountered.
97 func (p *printer) identList(list []*ast.Ident, indent bool, multiLine *bool) {
98 // convert into an expression list so we can re-use exprList formatting
99 xlist := make([]ast.Expr, len(list))
100 for i, x := range list {
107 p.exprList(token.NoPos, xlist, 1, mode, multiLine, token.NoPos)
111 // Print a list of expressions. If the list spans multiple
112 // source lines, the original line breaks are respected between
113 // expressions. Sets multiLine to true if the list spans multiple
116 // TODO(gri) Consider rewriting this to be independent of []ast.Expr
117 // so that we can use the algorithm for any kind of list
118 // (e.g., pass list via a channel over which to range).
119 func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exprListMode, multiLine *bool, next0 token.Pos) {
124 if mode&blankStart != 0 {
128 prev := p.fset.Position(prev0)
129 next := p.fset.Position(next0)
130 line := p.fset.Position(list[0].Pos()).Line
131 endLine := p.fset.Position(list[len(list)-1].End()).Line
133 if prev.IsValid() && prev.Line == line && line == endLine {
134 // all list entries on a single line
135 for i, x := range list {
137 if mode&commaSep != 0 {
142 p.expr0(x, depth, multiLine)
144 if mode&blankEnd != 0 {
150 // list entries span multiple lines;
151 // use source code positions to guide line breaks
153 // don't add extra indentation if noIndent is set;
154 // i.e., pretend that the first line is already indented
156 if mode&noIndent == 0 {
160 // the first linebreak is always a formfeed since this section must not
161 // depend on any previous formatting
162 prevBreak := -1 // index of last expression that was followed by a linebreak
163 if prev.IsValid() && prev.Line < line && p.linebreak(line, 0, ws, true) {
169 // initialize expression/key size: a zero value indicates expr/key doesn't fit on a single line
172 // print all list elements
173 for i, x := range list {
175 line = p.fset.Position(x.Pos()).Line
177 // determine if the next linebreak, if any, needs to use formfeed:
178 // in general, use the entire node size to make the decision; for
179 // key:value expressions, use the key size
180 // TODO(gri) for a better result, should probably incorporate both
181 // the key and the node size into the decision process
184 // determine element size: all bets are off if we don't have
185 // position information for the previous and next token (likely
186 // generated code - simply ignore the size in this case by setting
189 const infinity = 1e6 // larger than any source line
190 size = p.nodeSize(x, infinity)
191 pair, isPair := x.(*ast.KeyValueExpr)
192 if size <= infinity && prev.IsValid() && next.IsValid() {
193 // x fits on a single line
195 size = p.nodeSize(pair.Key, infinity) // size <= infinity
198 // size too large or we don't have good layout information
202 // if the previous line and the current line had single-
203 // line-expressions and the key sizes are small or the
204 // the ratio between the key sizes does not exceed a
205 // threshold, align columns and do not use formfeed
206 if prevSize > 0 && size > 0 {
208 if prevSize <= smallSize && size <= smallSize {
211 const r = 4 // threshold
212 ratio := float64(size) / float64(prevSize)
213 useFF = ratio <= 1/r || r <= ratio
218 if mode&commaSep != 0 {
221 if mode&periodSep != 0 {
222 p.print(token.PERIOD)
224 if prevLine < line && prevLine > 0 && line > 0 {
225 // lines are broken using newlines so comments remain aligned
226 // unless forceFF is set or there are multiple expressions on
227 // the same line in which case formfeed is used
228 if p.linebreak(line, 0, ws, useFF || prevBreak+1 < i) {
233 } else if mode&periodSep == 0 {
236 // period-separated list elements don't need a blank
239 if isPair && size > 0 && len(list) > 1 {
240 // we have a key:value expression that fits onto one line and
241 // is in a list with more then one entry: use a column for the
242 // key such that consecutive entries can align if possible
243 p.expr(pair.Key, multiLine)
244 p.print(pair.Colon, token.COLON, vtab)
245 p.expr(pair.Value, multiLine)
247 p.expr0(x, depth, multiLine)
251 if mode&commaTerm != 0 && next.IsValid() && p.pos.Line < next.Line {
252 // print a terminating comma if the next token is on a new line
254 if ws == ignore && mode&noIndent == 0 {
255 // unindent if we indented
258 p.print(formfeed) // terminating comma needs a line break to look good
262 if mode&blankEnd != 0 {
266 if ws == ignore && mode&noIndent == 0 {
267 // unindent if we indented
273 // Sets multiLine to true if the the parameter list spans multiple lines.
274 func (p *printer) parameters(fields *ast.FieldList, multiLine *bool) {
275 p.print(fields.Opening, token.LPAREN)
276 if len(fields.List) > 0 {
277 var prevLine, line int
278 for i, par := range fields.List {
281 if len(par.Names) > 0 {
282 line = p.fset.Position(par.Names[0].Pos()).Line
284 line = p.fset.Position(par.Type.Pos()).Line
286 if 0 < prevLine && prevLine < line && p.linebreak(line, 0, ignore, true) {
292 if len(par.Names) > 0 {
293 p.identList(par.Names, false, multiLine)
296 p.expr(par.Type, multiLine)
297 prevLine = p.fset.Position(par.Type.Pos()).Line
300 p.print(fields.Closing, token.RPAREN)
304 // Sets multiLine to true if the signature spans multiple lines.
305 func (p *printer) signature(params, result *ast.FieldList, multiLine *bool) {
306 p.parameters(params, multiLine)
307 n := result.NumFields()
310 if n == 1 && result.List[0].Names == nil {
311 // single anonymous result; no ()'s
312 p.expr(result.List[0].Type, multiLine)
315 p.parameters(result, multiLine)
320 func identListSize(list []*ast.Ident, maxSize int) (size int) {
321 for i, x := range list {
334 func (p *printer) isOneLineFieldList(list []*ast.Field) bool {
336 return false // allow only one field
339 if f.Tag != nil || f.Comment != nil {
340 return false // don't allow tags or comments
342 // only name(s) and type
343 const maxSize = 30 // adjust as appropriate, this is an approximate value
344 namesSize := identListSize(f.Names, maxSize)
346 namesSize = 1 // blank between names and types
348 typeSize := p.nodeSize(f.Type, maxSize)
349 return namesSize+typeSize <= maxSize
353 func (p *printer) setLineComment(text string) {
354 p.setComment(&ast.CommentGroup{[]*ast.Comment{&ast.Comment{token.NoPos, text}}})
358 func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool) {
364 lbrace := fields.Opening
366 rbrace := fields.Closing
367 srcIsOneLine := lbrace.IsValid() && rbrace.IsValid() && p.fset.Position(lbrace).Line == p.fset.Position(rbrace).Line
369 if !isIncomplete && !p.commentBefore(p.fset.Position(rbrace)) && srcIsOneLine {
370 // possibly a one-line struct/interface
372 // no blank between keyword and {} in this case
373 p.print(lbrace, token.LBRACE, rbrace, token.RBRACE)
375 } else if isStruct && p.isOneLineFieldList(list) { // for now ignore interfaces
376 // small enough - print on one line
377 // (don't use identList and ignore source line breaks)
378 p.print(lbrace, token.LBRACE, blank)
380 for i, x := range f.Names {
382 p.print(token.COMMA, blank)
384 p.expr(x, ignoreMultiLine)
386 if len(f.Names) > 0 {
389 p.expr(f.Type, ignoreMultiLine)
390 p.print(blank, rbrace, token.RBRACE)
395 // at least one entry or incomplete
396 p.print(blank, lbrace, token.LBRACE, indent, formfeed)
404 for i, f := range list {
406 p.linebreak(p.fset.Position(f.Pos()).Line, 1, ignore, ml)
411 if len(f.Names) > 0 {
413 p.identList(f.Names, false, &ml)
423 if len(f.Names) > 0 && sep == vtab {
430 if f.Comment != nil {
431 for ; extraTabs > 0; extraTabs-- {
434 p.setComment(f.Comment)
441 p.flush(p.fset.Position(rbrace), token.RBRACE) // make sure we don't loose the last line comment
442 p.setLineComment("// contains unexported fields")
445 } else { // interface
448 for i, f := range list {
450 p.linebreak(p.fset.Position(f.Pos()).Line, 1, ignore, ml)
454 if ftyp, isFtyp := f.Type.(*ast.FuncType); isFtyp {
456 p.expr(f.Names[0], &ml)
457 p.signature(ftyp.Params, ftyp.Results, &ml)
459 // embedded interface
462 p.setComment(f.Comment)
468 p.flush(p.fset.Position(rbrace), token.RBRACE) // make sure we don't loose the last line comment
469 p.setLineComment("// contains unexported methods")
473 p.print(unindent, formfeed, rbrace, token.RBRACE)
477 // ----------------------------------------------------------------------------
480 func walkBinary(e *ast.BinaryExpr) (has4, has5 bool, maxProblem int) {
481 switch e.Op.Precedence() {
488 switch l := e.X.(type) {
489 case *ast.BinaryExpr:
490 if l.Op.Precedence() < e.Op.Precedence() {
491 // parens will be inserted.
492 // pretend this is an *ast.ParenExpr and do nothing.
495 h4, h5, mp := walkBinary(l)
503 switch r := e.Y.(type) {
504 case *ast.BinaryExpr:
505 if r.Op.Precedence() <= e.Op.Precedence() {
506 // parens will be inserted.
507 // pretend this is an *ast.ParenExpr and do nothing.
510 h4, h5, mp := walkBinary(r)
518 if e.Op == token.QUO { // `*/`
523 switch e.Op.String() + r.Op.String() {
524 case "/*", "&&", "&^":
536 func cutoff(e *ast.BinaryExpr, depth int) int {
537 has4, has5, maxProblem := walkBinary(e)
539 return maxProblem + 1
554 func diffPrec(expr ast.Expr, prec int) int {
555 x, ok := expr.(*ast.BinaryExpr)
556 if !ok || prec != x.Op.Precedence() {
563 func reduceDepth(depth int) int {
572 // Format the binary expression: decide the cutoff and then format.
573 // Let's call depth == 1 Normal mode, and depth > 1 Compact mode.
574 // (Algorithm suggestion by Russ Cox.)
576 // The precedences are:
577 // 5 * / % << >> & &^
583 // The only decision is whether there will be spaces around levels 4 and 5.
584 // There are never spaces at level 6 (unary), and always spaces at levels 3 and below.
586 // To choose the cutoff, look at the whole expression but excluding primary
587 // expressions (function calls, parenthesized exprs), and apply these rules:
589 // 1) If there is a binary operator with a right side unary operand
590 // that would clash without a space, the cutoff must be (in order):
598 // (Comparison operators always have spaces around them.)
600 // 2) If there is a mix of level 5 and level 4 operators, then the cutoff
601 // is 5 (use spaces to distinguish precedence) in Normal mode
602 // and 4 (never use spaces) in Compact mode.
604 // 3) If there are no level 4 operators or no level 5 operators, then the
605 // cutoff is 6 (always use spaces) in Normal mode
606 // and 4 (never use spaces) in Compact mode.
608 // Sets multiLine to true if the binary expression spans multiple lines.
609 func (p *printer) binaryExpr(x *ast.BinaryExpr, prec1, cutoff, depth int, multiLine *bool) {
610 prec := x.Op.Precedence()
612 // parenthesis needed
613 // Note: The parser inserts an ast.ParenExpr node; thus this case
614 // can only occur if the AST is created in a different way.
615 p.print(token.LPAREN)
616 p.expr0(x, reduceDepth(depth), multiLine) // parentheses undo one level of depth
617 p.print(token.RPAREN)
621 printBlank := prec < cutoff
624 p.expr1(x.X, prec, depth+diffPrec(x.X, prec), multiLine)
628 xline := p.pos.Line // before the operator (it may be on the next line!)
629 yline := p.fset.Position(x.Y.Pos()).Line
630 p.print(x.OpPos, x.Op)
631 if xline != yline && xline > 0 && yline > 0 {
632 // at least one line break, but respect an extra empty line
634 if p.linebreak(yline, 1, ws, true) {
637 printBlank = false // no blank after line break
643 p.expr1(x.Y, prec+1, depth+1, multiLine)
650 func isBinary(expr ast.Expr) bool {
651 _, ok := expr.(*ast.BinaryExpr)
656 // If the expression contains one or more selector expressions, splits it into
657 // two expressions at the rightmost period. Writes entire expr to suffix when
658 // selector isn't found. Rewrites AST nodes for calls, index expressions and
659 // type assertions, all of which may be found in selector chains, to make them
660 // parts of the chain.
661 func splitSelector(expr ast.Expr) (body, suffix ast.Expr) {
662 switch x := expr.(type) {
663 case *ast.SelectorExpr:
664 body, suffix = x.X, x.Sel
667 body, suffix = splitSelector(x.Fun)
669 suffix = &ast.CallExpr{suffix, x.Lparen, x.Args, x.Ellipsis, x.Rparen}
673 body, suffix = splitSelector(x.X)
675 suffix = &ast.IndexExpr{suffix, x.Lbrack, x.Index, x.Rbrack}
679 body, suffix = splitSelector(x.X)
681 suffix = &ast.SliceExpr{suffix, x.Lbrack, x.Low, x.High, x.Rbrack}
684 case *ast.TypeAssertExpr:
685 body, suffix = splitSelector(x.X)
687 suffix = &ast.TypeAssertExpr{suffix, x.Type}
696 // Convert an expression into an expression list split at the periods of
697 // selector expressions.
698 func selectorExprList(expr ast.Expr) (list []ast.Expr) {
702 expr, suffix = splitSelector(expr)
703 list = append(list, suffix)
707 for i, j := 0, len(list)-1; i < j; i, j = i+1, j-1 {
708 list[i], list[j] = list[j], list[i]
715 // Sets multiLine to true if the expression spans multiple lines.
716 func (p *printer) expr1(expr ast.Expr, prec1, depth int, multiLine *bool) {
719 switch x := expr.(type) {
726 case *ast.BinaryExpr:
728 p.internalError("depth < 1:", depth)
731 p.binaryExpr(x, prec1, cutoff(x, depth), depth, multiLine)
733 case *ast.KeyValueExpr:
734 p.expr(x.Key, multiLine)
735 p.print(x.Colon, token.COLON, blank)
736 p.expr(x.Value, multiLine)
739 const prec = token.UnaryPrec
741 // parenthesis needed
742 p.print(token.LPAREN)
744 p.expr(x.X, multiLine)
745 p.print(token.RPAREN)
747 // no parenthesis needed
749 p.expr(x.X, multiLine)
753 const prec = token.UnaryPrec
755 // parenthesis needed
756 p.print(token.LPAREN)
758 p.print(token.RPAREN)
760 // no parenthesis needed
762 if x.Op == token.RANGE {
763 // TODO(gri) Remove this code if it cannot be reached.
766 p.expr1(x.X, prec, depth, multiLine)
773 p.expr(x.Type, multiLine)
774 p.funcBody(x.Body, p.distance(x.Type.Pos(), p.pos), true, multiLine)
777 if _, hasParens := x.X.(*ast.ParenExpr); hasParens {
778 // don't print parentheses around an already parenthesized expression
779 // TODO(gri) consider making this more general and incorporate precedence levels
780 p.expr0(x.X, reduceDepth(depth), multiLine) // parentheses undo one level of depth
782 p.print(token.LPAREN)
783 p.expr0(x.X, reduceDepth(depth), multiLine) // parentheses undo one level of depth
784 p.print(x.Rparen, token.RPAREN)
787 case *ast.SelectorExpr:
788 parts := selectorExprList(expr)
789 p.exprList(token.NoPos, parts, depth, periodSep, multiLine, token.NoPos)
791 case *ast.TypeAssertExpr:
792 p.expr1(x.X, token.HighestPrec, depth, multiLine)
793 p.print(token.PERIOD, token.LPAREN)
795 p.expr(x.Type, multiLine)
799 p.print(token.RPAREN)
802 // TODO(gri): should treat[] like parentheses and undo one level of depth
803 p.expr1(x.X, token.HighestPrec, 1, multiLine)
804 p.print(x.Lbrack, token.LBRACK)
805 p.expr0(x.Index, depth+1, multiLine)
806 p.print(x.Rbrack, token.RBRACK)
809 // TODO(gri): should treat[] like parentheses and undo one level of depth
810 p.expr1(x.X, token.HighestPrec, 1, multiLine)
811 p.print(x.Lbrack, token.LBRACK)
813 p.expr0(x.Low, depth+1, multiLine)
815 // blanks around ":" if both sides exist and either side is a binary expression
816 if depth <= 1 && x.Low != nil && x.High != nil && (isBinary(x.Low) || isBinary(x.High)) {
817 p.print(blank, token.COLON, blank)
822 p.expr0(x.High, depth+1, multiLine)
824 p.print(x.Rbrack, token.RBRACK)
830 p.expr1(x.Fun, token.HighestPrec, depth, multiLine)
831 p.print(x.Lparen, token.LPAREN)
832 p.exprList(x.Lparen, x.Args, depth, commaSep|commaTerm, multiLine, x.Rparen)
833 if x.Ellipsis.IsValid() {
834 p.print(x.Ellipsis, token.ELLIPSIS)
836 p.print(x.Rparen, token.RPAREN)
838 case *ast.CompositeLit:
839 // composite literal elements that are composite literals themselves may have the type omitted
841 p.expr1(x.Type, token.HighestPrec, depth, multiLine)
843 p.print(x.Lbrace, token.LBRACE)
844 p.exprList(x.Lbrace, x.Elts, 1, commaSep|commaTerm, multiLine, x.Rbrace)
845 // do not insert extra line breaks because of comments before
846 // the closing '}' as it might break the code if there is no
848 p.print(noExtraLinebreak, x.Rbrace, token.RBRACE, noExtraLinebreak)
851 p.print(token.ELLIPSIS)
853 p.expr(x.Elt, multiLine)
857 p.print(token.LBRACK)
859 p.expr(x.Len, multiLine)
861 p.print(token.RBRACK)
862 p.expr(x.Elt, multiLine)
864 case *ast.StructType:
865 p.print(token.STRUCT)
866 p.fieldList(x.Fields, true, x.Incomplete)
870 p.signature(x.Params, x.Results, multiLine)
872 case *ast.InterfaceType:
873 p.print(token.INTERFACE)
874 p.fieldList(x.Methods, false, x.Incomplete)
877 p.print(token.MAP, token.LBRACK)
878 p.expr(x.Key, multiLine)
879 p.print(token.RBRACK)
880 p.expr(x.Value, multiLine)
884 case ast.SEND | ast.RECV:
887 p.print(token.ARROW, token.CHAN)
889 p.print(token.CHAN, token.ARROW)
892 p.expr(x.Value, multiLine)
902 func (p *printer) expr0(x ast.Expr, depth int, multiLine *bool) {
903 p.expr1(x, token.LowestPrec, depth, multiLine)
907 // Sets multiLine to true if the expression spans multiple lines.
908 func (p *printer) expr(x ast.Expr, multiLine *bool) {
910 p.expr1(x, token.LowestPrec, depth, multiLine)
914 // ----------------------------------------------------------------------------
917 // Print the statement list indented, but without a newline after the last statement.
918 // Extra line breaks between statements in the source are respected but at most one
919 // empty line is printed between statements.
920 func (p *printer) stmtList(list []ast.Stmt, _indent int, nextIsRBrace bool) {
921 // TODO(gri): fix _indent code
926 for i, s := range list {
927 // _indent == 0 only for lists of switch/select case clauses;
928 // in those cases each clause is a new section
929 p.linebreak(p.fset.Position(s.Pos()).Line, 1, ignore, i == 0 || _indent == 0 || multiLine)
931 p.stmt(s, nextIsRBrace && i == len(list)-1, &multiLine)
939 // block prints an *ast.BlockStmt; it always spans at least two lines.
940 func (p *printer) block(s *ast.BlockStmt, indent int) {
941 p.print(s.Pos(), token.LBRACE)
942 p.stmtList(s.List, indent, true)
943 p.linebreak(p.fset.Position(s.Rbrace).Line, 1, ignore, true)
944 p.print(s.Rbrace, token.RBRACE)
948 func isTypeName(x ast.Expr) bool {
949 switch t := x.(type) {
952 case *ast.SelectorExpr:
953 return isTypeName(t.X)
959 func stripParens(x ast.Expr) ast.Expr {
960 if px, strip := x.(*ast.ParenExpr); strip {
961 // parentheses must not be stripped if there are any
962 // unparenthesized composite literals starting with
964 ast.Inspect(px.X, func(node ast.Node) bool {
965 switch x := node.(type) {
967 // parentheses protect enclosed composite literals
969 case *ast.CompositeLit:
970 if isTypeName(x.Type) {
971 strip = false // do not strip parentheses
975 // in all other cases, keep inspecting
979 return stripParens(px.X)
986 func (p *printer) controlClause(isForStmt bool, init ast.Stmt, expr ast.Expr, post ast.Stmt) {
989 if init == nil && post == nil {
990 // no semicolons required
992 p.expr(stripParens(expr), ignoreMultiLine)
996 // all semicolons required
997 // (they are not separators, print them explicitly)
999 p.stmt(init, false, ignoreMultiLine)
1001 p.print(token.SEMICOLON, blank)
1003 p.expr(stripParens(expr), ignoreMultiLine)
1007 p.print(token.SEMICOLON, blank)
1010 p.stmt(post, false, ignoreMultiLine)
1021 // Sets multiLine to true if the statements spans multiple lines.
1022 func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool, multiLine *bool) {
1025 switch s := stmt.(type) {
1030 p.decl(s.Decl, multiLine)
1032 case *ast.EmptyStmt:
1035 case *ast.LabeledStmt:
1036 // a "correcting" unindent immediately following a line break
1037 // is applied before the line break if there is no comment
1038 // between (see writeWhitespace)
1040 p.expr(s.Label, multiLine)
1041 p.print(s.Colon, token.COLON, indent)
1042 if e, isEmpty := s.Stmt.(*ast.EmptyStmt); isEmpty {
1044 p.print(newline, e.Pos(), token.SEMICOLON)
1048 p.linebreak(p.fset.Position(s.Stmt.Pos()).Line, 1, ignore, true)
1050 p.stmt(s.Stmt, nextIsRBrace, multiLine)
1054 p.expr0(s.X, depth, multiLine)
1058 p.expr0(s.Chan, depth, multiLine)
1059 p.print(blank, s.Arrow, token.ARROW, blank)
1060 p.expr0(s.Value, depth, multiLine)
1062 case *ast.IncDecStmt:
1064 p.expr0(s.X, depth+1, multiLine)
1065 p.print(s.TokPos, s.Tok)
1067 case *ast.AssignStmt:
1069 if len(s.Lhs) > 1 && len(s.Rhs) > 1 {
1072 p.exprList(s.Pos(), s.Lhs, depth, commaSep, multiLine, s.TokPos)
1073 p.print(blank, s.TokPos, s.Tok)
1074 p.exprList(s.TokPos, s.Rhs, depth, blankStart|commaSep, multiLine, token.NoPos)
1077 p.print(token.GO, blank)
1078 p.expr(s.Call, multiLine)
1080 case *ast.DeferStmt:
1081 p.print(token.DEFER, blank)
1082 p.expr(s.Call, multiLine)
1084 case *ast.ReturnStmt:
1085 p.print(token.RETURN)
1086 if s.Results != nil {
1087 p.exprList(s.Pos(), s.Results, 1, blankStart|commaSep, multiLine, token.NoPos)
1090 case *ast.BranchStmt:
1094 p.expr(s.Label, multiLine)
1097 case *ast.BlockStmt:
1103 p.controlClause(false, s.Init, s.Cond, nil)
1107 p.print(blank, token.ELSE, blank)
1108 switch s.Else.(type) {
1109 case *ast.BlockStmt, *ast.IfStmt:
1110 p.stmt(s.Else, nextIsRBrace, ignoreMultiLine)
1112 p.print(token.LBRACE, indent, formfeed)
1113 p.stmt(s.Else, true, ignoreMultiLine)
1114 p.print(unindent, formfeed, token.RBRACE)
1118 case *ast.CaseClause:
1121 p.exprList(s.Pos(), s.List, 1, blankStart|commaSep, multiLine, s.Colon)
1123 p.print(token.DEFAULT)
1125 p.print(s.Colon, token.COLON)
1126 p.stmtList(s.Body, 1, nextIsRBrace)
1128 case *ast.SwitchStmt:
1129 p.print(token.SWITCH)
1130 p.controlClause(false, s.Init, s.Tag, nil)
1134 case *ast.TypeSwitchStmt:
1135 p.print(token.SWITCH)
1138 p.stmt(s.Init, false, ignoreMultiLine)
1139 p.print(token.SEMICOLON)
1142 p.stmt(s.Assign, false, ignoreMultiLine)
1147 case *ast.CommClause:
1149 p.print(token.CASE, blank)
1150 p.stmt(s.Comm, false, ignoreMultiLine)
1152 p.print(token.DEFAULT)
1154 p.print(s.Colon, token.COLON)
1155 p.stmtList(s.Body, 1, nextIsRBrace)
1157 case *ast.SelectStmt:
1158 p.print(token.SELECT, blank)
1164 p.controlClause(true, s.Init, s.Cond, s.Post)
1168 case *ast.RangeStmt:
1169 p.print(token.FOR, blank)
1170 p.expr(s.Key, multiLine)
1172 p.print(token.COMMA, blank)
1173 p.expr(s.Value, multiLine)
1175 p.print(blank, s.TokPos, s.Tok, blank, token.RANGE, blank)
1176 p.expr(stripParens(s.X), multiLine)
1182 panic("unreachable")
1189 // ----------------------------------------------------------------------------
1192 // The parameter n is the number of specs in the group. If doIndent is set,
1193 // multi-line identifier lists in the spec are indented when the first
1194 // linebreak is encountered.
1195 // Sets multiLine to true if the spec spans multiple lines.
1197 func (p *printer) spec(spec ast.Spec, n int, doIndent bool, multiLine *bool) {
1198 switch s := spec.(type) {
1199 case *ast.ImportSpec:
1202 p.expr(s.Name, multiLine)
1205 p.expr(s.Path, multiLine)
1206 p.setComment(s.Comment)
1208 case *ast.ValueSpec:
1210 p.identList(s.Names, doIndent, multiLine) // always present
1214 p.expr(s.Type, multiLine)
1216 if s.Values != nil {
1217 p.print(blank, token.ASSIGN)
1218 p.exprList(token.NoPos, s.Values, 1, blankStart|commaSep, multiLine, token.NoPos)
1220 p.setComment(s.Comment)
1226 p.expr(s.Type, multiLine)
1229 if s.Values != nil {
1230 p.print(vtab, token.ASSIGN)
1231 p.exprList(token.NoPos, s.Values, 1, blankStart|commaSep, multiLine, token.NoPos)
1234 if s.Comment != nil {
1235 for ; extraTabs > 0; extraTabs-- {
1238 p.setComment(s.Comment)
1244 p.expr(s.Name, multiLine)
1250 p.expr(s.Type, multiLine)
1251 p.setComment(s.Comment)
1254 panic("unreachable")
1259 // Sets multiLine to true if the declaration spans multiple lines.
1260 func (p *printer) genDecl(d *ast.GenDecl, multiLine *bool) {
1262 p.print(d.Pos(), d.Tok, blank)
1264 if d.Lparen.IsValid() {
1265 // group of parenthesized declarations
1266 p.print(d.Lparen, token.LPAREN)
1267 if len(d.Specs) > 0 {
1268 p.print(indent, formfeed)
1270 for i, s := range d.Specs {
1272 p.linebreak(p.fset.Position(s.Pos()).Line, 1, ignore, ml)
1275 p.spec(s, len(d.Specs), false, &ml)
1277 p.print(unindent, formfeed)
1280 p.print(d.Rparen, token.RPAREN)
1283 // single declaration
1284 p.spec(d.Specs[0], 1, true, multiLine)
1289 // nodeSize determines the size of n in chars after formatting.
1290 // The result is <= maxSize if the node fits on one line with at
1291 // most maxSize chars and the formatted output doesn't contain
1292 // any control chars. Otherwise, the result is > maxSize.
1294 func (p *printer) nodeSize(n ast.Node, maxSize int) (size int) {
1295 // nodeSize invokes the printer, which may invoke nodeSize
1296 // recursively. For deep composite literal nests, this can
1297 // lead to an exponential algorithm. Remember previous
1298 // results to prune the recursion (was issue 1628).
1299 if size, found := p.nodeSizes[n]; found {
1303 size = maxSize + 1 // assume n doesn't fit
1304 p.nodeSizes[n] = size
1306 // nodeSize computation must be indendent of particular
1307 // style so that we always get the same decision; print
1309 cfg := Config{Mode: RawFormat}
1310 var buf bytes.Buffer
1311 if _, err := cfg.fprint(&buf, p.fset, n, p.nodeSizes); err != nil {
1314 if buf.Len() <= maxSize {
1315 for _, ch := range buf.Bytes() {
1320 size = buf.Len() // n fits
1321 p.nodeSizes[n] = size
1327 func (p *printer) isOneLineFunc(b *ast.BlockStmt, headerSize int) bool {
1330 if pos1.IsValid() && pos2.IsValid() && p.fset.Position(pos1).Line != p.fset.Position(pos2).Line {
1331 // opening and closing brace are on different lines - don't make it a one-liner
1334 if len(b.List) > 5 || p.commentBefore(p.fset.Position(pos2)) {
1335 // too many statements or there is a comment inside - don't make it a one-liner
1338 // otherwise, estimate body size
1341 for i, s := range b.List {
1343 bodySize += 2 // space for a semicolon and blank
1345 bodySize += p.nodeSize(s, maxSize)
1347 return headerSize+bodySize <= maxSize
1351 // Sets multiLine to true if the function body spans multiple lines.
1352 func (p *printer) funcBody(b *ast.BlockStmt, headerSize int, isLit bool, multiLine *bool) {
1362 if p.isOneLineFunc(b, headerSize) {
1367 p.print(sep, b.Lbrace, token.LBRACE)
1368 if len(b.List) > 0 {
1370 for i, s := range b.List {
1372 p.print(token.SEMICOLON, blank)
1374 p.stmt(s, i == len(b.List)-1, ignoreMultiLine)
1378 p.print(b.Rbrace, token.RBRACE)
1388 // distance returns the column difference between from and to if both
1389 // are on the same line; if they are on different lines (or unknown)
1390 // the result is infinity.
1391 func (p *printer) distance(from0 token.Pos, to token.Position) int {
1392 from := p.fset.Position(from0)
1393 if from.IsValid() && to.IsValid() && from.Line == to.Line {
1394 return to.Column - from.Column
1400 // Sets multiLine to true if the declaration spans multiple lines.
1401 func (p *printer) funcDecl(d *ast.FuncDecl, multiLine *bool) {
1403 p.print(d.Pos(), token.FUNC, blank)
1405 p.parameters(d.Recv, multiLine) // method: print receiver
1408 p.expr(d.Name, multiLine)
1409 p.signature(d.Type.Params, d.Type.Results, multiLine)
1410 p.funcBody(d.Body, p.distance(d.Pos(), p.pos), false, multiLine)
1414 // Sets multiLine to true if the declaration spans multiple lines.
1415 func (p *printer) decl(decl ast.Decl, multiLine *bool) {
1416 switch d := decl.(type) {
1418 p.print(d.Pos(), "BadDecl")
1420 p.genDecl(d, multiLine)
1422 p.funcDecl(d, multiLine)
1424 panic("unreachable")
1429 // ----------------------------------------------------------------------------
1432 func declToken(decl ast.Decl) (tok token.Token) {
1434 switch d := decl.(type) {
1444 func (p *printer) file(src *ast.File) {
1445 p.setComment(src.Doc)
1446 p.print(src.Pos(), token.PACKAGE, blank)
1447 p.expr(src.Name, ignoreMultiLine)
1449 if len(src.Decls) > 0 {
1450 tok := token.ILLEGAL
1451 for _, d := range src.Decls {
1454 // if the declaration token changed (e.g., from CONST to TYPE)
1455 // print an empty line between top-level declarations
1460 p.linebreak(p.fset.Position(d.Pos()).Line, min, ignore, false)
1461 p.decl(d, ignoreMultiLine)