Copyright 2009 The Go Authors. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
This file implements printing of AST nodes; specifically expressions, statements, declarations, and files. It uses the print functionality implemented in printer.go.

package printer

import (
	
	
	
	
	
	
	
	
)
Formatting issues: - better comment formatting for -style comments at the end of a line (e.g. a declaration) when the comment spans multiple lines; if such a comment is just two lines, formatting is not idempotent - formatting of expression lists - should use blank instead of tab to separate one-line function bodies from the function header unless there is a group of consecutive one-liners
---------------------------------------------------------------------------- Common AST nodes.
Print as many newlines as necessary (but at least min newlines) to get to the current line. ws is printed before the first line break. If newSection is set, the first line break is printed as formfeed. Returns 0 if no line breaks were printed, returns 1 if there was exactly one newline printed, and returns a value > 1 if there was a formfeed or more than one newline printed. TODO(gri): linebreak may add too many lines if the next statement at "line" is preceded by comments because the computation of n assumes the current position before the comment and the target position after the comment. Thus, after interspersing such comments, the space taken up by them is not considered to reduce the number of linebreaks. At the moment there is no easy way to know about future (not yet interspersed) comments in this function.
func ( *printer) (,  int,  whiteSpace,  bool) ( int) {
	 := nlimit( - .pos.Line)
	if  <  {
		 = 
	}
	if  > 0 {
		.print()
		if  {
			.print(formfeed)
			--
			 = 2
		}
		 += 
		for ;  > 0; -- {
			.print(newline)
		}
	}
	return
}
setComment sets g as the next comment if g != nil and if node comments are enabled - this mode is used when printing source code fragments such as exports only. It assumes that there is no pending comment in p.comments and at most one pending comment in the p.comment cache.
func ( *printer) ( *ast.CommentGroup) {
	if  == nil || !.useNodeComments {
		return
	}
initialize p.comments lazily
for some reason there are pending comments; this should never happen - handle gracefully and flush all comments up to g, ignore anything after that
		.flush(.posFor(.List[0].Pos()), token.ILLEGAL)
in debug mode, report error
		.internalError("setComment found pending comments")
	}
	.comments[0] = 
don't overwrite any pending comment in the p.comment cache (there may be a pending comment when a line comment is immediately followed by a lead comment with no other tokens between)
	if .commentOffset == infinity {
		.nextComment() // get comment ready for use
	}
}

type exprListMode uint

const (
	commaTerm exprListMode = 1 << iota // list is optionally terminated by a comma
	noIndent                           // no extra indentation in multi-line lists
)
If indent is set, a multi-line identifier list is indented after the first linebreak encountered.
convert into an expression list so we can re-use exprList formatting
	 := make([]ast.Expr, len())
	for ,  := range  {
		[] = 
	}
	var  exprListMode
	if ! {
		 = noIndent
	}
	.exprList(token.NoPos, , 1, , token.NoPos, false)
}

const filteredMsg = "contains filtered or unexported fields"
Print a list of expressions. If the list spans multiple source lines, the original line breaks are respected between expressions. TODO(gri) Consider rewriting this to be independent of []ast.Expr so that we can use the algorithm for any kind of list (e.g., pass list via a channel over which to range).
func ( *printer) ( token.Pos,  []ast.Expr,  int,  exprListMode,  token.Pos,  bool) {
	if len() == 0 {
		if  {
			 := .posFor()
			 := .posFor()
			if .IsValid() && .Line == .Line {
				.print("/* " + filteredMsg + " */")
			} else {
				.print(newline)
				.print(indent, "// "+filteredMsg, unindent, newline)
			}
		}
		return
	}

	 := .posFor()
	 := .posFor()
	 := .lineFor([0].Pos())
	 := .lineFor([len()-1].End())

all list entries on a single line
		for ,  := range  {
use position of expression following the comma as comma position for correct comment placement
				.print(.Pos(), token.COMMA, blank)
			}
			.expr0(, )
		}
		if  {
			.print(token.COMMA, blank, "/* "+filteredMsg+" */")
		}
		return
	}
list entries span multiple lines; use source code positions to guide line breaks
Don't add extra indentation if noIndent is set; i.e., pretend that the first line is already indented.
	 := ignore
	if &noIndent == 0 {
		 = indent
	}
The first linebreak is always a formfeed since this section must not depend on any previous formatting.
	 := -1 // index of last expression that was followed by a linebreak
	if .IsValid() && .Line <  && .linebreak(, 0, , true) > 0 {
		 = ignore
		 = 0
	}
initialize expression/key size: a zero value indicates expr/key doesn't fit on a single line
	 := 0
We use the ratio between the geometric mean of the previous key sizes and the current size to determine if there should be a break in the alignment. To compute the geometric mean we accumulate the ln(size) values (lnsum) and the number of sizes included (count).
	 := 0.0
	 := 0
print all list elements
	 := .Line
	for ,  := range  {
		 = .lineFor(.Pos())
Determine if the next linebreak, if any, needs to use formfeed: in general, use the entire node size to make the decision; for key:value expressions, use the key size. TODO(gri) for a better result, should probably incorporate both the key and the node size into the decision process
		 := true
Determine element size: All bets are off if we don't have position information for the previous and next token (likely generated code - simply ignore the size in this case by setting it to 0).
		 := 
		const  = 1e6 // larger than any source line
		 = .nodeSize(, )
		,  := .(*ast.KeyValueExpr)
x fits on a single line
			if  {
				 = .nodeSize(.Key, ) // size <= infinity
			}
size too large or we don't have good layout information
			 = 0
		}
