The Go Programming Language

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

release.r60.3. Except as noted, this content is licensed under a Creative Commons Attribution 3.0 License.