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