If the previous line and the current line had single- line-expressions and the key sizes are small or the ratio between the current key and the geometric mean if the previous key sizes does not exceed a threshold, align columns and do not use formfeed.
		if  > 0 &&  > 0 {
			const  = 40
			if  == 0 ||  <=  &&  <=  {
				 = false
			} else {
				const  = 2.5                               // threshold
				 := math.Exp( / float64()) // count > 0
				 := float64() / 
				 = * <= 1 ||  <= 
			}
		}

		 := 0 <  &&  < 
Use position of expression following the comma as comma position for correct comment placement, but only if the expression is on the same line.
			if ! {
				.print(.Pos())
			}
			.print(token.COMMA)
			 := true
Lines are broken using newlines so comments remain aligned unless useFF is set or there are multiple expressions on the same line in which case formfeed is used.
				 := .linebreak(, 0, ,  || +1 < )
				if  > 0 {
					 = ignore
					 = 
					 = false // we got a line break instead
If there was a new section or more than one new line (which means that the tabwriter will implicitly break the section), reset the geomean variables since we are starting a new group of elements with the next element.
				if  > 1 {
					 = 0
					 = 0
				}
			}
			if  {
				.print(blank)
			}
		}

We have a key:value expression that fits onto one line and it's not on the same line as the prior expression: Use a column for the key such that consecutive entries can align if possible. (needsLinebreak is set if we started a new line before)
			.expr(.Key)
			.print(.Colon, token.COLON, vtab)
			.expr(.Value)
		} else {
			.expr0(, )
		}

		if  > 0 {
			 += math.Log(float64())
			++
		}

		 = 
	}

Print a terminating comma if the next token is on a new line.
		.print(token.COMMA)
		if  {
			.print(newline)
			.print("// " + filteredMsg)
		}
unindent if we indented
			.print(unindent)
		}
		.print(formfeed) // terminating comma needs a line break to look good
		return
	}

	if  {
		.print(token.COMMA, newline)
		.print("// "+filteredMsg, newline)
	}

unindent if we indented
		.print(unindent)
	}
}

func ( *printer) ( *ast.FieldList) {
	.print(.Opening, token.LPAREN)
	if len(.List) > 0 {
		 := .lineFor(.Opening)
		 := indent
determine par begin and end line (may be different if there are multiple parameter names for this par or the type is on a separate line)
			var  int
			if len(.Names) > 0 {
				 = .lineFor(.Names[0].Pos())
			} else {
				 = .lineFor(.Type.Pos())
			}
separating "," if needed
			 := 0 <  &&  < 
use position of parameter following the comma as comma position for correct comma placement, but only if the next parameter is on the same line
				if ! {
					.print(.Pos())
				}
				.print(token.COMMA)
separator if needed (linebreak or blank)
break line if the opening "(" or previous parameter ended on a different line
				 = ignore
			} else if  > 0 {
				.print(blank)
parameter names
Very subtle: If we indented before (ws == ignore), identList won't indent again. If we didn't (ws == indent), identList will indent if the identList spans multiple lines, and it will outdent again at the end (and still ws == indent). Thus, a subsequent indent by a linebreak call after a type, or in the next multi-line identList will do the right thing.
				.identList(.Names,  == indent)
				.print(blank)
parameter type
			.expr(stripParensAlways(.Type))
			 = 
if the closing ")" is on a separate line from the last parameter, print an additional "," and line break
		if  := .lineFor(.Closing); 0 <  &&  <  {
			.print(token.COMMA)
			.linebreak(, 0, ignore, true)
unindent if we indented
		if  == ignore {
			.print(unindent)
		}
	}
	.print(.Closing, token.RPAREN)
}

func ( *printer) (,  *ast.FieldList) {
	if  != nil {
		.parameters()
	} else {
		.print(token.LPAREN, token.RPAREN)
	}
	 := .NumFields()
result != nil
single anonymous result; no ()'s
			.expr(stripParensAlways(.List[0].Type))
			return
		}
		.parameters()
	}
}

func ( []*ast.Ident,  int) ( int) {
	for ,  := range  {
		if  > 0 {
			 += len(", ")
		}
		 += utf8.RuneCountInString(.Name)
		if  >=  {
			break
		}
	}
	return
}

func ( *printer) ( []*ast.Field) bool {
	if len() != 1 {
		return false // allow only one field
	}
	 := [0]
	if .Tag != nil || .Comment != nil {
		return false // don't allow tags or comments
only name(s) and type
	const  = 30 // adjust as appropriate, this is an approximate value
	 := identListSize(.Names, )
	if  > 0 {
		 = 1 // blank between names and types
	}
	 := .nodeSize(.Type, )
	return + <= 
}

func ( *printer) ( string) {
	.setComment(&ast.CommentGroup{List: []*ast.Comment{{Slash: token.NoPos, Text: }}})
}

func ( *printer) ( *ast.FieldList, ,  bool) {
	 := .Opening
	 := .List
	 := .Closing
	 :=  || .commentBefore(.posFor())
	 := .IsValid() && .IsValid() && .lineFor() == .lineFor()

possibly a one-line struct/interface
no blank between keyword and {} in this case
			.print(, token.LBRACE, , token.RBRACE)
			return
small enough - print on one line (don't use identList and ignore source line breaks)
			.print(, token.LBRACE, blank)
			 := [0]
			if  {
				for ,  := range .Names {
no comments so no need for comma position
						.print(token.COMMA, blank)
					}
					.expr()
				}
				if len(.Names) > 0 {
					.print(blank)
				}
				.expr(.Type)
			} else { // interface
method
					.expr(.Names[0])
					.signature(.Params, .Results)
embedded interface
					.expr(.Type)
				}
			}
			.print(blank, , token.RBRACE)
			return
		}
