Imported Upstream version 4.7.3
[platform/upstream/gcc48.git] / libgo / go / go / printer / nodes.go
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.
4
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.
8
9 package printer
10
11 import (
12         "bytes"
13         "go/ast"
14         "go/token"
15         "unicode/utf8"
16 )
17
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
25
26 // ----------------------------------------------------------------------------
27 // Common AST nodes.
28
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.
33 //
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.
41 //
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
46         }
47         if n > 0 {
48                 p.print(ws)
49                 if newSection {
50                         p.print(formfeed)
51                         n--
52                 }
53                 for ; n > 0; n-- {
54                         p.print(newline)
55                 }
56                 printedBreak = true
57         }
58         return
59 }
60
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
68         }
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")
80         }
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 inbetween)
87         if p.commentOffset == infinity {
88                 p.nextComment() // get comment ready for use
89         }
90 }
91
92 type exprListMode uint
93
94 const (
95         commaTerm exprListMode = 1 << iota // list is optionally terminated by a comma
96         noIndent                           // no extra indentation in multi-line lists
97 )
98
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
106         }
107         var mode exprListMode
108         if !indent {
109                 mode = noIndent
110         }
111         p.exprList(token.NoPos, xlist, 1, mode, token.NoPos)
112 }
113
114 // Print a list of expressions. If the list spans multiple
115 // source lines, the original line breaks are respected between
116 // expressions.
117 //
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
124         }
125
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())
130
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)
138                         }
139                         p.expr0(x, depth)
140                 }
141                 return
142         }
143
144         // list entries span multiple lines;
145         // use source code positions to guide line breaks
146
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
152         }
153
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
160         }
161
162         // initialize expression/key size: a zero value indicates expr/key doesn't fit on a single line
163         size := 0
164
165         // print all list elements
166         for i, x := range list {
167                 prevLine := line
168                 line = p.lineFor(x.Pos())
169
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
176
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
189                         }
190                 } else {
191                         // size too large or we don't have good layout information
192                         size = 0
193                 }
194
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/r || r <= ratio
207                         }
208                 }
209
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())
217                         }
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
228                                 }
229                         }
230                         if needsBlank {
231                                 p.print(blank)
232                         }
233                 }
234
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)
244                 }
245         }
246
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)
253                 }
254                 p.print(formfeed) // terminating comma needs a line break to look good
255                 return
256         }
257
258         if ws == ignore && mode&noIndent == 0 {
259                 // unindent if we indented
260                 p.print(unindent)
261         }
262 }
263
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                         var parLineEnd = p.lineFor(par.Type.Pos())
275                         if len(par.Names) > 0 {
276                                 parLineBeg = p.lineFor(par.Names[0].Pos())
277                         } else {
278                                 parLineBeg = parLineEnd
279                         }
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())
288                                 }
289                                 p.print(token.COMMA)
290                         }
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)
297                         }
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)
308                         }
309                         // parameter type
310                         p.expr(par.Type)
311                         prevLine = parLineEnd
312                 }
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)
318                 }
319                 // unindent if we indented
320                 if ws == ignore {
321                         p.print(unindent)
322                 }
323         }
324         p.print(fields.Closing, token.RPAREN)
325 }
326
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)
332         }
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(result.List[0].Type)
340                         return
341                 }
342                 p.parameters(result)
343         }
344 }
345
346 func identListSize(list []*ast.Ident, maxSize int) (size int) {
347         for i, x := range list {
348                 if i > 0 {
349                         size += len(", ")
350                 }
351                 size += utf8.RuneCountInString(x.Name)
352                 if size >= maxSize {
353                         break
354                 }
355         }
356         return
357 }
358
359 func (p *printer) isOneLineFieldList(list []*ast.Field) bool {
360         if len(list) != 1 {
361                 return false // allow only one field
362         }
363         f := list[0]
364         if f.Tag != nil || f.Comment != nil {
365                 return false // don't allow tags or comments
366         }
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
372         }
373         typeSize := p.nodeSize(f.Type, maxSize)
374         return namesSize+typeSize <= maxSize
375 }
376
377 func (p *printer) setLineComment(text string) {
378         p.setComment(&ast.CommentGroup{List: []*ast.Comment{{Slash: token.NoPos, Text: text}}})
379 }
380
381 func (p *printer) isMultiLine(n ast.Node) bool {
382         return p.lineFor(n.End())-p.lineFor(n.Pos()) > 0
383 }
384
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)
391
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)
407                                 }
408                                 p.expr(x)
409                         }
410                         if len(f.Names) > 0 {
411                                 p.print(blank)
412                         }
413                         p.expr(f.Type)
414                         p.print(blank, rbrace, token.RBRACE)
415                         return
416                 }
417         }
418         // hasComments || !srcIsOneLine
419
420         p.print(blank, lbrace, token.LBRACE, indent)
421         if hasComments || len(list) > 0 {
422                 p.print(formfeed)
423         }
424
425         if isStruct {
426
427                 sep := vtab
428                 if len(list) == 1 {
429                         sep = blank
430                 }
431                 newSection := false
432                 for i, f := range list {
433                         if i > 0 {
434                                 p.linebreak(p.lineFor(f.Pos()), 1, ignore, newSection)
435                         }
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
448                         }
449                         if f.Tag != nil {
450                                 if len(f.Names) > 0 && sep == vtab {
451                                         p.print(sep)
452                                 }
453                                 p.print(sep)
454                                 p.expr(f.Tag)
455                                 extraTabs = 0
456                         }
457                         if f.Comment != nil {
458                                 for ; extraTabs > 0; extraTabs-- {
459                                         p.print(sep)
460                                 }
461                                 p.setComment(f.Comment)
462                         }
463                         newSection = p.isMultiLine(f)
464                 }
465                 if isIncomplete {
466                         if len(list) > 0 {
467                                 p.print(formfeed)
468                         }
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")
471                 }
472
473         } else { // interface
474
475                 newSection := false
476                 for i, f := range list {
477                         if i > 0 {
478                                 p.linebreak(p.lineFor(f.Pos()), 1, ignore, newSection)
479                         }
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)
488                         }
489                         p.setComment(f.Comment)
490                         newSection = p.isMultiLine(f)
491                 }
492                 if isIncomplete {
493                         if len(list) > 0 {
494                                 p.print(formfeed)
495                         }
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")
498                 }
499
500         }
501         p.print(unindent, formfeed, rbrace, token.RBRACE)
502 }
503
504 // ----------------------------------------------------------------------------
505 // Expressions
506
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
513         }
514
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
521                 }
522                 h4, h5, mp := walkBinary(l)
523                 has4 = has4 || h4
524                 has5 = has5 || h5
525                 if maxProblem < mp {
526                         maxProblem = mp
527                 }
528         }
529
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
536                 }
537                 h4, h5, mp := walkBinary(r)
538                 has4 = has4 || h4
539                 has5 = has5 || h5
540                 if maxProblem < mp {
541                         maxProblem = mp
542                 }
543
544         case *ast.StarExpr:
545                 if e.Op == token.QUO { // `*/`
546                         maxProblem = 5
547                 }
548
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
556                         }
557                 }
558         }
559         return
560 }
561
562 func cutoff(e *ast.BinaryExpr, depth int) int {
563         has4, has5, maxProblem := walkBinary(e)
564         if maxProblem > 0 {
565                 return maxProblem + 1
566         }
567         if has4 && has5 {
568                 if depth == 1 {
569                         return 5
570                 }
571                 return 4
572         }
573         if depth == 1 {
574                 return 6
575         }
576         return 4
577 }
578
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
583         }
584         return 0
585 }
586
587 func reduceDepth(depth int) int {
588         depth--
589         if depth < 1 {
590                 depth = 1
591         }
592         return depth
593 }
594
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.)
598 //
599 // The precedences are:
600 //      5             *  /  %  <<  >>  &  &^
601 //      4             +  -  |  ^
602 //      3             ==  !=  <  <=  >  >=
603 //      2             &&
604 //      1             ||
605 //
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.
608 //
609 // To choose the cutoff, look at the whole expression but excluding primary
610 // expressions (function calls, parenthesized exprs), and apply these rules:
611 //
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):
614 //
615 //              /*      6
616 //              &&      6
617 //              &^      6
618 //              ++      5
619 //              --      5
620 //
621 //         (Comparison operators always have spaces around them.)
622 //
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.
626 //
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.
630 //
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
641         }
642
643         printBlank := prec < cutoff
644
645         ws := indent
646         p.expr1(x.X, prec, depth+diffPrec(x.X, prec))
647         if printBlank {
648                 p.print(blank)
649         }
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
659                 }
660         }
661         if printBlank {
662                 p.print(blank)
663         }
664         p.expr1(x.Y, prec+1, depth+1)
665         if ws == ignore {
666                 p.print(unindent)
667         }
668 }
669
670 func isBinary(expr ast.Expr) bool {
671         _, ok := expr.(*ast.BinaryExpr)
672         return ok
673 }
674
675 func (p *printer) expr1(expr ast.Expr, prec1, depth int) {
676         p.print(expr.Pos())
677
678         switch x := expr.(type) {
679         case *ast.BadExpr:
680                 p.print("BadExpr")
681
682         case *ast.Ident:
683                 p.print(x)
684
685         case *ast.BinaryExpr:
686                 if depth < 1 {
687                         p.internalError("depth < 1:", depth)
688                         depth = 1
689                 }
690                 p.binaryExpr(x, prec1, cutoff(x, depth), depth)
691
692         case *ast.KeyValueExpr:
693                 p.expr(x.Key)
694                 p.print(x.Colon, token.COLON, blank)
695                 p.expr(x.Value)
696
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)
709                 }
710
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)
724                         }
725                         p.expr1(x.X, prec, depth)
726                 }
727
728         case *ast.BasicLit:
729                 p.print(x)
730
731         case *ast.FuncLit:
732                 p.expr(x.Type)
733                 p.funcBody(x.Body, p.distance(x.Type.Pos(), p.pos), true)
734
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)
744                 }
745
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)
753                 }
754
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)
762                 }
763                 p.print(token.RPAREN)
764
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)
771
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)
778                 }
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)
784                 }
785                 if x.High != nil {
786                         p.expr0(x.High, depth+1)
787                 }
788                 p.print(x.Rbrack, token.RBRACK)
789
790         case *ast.CallExpr:
791                 if len(x.Args) > 1 {
792                         depth++
793                 }
794                 p.expr1(x.Fun, token.HighestPrec, depth)
795                 p.print(x.Lparen, token.LPAREN)
796                 if x.Ellipsis.IsValid() {
797                         p.exprList(x.Lparen, x.Args, depth, 0, x.Ellipsis)
798                         p.print(x.Ellipsis, token.ELLIPSIS)
799                         if x.Rparen.IsValid() && p.lineFor(x.Ellipsis) < p.lineFor(x.Rparen) {
800                                 p.print(token.COMMA, formfeed)
801                         }
802                 } else {
803                         p.exprList(x.Lparen, x.Args, depth, commaTerm, x.Rparen)
804                 }
805                 p.print(x.Rparen, token.RPAREN)
806
807         case *ast.CompositeLit:
808                 // composite literal elements that are composite literals themselves may have the type omitted
809                 if x.Type != nil {
810                         p.expr1(x.Type, token.HighestPrec, depth)
811                 }
812                 p.print(x.Lbrace, token.LBRACE)
813                 p.exprList(x.Lbrace, x.Elts, 1, commaTerm, x.Rbrace)
814                 // do not insert extra line breaks because of comments before
815                 // the closing '}' as it might break the code if there is no
816                 // trailing ','
817                 p.print(noExtraLinebreak, x.Rbrace, token.RBRACE, noExtraLinebreak)
818
819         case *ast.Ellipsis:
820                 p.print(token.ELLIPSIS)
821                 if x.Elt != nil {
822                         p.expr(x.Elt)
823                 }
824
825         case *ast.ArrayType:
826                 p.print(token.LBRACK)
827                 if x.Len != nil {
828                         p.expr(x.Len)
829                 }
830                 p.print(token.RBRACK)
831                 p.expr(x.Elt)
832
833         case *ast.StructType:
834                 p.print(token.STRUCT)
835                 p.fieldList(x.Fields, true, x.Incomplete)
836
837         case *ast.FuncType:
838                 p.print(token.FUNC)
839                 p.signature(x.Params, x.Results)
840
841         case *ast.InterfaceType:
842                 p.print(token.INTERFACE)
843                 p.fieldList(x.Methods, false, x.Incomplete)
844
845         case *ast.MapType:
846                 p.print(token.MAP, token.LBRACK)
847                 p.expr(x.Key)
848                 p.print(token.RBRACK)
849                 p.expr(x.Value)
850
851         case *ast.ChanType:
852                 switch x.Dir {
853                 case ast.SEND | ast.RECV:
854                         p.print(token.CHAN)
855                 case ast.RECV:
856                         p.print(token.ARROW, token.CHAN)
857                 case ast.SEND:
858                         p.print(token.CHAN, token.ARROW)
859                 }
860                 p.print(blank)
861                 p.expr(x.Value)
862
863         default:
864                 panic("unreachable")
865         }
866
867         return
868 }
869
870 func (p *printer) expr0(x ast.Expr, depth int) {
871         p.expr1(x, token.LowestPrec, depth)
872 }
873
874 func (p *printer) expr(x ast.Expr) {
875         const depth = 1
876         p.expr1(x, token.LowestPrec, depth)
877 }
878
879 // ----------------------------------------------------------------------------
880 // Statements
881
882 // Print the statement list indented, but without a newline after the last statement.
883 // Extra line breaks between statements in the source are respected but at most one
884 // empty line is printed between statements.
885 func (p *printer) stmtList(list []ast.Stmt, _indent int, nextIsRBrace bool) {
886         // TODO(gri): fix _indent code
887         if _indent > 0 {
888                 p.print(indent)
889         }
890         multiLine := false
891         for i, s := range list {
892                 // _indent == 0 only for lists of switch/select case clauses;
893                 // in those cases each clause is a new section
894                 p.linebreak(p.lineFor(s.Pos()), 1, ignore, i == 0 || _indent == 0 || multiLine)
895                 p.stmt(s, nextIsRBrace && i == len(list)-1)
896                 multiLine = p.isMultiLine(s)
897         }
898         if _indent > 0 {
899                 p.print(unindent)
900         }
901 }
902
903 // block prints an *ast.BlockStmt; it always spans at least two lines.
904 func (p *printer) block(s *ast.BlockStmt, indent int) {
905         p.print(s.Pos(), token.LBRACE)
906         p.stmtList(s.List, indent, true)
907         p.linebreak(p.lineFor(s.Rbrace), 1, ignore, true)
908         p.print(s.Rbrace, token.RBRACE)
909 }
910
911 func isTypeName(x ast.Expr) bool {
912         switch t := x.(type) {
913         case *ast.Ident:
914                 return true
915         case *ast.SelectorExpr:
916                 return isTypeName(t.X)
917         }
918         return false
919 }
920
921 func stripParens(x ast.Expr) ast.Expr {
922         if px, strip := x.(*ast.ParenExpr); strip {
923                 // parentheses must not be stripped if there are any
924                 // unparenthesized composite literals starting with
925                 // a type name
926                 ast.Inspect(px.X, func(node ast.Node) bool {
927                         switch x := node.(type) {
928                         case *ast.ParenExpr:
929                                 // parentheses protect enclosed composite literals
930                                 return false
931                         case *ast.CompositeLit:
932                                 if isTypeName(x.Type) {
933                                         strip = false // do not strip parentheses
934                                 }
935                                 return false
936                         }
937                         // in all other cases, keep inspecting
938                         return true
939                 })
940                 if strip {
941                         return stripParens(px.X)
942                 }
943         }
944         return x
945 }
946
947 func (p *printer) controlClause(isForStmt bool, init ast.Stmt, expr ast.Expr, post ast.Stmt) {
948         p.print(blank)
949         needsBlank := false
950         if init == nil && post == nil {
951                 // no semicolons required
952                 if expr != nil {
953                         p.expr(stripParens(expr))
954                         needsBlank = true
955                 }
956         } else {
957                 // all semicolons required
958                 // (they are not separators, print them explicitly)
959                 if init != nil {
960                         p.stmt(init, false)
961                 }
962                 p.print(token.SEMICOLON, blank)
963                 if expr != nil {
964                         p.expr(stripParens(expr))
965                         needsBlank = true
966                 }
967                 if isForStmt {
968                         p.print(token.SEMICOLON, blank)
969                         needsBlank = false
970                         if post != nil {
971                                 p.stmt(post, false)
972                                 needsBlank = true
973                         }
974                 }
975         }
976         if needsBlank {
977                 p.print(blank)
978         }
979 }
980
981 // indentList reports whether an expression list would look better if it
982 // were indented wholesale (starting with the very first element, rather
983 // than starting at the first line break).
984 //
985 func (p *printer) indentList(list []ast.Expr) bool {
986         // Heuristic: indentList returns true if there are more than one multi-
987         // line element in the list, or if there is any element that is not
988         // starting on the same line as the previous one ends.
989         if len(list) >= 2 {
990                 var b = p.lineFor(list[0].Pos())
991                 var e = p.lineFor(list[len(list)-1].End())
992                 if 0 < b && b < e {
993                         // list spans multiple lines
994                         n := 0 // multi-line element count
995                         line := b
996                         for _, x := range list {
997                                 xb := p.lineFor(x.Pos())
998                                 xe := p.lineFor(x.End())
999                                 if line < xb {
1000                                         // x is not starting on the same
1001                                         // line as the previous one ended
1002                                         return true
1003                                 }
1004                                 if xb < xe {
1005                                         // x is a multi-line element
1006                                         n++
1007                                 }
1008                                 line = xe
1009                         }
1010                         return n > 1
1011                 }
1012         }
1013         return false
1014 }
1015
1016 func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool) {
1017         p.print(stmt.Pos())
1018
1019         switch s := stmt.(type) {
1020         case *ast.BadStmt:
1021                 p.print("BadStmt")
1022
1023         case *ast.DeclStmt:
1024                 p.decl(s.Decl)
1025
1026         case *ast.EmptyStmt:
1027                 // nothing to do
1028
1029         case *ast.LabeledStmt:
1030                 // a "correcting" unindent immediately following a line break
1031                 // is applied before the line break if there is no comment
1032                 // between (see writeWhitespace)
1033                 p.print(unindent)
1034                 p.expr(s.Label)
1035                 p.print(s.Colon, token.COLON, indent)
1036                 if e, isEmpty := s.Stmt.(*ast.EmptyStmt); isEmpty {
1037                         if !nextIsRBrace {
1038                                 p.print(newline, e.Pos(), token.SEMICOLON)
1039                                 break
1040                         }
1041                 } else {
1042                         p.linebreak(p.lineFor(s.Stmt.Pos()), 1, ignore, true)
1043                 }
1044                 p.stmt(s.Stmt, nextIsRBrace)
1045
1046         case *ast.ExprStmt:
1047                 const depth = 1
1048                 p.expr0(s.X, depth)
1049
1050         case *ast.SendStmt:
1051                 const depth = 1
1052                 p.expr0(s.Chan, depth)
1053                 p.print(blank, s.Arrow, token.ARROW, blank)
1054                 p.expr0(s.Value, depth)
1055
1056         case *ast.IncDecStmt:
1057                 const depth = 1
1058                 p.expr0(s.X, depth+1)
1059                 p.print(s.TokPos, s.Tok)
1060
1061         case *ast.AssignStmt:
1062                 var depth = 1
1063                 if len(s.Lhs) > 1 && len(s.Rhs) > 1 {
1064                         depth++
1065                 }
1066                 p.exprList(s.Pos(), s.Lhs, depth, 0, s.TokPos)
1067                 p.print(blank, s.TokPos, s.Tok, blank)
1068                 p.exprList(s.TokPos, s.Rhs, depth, 0, token.NoPos)
1069
1070         case *ast.GoStmt:
1071                 p.print(token.GO, blank)
1072                 p.expr(s.Call)
1073
1074         case *ast.DeferStmt:
1075                 p.print(token.DEFER, blank)
1076                 p.expr(s.Call)
1077
1078         case *ast.ReturnStmt:
1079                 p.print(token.RETURN)
1080                 if s.Results != nil {
1081                         p.print(blank)
1082                         // Use indentList heuristic to make corner cases look
1083                         // better (issue 1207). A more systematic approach would
1084                         // always indent, but this would cause significant
1085                         // reformatting of the code base and not necessarily
1086                         // lead to more nicely formatted code in general.
1087                         if p.indentList(s.Results) {
1088                                 p.print(indent)
1089                                 p.exprList(s.Pos(), s.Results, 1, noIndent, token.NoPos)
1090                                 p.print(unindent)
1091                         } else {
1092                                 p.exprList(s.Pos(), s.Results, 1, 0, token.NoPos)
1093                         }
1094                 }
1095
1096         case *ast.BranchStmt:
1097                 p.print(s.Tok)
1098                 if s.Label != nil {
1099                         p.print(blank)
1100                         p.expr(s.Label)
1101                 }
1102
1103         case *ast.BlockStmt:
1104                 p.block(s, 1)
1105
1106         case *ast.IfStmt:
1107                 p.print(token.IF)
1108                 p.controlClause(false, s.Init, s.Cond, nil)
1109                 p.block(s.Body, 1)
1110                 if s.Else != nil {
1111                         p.print(blank, token.ELSE, blank)
1112                         switch s.Else.(type) {
1113                         case *ast.BlockStmt, *ast.IfStmt:
1114                                 p.stmt(s.Else, nextIsRBrace)
1115                         default:
1116                                 p.print(token.LBRACE, indent, formfeed)
1117                                 p.stmt(s.Else, true)
1118                                 p.print(unindent, formfeed, token.RBRACE)
1119                         }
1120                 }
1121
1122         case *ast.CaseClause:
1123                 if s.List != nil {
1124                         p.print(token.CASE, blank)
1125                         p.exprList(s.Pos(), s.List, 1, 0, s.Colon)
1126                 } else {
1127                         p.print(token.DEFAULT)
1128                 }
1129                 p.print(s.Colon, token.COLON)
1130                 p.stmtList(s.Body, 1, nextIsRBrace)
1131
1132         case *ast.SwitchStmt:
1133                 p.print(token.SWITCH)
1134                 p.controlClause(false, s.Init, s.Tag, nil)
1135                 p.block(s.Body, 0)
1136
1137         case *ast.TypeSwitchStmt:
1138                 p.print(token.SWITCH)
1139                 if s.Init != nil {
1140                         p.print(blank)
1141                         p.stmt(s.Init, false)
1142                         p.print(token.SEMICOLON)
1143                 }
1144                 p.print(blank)
1145                 p.stmt(s.Assign, false)
1146                 p.print(blank)
1147                 p.block(s.Body, 0)
1148
1149         case *ast.CommClause:
1150                 if s.Comm != nil {
1151                         p.print(token.CASE, blank)
1152                         p.stmt(s.Comm, false)
1153                 } else {
1154                         p.print(token.DEFAULT)
1155                 }
1156                 p.print(s.Colon, token.COLON)
1157                 p.stmtList(s.Body, 1, nextIsRBrace)
1158
1159         case *ast.SelectStmt:
1160                 p.print(token.SELECT, blank)
1161                 body := s.Body
1162                 if len(body.List) == 0 && !p.commentBefore(p.posFor(body.Rbrace)) {
1163                         // print empty select statement w/o comments on one line
1164                         p.print(body.Lbrace, token.LBRACE, body.Rbrace, token.RBRACE)
1165                 } else {
1166                         p.block(body, 0)
1167                 }
1168
1169         case *ast.ForStmt:
1170                 p.print(token.FOR)
1171                 p.controlClause(true, s.Init, s.Cond, s.Post)
1172                 p.block(s.Body, 1)
1173
1174         case *ast.RangeStmt:
1175                 p.print(token.FOR, blank)
1176                 p.expr(s.Key)
1177                 if s.Value != nil {
1178                         // use position of value following the comma as
1179                         // comma position for correct comment placement
1180                         p.print(s.Value.Pos(), token.COMMA, blank)
1181                         p.expr(s.Value)
1182                 }
1183                 p.print(blank, s.TokPos, s.Tok, blank, token.RANGE, blank)
1184                 p.expr(stripParens(s.X))
1185                 p.print(blank)
1186                 p.block(s.Body, 1)
1187
1188         default:
1189                 panic("unreachable")
1190         }
1191
1192         return
1193 }
1194
1195 // ----------------------------------------------------------------------------
1196 // Declarations
1197
1198 // The keepTypeColumn function determines if the type column of a series of
1199 // consecutive const or var declarations must be kept, or if initialization
1200 // values (V) can be placed in the type column (T) instead. The i'th entry
1201 // in the result slice is true if the type column in spec[i] must be kept.
1202 //
1203 // For example, the declaration:
1204 //
1205 //      const (
1206 //              foobar int = 42 // comment
1207 //              x          = 7  // comment
1208 //              foo
1209 //              bar = 991
1210 //      )
1211 //
1212 // leads to the type/values matrix below. A run of value columns (V) can
1213 // be moved into the type column if there is no type for any of the values
1214 // in that column (we only move entire columns so that they align properly).
1215 //
1216 //      matrix        formatted     result
1217 //                    matrix
1218 //      T  V    ->    T  V     ->   true      there is a T and so the type
1219 //      -  V          -  V          true      column must be kept
1220 //      -  -          -  -          false
1221 //      -  V          V  -          false     V is moved into T column
1222 //
1223 func keepTypeColumn(specs []ast.Spec) []bool {
1224         m := make([]bool, len(specs))
1225
1226         populate := func(i, j int, keepType bool) {
1227                 if keepType {
1228                         for ; i < j; i++ {
1229                                 m[i] = true
1230                         }
1231                 }
1232         }
1233
1234         i0 := -1 // if i0 >= 0 we are in a run and i0 is the start of the run
1235         var keepType bool
1236         for i, s := range specs {
1237                 t := s.(*ast.ValueSpec)
1238                 if t.Values != nil {
1239                         if i0 < 0 {
1240                                 // start of a run of ValueSpecs with non-nil Values
1241                                 i0 = i
1242                                 keepType = false
1243                         }
1244                 } else {
1245                         if i0 >= 0 {
1246                                 // end of a run
1247                                 populate(i0, i, keepType)
1248                                 i0 = -1
1249                         }
1250                 }
1251                 if t.Type != nil {
1252                         keepType = true
1253                 }
1254         }
1255         if i0 >= 0 {
1256                 // end of a run
1257                 populate(i0, len(specs), keepType)
1258         }
1259
1260         return m
1261 }
1262
1263 func (p *printer) valueSpec(s *ast.ValueSpec, keepType bool) {
1264         p.setComment(s.Doc)
1265         p.identList(s.Names, false) // always present
1266         extraTabs := 3
1267         if s.Type != nil || keepType {
1268                 p.print(vtab)
1269                 extraTabs--
1270         }
1271         if s.Type != nil {
1272                 p.expr(s.Type)
1273         }
1274         if s.Values != nil {
1275                 p.print(vtab, token.ASSIGN, blank)
1276                 p.exprList(token.NoPos, s.Values, 1, 0, token.NoPos)
1277                 extraTabs--
1278         }
1279         if s.Comment != nil {
1280                 for ; extraTabs > 0; extraTabs-- {
1281                         p.print(vtab)
1282                 }
1283                 p.setComment(s.Comment)
1284         }
1285 }
1286
1287 // The parameter n is the number of specs in the group. If doIndent is set,
1288 // multi-line identifier lists in the spec are indented when the first
1289 // linebreak is encountered.
1290 //
1291 func (p *printer) spec(spec ast.Spec, n int, doIndent bool) {
1292         switch s := spec.(type) {
1293         case *ast.ImportSpec:
1294                 p.setComment(s.Doc)
1295                 if s.Name != nil {
1296                         p.expr(s.Name)
1297                         p.print(blank)
1298                 }
1299                 p.expr(s.Path)
1300                 p.setComment(s.Comment)
1301                 p.print(s.EndPos)
1302
1303         case *ast.ValueSpec:
1304                 if n != 1 {
1305                         p.internalError("expected n = 1; got", n)
1306                 }
1307                 p.setComment(s.Doc)
1308                 p.identList(s.Names, doIndent) // always present
1309                 if s.Type != nil {
1310                         p.print(blank)
1311                         p.expr(s.Type)
1312                 }
1313                 if s.Values != nil {
1314                         p.print(blank, token.ASSIGN, blank)
1315                         p.exprList(token.NoPos, s.Values, 1, 0, token.NoPos)
1316                 }
1317                 p.setComment(s.Comment)
1318
1319         case *ast.TypeSpec:
1320                 p.setComment(s.Doc)
1321                 p.expr(s.Name)
1322                 if n == 1 {
1323                         p.print(blank)
1324                 } else {
1325                         p.print(vtab)
1326                 }
1327                 p.expr(s.Type)
1328                 p.setComment(s.Comment)
1329
1330         default:
1331                 panic("unreachable")
1332         }
1333 }
1334
1335 func (p *printer) genDecl(d *ast.GenDecl) {
1336         p.setComment(d.Doc)
1337         p.print(d.Pos(), d.Tok, blank)
1338
1339         if d.Lparen.IsValid() {
1340                 // group of parenthesized declarations
1341                 p.print(d.Lparen, token.LPAREN)
1342                 if n := len(d.Specs); n > 0 {
1343                         p.print(indent, formfeed)
1344                         if n > 1 && (d.Tok == token.CONST || d.Tok == token.VAR) {
1345                                 // two or more grouped const/var declarations:
1346                                 // determine if the type column must be kept
1347                                 keepType := keepTypeColumn(d.Specs)
1348                                 newSection := false
1349                                 for i, s := range d.Specs {
1350                                         if i > 0 {
1351                                                 p.linebreak(p.lineFor(s.Pos()), 1, ignore, newSection)
1352                                         }
1353                                         p.valueSpec(s.(*ast.ValueSpec), keepType[i])
1354                                         newSection = p.isMultiLine(s)
1355                                 }
1356                         } else {
1357                                 newSection := false
1358                                 for i, s := range d.Specs {
1359                                         if i > 0 {
1360                                                 p.linebreak(p.lineFor(s.Pos()), 1, ignore, newSection)
1361                                         }
1362                                         p.spec(s, n, false)
1363                                         newSection = p.isMultiLine(s)
1364                                 }
1365                         }
1366                         p.print(unindent, formfeed)
1367                 }
1368                 p.print(d.Rparen, token.RPAREN)
1369
1370         } else {
1371                 // single declaration
1372                 p.spec(d.Specs[0], 1, true)
1373         }
1374 }
1375
1376 // nodeSize determines the size of n in chars after formatting.
1377 // The result is <= maxSize if the node fits on one line with at
1378 // most maxSize chars and the formatted output doesn't contain
1379 // any control chars. Otherwise, the result is > maxSize.
1380 //
1381 func (p *printer) nodeSize(n ast.Node, maxSize int) (size int) {
1382         // nodeSize invokes the printer, which may invoke nodeSize
1383         // recursively. For deep composite literal nests, this can
1384         // lead to an exponential algorithm. Remember previous
1385         // results to prune the recursion (was issue 1628).
1386         if size, found := p.nodeSizes[n]; found {
1387                 return size
1388         }
1389
1390         size = maxSize + 1 // assume n doesn't fit
1391         p.nodeSizes[n] = size
1392
1393         // nodeSize computation must be independent of particular
1394         // style so that we always get the same decision; print
1395         // in RawFormat
1396         cfg := Config{Mode: RawFormat}
1397         var buf bytes.Buffer
1398         if err := cfg.fprint(&buf, p.fset, n, p.nodeSizes); err != nil {
1399                 return
1400         }
1401         if buf.Len() <= maxSize {
1402                 for _, ch := range buf.Bytes() {
1403                         if ch < ' ' {
1404                                 return
1405                         }
1406                 }
1407                 size = buf.Len() // n fits
1408                 p.nodeSizes[n] = size
1409         }
1410         return
1411 }
1412
1413 func (p *printer) isOneLineFunc(b *ast.BlockStmt, headerSize int) bool {
1414         pos1 := b.Pos()
1415         pos2 := b.Rbrace
1416         if pos1.IsValid() && pos2.IsValid() && p.lineFor(pos1) != p.lineFor(pos2) {
1417                 // opening and closing brace are on different lines - don't make it a one-liner
1418                 return false
1419         }
1420         if len(b.List) > 5 || p.commentBefore(p.posFor(pos2)) {
1421                 // too many statements or there is a comment inside - don't make it a one-liner
1422                 return false
1423         }
1424         // otherwise, estimate body size
1425         const maxSize = 100
1426         bodySize := 0
1427         for i, s := range b.List {
1428                 if i > 0 {
1429                         bodySize += 2 // space for a semicolon and blank
1430                 }
1431                 bodySize += p.nodeSize(s, maxSize)
1432         }
1433         return headerSize+bodySize <= maxSize
1434 }
1435
1436 func (p *printer) funcBody(b *ast.BlockStmt, headerSize int, isLit bool) {
1437         if b == nil {
1438                 return
1439         }
1440
1441         if p.isOneLineFunc(b, headerSize) {
1442                 sep := vtab
1443                 if isLit {
1444                         sep = blank
1445                 }
1446                 p.print(sep, b.Lbrace, token.LBRACE)
1447                 if len(b.List) > 0 {
1448                         p.print(blank)
1449                         for i, s := range b.List {
1450                                 if i > 0 {
1451                                         p.print(token.SEMICOLON, blank)
1452                                 }
1453                                 p.stmt(s, i == len(b.List)-1)
1454                         }
1455                         p.print(blank)
1456                 }
1457                 p.print(b.Rbrace, token.RBRACE)
1458                 return
1459         }
1460
1461         p.print(blank)
1462         p.block(b, 1)
1463 }
1464
1465 // distance returns the column difference between from and to if both
1466 // are on the same line; if they are on different lines (or unknown)
1467 // the result is infinity.
1468 func (p *printer) distance(from0 token.Pos, to token.Position) int {
1469         from := p.posFor(from0)
1470         if from.IsValid() && to.IsValid() && from.Line == to.Line {
1471                 return to.Column - from.Column
1472         }
1473         return infinity
1474 }
1475
1476 func (p *printer) funcDecl(d *ast.FuncDecl) {
1477         p.setComment(d.Doc)
1478         p.print(d.Pos(), token.FUNC, blank)
1479         if d.Recv != nil {
1480                 p.parameters(d.Recv) // method: print receiver
1481                 p.print(blank)
1482         }
1483         p.expr(d.Name)
1484         p.signature(d.Type.Params, d.Type.Results)
1485         p.funcBody(d.Body, p.distance(d.Pos(), p.pos), false)
1486 }
1487
1488 func (p *printer) decl(decl ast.Decl) {
1489         switch d := decl.(type) {
1490         case *ast.BadDecl:
1491                 p.print(d.Pos(), "BadDecl")
1492         case *ast.GenDecl:
1493                 p.genDecl(d)
1494         case *ast.FuncDecl:
1495                 p.funcDecl(d)
1496         default:
1497                 panic("unreachable")
1498         }
1499 }
1500
1501 // ----------------------------------------------------------------------------
1502 // Files
1503
1504 func declToken(decl ast.Decl) (tok token.Token) {
1505         tok = token.ILLEGAL
1506         switch d := decl.(type) {
1507         case *ast.GenDecl:
1508                 tok = d.Tok
1509         case *ast.FuncDecl:
1510                 tok = token.FUNC
1511         }
1512         return
1513 }
1514
1515 func (p *printer) file(src *ast.File) {
1516         p.setComment(src.Doc)
1517         p.print(src.Pos(), token.PACKAGE, blank)
1518         p.expr(src.Name)
1519
1520         if len(src.Decls) > 0 {
1521                 tok := token.ILLEGAL
1522                 for _, d := range src.Decls {
1523                         prev := tok
1524                         tok = declToken(d)
1525                         // if the declaration token changed (e.g., from CONST to TYPE)
1526                         // or the next declaration has documentation associated with it,
1527                         // print an empty line between top-level declarations
1528                         // (because p.linebreak is called with the position of d, which
1529                         // is past any documentation, the minimum requirement is satisfied
1530                         // even w/o the extra getDoc(d) nil-check - leave it in case the
1531                         // linebreak logic improves - there's already a TODO).
1532                         min := 1
1533                         if prev != tok || getDoc(d) != nil {
1534                                 min = 2
1535                         }
1536                         p.linebreak(p.lineFor(d.Pos()), min, ignore, false)
1537                         p.decl(d)
1538                 }
1539         }
1540
1541         p.print(newline)
1542 }