OSDN Git Service

Update Go compiler, library, and testsuite on gcc 4.7 branch.
[pf3gnuchains/gcc-fork.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 // 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
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 are no other pending comments to
64 // intersperse.
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         }
78         p.comments[0] = g
79         p.cindex = 0
80         p.nextComment() // get comment ready for use
81 }
82
83 type exprListMode uint
84
85 const (
86         commaTerm exprListMode = 1 << iota // list is optionally terminated by a comma
87         noIndent                           // no extra indentation in multi-line lists
88 )
89
90 // If indent is set, a multi-line identifier list is indented after the
91 // first linebreak encountered.
92 func (p *printer) identList(list []*ast.Ident, indent bool) {
93         // convert into an expression list so we can re-use exprList formatting
94         xlist := make([]ast.Expr, len(list))
95         for i, x := range list {
96                 xlist[i] = x
97         }
98         var mode exprListMode
99         if !indent {
100                 mode = noIndent
101         }
102         p.exprList(token.NoPos, xlist, 1, mode, token.NoPos)
103 }
104
105 // Print a list of expressions. If the list spans multiple
106 // source lines, the original line breaks are respected between
107 // expressions.
108 //
109 // TODO(gri) Consider rewriting this to be independent of []ast.Expr
110 //           so that we can use the algorithm for any kind of list
111 //           (e.g., pass list via a channel over which to range).
112 func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exprListMode, next0 token.Pos) {
113         if len(list) == 0 {
114                 return
115         }
116
117         prev := p.posFor(prev0)
118         next := p.posFor(next0)
119         line := p.lineFor(list[0].Pos())
120         endLine := p.lineFor(list[len(list)-1].End())
121
122         if prev.IsValid() && prev.Line == line && line == endLine {
123                 // all list entries on a single line
124                 for i, x := range list {
125                         if i > 0 {
126                                 // use position of expression following the comma as
127                                 // comma position for correct comment placement
128                                 p.print(x.Pos(), token.COMMA, blank)
129                         }
130                         p.expr0(x, depth)
131                 }
132                 return
133         }
134
135         // list entries span multiple lines;
136         // use source code positions to guide line breaks
137
138         // don't add extra indentation if noIndent is set;
139         // i.e., pretend that the first line is already indented
140         ws := ignore
141         if mode&noIndent == 0 {
142                 ws = indent
143         }
144
145         // the first linebreak is always a formfeed since this section must not
146         // depend on any previous formatting
147         prevBreak := -1 // index of last expression that was followed by a linebreak
148         if prev.IsValid() && prev.Line < line && p.linebreak(line, 0, ws, true) {
149                 ws = ignore
150                 prevBreak = 0
151         }
152
153         // initialize expression/key size: a zero value indicates expr/key doesn't fit on a single line
154         size := 0
155
156         // print all list elements
157         for i, x := range list {
158                 prevLine := line
159                 line = p.lineFor(x.Pos())
160
161                 // determine if the next linebreak, if any, needs to use formfeed:
162                 // in general, use the entire node size to make the decision; for
163                 // key:value expressions, use the key size
164                 // TODO(gri) for a better result, should probably incorporate both
165                 //           the key and the node size into the decision process
166                 useFF := true
167
168                 // determine element size: all bets are off if we don't have
169                 // position information for the previous and next token (likely
170                 // generated code - simply ignore the size in this case by setting
171                 // it to 0)
172                 prevSize := size
173                 const infinity = 1e6 // larger than any source line
174                 size = p.nodeSize(x, infinity)
175                 pair, isPair := x.(*ast.KeyValueExpr)
176                 if size <= infinity && prev.IsValid() && next.IsValid() {
177                         // x fits on a single line
178                         if isPair {
179                                 size = p.nodeSize(pair.Key, infinity) // size <= infinity
180                         }
181                 } else {
182                         // size too large or we don't have good layout information
183                         size = 0
184                 }
185
186                 // if the previous line and the current line had single-
187                 // line-expressions and the key sizes are small or the
188                 // the ratio between the key sizes does not exceed a
189                 // threshold, align columns and do not use formfeed
190                 if prevSize > 0 && size > 0 {
191                         const smallSize = 20
192                         if prevSize <= smallSize && size <= smallSize {
193                                 useFF = false
194                         } else {
195                                 const r = 4 // threshold
196                                 ratio := float64(size) / float64(prevSize)
197                                 useFF = ratio <= 1/r || r <= ratio
198                         }
199                 }
200
201                 if i > 0 {
202                         needsLinebreak := prevLine < line && prevLine > 0 && line > 0
203                         // use position of expression following the comma as
204                         // comma position for correct comment placement, but
205                         // only if the expression is on the same line
206                         if !needsLinebreak {
207                                 p.print(x.Pos())
208                         }
209                         p.print(token.COMMA)
210                         needsBlank := true
211                         if needsLinebreak {
212                                 // lines are broken using newlines so comments remain aligned
213                                 // unless forceFF is set or there are multiple expressions on
214                                 // the same line in which case formfeed is used
215                                 if p.linebreak(line, 0, ws, useFF || prevBreak+1 < i) {
216                                         ws = ignore
217                                         prevBreak = i
218                                         needsBlank = false // we got a line break instead
219                                 }
220                         }
221                         if needsBlank {
222                                 p.print(blank)
223                         }
224                 }
225
226                 if isPair && size > 0 && len(list) > 1 {
227                         // we have a key:value expression that fits onto one line and
228                         // is in a list with more then one entry: use a column for the
229                         // key such that consecutive entries can align if possible
230                         p.expr(pair.Key)
231                         p.print(pair.Colon, token.COLON, vtab)
232                         p.expr(pair.Value)
233                 } else {
234                         p.expr0(x, depth)
235                 }
236         }
237
238         if mode&commaTerm != 0 && next.IsValid() && p.pos.Line < next.Line {
239                 // print a terminating comma if the next token is on a new line
240                 p.print(token.COMMA)
241                 if ws == ignore && mode&noIndent == 0 {
242                         // unindent if we indented
243                         p.print(unindent)
244                 }
245                 p.print(formfeed) // terminating comma needs a line break to look good
246                 return
247         }
248
249         if ws == ignore && mode&noIndent == 0 {
250                 // unindent if we indented
251                 p.print(unindent)
252         }
253 }
254
255 func (p *printer) parameters(fields *ast.FieldList) {
256         p.print(fields.Opening, token.LPAREN)
257         if len(fields.List) > 0 {
258                 prevLine := p.lineFor(fields.Opening)
259                 ws := indent
260                 for i, par := range fields.List {
261                         // determine par begin and end line (may be different
262                         // if there are multiple parameter names for this par
263                         // or the type is on a separate line)
264                         var parLineBeg int
265                         var parLineEnd = p.lineFor(par.Type.Pos())
266                         if len(par.Names) > 0 {
267                                 parLineBeg = p.lineFor(par.Names[0].Pos())
268                         } else {
269                                 parLineBeg = parLineEnd
270                         }
271                         // separating "," if needed
272                         needsLinebreak := 0 < prevLine && prevLine < parLineBeg
273                         if i > 0 {
274                                 // use position of parameter following the comma as
275                                 // comma position for correct comma placement, but
276                                 // only if the next parameter is on the same line
277                                 if !needsLinebreak {
278                                         p.print(par.Pos())
279                                 }
280                                 p.print(token.COMMA)
281                         }
282                         // separator if needed (linebreak or blank)
283                         if needsLinebreak && p.linebreak(parLineBeg, 0, ws, true) {
284                                 // break line if the opening "(" or previous parameter ended on a different line
285                                 ws = ignore
286                         } else if i > 0 {
287                                 p.print(blank)
288                         }
289                         // parameter names
290                         if len(par.Names) > 0 {
291                                 // Very subtle: If we indented before (ws == ignore), identList
292                                 // won't indent again. If we didn't (ws == indent), identList will
293                                 // indent if the identList spans multiple lines, and it will outdent
294                                 // again at the end (and still ws == indent). Thus, a subsequent indent
295                                 // by a linebreak call after a type, or in the next multi-line identList
296                                 // will do the right thing.
297                                 p.identList(par.Names, ws == indent)
298                                 p.print(blank)
299                         }
300                         // parameter type
301                         p.expr(par.Type)
302                         prevLine = parLineEnd
303                 }
304                 // if the closing ")" is on a separate line from the last parameter,
305                 // print an additional "," and line break
306                 if closing := p.lineFor(fields.Closing); 0 < prevLine && prevLine < closing {
307                         p.print(token.COMMA)
308                         p.linebreak(closing, 0, ignore, true)
309                 }
310                 // unindent if we indented
311                 if ws == ignore {
312                         p.print(unindent)
313                 }
314         }
315         p.print(fields.Closing, token.RPAREN)
316 }
317
318 func (p *printer) signature(params, result *ast.FieldList) {
319         p.parameters(params)
320         n := result.NumFields()
321         if n > 0 {
322                 p.print(blank)
323                 if n == 1 && result.List[0].Names == nil {
324                         // single anonymous result; no ()'s
325                         p.expr(result.List[0].Type)
326                         return
327                 }
328                 p.parameters(result)
329         }
330 }
331
332 func identListSize(list []*ast.Ident, maxSize int) (size int) {
333         for i, x := range list {
334                 if i > 0 {
335                         size += len(", ")
336                 }
337                 size += utf8.RuneCountInString(x.Name)
338                 if size >= maxSize {
339                         break
340                 }
341         }
342         return
343 }
344
345 func (p *printer) isOneLineFieldList(list []*ast.Field) bool {
346         if len(list) != 1 {
347                 return false // allow only one field
348         }
349         f := list[0]
350         if f.Tag != nil || f.Comment != nil {
351                 return false // don't allow tags or comments
352         }
353         // only name(s) and type
354         const maxSize = 30 // adjust as appropriate, this is an approximate value
355         namesSize := identListSize(f.Names, maxSize)
356         if namesSize > 0 {
357                 namesSize = 1 // blank between names and types
358         }
359         typeSize := p.nodeSize(f.Type, maxSize)
360         return namesSize+typeSize <= maxSize
361 }
362
363 func (p *printer) setLineComment(text string) {
364         p.setComment(&ast.CommentGroup{List: []*ast.Comment{{Slash: token.NoPos, Text: text}}})
365 }
366
367 func (p *printer) isMultiLine(n ast.Node) bool {
368         return p.lineFor(n.End())-p.lineFor(n.Pos()) > 1
369 }
370
371 func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool) {
372         lbrace := fields.Opening
373         list := fields.List
374         rbrace := fields.Closing
375         hasComments := isIncomplete || p.commentBefore(p.posFor(rbrace))
376         srcIsOneLine := lbrace.IsValid() && rbrace.IsValid() && p.lineFor(lbrace) == p.lineFor(rbrace)
377
378         if !hasComments && srcIsOneLine {
379                 // possibly a one-line struct/interface
380                 if len(list) == 0 {
381                         // no blank between keyword and {} in this case
382                         p.print(lbrace, token.LBRACE, rbrace, token.RBRACE)
383                         return
384                 } else if isStruct && p.isOneLineFieldList(list) { // for now ignore interfaces
385                         // small enough - print on one line
386                         // (don't use identList and ignore source line breaks)
387                         p.print(lbrace, token.LBRACE, blank)
388                         f := list[0]
389                         for i, x := range f.Names {
390                                 if i > 0 {
391                                         // no comments so no need for comma position
392                                         p.print(token.COMMA, blank)
393                                 }
394                                 p.expr(x)
395                         }
396                         if len(f.Names) > 0 {
397                                 p.print(blank)
398                         }
399                         p.expr(f.Type)
400                         p.print(blank, rbrace, token.RBRACE)
401                         return
402                 }
403         }
404         // hasComments || !srcIsOneLine
405
406         p.print(blank, lbrace, token.LBRACE, indent)
407         if hasComments || len(list) > 0 {
408                 p.print(formfeed)
409         }
410
411         if isStruct {
412
413                 sep := vtab
414                 if len(list) == 1 {
415                         sep = blank
416                 }
417                 newSection := false
418                 for i, f := range list {
419                         if i > 0 {
420                                 p.linebreak(p.lineFor(f.Pos()), 1, ignore, newSection)
421                         }
422                         extraTabs := 0
423                         p.setComment(f.Doc)
424                         if len(f.Names) > 0 {
425                                 // named fields
426                                 p.identList(f.Names, false)
427                                 p.print(sep)
428                                 p.expr(f.Type)
429                                 extraTabs = 1
430                         } else {
431                                 // anonymous field
432                                 p.expr(f.Type)
433                                 extraTabs = 2
434                         }
435                         if f.Tag != nil {
436                                 if len(f.Names) > 0 && sep == vtab {
437                                         p.print(sep)
438                                 }
439                                 p.print(sep)
440                                 p.expr(f.Tag)
441                                 extraTabs = 0
442                         }
443                         if f.Comment != nil {
444                                 for ; extraTabs > 0; extraTabs-- {
445                                         p.print(sep)
446                                 }
447                                 p.setComment(f.Comment)
448                         }
449                         newSection = p.isMultiLine(f)
450                 }
451                 if isIncomplete {
452                         if len(list) > 0 {
453                                 p.print(formfeed)
454                         }
455                         p.flush(p.posFor(rbrace), token.RBRACE) // make sure we don't lose the last line comment
456                         p.setLineComment("// contains filtered or unexported fields")
457                 }
458
459         } else { // interface
460
461                 newSection := false
462                 for i, f := range list {
463                         if i > 0 {
464                                 p.linebreak(p.lineFor(f.Pos()), 1, ignore, newSection)
465                         }
466                         p.setComment(f.Doc)
467                         if ftyp, isFtyp := f.Type.(*ast.FuncType); isFtyp {
468                                 // method
469                                 p.expr(f.Names[0])
470                                 p.signature(ftyp.Params, ftyp.Results)
471                         } else {
472                                 // embedded interface
473                                 p.expr(f.Type)
474                         }
475                         p.setComment(f.Comment)
476                         newSection = p.isMultiLine(f)
477                 }
478                 if isIncomplete {
479                         if len(list) > 0 {
480                                 p.print(formfeed)
481                         }
482                         p.flush(p.posFor(rbrace), token.RBRACE) // make sure we don't lose the last line comment
483                         p.setLineComment("// contains filtered or unexported methods")
484                 }
485
486         }
487         p.print(unindent, formfeed, rbrace, token.RBRACE)
488 }
489
490 // ----------------------------------------------------------------------------
491 // Expressions
492
493 func walkBinary(e *ast.BinaryExpr) (has4, has5 bool, maxProblem int) {
494         switch e.Op.Precedence() {
495         case 4:
496                 has4 = true
497         case 5:
498                 has5 = true
499         }
500
501         switch l := e.X.(type) {
502         case *ast.BinaryExpr:
503                 if l.Op.Precedence() < e.Op.Precedence() {
504                         // parens will be inserted.
505                         // pretend this is an *ast.ParenExpr and do nothing.
506                         break
507                 }
508                 h4, h5, mp := walkBinary(l)
509                 has4 = has4 || h4
510                 has5 = has5 || h5
511                 if maxProblem < mp {
512                         maxProblem = mp
513                 }
514         }
515
516         switch r := e.Y.(type) {
517         case *ast.BinaryExpr:
518                 if r.Op.Precedence() <= e.Op.Precedence() {
519                         // parens will be inserted.
520                         // pretend this is an *ast.ParenExpr and do nothing.
521                         break
522                 }
523                 h4, h5, mp := walkBinary(r)
524                 has4 = has4 || h4
525                 has5 = has5 || h5
526                 if maxProblem < mp {
527                         maxProblem = mp
528                 }
529
530         case *ast.StarExpr:
531                 if e.Op == token.QUO { // `*/`
532                         maxProblem = 5
533                 }
534
535         case *ast.UnaryExpr:
536                 switch e.Op.String() + r.Op.String() {
537                 case "/*", "&&", "&^":
538                         maxProblem = 5
539                 case "++", "--":
540                         if maxProblem < 4 {
541                                 maxProblem = 4
542                         }
543                 }
544         }
545         return
546 }
547
548 func cutoff(e *ast.BinaryExpr, depth int) int {
549         has4, has5, maxProblem := walkBinary(e)
550         if maxProblem > 0 {
551                 return maxProblem + 1
552         }
553         if has4 && has5 {
554                 if depth == 1 {
555                         return 5
556                 }
557                 return 4
558         }
559         if depth == 1 {
560                 return 6
561         }
562         return 4
563 }
564
565 func diffPrec(expr ast.Expr, prec int) int {
566         x, ok := expr.(*ast.BinaryExpr)
567         if !ok || prec != x.Op.Precedence() {
568                 return 1
569         }
570         return 0
571 }
572
573 func reduceDepth(depth int) int {
574         depth--
575         if depth < 1 {
576                 depth = 1
577         }
578         return depth
579 }
580
581 // Format the binary expression: decide the cutoff and then format.
582 // Let's call depth == 1 Normal mode, and depth > 1 Compact mode.
583 // (Algorithm suggestion by Russ Cox.)
584 //
585 // The precedences are:
586 //      5             *  /  %  <<  >>  &  &^
587 //      4             +  -  |  ^
588 //      3             ==  !=  <  <=  >  >=
589 //      2             &&
590 //      1             ||
591 //
592 // The only decision is whether there will be spaces around levels 4 and 5.
593 // There are never spaces at level 6 (unary), and always spaces at levels 3 and below.
594 //
595 // To choose the cutoff, look at the whole expression but excluding primary
596 // expressions (function calls, parenthesized exprs), and apply these rules:
597 //
598 //      1) If there is a binary operator with a right side unary operand
599 //         that would clash without a space, the cutoff must be (in order):
600 //
601 //              /*      6
602 //              &&      6
603 //              &^      6
604 //              ++      5
605 //              --      5
606 //
607 //         (Comparison operators always have spaces around them.)
608 //
609 //      2) If there is a mix of level 5 and level 4 operators, then the cutoff
610 //         is 5 (use spaces to distinguish precedence) in Normal mode
611 //         and 4 (never use spaces) in Compact mode.
612 //
613 //      3) If there are no level 4 operators or no level 5 operators, then the
614 //         cutoff is 6 (always use spaces) in Normal mode
615 //         and 4 (never use spaces) in Compact mode.
616 //
617 func (p *printer) binaryExpr(x *ast.BinaryExpr, prec1, cutoff, depth int) {
618         prec := x.Op.Precedence()
619         if prec < prec1 {
620                 // parenthesis needed
621                 // Note: The parser inserts an ast.ParenExpr node; thus this case
622                 //       can only occur if the AST is created in a different way.
623                 p.print(token.LPAREN)
624                 p.expr0(x, reduceDepth(depth)) // parentheses undo one level of depth
625                 p.print(token.RPAREN)
626                 return
627         }
628
629         printBlank := prec < cutoff
630
631         ws := indent
632         p.expr1(x.X, prec, depth+diffPrec(x.X, prec))
633         if printBlank {
634                 p.print(blank)
635         }
636         xline := p.pos.Line // before the operator (it may be on the next line!)
637         yline := p.lineFor(x.Y.Pos())
638         p.print(x.OpPos, x.Op)
639         if xline != yline && xline > 0 && yline > 0 {
640                 // at least one line break, but respect an extra empty line
641                 // in the source
642                 if p.linebreak(yline, 1, ws, true) {
643                         ws = ignore
644                         printBlank = false // no blank after line break
645                 }
646         }
647         if printBlank {
648                 p.print(blank)
649         }
650         p.expr1(x.Y, prec+1, depth+1)
651         if ws == ignore {
652                 p.print(unindent)
653         }
654 }
655
656 func isBinary(expr ast.Expr) bool {
657         _, ok := expr.(*ast.BinaryExpr)
658         return ok
659 }
660
661 func (p *printer) expr1(expr ast.Expr, prec1, depth int) {
662         p.print(expr.Pos())
663
664         switch x := expr.(type) {
665         case *ast.BadExpr:
666                 p.print("BadExpr")
667
668         case *ast.Ident:
669                 p.print(x)
670
671         case *ast.BinaryExpr:
672                 if depth < 1 {
673                         p.internalError("depth < 1:", depth)
674                         depth = 1
675                 }
676                 p.binaryExpr(x, prec1, cutoff(x, depth), depth)
677
678         case *ast.KeyValueExpr:
679                 p.expr(x.Key)
680                 p.print(x.Colon, token.COLON, blank)
681                 p.expr(x.Value)
682
683         case *ast.StarExpr:
684                 const prec = token.UnaryPrec
685                 if prec < prec1 {
686                         // parenthesis needed
687                         p.print(token.LPAREN)
688                         p.print(token.MUL)
689                         p.expr(x.X)
690                         p.print(token.RPAREN)
691                 } else {
692                         // no parenthesis needed
693                         p.print(token.MUL)
694                         p.expr(x.X)
695                 }
696
697         case *ast.UnaryExpr:
698                 const prec = token.UnaryPrec
699                 if prec < prec1 {
700                         // parenthesis needed
701                         p.print(token.LPAREN)
702                         p.expr(x)
703                         p.print(token.RPAREN)
704                 } else {
705                         // no parenthesis needed
706                         p.print(x.Op)
707                         if x.Op == token.RANGE {
708                                 // TODO(gri) Remove this code if it cannot be reached.
709                                 p.print(blank)
710                         }
711                         p.expr1(x.X, prec, depth)
712                 }
713
714         case *ast.BasicLit:
715                 p.print(x)
716
717         case *ast.FuncLit:
718                 p.expr(x.Type)
719                 p.funcBody(x.Body, p.distance(x.Type.Pos(), p.pos), true)
720
721         case *ast.ParenExpr:
722                 if _, hasParens := x.X.(*ast.ParenExpr); hasParens {
723                         // don't print parentheses around an already parenthesized expression
724                         // TODO(gri) consider making this more general and incorporate precedence levels
725                         p.expr0(x.X, reduceDepth(depth)) // parentheses undo one level of depth
726                 } else {
727                         p.print(token.LPAREN)
728                         p.expr0(x.X, reduceDepth(depth)) // parentheses undo one level of depth
729                         p.print(x.Rparen, token.RPAREN)
730                 }
731
732         case *ast.SelectorExpr:
733                 p.expr1(x.X, token.HighestPrec, depth)
734                 p.print(token.PERIOD)
735                 if line := p.lineFor(x.Sel.Pos()); p.pos.IsValid() && p.pos.Line < line {
736                         p.print(indent, newline, x.Sel.Pos(), x.Sel, unindent)
737                 } else {
738                         p.print(x.Sel.Pos(), x.Sel)
739                 }
740
741         case *ast.TypeAssertExpr:
742                 p.expr1(x.X, token.HighestPrec, depth)
743                 p.print(token.PERIOD, token.LPAREN)
744                 if x.Type != nil {
745                         p.expr(x.Type)
746                 } else {
747                         p.print(token.TYPE)
748                 }
749                 p.print(token.RPAREN)
750
751         case *ast.IndexExpr:
752                 // TODO(gri): should treat[] like parentheses and undo one level of depth
753                 p.expr1(x.X, token.HighestPrec, 1)
754                 p.print(x.Lbrack, token.LBRACK)
755                 p.expr0(x.Index, depth+1)
756                 p.print(x.Rbrack, token.RBRACK)
757
758         case *ast.SliceExpr:
759                 // TODO(gri): should treat[] like parentheses and undo one level of depth
760                 p.expr1(x.X, token.HighestPrec, 1)
761                 p.print(x.Lbrack, token.LBRACK)
762                 if x.Low != nil {
763                         p.expr0(x.Low, depth+1)
764                 }
765                 // blanks around ":" if both sides exist and either side is a binary expression
766                 if depth <= 1 && x.Low != nil && x.High != nil && (isBinary(x.Low) || isBinary(x.High)) {
767                         p.print(blank, token.COLON, blank)
768                 } else {
769                         p.print(token.COLON)
770                 }
771                 if x.High != nil {
772                         p.expr0(x.High, depth+1)
773                 }
774                 p.print(x.Rbrack, token.RBRACK)
775
776         case *ast.CallExpr:
777                 if len(x.Args) > 1 {
778                         depth++
779                 }
780                 p.expr1(x.Fun, token.HighestPrec, depth)
781                 p.print(x.Lparen, token.LPAREN)
782                 if x.Ellipsis.IsValid() {
783                         p.exprList(x.Lparen, x.Args, depth, 0, x.Ellipsis)
784                         p.print(x.Ellipsis, token.ELLIPSIS)
785                         if x.Rparen.IsValid() && p.lineFor(x.Ellipsis) < p.lineFor(x.Rparen) {
786                                 p.print(token.COMMA, formfeed)
787                         }
788                 } else {
789                         p.exprList(x.Lparen, x.Args, depth, commaTerm, x.Rparen)
790                 }
791                 p.print(x.Rparen, token.RPAREN)
792
793         case *ast.CompositeLit:
794                 // composite literal elements that are composite literals themselves may have the type omitted
795                 if x.Type != nil {
796                         p.expr1(x.Type, token.HighestPrec, depth)
797                 }
798                 p.print(x.Lbrace, token.LBRACE)
799                 p.exprList(x.Lbrace, x.Elts, 1, commaTerm, x.Rbrace)
800                 // do not insert extra line breaks because of comments before
801                 // the closing '}' as it might break the code if there is no
802                 // trailing ','
803                 p.print(noExtraLinebreak, x.Rbrace, token.RBRACE, noExtraLinebreak)
804
805         case *ast.Ellipsis:
806                 p.print(token.ELLIPSIS)
807                 if x.Elt != nil {
808                         p.expr(x.Elt)
809                 }
810
811         case *ast.ArrayType:
812                 p.print(token.LBRACK)
813                 if x.Len != nil {
814                         p.expr(x.Len)
815                 }
816                 p.print(token.RBRACK)
817                 p.expr(x.Elt)
818
819         case *ast.StructType:
820                 p.print(token.STRUCT)
821                 p.fieldList(x.Fields, true, x.Incomplete)
822
823         case *ast.FuncType:
824                 p.print(token.FUNC)
825                 p.signature(x.Params, x.Results)
826
827         case *ast.InterfaceType:
828                 p.print(token.INTERFACE)
829                 p.fieldList(x.Methods, false, x.Incomplete)
830
831         case *ast.MapType:
832                 p.print(token.MAP, token.LBRACK)
833                 p.expr(x.Key)
834                 p.print(token.RBRACK)
835                 p.expr(x.Value)
836
837         case *ast.ChanType:
838                 switch x.Dir {
839                 case ast.SEND | ast.RECV:
840                         p.print(token.CHAN)
841                 case ast.RECV:
842                         p.print(token.ARROW, token.CHAN)
843                 case ast.SEND:
844                         p.print(token.CHAN, token.ARROW)
845                 }
846                 p.print(blank)
847                 p.expr(x.Value)
848
849         default:
850                 panic("unreachable")
851         }
852
853         return
854 }
855
856 func (p *printer) expr0(x ast.Expr, depth int) {
857         p.expr1(x, token.LowestPrec, depth)
858 }
859
860 func (p *printer) expr(x ast.Expr) {
861         const depth = 1
862         p.expr1(x, token.LowestPrec, depth)
863 }
864
865 // ----------------------------------------------------------------------------
866 // Statements
867
868 // Print the statement list indented, but without a newline after the last statement.
869 // Extra line breaks between statements in the source are respected but at most one
870 // empty line is printed between statements.
871 func (p *printer) stmtList(list []ast.Stmt, _indent int, nextIsRBrace bool) {
872         // TODO(gri): fix _indent code
873         if _indent > 0 {
874                 p.print(indent)
875         }
876         multiLine := false
877         for i, s := range list {
878                 // _indent == 0 only for lists of switch/select case clauses;
879                 // in those cases each clause is a new section
880                 p.linebreak(p.lineFor(s.Pos()), 1, ignore, i == 0 || _indent == 0 || multiLine)
881                 p.stmt(s, nextIsRBrace && i == len(list)-1)
882                 multiLine = p.isMultiLine(s)
883         }
884         if _indent > 0 {
885                 p.print(unindent)
886         }
887 }
888
889 // block prints an *ast.BlockStmt; it always spans at least two lines.
890 func (p *printer) block(s *ast.BlockStmt, indent int) {
891         p.print(s.Pos(), token.LBRACE)
892         p.stmtList(s.List, indent, true)
893         p.linebreak(p.lineFor(s.Rbrace), 1, ignore, true)
894         p.print(s.Rbrace, token.RBRACE)
895 }
896
897 func isTypeName(x ast.Expr) bool {
898         switch t := x.(type) {
899         case *ast.Ident:
900                 return true
901         case *ast.SelectorExpr:
902                 return isTypeName(t.X)
903         }
904         return false
905 }
906
907 func stripParens(x ast.Expr) ast.Expr {
908         if px, strip := x.(*ast.ParenExpr); strip {
909                 // parentheses must not be stripped if there are any
910                 // unparenthesized composite literals starting with
911                 // a type name
912                 ast.Inspect(px.X, func(node ast.Node) bool {
913                         switch x := node.(type) {
914                         case *ast.ParenExpr:
915                                 // parentheses protect enclosed composite literals
916                                 return false
917                         case *ast.CompositeLit:
918                                 if isTypeName(x.Type) {
919                                         strip = false // do not strip parentheses
920                                 }
921                                 return false
922                         }
923                         // in all other cases, keep inspecting
924                         return true
925                 })
926                 if strip {
927                         return stripParens(px.X)
928                 }
929         }
930         return x
931 }
932
933 func (p *printer) controlClause(isForStmt bool, init ast.Stmt, expr ast.Expr, post ast.Stmt) {
934         p.print(blank)
935         needsBlank := false
936         if init == nil && post == nil {
937                 // no semicolons required
938                 if expr != nil {
939                         p.expr(stripParens(expr))
940                         needsBlank = true
941                 }
942         } else {
943                 // all semicolons required
944                 // (they are not separators, print them explicitly)
945                 if init != nil {
946                         p.stmt(init, false)
947                 }
948                 p.print(token.SEMICOLON, blank)
949                 if expr != nil {
950                         p.expr(stripParens(expr))
951                         needsBlank = true
952                 }
953                 if isForStmt {
954                         p.print(token.SEMICOLON, blank)
955                         needsBlank = false
956                         if post != nil {
957                                 p.stmt(post, false)
958                                 needsBlank = true
959                         }
960                 }
961         }
962         if needsBlank {
963                 p.print(blank)
964         }
965 }
966
967 func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool) {
968         p.print(stmt.Pos())
969
970         switch s := stmt.(type) {
971         case *ast.BadStmt:
972                 p.print("BadStmt")
973
974         case *ast.DeclStmt:
975                 p.decl(s.Decl)
976
977         case *ast.EmptyStmt:
978                 // nothing to do
979
980         case *ast.LabeledStmt:
981                 // a "correcting" unindent immediately following a line break
982                 // is applied before the line break if there is no comment
983                 // between (see writeWhitespace)
984                 p.print(unindent)
985                 p.expr(s.Label)
986                 p.print(s.Colon, token.COLON, indent)
987                 if e, isEmpty := s.Stmt.(*ast.EmptyStmt); isEmpty {
988                         if !nextIsRBrace {
989                                 p.print(newline, e.Pos(), token.SEMICOLON)
990                                 break
991                         }
992                 } else {
993                         p.linebreak(p.lineFor(s.Stmt.Pos()), 1, ignore, true)
994                 }
995                 p.stmt(s.Stmt, nextIsRBrace)
996
997         case *ast.ExprStmt:
998                 const depth = 1
999                 p.expr0(s.X, depth)
1000
1001         case *ast.SendStmt:
1002                 const depth = 1
1003                 p.expr0(s.Chan, depth)
1004                 p.print(blank, s.Arrow, token.ARROW, blank)
1005                 p.expr0(s.Value, depth)
1006
1007         case *ast.IncDecStmt:
1008                 const depth = 1
1009                 p.expr0(s.X, depth+1)
1010                 p.print(s.TokPos, s.Tok)
1011
1012         case *ast.AssignStmt:
1013                 var depth = 1
1014                 if len(s.Lhs) > 1 && len(s.Rhs) > 1 {
1015                         depth++
1016                 }
1017                 p.exprList(s.Pos(), s.Lhs, depth, 0, s.TokPos)
1018                 p.print(blank, s.TokPos, s.Tok, blank)
1019                 p.exprList(s.TokPos, s.Rhs, depth, 0, token.NoPos)
1020
1021         case *ast.GoStmt:
1022                 p.print(token.GO, blank)
1023                 p.expr(s.Call)
1024
1025         case *ast.DeferStmt:
1026                 p.print(token.DEFER, blank)
1027                 p.expr(s.Call)
1028
1029         case *ast.ReturnStmt:
1030                 p.print(token.RETURN)
1031                 if s.Results != nil {
1032                         p.print(blank)
1033                         p.exprList(s.Pos(), s.Results, 1, 0, token.NoPos)
1034                 }
1035
1036         case *ast.BranchStmt:
1037                 p.print(s.Tok)
1038                 if s.Label != nil {
1039                         p.print(blank)
1040                         p.expr(s.Label)
1041                 }
1042
1043         case *ast.BlockStmt:
1044                 p.block(s, 1)
1045
1046         case *ast.IfStmt:
1047                 p.print(token.IF)
1048                 p.controlClause(false, s.Init, s.Cond, nil)
1049                 p.block(s.Body, 1)
1050                 if s.Else != nil {
1051                         p.print(blank, token.ELSE, blank)
1052                         switch s.Else.(type) {
1053                         case *ast.BlockStmt, *ast.IfStmt:
1054                                 p.stmt(s.Else, nextIsRBrace)
1055                         default:
1056                                 p.print(token.LBRACE, indent, formfeed)
1057                                 p.stmt(s.Else, true)
1058                                 p.print(unindent, formfeed, token.RBRACE)
1059                         }
1060                 }
1061
1062         case *ast.CaseClause:
1063                 if s.List != nil {
1064                         p.print(token.CASE, blank)
1065                         p.exprList(s.Pos(), s.List, 1, 0, s.Colon)
1066                 } else {
1067                         p.print(token.DEFAULT)
1068                 }
1069                 p.print(s.Colon, token.COLON)
1070                 p.stmtList(s.Body, 1, nextIsRBrace)
1071
1072         case *ast.SwitchStmt:
1073                 p.print(token.SWITCH)
1074                 p.controlClause(false, s.Init, s.Tag, nil)
1075                 p.block(s.Body, 0)
1076
1077         case *ast.TypeSwitchStmt:
1078                 p.print(token.SWITCH)
1079                 if s.Init != nil {
1080                         p.print(blank)
1081                         p.stmt(s.Init, false)
1082                         p.print(token.SEMICOLON)
1083                 }
1084                 p.print(blank)
1085                 p.stmt(s.Assign, false)
1086                 p.print(blank)
1087                 p.block(s.Body, 0)
1088
1089         case *ast.CommClause:
1090                 if s.Comm != nil {
1091                         p.print(token.CASE, blank)
1092                         p.stmt(s.Comm, false)
1093                 } else {
1094                         p.print(token.DEFAULT)
1095                 }
1096                 p.print(s.Colon, token.COLON)
1097                 p.stmtList(s.Body, 1, nextIsRBrace)
1098
1099         case *ast.SelectStmt:
1100                 p.print(token.SELECT, blank)
1101                 body := s.Body
1102                 if len(body.List) == 0 && !p.commentBefore(p.posFor(body.Rbrace)) {
1103                         // print empty select statement w/o comments on one line
1104                         p.print(body.Lbrace, token.LBRACE, body.Rbrace, token.RBRACE)
1105                 } else {
1106                         p.block(body, 0)
1107                 }
1108
1109         case *ast.ForStmt:
1110                 p.print(token.FOR)
1111                 p.controlClause(true, s.Init, s.Cond, s.Post)
1112                 p.block(s.Body, 1)
1113
1114         case *ast.RangeStmt:
1115                 p.print(token.FOR, blank)
1116                 p.expr(s.Key)
1117                 if s.Value != nil {
1118                         // use position of value following the comma as
1119                         // comma position for correct comment placement
1120                         p.print(s.Value.Pos(), token.COMMA, blank)
1121                         p.expr(s.Value)
1122                 }
1123                 p.print(blank, s.TokPos, s.Tok, blank, token.RANGE, blank)
1124                 p.expr(stripParens(s.X))
1125                 p.print(blank)
1126                 p.block(s.Body, 1)
1127
1128         default:
1129                 panic("unreachable")
1130         }
1131
1132         return
1133 }
1134
1135 // ----------------------------------------------------------------------------
1136 // Declarations
1137
1138 // The keepTypeColumn function determines if the type column of a series of
1139 // consecutive const or var declarations must be kept, or if initialization
1140 // values (V) can be placed in the type column (T) instead. The i'th entry
1141 // in the result slice is true if the type column in spec[i] must be kept.
1142 //
1143 // For example, the declaration:
1144 //
1145 //      const (
1146 //              foobar int = 42 // comment
1147 //              x          = 7  // comment
1148 //              foo
1149 //              bar = 991
1150 //      )
1151 //
1152 // leads to the type/values matrix below. A run of value columns (V) can
1153 // be moved into the type column if there is no type for any of the values
1154 // in that column (we only move entire columns so that they align properly).
1155 //
1156 //      matrix        formatted     result
1157 //                    matrix
1158 //      T  V    ->    T  V     ->   true      there is a T and so the type
1159 //      -  V          -  V          true      column must be kept
1160 //      -  -          -  -          false
1161 //      -  V          V  -          false     V is moved into T column
1162 //
1163 func keepTypeColumn(specs []ast.Spec) []bool {
1164         m := make([]bool, len(specs))
1165
1166         populate := func(i, j int, keepType bool) {
1167                 if keepType {
1168                         for ; i < j; i++ {
1169                                 m[i] = true
1170                         }
1171                 }
1172         }
1173
1174         i0 := -1 // if i0 >= 0 we are in a run and i0 is the start of the run
1175         var keepType bool
1176         for i, s := range specs {
1177                 t := s.(*ast.ValueSpec)
1178                 if t.Values != nil {
1179                         if i0 < 0 {
1180                                 // start of a run of ValueSpecs with non-nil Values
1181                                 i0 = i
1182                                 keepType = false
1183                         }
1184                 } else {
1185                         if i0 >= 0 {
1186                                 // end of a run
1187                                 populate(i0, i, keepType)
1188                                 i0 = -1
1189                         }
1190                 }
1191                 if t.Type != nil {
1192                         keepType = true
1193                 }
1194         }
1195         if i0 >= 0 {
1196                 // end of a run
1197                 populate(i0, len(specs), keepType)
1198         }
1199
1200         return m
1201 }
1202
1203 func (p *printer) valueSpec(s *ast.ValueSpec, keepType, doIndent bool) {
1204         p.setComment(s.Doc)
1205         p.identList(s.Names, doIndent) // always present
1206         extraTabs := 3
1207         if s.Type != nil || keepType {
1208                 p.print(vtab)
1209                 extraTabs--
1210         }
1211         if s.Type != nil {
1212                 p.expr(s.Type)
1213         }
1214         if s.Values != nil {
1215                 p.print(vtab, token.ASSIGN, blank)
1216                 p.exprList(token.NoPos, s.Values, 1, 0, token.NoPos)
1217                 extraTabs--
1218         }
1219         if s.Comment != nil {
1220                 for ; extraTabs > 0; extraTabs-- {
1221                         p.print(vtab)
1222                 }
1223                 p.setComment(s.Comment)
1224         }
1225 }
1226
1227 // The parameter n is the number of specs in the group. If doIndent is set,
1228 // multi-line identifier lists in the spec are indented when the first
1229 // linebreak is encountered.
1230 //
1231 func (p *printer) spec(spec ast.Spec, n int, doIndent bool) {
1232         switch s := spec.(type) {
1233         case *ast.ImportSpec:
1234                 p.setComment(s.Doc)
1235                 if s.Name != nil {
1236                         p.expr(s.Name)
1237                         p.print(blank)
1238                 }
1239                 p.expr(s.Path)
1240                 p.setComment(s.Comment)
1241                 p.print(s.EndPos)
1242
1243         case *ast.ValueSpec:
1244                 if n != 1 {
1245                         p.internalError("expected n = 1; got", n)
1246                 }
1247                 p.setComment(s.Doc)
1248                 p.identList(s.Names, doIndent) // always present
1249                 if s.Type != nil {
1250                         p.print(blank)
1251                         p.expr(s.Type)
1252                 }
1253                 if s.Values != nil {
1254                         p.print(blank, token.ASSIGN, blank)
1255                         p.exprList(token.NoPos, s.Values, 1, 0, token.NoPos)
1256                 }
1257                 p.setComment(s.Comment)
1258
1259         case *ast.TypeSpec:
1260                 p.setComment(s.Doc)
1261                 p.expr(s.Name)
1262                 if n == 1 {
1263                         p.print(blank)
1264                 } else {
1265                         p.print(vtab)
1266                 }
1267                 p.expr(s.Type)
1268                 p.setComment(s.Comment)
1269
1270         default:
1271                 panic("unreachable")
1272         }
1273 }
1274
1275 func (p *printer) genDecl(d *ast.GenDecl) {
1276         p.setComment(d.Doc)
1277         p.print(d.Pos(), d.Tok, blank)
1278
1279         if d.Lparen.IsValid() {
1280                 // group of parenthesized declarations
1281                 p.print(d.Lparen, token.LPAREN)
1282                 if n := len(d.Specs); n > 0 {
1283                         p.print(indent, formfeed)
1284                         if n > 1 && (d.Tok == token.CONST || d.Tok == token.VAR) {
1285                                 // two or more grouped const/var declarations:
1286                                 // determine if the type column must be kept
1287                                 keepType := keepTypeColumn(d.Specs)
1288                                 newSection := false
1289                                 for i, s := range d.Specs {
1290                                         if i > 0 {
1291                                                 p.linebreak(p.lineFor(s.Pos()), 1, ignore, newSection)
1292                                         }
1293                                         p.valueSpec(s.(*ast.ValueSpec), keepType[i], false)
1294                                         newSection = p.isMultiLine(s)
1295                                 }
1296                         } else {
1297                                 newSection := false
1298                                 for i, s := range d.Specs {
1299                                         if i > 0 {
1300                                                 p.linebreak(p.lineFor(s.Pos()), 1, ignore, newSection)
1301                                         }
1302                                         p.spec(s, n, false)
1303                                         newSection = p.isMultiLine(s)
1304                                 }
1305                         }
1306                         p.print(unindent, formfeed)
1307                 }
1308                 p.print(d.Rparen, token.RPAREN)
1309
1310         } else {
1311                 // single declaration
1312                 p.spec(d.Specs[0], 1, true)
1313         }
1314 }
1315
1316 // nodeSize determines the size of n in chars after formatting.
1317 // The result is <= maxSize if the node fits on one line with at
1318 // most maxSize chars and the formatted output doesn't contain
1319 // any control chars. Otherwise, the result is > maxSize.
1320 //
1321 func (p *printer) nodeSize(n ast.Node, maxSize int) (size int) {
1322         // nodeSize invokes the printer, which may invoke nodeSize
1323         // recursively. For deep composite literal nests, this can
1324         // lead to an exponential algorithm. Remember previous
1325         // results to prune the recursion (was issue 1628).
1326         if size, found := p.nodeSizes[n]; found {
1327                 return size
1328         }
1329
1330         size = maxSize + 1 // assume n doesn't fit
1331         p.nodeSizes[n] = size
1332
1333         // nodeSize computation must be independent of particular
1334         // style so that we always get the same decision; print
1335         // in RawFormat
1336         cfg := Config{Mode: RawFormat}
1337         var buf bytes.Buffer
1338         if err := cfg.fprint(&buf, p.fset, n, p.nodeSizes); err != nil {
1339                 return
1340         }
1341         if buf.Len() <= maxSize {
1342                 for _, ch := range buf.Bytes() {
1343                         if ch < ' ' {
1344                                 return
1345                         }
1346                 }
1347                 size = buf.Len() // n fits
1348                 p.nodeSizes[n] = size
1349         }
1350         return
1351 }
1352
1353 func (p *printer) isOneLineFunc(b *ast.BlockStmt, headerSize int) bool {
1354         pos1 := b.Pos()
1355         pos2 := b.Rbrace
1356         if pos1.IsValid() && pos2.IsValid() && p.lineFor(pos1) != p.lineFor(pos2) {
1357                 // opening and closing brace are on different lines - don't make it a one-liner
1358                 return false
1359         }
1360         if len(b.List) > 5 || p.commentBefore(p.posFor(pos2)) {
1361                 // too many statements or there is a comment inside - don't make it a one-liner
1362                 return false
1363         }
1364         // otherwise, estimate body size
1365         const maxSize = 100
1366         bodySize := 0
1367         for i, s := range b.List {
1368                 if i > 0 {
1369                         bodySize += 2 // space for a semicolon and blank
1370                 }
1371                 bodySize += p.nodeSize(s, maxSize)
1372         }
1373         return headerSize+bodySize <= maxSize
1374 }
1375
1376 func (p *printer) funcBody(b *ast.BlockStmt, headerSize int, isLit bool) {
1377         if b == nil {
1378                 return
1379         }
1380
1381         if p.isOneLineFunc(b, headerSize) {
1382                 sep := vtab
1383                 if isLit {
1384                         sep = blank
1385                 }
1386                 p.print(sep, b.Lbrace, token.LBRACE)
1387                 if len(b.List) > 0 {
1388                         p.print(blank)
1389                         for i, s := range b.List {
1390                                 if i > 0 {
1391                                         p.print(token.SEMICOLON, blank)
1392                                 }
1393                                 p.stmt(s, i == len(b.List)-1)
1394                         }
1395                         p.print(blank)
1396                 }
1397                 p.print(b.Rbrace, token.RBRACE)
1398                 return
1399         }
1400
1401         p.print(blank)
1402         p.block(b, 1)
1403 }
1404
1405 // distance returns the column difference between from and to if both
1406 // are on the same line; if they are on different lines (or unknown)
1407 // the result is infinity.
1408 func (p *printer) distance(from0 token.Pos, to token.Position) int {
1409         from := p.posFor(from0)
1410         if from.IsValid() && to.IsValid() && from.Line == to.Line {
1411                 return to.Column - from.Column
1412         }
1413         return infinity
1414 }
1415
1416 func (p *printer) funcDecl(d *ast.FuncDecl) {
1417         p.setComment(d.Doc)
1418         p.print(d.Pos(), token.FUNC, blank)
1419         if d.Recv != nil {
1420                 p.parameters(d.Recv) // method: print receiver
1421                 p.print(blank)
1422         }
1423         p.expr(d.Name)
1424         p.signature(d.Type.Params, d.Type.Results)
1425         p.funcBody(d.Body, p.distance(d.Pos(), p.pos), false)
1426 }
1427
1428 func (p *printer) decl(decl ast.Decl) {
1429         switch d := decl.(type) {
1430         case *ast.BadDecl:
1431                 p.print(d.Pos(), "BadDecl")
1432         case *ast.GenDecl:
1433                 p.genDecl(d)
1434         case *ast.FuncDecl:
1435                 p.funcDecl(d)
1436         default:
1437                 panic("unreachable")
1438         }
1439 }
1440
1441 // ----------------------------------------------------------------------------
1442 // Files
1443
1444 func declToken(decl ast.Decl) (tok token.Token) {
1445         tok = token.ILLEGAL
1446         switch d := decl.(type) {
1447         case *ast.GenDecl:
1448                 tok = d.Tok
1449         case *ast.FuncDecl:
1450                 tok = token.FUNC
1451         }
1452         return
1453 }
1454
1455 func (p *printer) file(src *ast.File) {
1456         p.setComment(src.Doc)
1457         p.print(src.Pos(), token.PACKAGE, blank)
1458         p.expr(src.Name)
1459
1460         if len(src.Decls) > 0 {
1461                 tok := token.ILLEGAL
1462                 for _, d := range src.Decls {
1463                         prev := tok
1464                         tok = declToken(d)
1465                         // if the declaration token changed (e.g., from CONST to TYPE)
1466                         // or the next declaration has documentation associated with it,
1467                         // print an empty line between top-level declarations
1468                         // (because p.linebreak is called with the position of d, which
1469                         // is past any documentation, the minimum requirement is satisfied
1470                         // even w/o the extra getDoc(d) nil-check - leave it in case the
1471                         // linebreak logic improves - there's already a TODO).
1472                         min := 1
1473                         if prev != tok || getDoc(d) != nil {
1474                                 min = 2
1475                         }
1476                         p.linebreak(p.lineFor(d.Pos()), min, ignore, false)
1477                         p.decl(d)
1478                 }
1479         }
1480
1481         p.print(newline)
1482 }