hasComments || !srcIsOneLine

	.print(blank, , token.LBRACE, indent)
	if  || len() > 0 {
		.print(formfeed)
	}

	if  {

		 := vtab
		if len() == 1 {
			 = blank
		}
		var  int
		for ,  := range  {
			if  > 0 {
				.linebreak(.lineFor(.Pos()), 1, ignore, .linesFrom() > 0)
			}
			 := 0
			.setComment(.Doc)
			.recordLine(&)
named fields
				.identList(.Names, false)
				.print()
				.expr(.Type)
				 = 1
anonymous field
				.expr(.Type)
				 = 2
			}
			if .Tag != nil {
				if len(.Names) > 0 &&  == vtab {
					.print()
				}
				.print()
				.expr(.Tag)
				 = 0
			}
			if .Comment != nil {
				for ;  > 0; -- {
					.print()
				}
				.setComment(.Comment)
			}
		}
		if  {
			if len() > 0 {
				.print(formfeed)
			}
			.flush(.posFor(), token.RBRACE) // make sure we don't lose the last line comment
			.setLineComment("// " + filteredMsg)
		}

	} else { // interface

		var  int
		for ,  := range  {
			if  > 0 {
				.linebreak(.lineFor(.Pos()), 1, ignore, .linesFrom() > 0)
			}
			.setComment(.Doc)
			.recordLine(&)
method
				.expr(.Names[0])
				.signature(.Params, .Results)
embedded interface
				.expr(.Type)
			}
			.setComment(.Comment)
		}
		if  {
			if len() > 0 {
				.print(formfeed)
			}
			.flush(.posFor(), token.RBRACE) // make sure we don't lose the last line comment
			.setLineComment("// contains filtered or unexported methods")
		}

	}
	.print(unindent, formfeed, , token.RBRACE)
}
---------------------------------------------------------------------------- Expressions

func ( *ast.BinaryExpr) (,  bool,  int) {
	switch .Op.Precedence() {
	case 4:
		 = true
	case 5:
		 = true
	}

	switch l := .X.(type) {
	case *ast.BinaryExpr:
parens will be inserted. pretend this is an *ast.ParenExpr and do nothing.
			break
		}
		, ,  := ()
		 =  || 
		 =  || 
		if  <  {
			 = 
		}
	}

	switch r := .Y.(type) {
	case *ast.BinaryExpr:
parens will be inserted. pretend this is an *ast.ParenExpr and do nothing.
			break
		}
		, ,  := ()
		 =  || 
		 =  || 
		if  <  {
			 = 
		}

	case *ast.StarExpr:
		if .Op == token.QUO { // `*/`
			 = 5
		}

	case *ast.UnaryExpr:
		switch .Op.String() + .Op.String() {
		case "/*", "&&", "&^":
			 = 5
		case "++", "--":
			if  < 4 {
				 = 4
			}
		}
	}
	return
}

func ( *ast.BinaryExpr,  int) int {
	, ,  := walkBinary()
	if  > 0 {
		return  + 1
	}
	if  &&  {
		if  == 1 {
			return 5
		}
		return 4
	}
	if  == 1 {
		return 6
	}
	return 4
}

func ( ast.Expr,  int) int {
	,  := .(*ast.BinaryExpr)
	if ! ||  != .Op.Precedence() {
		return 1
	}
	return 0
}

func ( int) int {
	--
	if  < 1 {
		 = 1
	}
	return 
}
Format the binary expression: decide the cutoff and then format. Let's call depth == 1 Normal mode, and depth > 1 Compact mode. (Algorithm suggestion by Russ Cox.) The precedences are: 5 * / % << >> & &^ 4 + - | ^ 3 == != < <= > >= 2 && 1 || The only decision is whether there will be spaces around levels 4 and 5. There are never spaces at level 6 (unary), and always spaces at levels 3 and below. To choose the cutoff, look at the whole expression but excluding primary expressions (function calls, parenthesized exprs), and apply these rules: 1) If there is a binary operator with a right side unary operand that would clash without a space, the cutoff must be (in order): 6 && 6 &^ 6 ++ 5 -- 5 (Comparison operators always have spaces around them.) 2) If there is a mix of level 5 and level 4 operators, then the cutoff is 5 (use spaces to distinguish precedence) in Normal mode and 4 (never use spaces) in Compact mode. 3) If there are no level 4 operators or no level 5 operators, then the cutoff is 6 (always use spaces) in Normal mode and 4 (never use spaces) in Compact mode.
func ( *printer) ( *ast.BinaryExpr, , ,  int) {
	 := .Op.Precedence()
parenthesis needed Note: The parser inserts an ast.ParenExpr node; thus this case can only occur if the AST is created in a different way.
		.print(token.LPAREN)
		.expr0(, reduceDepth()) // parentheses undo one level of depth
		.print(token.RPAREN)
		return
	}

	 :=  < 

	 := indent
	.expr1(.X, , +diffPrec(.X, ))
	if  {
		.print(blank)
	}
	 := .pos.Line // before the operator (it may be on the next line!)
	 := .lineFor(.Y.Pos())
	.print(.OpPos, .Op)
at least one line break, but respect an extra empty line in the source
		if .linebreak(, 1, , true) > 0 {
			 = ignore
			 = false // no blank after line break
		}
	}
	if  {
		.print(blank)
	}
	.expr1(.Y, +1, +1)
	if  == ignore {
		.print(unindent)
	}
}

func ( ast.Expr) bool {
	,  := .(*ast.BinaryExpr)
	return 
}

