}
-// Compute the key size of a key:value expression.
-// Returns 0 if the expression doesn't fit onto a single line.
-func (p *printer) keySize(pair *ast.KeyValueExpr) int {
- if p.nodeSize(pair, infinity) <= infinity {
- // entire expression fits on one line - return key size
- return p.nodeSize(pair.Key, infinity)
- }
- return 0
-}
-
-
// Print a list of expressions. If the list spans multiple
// source lines, the original line breaks are respected between
// expressions. Sets multiLine to true if the list spans multiple
// the key and the node size into the decision process
useFF := true
- // determine size
+ // 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)
prevSize := size
const infinity = 1e6 // larger than any source line
size = p.nodeSize(x, infinity)
pair, isPair := x.(*ast.KeyValueExpr)
- if size <= infinity {
+ if size <= infinity && prev.IsValid() && next.IsValid() {
// x fits on a single line
if isPair {
size = p.nodeSize(pair.Key, infinity) // size <= infinity
}
} else {
+ // size too large or we don't have good layout information
size = 0
}
// lines are broken using newlines so comments remain aligned
// unless forceFF is set or there are multiple expressions on
// the same line in which case formfeed is used
- // broken with a formfeed
if p.linebreak(line, linebreakMin, ws, useFF || prevBreak+1 < i) {
ws = ignore
*multiLine = true
}
-func (p *printer) fieldList(fields *ast.FieldList, isIncomplete bool, ctxt exprContext) {
+func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool) {
p.nesting++
defer func() {
p.nesting--
lbrace := fields.Opening
list := fields.List
rbrace := fields.Closing
+ srcIsOneLine := lbrace.IsValid() && rbrace.IsValid() && p.fset.Position(lbrace).Line == p.fset.Position(rbrace).Line
- if !isIncomplete && !p.commentBefore(p.fset.Position(rbrace)) {
+ if !isIncomplete && !p.commentBefore(p.fset.Position(rbrace)) && srcIsOneLine {
// possibly a one-line struct/interface
if len(list) == 0 {
// no blank between keyword and {} in this case
p.print(lbrace, token.LBRACE, rbrace, token.RBRACE)
return
- } else if ctxt&(compositeLit|structType) == compositeLit|structType &&
- p.isOneLineFieldList(list) { // for now ignore interfaces
+ } else if isStruct && p.isOneLineFieldList(list) { // for now ignore interfaces
// small enough - print on one line
// (don't use identList and ignore source line breaks)
p.print(lbrace, token.LBRACE, blank)
// at least one entry or incomplete
p.print(blank, lbrace, token.LBRACE, indent, formfeed)
- if ctxt&structType != 0 {
+ if isStruct {
sep := vtab
if len(list) == 1 {
// ----------------------------------------------------------------------------
// Expressions
-// exprContext describes the syntactic environment in which an expression node is printed.
-type exprContext uint
-
-const (
- compositeLit exprContext = 1 << iota
- structType
-)
-
-
func walkBinary(e *ast.BinaryExpr) (has4, has5 bool, maxProblem int) {
switch e.Op.Precedence() {
case 4:
printBlank := prec < cutoff
ws := indent
- p.expr1(x.X, prec, depth+diffPrec(x.X, prec), 0, multiLine)
+ p.expr1(x.X, prec, depth+diffPrec(x.X, prec), multiLine)
if printBlank {
p.print(blank)
}
if printBlank {
p.print(blank)
}
- p.expr1(x.Y, prec+1, depth+1, 0, multiLine)
+ p.expr1(x.Y, prec+1, depth+1, multiLine)
if ws == ignore {
p.print(unindent)
}
// Sets multiLine to true if the expression spans multiple lines.
-func (p *printer) expr1(expr ast.Expr, prec1, depth int, ctxt exprContext, multiLine *bool) {
+func (p *printer) expr1(expr ast.Expr, prec1, depth int, multiLine *bool) {
p.print(expr.Pos())
switch x := expr.(type) {
// TODO(gri) Remove this code if it cannot be reached.
p.print(blank)
}
- p.expr1(x.X, prec, depth, 0, multiLine)
+ p.expr1(x.X, prec, depth, multiLine)
}
case *ast.BasicLit:
p.exprList(token.NoPos, parts, depth, periodSep, multiLine, token.NoPos)
case *ast.TypeAssertExpr:
- p.expr1(x.X, token.HighestPrec, depth, 0, multiLine)
+ p.expr1(x.X, token.HighestPrec, depth, multiLine)
p.print(token.PERIOD, token.LPAREN)
if x.Type != nil {
p.expr(x.Type, multiLine)
case *ast.IndexExpr:
// TODO(gri): should treat[] like parentheses and undo one level of depth
- p.expr1(x.X, token.HighestPrec, 1, 0, multiLine)
+ p.expr1(x.X, token.HighestPrec, 1, multiLine)
p.print(x.Lbrack, token.LBRACK)
p.expr0(x.Index, depth+1, multiLine)
p.print(x.Rbrack, token.RBRACK)
case *ast.SliceExpr:
// TODO(gri): should treat[] like parentheses and undo one level of depth
- p.expr1(x.X, token.HighestPrec, 1, 0, multiLine)
+ p.expr1(x.X, token.HighestPrec, 1, multiLine)
p.print(x.Lbrack, token.LBRACK)
if x.Low != nil {
p.expr0(x.Low, depth+1, multiLine)
if len(x.Args) > 1 {
depth++
}
- p.expr1(x.Fun, token.HighestPrec, depth, 0, multiLine)
+ p.expr1(x.Fun, token.HighestPrec, depth, multiLine)
p.print(x.Lparen, token.LPAREN)
p.exprList(x.Lparen, x.Args, depth, commaSep|commaTerm, multiLine, x.Rparen)
if x.Ellipsis.IsValid() {
case *ast.CompositeLit:
// composite literal elements that are composite literals themselves may have the type omitted
if x.Type != nil {
- p.expr1(x.Type, token.HighestPrec, depth, compositeLit, multiLine)
+ p.expr1(x.Type, token.HighestPrec, depth, multiLine)
}
p.print(x.Lbrace, token.LBRACE)
p.exprList(x.Lbrace, x.Elts, 1, commaSep|commaTerm, multiLine, x.Rbrace)
case *ast.StructType:
p.print(token.STRUCT)
- p.fieldList(x.Fields, x.Incomplete, ctxt|structType)
+ p.fieldList(x.Fields, true, x.Incomplete)
case *ast.FuncType:
p.print(token.FUNC)
case *ast.InterfaceType:
p.print(token.INTERFACE)
- p.fieldList(x.Methods, x.Incomplete, ctxt)
+ p.fieldList(x.Methods, false, x.Incomplete)
case *ast.MapType:
p.print(token.MAP, token.LBRACK)
func (p *printer) expr0(x ast.Expr, depth int, multiLine *bool) {
- p.expr1(x, token.LowestPrec, depth, 0, multiLine)
+ p.expr1(x, token.LowestPrec, depth, multiLine)
}
// Sets multiLine to true if the expression spans multiple lines.
func (p *printer) expr(x ast.Expr, multiLine *bool) {
const depth = 1
- p.expr1(x, token.LowestPrec, depth, 0, multiLine)
+ p.expr1(x, token.LowestPrec, depth, multiLine)
}
}
case *ast.CaseClause:
- if s.Values != nil {
+ if s.List != nil {
p.print(token.CASE)
- p.exprList(s.Pos(), s.Values, 1, blankStart|commaSep, multiLine, s.Colon)
+ p.exprList(s.Pos(), s.List, 1, blankStart|commaSep, multiLine, s.Colon)
} else {
p.print(token.DEFAULT)
}
p.block(s.Body, 0)
*multiLine = true
- case *ast.TypeCaseClause:
- if s.Types != nil {
- p.print(token.CASE)
- p.exprList(s.Pos(), s.Types, 1, blankStart|commaSep, multiLine, s.Colon)
- } else {
- p.print(token.DEFAULT)
- }
- p.print(s.Colon, token.COLON)
- p.stmtList(s.Body, 1, nextIsRBrace)
-
case *ast.TypeSwitchStmt:
p.print(token.SWITCH)
if s.Init != nil {
// any control chars. Otherwise, the result is > maxSize.
//
func (p *printer) nodeSize(n ast.Node, maxSize int) (size int) {
+ // 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 size, found := p.nodeSizes[n]; found {
+ return size
+ }
+
size = maxSize + 1 // assume n doesn't fit
+ p.nodeSizes[n] = size
+
// nodeSize computation must be indendent of particular
// style so that we always get the same decision; print
// in RawFormat
cfg := Config{Mode: RawFormat}
var buf bytes.Buffer
- if _, err := cfg.Fprint(&buf, p.fset, n); err != nil {
+ if _, err := cfg.fprint(&buf, p.fset, n, p.nodeSizes); err != nil {
return
}
if buf.Len() <= maxSize {
}
}
size = buf.Len() // n fits
+ p.nodeSizes[n] = size
}
return
}