func ( *printer) ( ast.Expr, ,  int) {
	.print(.Pos())

	switch x := .(type) {
	case *ast.BadExpr:
		.print("BadExpr")

	case *ast.Ident:
		.print()

	case *ast.BinaryExpr:
		if  < 1 {
			.internalError("depth < 1:", )
			 = 1
		}
		.binaryExpr(, , cutoff(, ), )

	case *ast.KeyValueExpr:
		.expr(.Key)
		.print(.Colon, token.COLON, blank)
		.expr(.Value)

	case *ast.StarExpr:
		const  = token.UnaryPrec
parenthesis needed
no parenthesis needed
			.print(token.MUL)
			.expr(.X)
		}

	case *ast.UnaryExpr:
		const  = token.UnaryPrec
parenthesis needed
no parenthesis needed
			.print(.Op)
TODO(gri) Remove this code if it cannot be reached.
				.print(blank)
			}
			.(.X, , )
		}

	case *ast.BasicLit:
		if .Config.Mode&normalizeNumbers != 0 {
			 = normalizedNumber()
		}
		.print()

	case *ast.FuncLit:
See the comment in funcDecl about how the header size is computed.
		 := .out.Column - len("func")
		.signature(.Type.Params, .Type.Results)
		.funcBody(.distanceFrom(.Type.Pos(), ), blank, .Body)

	case *ast.ParenExpr:
don't print parentheses around an already parenthesized expression TODO(gri) consider making this more general and incorporate precedence levels
			.expr0(.X, )
		} else {
			.print(token.LPAREN)
			.expr0(.X, reduceDepth()) // parentheses undo one level of depth
			.print(.Rparen, token.RPAREN)
		}

	case *ast.SelectorExpr:
		.selectorExpr(, , false)

	case *ast.TypeAssertExpr:
		.(.X, token.HighestPrec, )
		.print(token.PERIOD, .Lparen, token.LPAREN)
		if .Type != nil {
			.expr(.Type)
		} else {
			.print(token.TYPE)
		}
		.print(.Rparen, token.RPAREN)

TODO(gri): should treat[] like parentheses and undo one level of depth
		.(.X, token.HighestPrec, 1)
		.print(.Lbrack, token.LBRACK)
		.expr0(.Index, +1)
		.print(.Rbrack, token.RBRACK)

TODO(gri): should treat[] like parentheses and undo one level of depth
		.(.X, token.HighestPrec, 1)
		.print(.Lbrack, token.LBRACK)
		 := []ast.Expr{.Low, .High}
		if .Max != nil {
			 = append(, .Max)
determine if we need extra blanks around ':'
		var  bool
		if  <= 1 {
			var  int
			var  bool
			for ,  := range  {
				if  != nil {
					++
					if isBinary() {
						 = true
					}
				}
			}
			if  > 1 &&  {
				 = true
			}
		}
		for ,  := range  {
			if  > 0 {
				if [-1] != nil &&  {
					.print(blank)
				}
				.print(token.COLON)
				if  != nil &&  {
					.print(blank)
				}
			}
			if  != nil {
				.expr0(, +1)
			}
		}
		.print(.Rbrack, token.RBRACK)

	case *ast.CallExpr:
		if len(.Args) > 1 {
			++
		}
		var  bool
conversions to literal function types require parentheses around the type
			.print(token.LPAREN)
			 = .possibleSelectorExpr(.Fun, token.HighestPrec, )
			.print(token.RPAREN)
		} else {
			 = .possibleSelectorExpr(.Fun, token.HighestPrec, )
		}
		.print(.Lparen, token.LPAREN)
		if .Ellipsis.IsValid() {
			.exprList(.Lparen, .Args, , 0, .Ellipsis, false)
			.print(.Ellipsis, token.ELLIPSIS)
			if .Rparen.IsValid() && .lineFor(.Ellipsis) < .lineFor(.Rparen) {
				.print(token.COMMA, formfeed)
			}
		} else {
			.exprList(.Lparen, .Args, , commaTerm, .Rparen, false)
		}
		.print(.Rparen, token.RPAREN)
		if  {
			.print(unindent)
		}

composite literal elements that are composite literals themselves may have the type omitted
		if .Type != nil {
			.(.Type, token.HighestPrec, )
		}
		.level++
		.print(.Lbrace, token.LBRACE)
do not insert extra line break following a -style comment before the closing '}' as it might break the code if there is no trailing ','
do not insert extra blank following a -style comment before the closing '}' unless the literal is empty
		if len(.Elts) > 0 {
			 |= noExtraBlank
need the initial indent to print lone comments with the proper level of indentation
		.print(indent, unindent, , .Rbrace, token.RBRACE, )
		.level--

	case *ast.Ellipsis:
		.print(token.ELLIPSIS)
		if .Elt != nil {
			.expr(.Elt)
		}

	case *ast.ArrayType:
		.print(token.LBRACK)
		if .Len != nil {
			.expr(.Len)
		}
		.print(token.RBRACK)
		.expr(.Elt)

	case *ast.StructType:
		.print(token.STRUCT)
		.fieldList(.Fields, true, .Incomplete)

	case *ast.FuncType:
		.print(token.FUNC)
		.signature(.Params, .Results)

	case *ast.InterfaceType:
		.print(token.INTERFACE)
		.fieldList(.Methods, false, .Incomplete)

	case *ast.MapType:
		.print(token.MAP, token.LBRACK)
		.expr(.Key)
		.print(token.RBRACK)
		.expr(.Value)

	case *ast.ChanType:
		switch .Dir {
		case ast.SEND | ast.RECV:
			.print(token.CHAN)
		case ast.RECV:
			.print(token.ARROW, token.CHAN) // x.Arrow and x.Pos() are the same
		case ast.SEND:
			.print(token.CHAN, .Arrow, token.ARROW)
		}
		.print(blank)
		.expr(.Value)

	default:
		panic("unreachable")
	}
}
normalizedNumber rewrites base prefixes and exponents of numbers to use lower-case letters (0X123 to 0x123 and 1.2E3 to 1.2e3), and removes leading 0's from integer imaginary literals (0765i to 765i). It leaves hexadecimal digits alone. normalizedNumber doesn't modify the ast.BasicLit value lit points to. If lit is not a number or a number in canonical format already, lit is returned as is. Otherwise a new ast.BasicLit is created.
func ( *ast.BasicLit) *ast.BasicLit {
	if .Kind != token.INT && .Kind != token.FLOAT && .Kind != token.IMAG {
		return  // not a number - nothing to do
	}
	if len(.Value) < 2 {
		return  // only one digit (common case) - nothing to do
len(lit.Value) >= 2
We ignore lit.Kind because for lit.Kind == token.IMAG the literal may be an integer or floating-point value, decimal or not. Instead, just consider the literal pattern.
	 := .Value
	switch [:2] {
0-prefix octal, decimal int, or float (possibly with 'i' suffix)
		if  := strings.LastIndexByte(, 'E');  >= 0 {
			 = [:] + "e" + [+1:]
			break
remove leading 0's from integer (but not floating-point) imaginary literals
		if [len()-1] == 'i' && strings.IndexByte(, '.') < 0 && strings.IndexByte(, 'e') < 0 {
			 = strings.TrimLeft(, "0_")
			if  == "i" {
				 = "0i"
			}
		}
	case "0X":
possibly a hexadecimal float
		if  := strings.LastIndexByte(, 'P');  >= 0 {
			 = [:] + "p" + [+1:]
		}
possibly a hexadecimal float
		 := strings.LastIndexByte(, 'P')
		if  == -1 {
			return  // nothing to do
		}
		 = [:] + "p" + [+1:]
	case "0O":
		 = "0o" + [2:]
	case "0o":
		return  // nothing to do
	case "0B":
		 = "0b" + [2:]
	case "0b":
		return  // nothing to do
	}

	return &ast.BasicLit{ValuePos: .ValuePos, Kind: .Kind, Value: }
}

func ( *printer) ( ast.Expr, ,  int) bool {
	if ,  := .(*ast.SelectorExpr);  {
		return .selectorExpr(, , true)
	}
	.expr1(, , )
	return false
}
selectorExpr handles an *ast.SelectorExpr node and reports whether x spans multiple lines.
func ( *printer) ( *ast.SelectorExpr,  int,  bool) bool {
	.expr1(.X, token.HighestPrec, )
	.print(token.PERIOD)
	if  := .lineFor(.Sel.Pos()); .pos.IsValid() && .pos.Line <  {
		.print(indent, newline, .Sel.Pos(), .Sel)
		if ! {
			.print(unindent)
		}
		return true
	}
	.print(.Sel.Pos(), .Sel)
	return false
}

func ( *printer) ( ast.Expr,  int) {
	.expr1(, token.LowestPrec, )
}

func ( *printer) ( ast.Expr) {
	const  = 1
	.expr1(, token.LowestPrec, )
}
---------------------------------------------------------------------------- Statements
Print the statement list indented, but without a newline after the last statement. Extra line breaks between statements in the source are respected but at most one empty line is printed between statements.
func ( *printer) ( []ast.Stmt,  int,  bool) {
	if  > 0 {
		.print(indent)
	}
	var  int
	 := 0
ignore empty statements (was issue 3466)
nindent == 0 only for lists of switch/select case clauses; in those cases each clause is a new section
only print line break if we are not at the beginning of the output (i.e., we are not printing only a partial program)
				.linebreak(.lineFor(.Pos()), 1, ignore,  == 0 ||  == 0 || .linesFrom() > 0)
			}
			.recordLine(&)
labeled statements put labels on a separate line, but here we only care about the start line of the actual statement without label - correct line for each label
			for  := ; ; {
				,  := .(*ast.LabeledStmt)
				if  == nil {
					break
				}
				++
				 = .Stmt
			}
			++
		}
	}
	if  > 0 {
		.print(unindent)
	}
}
block prints an *ast.BlockStmt; it always spans at least two lines.
func ( *printer) ( *ast.BlockStmt,  int) {
	.print(.Lbrace, token.LBRACE)
	.stmtList(.List, , true)
	.linebreak(.lineFor(.Rbrace), 1, ignore, true)
	.print(.Rbrace, token.RBRACE)
}

func ( ast.Expr) bool {
	switch t := .(type) {
	case *ast.Ident:
		return true
	case *ast.SelectorExpr:
		return (.X)
	}
	return false
}

func ( ast.Expr) ast.Expr {
parentheses must not be stripped if there are any unparenthesized composite literals starting with a type name
		ast.Inspect(.X, func( ast.Node) bool {
			switch x := .(type) {
parentheses protect enclosed composite literals
				return false
			case *ast.CompositeLit:
				if isTypeName(.Type) {
					 = false // do not strip parentheses
				}
				return false
in all other cases, keep inspecting
			return true
		})
		if  {
			return (.X)
		}
	}
	return 
}

func ( ast.Expr) ast.Expr {
	if ,  := .(*ast.ParenExpr);  {
		return (.X)
	}
	return 
}

func ( *printer) ( bool,  ast.Stmt,  ast.Expr,  ast.Stmt) {
	.print(blank)
	 := false
no semicolons required
		if  != nil {
			.expr(stripParens())
			 = true
		}
all semicolons required (they are not separators, print them explicitly)
		if  != nil {
			.stmt(, false)
		}
		.print(token.SEMICOLON, blank)
		if  != nil {
			.expr(stripParens())
			 = true
		}
		if  {
			.print(token.SEMICOLON, blank)
			 = false
			if  != nil {
				.stmt(, false)
				 = true
			}
		}
	}
	if  {
		.print(blank)
	}
}
indentList reports whether an expression list would look better if it were indented wholesale (starting with the very first element, rather than starting at the first line break).
Heuristic: indentList reports whether there are more than one multi- line element in the list, or if there is any element that is not starting on the same line as the previous one ends.
	if len() >= 2 {
		var  = .lineFor([0].Pos())
		var  = .lineFor([len()-1].End())
list spans multiple lines
			 := 0 // multi-line element count
			 := 
			for ,  := range  {
				 := .lineFor(.Pos())
				 := .lineFor(.End())
x is not starting on the same line as the previous one ended
					return true
				}
x is a multi-line element
					++
				}
				 = 
			}
			return  > 1
		}
	}
	return false
}

func ( *printer) ( ast.Stmt,  bool) {
	.print(.Pos())

	switch s := .(type) {
	case *ast.BadStmt:
		.print("BadStmt")

	case *ast.DeclStmt:
		.decl(.Decl)

nothing to do

a "correcting" unindent immediately following a line break is applied before the line break if there is no comment between (see writeWhitespace)
		.print(unindent)
		.expr(.Label)
		.print(.Colon, token.COLON, indent)
		if ,  := .Stmt.(*ast.EmptyStmt);  {
			if ! {
				.print(newline, .Pos(), token.SEMICOLON)
				break
			}
		} else {
			.linebreak(.lineFor(.Stmt.Pos()), 1, ignore, true)
		}
		.(.Stmt, )

	case *ast.ExprStmt:
		const  = 1
		.expr0(.X, )

	case *ast.SendStmt:
		const  = 1
		.expr0(.Chan, )
		.print(blank, .Arrow, token.ARROW, blank)
		.expr0(.Value, )

	case *ast.IncDecStmt:
		const  = 1
		.expr0(.X, +1)
		.print(.TokPos, .Tok)

	case *ast.AssignStmt:
		var  = 1
		if len(.Lhs) > 1 && len(.Rhs) > 1 {
			++
		}
		.exprList(.Pos(), .Lhs, , 0, .TokPos, false)
		.print(blank, .TokPos, .Tok, blank)
		.exprList(.TokPos, .Rhs, , 0, token.NoPos, false)

	case *ast.GoStmt:
		.print(token.GO, blank)
		.expr(.Call)

	case *ast.DeferStmt:
		.print(token.DEFER, blank)
		.expr(.Call)

	case *ast.ReturnStmt:
		.print(token.RETURN)
		if .Results != nil {
Use indentList heuristic to make corner cases look better (issue 1207). A more systematic approach would always indent, but this would cause significant reformatting of the code base and not necessarily lead to more nicely formatted code in general.
			if .indentList(.Results) {
Use NoPos so that a newline never goes before the results (see issue #32854).
				.exprList(token.NoPos, .Results, 1, noIndent, token.NoPos, false)
				.print(unindent)
			} else {
				.exprList(token.NoPos, .Results, 1, 0, token.NoPos, false)
			}
		}

	case *ast.BranchStmt:
		.print(.Tok)
		if .Label != nil {
			.print(blank)
			.expr(.Label)
		}

	case *ast.BlockStmt:
		.block(, 1)

	case *ast.IfStmt:
		.print(token.IF)
		.controlClause(false, .Init, .Cond, nil)
		.block(.Body, 1)
		if .Else != nil {
			.print(blank, token.ELSE, blank)
			switch .Else.(type) {
			case *ast.BlockStmt, *ast.IfStmt:
				.(.Else, )
This can only happen with an incorrectly constructed AST. Permit it but print so that it can be parsed without errors.
				.print(token.LBRACE, indent, formfeed)
				.(.Else, true)
				.print(unindent, formfeed, token.RBRACE)
			}
		}

	case *ast.CaseClause:
		if .List != nil {
			.print(token.CASE, blank)
			.exprList(.Pos(), .List, 1, 0, .Colon, false)
		} else {
			.print(token.DEFAULT)
		}
		.print(.Colon, token.COLON)
		.stmtList(.Body, 1, )

	case *ast.SwitchStmt:
		.print(token.SWITCH)
		.controlClause(false, .Init, .Tag, nil)
		.block(.Body, 0)

	case *ast.TypeSwitchStmt:
		.print(token.SWITCH)
		if .Init != nil {
			.print(blank)
			.(.Init, false)
			.print(token.SEMICOLON)
		}
		.print(blank)
		.(.Assign, false)
		.print(blank)
		.block(.Body, 0)

	case *ast.CommClause:
		if .Comm != nil {
			.print(token.CASE, blank)
			.(.Comm, false)
		} else {
			.print(token.DEFAULT)
		}
		.print(.Colon, token.COLON)
		.stmtList(.Body, 1, )

	case *ast.SelectStmt:
		.print(token.SELECT, blank)
		 := .Body
print empty select statement w/o comments on one line
			.print(.Lbrace, token.LBRACE, .Rbrace, token.RBRACE)
		} else {
			.block(, 0)
		}

	case *ast.ForStmt:
		.print(token.FOR)
		.controlClause(true, .Init, .Cond, .Post)
		.block(.Body, 1)

	case *ast.RangeStmt:
		.print(token.FOR, blank)
		if .Key != nil {
			.expr(.Key)
use position of value following the comma as comma position for correct comment placement
				.print(.Value.Pos(), token.COMMA, blank)
				.expr(.Value)
			}
			.print(blank, .TokPos, .Tok, blank)
		}
		.print(token.RANGE, blank)
		.expr(stripParens(.X))
		.print(blank)
		.block(.Body, 1)

	default:
		panic("unreachable")
	}
}
---------------------------------------------------------------------------- Declarations
The keepTypeColumn function determines if the type column of a series of consecutive const or var declarations must be kept, or if initialization values (V) can be placed in the type column (T) instead. The i'th entry in the result slice is true if the type column in spec[i] must be kept. For example, the declaration: const ( foobar int = 42 // comment x = 7 // comment foo bar = 991 ) leads to the type/values matrix below. A run of value columns (V) can be moved into the type column if there is no type for any of the values in that column (we only move entire columns so that they align properly). matrix formatted result matrix T V -> T V -> true there is a T and so the type - V - V true column must be kept - - - - false - V V - false V is moved into T column
func ( []ast.Spec) []bool {
	 := make([]bool, len())

	 := func(,  int,  bool) {
		if  {
			for ;  < ; ++ {
				[] = true
			}
		}
	}

	 := -1 // if i0 >= 0 we are in a run and i0 is the start of the run
	var  bool
	for ,  := range  {
		 := .(*ast.ValueSpec)
		if .Values != nil {
start of a run of ValueSpecs with non-nil Values
				 = 
				 = false
			}
		} else {
end of a run
				(, , )
				 = -1
			}
		}
		if .Type != nil {
			 = true
		}
	}
end of a run
		(, len(), )
	}

	return 
}

func ( *printer) ( *ast.ValueSpec,  bool) {
	.setComment(.Doc)
	.identList(.Names, false) // always present
	 := 3
	if .Type != nil ||  {
		.print(vtab)
		--
	}
	if .Type != nil {
		.expr(.Type)
	}
	if .Values != nil {
		.print(vtab, token.ASSIGN, blank)
		.exprList(token.NoPos, .Values, 1, 0, token.NoPos, false)
		--
	}
	if .Comment != nil {
		for ;  > 0; -- {
			.print(vtab)
		}
		.setComment(.Comment)
	}
}

Note: An unmodified AST generated by go/parser will already contain a backward- or double-quoted path string that does not contain any invalid characters, and most of the work here is not needed. However, a modified or generated AST may possibly contain non-canonical paths. Do the work in all cases since it's not too hard and not speed-critical.
if we don't have a proper string, be conservative and return whatever we have
	if .Kind != token.STRING {
		return 
	}
	,  := strconv.Unquote(.Value)
	if  != nil {
		return 
	}
if the string is an invalid path, return whatever we have spec: "Implementation restriction: A compiler may restrict ImportPaths to non-empty strings using only characters belonging to Unicode's L, M, N, P, and S general categories (the Graphic characters without spaces) and may also exclude the characters !"#$%&'()*,:;<=>?[\]^`{|} and the Unicode replacement character U+FFFD."
	if  == "" {
		return 
	}
	const  = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD"
	for ,  := range  {
		if !unicode.IsGraphic() || unicode.IsSpace() || strings.ContainsRune(, ) {
			return 
		}
	}
otherwise, return the double-quoted path
	 = strconv.Quote()
	if  == .Value {
		return  // nothing wrong with lit
	}
	return &ast.BasicLit{ValuePos: .ValuePos, Kind: token.STRING, Value: }
}
The parameter n is the number of specs in the group. If doIndent is set, multi-line identifier lists in the spec are indented when the first linebreak is encountered.
func ( *printer) ( ast.Spec,  int,  bool) {
	switch s := .(type) {
	case *ast.ImportSpec:
		.setComment(.Doc)
		if .Name != nil {
			.expr(.Name)
			.print(blank)
		}
		.expr(sanitizeImportPath(.Path))
		.setComment(.Comment)
		.print(.EndPos)

	case *ast.ValueSpec:
		if  != 1 {
			.internalError("expected n = 1; got", )
		}
		.setComment(.Doc)
		.identList(.Names, ) // always present
		if .Type != nil {
			.print(blank)
			.expr(.Type)
		}
		if .Values != nil {
			.print(blank, token.ASSIGN, blank)
			.exprList(token.NoPos, .Values, 1, 0, token.NoPos, false)
		}
		.setComment(.Comment)

	case *ast.TypeSpec:
		.setComment(.Doc)
		.expr(.Name)
		if  == 1 {
			.print(blank)
		} else {
			.print(vtab)
		}
		if .Assign.IsValid() {
			.print(token.ASSIGN, blank)
		}
		.expr(.Type)
		.setComment(.Comment)

	default:
		panic("unreachable")
	}
}

func ( *printer) ( *ast.GenDecl) {
	.setComment(.Doc)
	.print(.Pos(), .Tok, blank)

group of parenthesized declarations
		.print(.Lparen, token.LPAREN)
		if  := len(.Specs);  > 0 {
			.print(indent, formfeed)
two or more grouped const/var declarations: determine if the type column must be kept
				 := keepTypeColumn(.Specs)
				var  int
				for ,  := range .Specs {
					if  > 0 {
						.linebreak(.lineFor(.Pos()), 1, ignore, .linesFrom() > 0)
					}
					.recordLine(&)
					.valueSpec(.(*ast.ValueSpec), [])
				}
			} else {
				var  int
				for ,  := range .Specs {
					if  > 0 {
						.linebreak(.lineFor(.Pos()), 1, ignore, .linesFrom() > 0)
					}
					.recordLine(&)
					.spec(, , false)
				}
			}
			.print(unindent, formfeed)
		}
		.print(.Rparen, token.RPAREN)

single declaration
		.spec(.Specs[0], 1, true)
	}
}
nodeSize determines the size of n in chars after formatting. The result is <= maxSize if the node fits on one line with at most maxSize chars and the formatted output doesn't contain any control chars. Otherwise, the result is > maxSize.
nodeSize invokes the printer, which may invoke nodeSize recursively. For deep composite literal nests, this can lead to an exponential algorithm. Remember previous results to prune the recursion (was issue 1628).
	if ,  := .nodeSizes[];  {
		return 
	}

	 =  + 1 // assume n doesn't fit
	.nodeSizes[] = 
nodeSize computation must be independent of particular style so that we always get the same decision; print in RawFormat
	 := Config{Mode: RawFormat}
	var  bytes.Buffer
	if  := .fprint(&, .fset, , .nodeSizes);  != nil {
		return
	}
	if .Len() <=  {
		for ,  := range .Bytes() {
			if  < ' ' {
				return
			}
		}
		 = .Len() // n fits
		.nodeSizes[] = 
	}
	return
}
numLines returns the number of lines spanned by node n in the original source.
func ( *printer) ( ast.Node) int {
	if  := .Pos(); .IsValid() {
		if  := .End(); .IsValid() {
			return .lineFor() - .lineFor() + 1
		}
	}
	return infinity
}
bodySize is like nodeSize but it is specialized for *ast.BlockStmt's.
func ( *printer) ( *ast.BlockStmt,  int) int {
	 := .Pos()
	 := .Rbrace
opening and closing brace are on different lines - don't make it a one-liner
		return  + 1
	}
too many statements - don't make it a one-liner
		return  + 1
otherwise, estimate body size
	 := .commentSizeBefore(.posFor())
	for ,  := range .List {
		if  >  {
			break // no need to continue
		}
		if  > 0 {
			 += 2 // space for a semicolon and blank
		}
		 += .nodeSize(, )
	}
	return 
}
funcBody prints a function body following a function header of given headerSize. If the header's and block's size are "small enough" and the block is "simple enough", the block is printed on the current line, without line breaks, spaced from the header by sep. Otherwise the block's opening "{" is printed on the current line, followed by lines for the block's statements and its closing "}".
func ( *printer) ( int,  whiteSpace,  *ast.BlockStmt) {
	if  == nil {
		return
	}
save/restore composite literal nesting level
	defer func( int) {
		.level = 
	}(.level)
	.level = 0

	const  = 100
	if +.bodySize(, ) <=  {
		.print(, .Lbrace, token.LBRACE)
		if len(.List) > 0 {
			.print(blank)
			for ,  := range .List {
				if  > 0 {
					.print(token.SEMICOLON, blank)
				}
				.stmt(,  == len(.List)-1)
			}
			.print(blank)
		}
		.print(noExtraLinebreak, .Rbrace, token.RBRACE, noExtraLinebreak)
		return
	}

	if  != ignore {
		.print(blank) // always use blank
	}
	.block(, 1)
}
distanceFrom returns the column difference between p.out (the current output position) and startOutCol. If the start position is on a different line from the current position (or either is unknown), the result is infinity.
func ( *printer) ( token.Pos,  int) int {
	if .IsValid() && .pos.IsValid() && .posFor().Line == .pos.Line {
		return .out.Column - 
	}
	return infinity
}

func ( *printer) ( *ast.FuncDecl) {
	.setComment(.Doc)
We have to save startCol only after emitting FUNC; otherwise it can be on a different line (all whitespace preceding the FUNC is emitted only when the FUNC is emitted).
	 := .out.Column - len("func ")
	if .Recv != nil {
		.parameters(.Recv) // method: print receiver
		.print(blank)
	}
	.expr(.Name)
	.signature(.Type.Params, .Type.Results)
	.funcBody(.distanceFrom(.Pos(), ), vtab, .Body)
}

func ( *printer) ( ast.Decl) {
	switch d := .(type) {
	case *ast.BadDecl:
		.print(.Pos(), "BadDecl")
	case *ast.GenDecl:
		.genDecl()
	case *ast.FuncDecl:
		.funcDecl()
	default:
		panic("unreachable")
	}
}
---------------------------------------------------------------------------- Files

func ( ast.Decl) ( token.Token) {
	 = token.ILLEGAL
	switch d := .(type) {
	case *ast.GenDecl:
		 = .Tok
	case *ast.FuncDecl:
		 = token.FUNC
	}
	return
}

func ( *printer) ( []ast.Decl) {
	 := token.ILLEGAL
	for ,  := range  {
		 := 
If the declaration token changed (e.g., from CONST to TYPE) or the next declaration has documentation associated with it, print an empty line between top-level declarations. (because p.linebreak is called with the position of d, which is past any documentation, the minimum requirement is satisfied even w/o the extra getDoc(d) nil-check - leave it in case the linebreak logic improves - there's already a TODO).
only print line break if we are not at the beginning of the output (i.e., we are not printing only a partial program)
			 := 1
			if  !=  || getDoc() != nil {
				 = 2
start a new section if the next declaration is a function that spans multiple lines (see also issue #19544)
			.linebreak(.lineFor(.Pos()), , ignore,  == token.FUNC && .numLines() > 1)
		}
		.decl()
	}
}

func ( *printer) ( *ast.File) {
	.setComment(.Doc)
	.print(.Pos(), token.PACKAGE, blank)
	.expr(.Name)
	.declList(.Decls)
	.print(newline)