OSDN Git Service

libgo: Update to weekly.2011-12-06.
[pf3gnuchains/gcc-fork.git] / libgo / go / go / printer / nodes.go
index 8207996..b2a48c2 100644 (file)
@@ -14,7 +14,6 @@ import (
        "go/token"
 )
 
-
 // Other 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
@@ -23,7 +22,6 @@ import (
 // - 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.
 
@@ -33,7 +31,7 @@ import (
 // line break was printed; returns false otherwise.
 //
 // TODO(gri): linebreak may add too many lines if the next statement at "line"
-//            is preceeded by comments because the computation of n assumes
+//            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
@@ -56,7 +54,6 @@ func (p *printer) linebreak(line, min int, ws whiteSpace, newSection bool) (prin
        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 are no other pending comments to
@@ -78,7 +75,6 @@ func (p *printer) setComment(g *ast.CommentGroup) {
        p.cindex = 0
 }
 
-
 type exprListMode uint
 
 const (
@@ -90,7 +86,6 @@ const (
        periodSep                           // elements are separated by periods
 )
 
-
 // Sets multiLine to true if the identifier list spans multiple lines.
 // If indent is set, a multi-line identifier list is indented after the
 // first linebreak encountered.
@@ -107,18 +102,6 @@ func (p *printer) identList(list []*ast.Ident, indent bool, multiLine *bool) {
        p.exprList(token.NoPos, xlist, 1, mode, multiLine, token.NoPos)
 }
 
-
-// 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
@@ -171,19 +154,7 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp
        // the first linebreak is always a formfeed since this section must not
        // depend on any previous formatting
        prevBreak := -1 // index of last expression that was followed by a linebreak
-       linebreakMin := 1
-       if mode&periodSep != 0 {
-               // Make fragments like
-               //
-               // a.Bar(1,
-               //   2).Foo
-               //
-               // format correctly (a linebreak shouldn't be added before Foo) when
-               // doing period-separated expr lists by setting minimum linebreak to 0
-               // lines for them.
-               linebreakMin = 0
-       }
-       if prev.IsValid() && prev.Line < line && p.linebreak(line, linebreakMin, ws, true) {
+       if prev.IsValid() && prev.Line < line && p.linebreak(line, 0, ws, true) {
                ws = ignore
                *multiLine = true
                prevBreak = 0
@@ -204,17 +175,21 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp
                //           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
                }
 
@@ -234,26 +209,27 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp
                }
 
                if i > 0 {
-                       if mode&commaSep != 0 {
+                       switch {
+                       case mode&commaSep != 0:
                                p.print(token.COMMA)
-                       }
-                       if mode&periodSep != 0 {
+                       case mode&periodSep != 0:
                                p.print(token.PERIOD)
                        }
+                       needsBlank := mode&periodSep == 0 // period-separated list elements don't need a blank
                        if prevLine < line && prevLine > 0 && line > 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) {
+                               if p.linebreak(line, 0, ws, useFF || prevBreak+1 < i) {
                                        ws = ignore
                                        *multiLine = true
                                        prevBreak = i
+                                       needsBlank = false // we got a line break instead
                                }
-                       } else if mode&periodSep == 0 {
+                       }
+                       if needsBlank {
                                p.print(blank)
                        }
-                       // period-separated list elements don't need a blank
                }
 
                if isPair && size > 0 && len(list) > 1 {
@@ -289,11 +265,11 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp
        }
 }
 
-
 // Sets multiLine to true if the the parameter list spans multiple lines.
 func (p *printer) parameters(fields *ast.FieldList, multiLine *bool) {
        p.print(fields.Opening, token.LPAREN)
        if len(fields.List) > 0 {
+               ws := indent
                var prevLine, line int
                for i, par := range fields.List {
                        if i > 0 {
@@ -303,24 +279,34 @@ func (p *printer) parameters(fields *ast.FieldList, multiLine *bool) {
                                } else {
                                        line = p.fset.Position(par.Type.Pos()).Line
                                }
-                               if 0 < prevLine && prevLine < line && p.linebreak(line, 0, ignore, true) {
+                               if 0 < prevLine && prevLine < line && p.linebreak(line, 0, ws, true) {
+                                       ws = ignore
                                        *multiLine = true
                                } else {
                                        p.print(blank)
                                }
                        }
                        if len(par.Names) > 0 {
-                               p.identList(par.Names, false, multiLine)
+                               // 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.
+                               p.identList(par.Names, ws == indent, multiLine)
                                p.print(blank)
                        }
                        p.expr(par.Type, multiLine)
                        prevLine = p.fset.Position(par.Type.Pos()).Line
                }
+               if ws == ignore {
+                       // unindent if we indented
+                       p.print(unindent)
+               }
        }
        p.print(fields.Closing, token.RPAREN)
 }
 
-
 // Sets multiLine to true if the signature spans multiple lines.
 func (p *printer) signature(params, result *ast.FieldList, multiLine *bool) {
        p.parameters(params, multiLine)
@@ -336,7 +322,6 @@ func (p *printer) signature(params, result *ast.FieldList, multiLine *bool) {
        }
 }
 
-
 func identListSize(list []*ast.Ident, maxSize int) (size int) {
        for i, x := range list {
                if i > 0 {
@@ -350,7 +335,6 @@ func identListSize(list []*ast.Ident, maxSize int) (size int) {
        return
 }
 
-
 func (p *printer) isOneLineFieldList(list []*ast.Field) bool {
        if len(list) != 1 {
                return false // allow only one field
@@ -369,30 +353,23 @@ func (p *printer) isOneLineFieldList(list []*ast.Field) bool {
        return namesSize+typeSize <= maxSize
 }
 
-
 func (p *printer) setLineComment(text string) {
-       p.setComment(&ast.CommentGroup{[]*ast.Comment{&ast.Comment{token.NoPos, []byte(text)}}})
+       p.setComment(&ast.CommentGroup{[]*ast.Comment{{token.NoPos, text}}})
 }
 
-
-func (p *printer) fieldList(fields *ast.FieldList, isIncomplete bool, ctxt exprContext) {
-       p.nesting++
-       defer func() {
-               p.nesting--
-       }()
-
+func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool) {
        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)
@@ -414,7 +391,7 @@ func (p *printer) fieldList(fields *ast.FieldList, isIncomplete bool, ctxt exprC
 
        // 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 {
@@ -458,8 +435,8 @@ func (p *printer) fieldList(fields *ast.FieldList, isIncomplete bool, ctxt exprC
                        if len(list) > 0 {
                                p.print(formfeed)
                        }
-                       p.flush(p.fset.Position(rbrace), token.RBRACE) // make sure we don't loose the last line comment
-                       p.setLineComment("// contains unexported fields")
+                       p.flush(p.fset.Position(rbrace), token.RBRACE) // make sure we don't lose the last line comment
+                       p.setLineComment("// contains filtered or unexported fields")
                }
 
        } else { // interface
@@ -485,33 +462,23 @@ func (p *printer) fieldList(fields *ast.FieldList, isIncomplete bool, ctxt exprC
                        if len(list) > 0 {
                                p.print(formfeed)
                        }
-                       p.flush(p.fset.Position(rbrace), token.RBRACE) // make sure we don't loose the last line comment
-                       p.setLineComment("// contains unexported methods")
+                       p.flush(p.fset.Position(rbrace), token.RBRACE) // make sure we don't lose the last line comment
+                       p.setLineComment("// contains filtered or unexported methods")
                }
 
        }
        p.print(unindent, formfeed, rbrace, token.RBRACE)
 }
 
-
 // ----------------------------------------------------------------------------
 // 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) (has5, has6 bool, maxProblem int) {
+func walkBinary(e *ast.BinaryExpr) (has4, has5 bool, maxProblem int) {
        switch e.Op.Precedence() {
+       case 4:
+               has4 = true
        case 5:
                has5 = true
-       case 6:
-               has6 = true
        }
 
        switch l := e.X.(type) {
@@ -521,9 +488,9 @@ func walkBinary(e *ast.BinaryExpr) (has5, has6 bool, maxProblem int) {
                        // pretend this is an *ast.ParenExpr and do nothing.
                        break
                }
-               h5, h6, mp := walkBinary(l)
+               h4, h5, mp := walkBinary(l)
+               has4 = has4 || h4
                has5 = has5 || h5
-               has6 = has6 || h6
                if maxProblem < mp {
                        maxProblem = mp
                }
@@ -536,50 +503,48 @@ func walkBinary(e *ast.BinaryExpr) (has5, has6 bool, maxProblem int) {
                        // pretend this is an *ast.ParenExpr and do nothing.
                        break
                }
-               h5, h6, mp := walkBinary(r)
+               h4, h5, mp := walkBinary(r)
+               has4 = has4 || h4
                has5 = has5 || h5
-               has6 = has6 || h6
                if maxProblem < mp {
                        maxProblem = mp
                }
 
        case *ast.StarExpr:
-               if e.Op.String() == "/" {
-                       maxProblem = 6
+               if e.Op == token.QUO { // `*/`
+                       maxProblem = 5
                }
 
        case *ast.UnaryExpr:
                switch e.Op.String() + r.Op.String() {
                case "/*", "&&", "&^":
-                       maxProblem = 6
+                       maxProblem = 5
                case "++", "--":
-                       if maxProblem < 5 {
-                               maxProblem = 5
+                       if maxProblem < 4 {
+                               maxProblem = 4
                        }
                }
        }
        return
 }
 
-
 func cutoff(e *ast.BinaryExpr, depth int) int {
-       has5, has6, maxProblem := walkBinary(e)
+       has4, has5, maxProblem := walkBinary(e)
        if maxProblem > 0 {
                return maxProblem + 1
        }
-       if has5 && has6 {
+       if has4 && has5 {
                if depth == 1 {
-                       return 6
+                       return 5
                }
-               return 5
+               return 4
        }
        if depth == 1 {
-               return 7
+               return 6
        }
-       return 5
+       return 4
 }
 
-
 func diffPrec(expr ast.Expr, prec int) int {
        x, ok := expr.(*ast.BinaryExpr)
        if !ok || prec != x.Op.Precedence() {
@@ -588,7 +553,6 @@ func diffPrec(expr ast.Expr, prec int) int {
        return 0
 }
 
-
 func reduceDepth(depth int) int {
        depth--
        if depth < 1 {
@@ -597,21 +561,19 @@ func reduceDepth(depth int) int {
        return depth
 }
 
-
 // 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:
-//     6             *  /  %  <<  >>  &  &^
-//     5             +  -  |  ^
-//     4             ==  !=  <  <=  >  >=
-//     3             <-
+//     5             *  /  %  <<  >>  &  &^
+//     4             +  -  |  ^
+//     3             ==  !=  <  <=  >  >=
 //     2             &&
 //     1             ||
 //
-// The only decision is whether there will be spaces around levels 5 and 6.
-// There are never spaces at level 7 (unary), and always spaces at levels 4 and below.
+// 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:
@@ -619,21 +581,21 @@ func reduceDepth(depth int) int {
 //     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):
 //
-//             /*      7
-//             &&      7
-//             &^      7
-//             ++      6
-//             --      6
+//             /*      6
+//             &&      6
+//             &^      6
+//             ++      5
+//             --      5
 //
 //         (Comparison operators always have spaces around them.)
 //
-//     2) If there is a mix of level 6 and level 5 operators, then the cutoff
-//        is 6 (use spaces to distinguish precedence) in Normal mode
-//        and 5 (never use spaces) in Compact mode.
+//     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 5 operators or no level 6 operators, then the
-//        cutoff is 7 (always use spaces) in Normal mode
-//        and 5 (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.
 //
 // Sets multiLine to true if the binary expression spans multiple lines.
 func (p *printer) binaryExpr(x *ast.BinaryExpr, prec1, cutoff, depth int, multiLine *bool) {
@@ -651,7 +613,7 @@ func (p *printer) binaryExpr(x *ast.BinaryExpr, prec1, cutoff, depth int, multiL
        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)
        }
@@ -670,19 +632,17 @@ func (p *printer) binaryExpr(x *ast.BinaryExpr, prec1, cutoff, depth int, multiL
        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)
        }
 }
 
-
 func isBinary(expr ast.Expr) bool {
        _, ok := expr.(*ast.BinaryExpr)
        return ok
 }
 
-
 // If the expression contains one or more selector expressions, splits it into
 // two expressions at the rightmost period. Writes entire expr to suffix when
 // selector isn't found. Rewrites AST nodes for calls, index expressions and
@@ -722,7 +682,6 @@ func splitSelector(expr ast.Expr) (body, suffix ast.Expr) {
        return
 }
 
-
 // Convert an expression into an expression list split at the periods of
 // selector expressions.
 func selectorExprList(expr ast.Expr) (list []ast.Expr) {
@@ -741,9 +700,8 @@ func selectorExprList(expr ast.Expr) (list []ast.Expr) {
        return
 }
 
-
 // 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) {
@@ -793,7 +751,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int, ctxt exprContext, multi
                                // 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:
@@ -819,7 +777,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int, ctxt exprContext, multi
                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)
@@ -830,14 +788,14 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int, ctxt exprContext, multi
 
        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)
@@ -857,7 +815,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int, ctxt exprContext, multi
                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() {
@@ -868,7 +826,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int, ctxt exprContext, multi
        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)
@@ -893,7 +851,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int, ctxt exprContext, multi
 
        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)
@@ -901,7 +859,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int, ctxt exprContext, multi
 
        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)
@@ -928,19 +886,16 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int, ctxt exprContext, multi
        return
 }
 
-
 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)
 }
 
-
 // ----------------------------------------------------------------------------
 // Statements
 
@@ -965,7 +920,6 @@ func (p *printer) stmtList(list []ast.Stmt, _indent int, nextIsRBrace bool) {
        }
 }
 
-
 // block prints an *ast.BlockStmt; it always spans at least two lines.
 func (p *printer) block(s *ast.BlockStmt, indent int) {
        p.print(s.Pos(), token.LBRACE)
@@ -974,7 +928,6 @@ func (p *printer) block(s *ast.BlockStmt, indent int) {
        p.print(s.Rbrace, token.RBRACE)
 }
 
-
 func isTypeName(x ast.Expr) bool {
        switch t := x.(type) {
        case *ast.Ident:
@@ -985,7 +938,6 @@ func isTypeName(x ast.Expr) bool {
        return false
 }
 
-
 func stripParens(x ast.Expr) ast.Expr {
        if px, strip := x.(*ast.ParenExpr); strip {
                // parentheses must not be stripped if there are any
@@ -1012,7 +964,6 @@ func stripParens(x ast.Expr) ast.Expr {
        return x
 }
 
-
 func (p *printer) controlClause(isForStmt bool, init ast.Stmt, expr ast.Expr, post ast.Stmt) {
        p.print(blank)
        needsBlank := false
@@ -1047,7 +998,6 @@ func (p *printer) controlClause(isForStmt bool, init ast.Stmt, expr ast.Expr, po
        }
 }
 
-
 // Sets multiLine to true if the statements spans multiple lines.
 func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool, multiLine *bool) {
        p.print(stmt.Pos())
@@ -1083,6 +1033,12 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool, multiLine *bool) {
                const depth = 1
                p.expr0(s.X, depth, multiLine)
 
+       case *ast.SendStmt:
+               const depth = 1
+               p.expr0(s.Chan, depth, multiLine)
+               p.print(blank, s.Arrow, token.ARROW, blank)
+               p.expr0(s.Value, depth, multiLine)
+
        case *ast.IncDecStmt:
                const depth = 1
                p.expr0(s.X, depth+1, multiLine)
@@ -1140,9 +1096,9 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool, multiLine *bool) {
                }
 
        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)
                }
@@ -1155,16 +1111,6 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool, multiLine *bool) {
                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 {
@@ -1179,13 +1125,9 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool, multiLine *bool) {
                *multiLine = true
 
        case *ast.CommClause:
-               if s.Rhs != nil {
+               if s.Comm != nil {
                        p.print(token.CASE, blank)
-                       if s.Lhs != nil {
-                               p.expr(s.Lhs, multiLine)
-                               p.print(blank, s.Tok, blank)
-                       }
-                       p.expr(s.Rhs, multiLine)
+                       p.stmt(s.Comm, false, ignoreMultiLine)
                } else {
                        p.print(token.DEFAULT)
                }
@@ -1194,8 +1136,14 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool, multiLine *bool) {
 
        case *ast.SelectStmt:
                p.print(token.SELECT, blank)
-               p.block(s.Body, 0)
-               *multiLine = true
+               body := s.Body
+               if len(body.List) == 0 && !p.commentBefore(p.fset.Position(body.Rbrace)) {
+                       // print empty select statement w/o comments on one line
+                       p.print(body.Lbrace, token.LBRACE, body.Rbrace, token.RBRACE)
+               } else {
+                       p.block(body, 0)
+                       *multiLine = true
+               }
 
        case *ast.ForStmt:
                p.print(token.FOR)
@@ -1223,10 +1171,98 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool, multiLine *bool) {
        return
 }
 
-
 // ----------------------------------------------------------------------------
 // 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 keepTypeColumn(specs []ast.Spec) []bool {
+       m := make([]bool, len(specs))
+
+       populate := func(i, j int, keepType bool) {
+               if keepType {
+                       for ; i < j; i++ {
+                               m[i] = true
+                       }
+               }
+       }
+
+       i0 := -1 // if i0 >= 0 we are in a run and i0 is the start of the run
+       var keepType bool
+       for i, s := range specs {
+               t := s.(*ast.ValueSpec)
+               if t.Values != nil {
+                       if i0 < 0 {
+                               // start of a run of ValueSpecs with non-nil Values
+                               i0 = i
+                               keepType = false
+                       }
+               } else {
+                       if i0 >= 0 {
+                               // end of a run
+                               populate(i0, i, keepType)
+                               i0 = -1
+                       }
+               }
+               if t.Type != nil {
+                       keepType = true
+               }
+       }
+       if i0 >= 0 {
+               // end of a run
+               populate(i0, len(specs), keepType)
+       }
+
+       return m
+}
+
+func (p *printer) valueSpec(s *ast.ValueSpec, keepType, doIndent bool, multiLine *bool) {
+       p.setComment(s.Doc)
+       p.identList(s.Names, doIndent, multiLine) // always present
+       extraTabs := 3
+       if s.Type != nil || keepType {
+               p.print(vtab)
+               extraTabs--
+       }
+       if s.Type != nil {
+               p.expr(s.Type, multiLine)
+       }
+       if s.Values != nil {
+               p.print(vtab, token.ASSIGN)
+               p.exprList(token.NoPos, s.Values, 1, blankStart|commaSep, multiLine, token.NoPos)
+               extraTabs--
+       }
+       if s.Comment != nil {
+               for ; extraTabs > 0; extraTabs-- {
+                       p.print(vtab)
+               }
+               p.setComment(s.Comment)
+       }
+}
+
 // 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.
@@ -1238,44 +1274,27 @@ func (p *printer) spec(spec ast.Spec, n int, doIndent bool, multiLine *bool) {
                p.setComment(s.Doc)
                if s.Name != nil {
                        p.expr(s.Name, multiLine)
-                       p.print(vtab)
+                       p.print(blank)
                }
                p.expr(s.Path, multiLine)
                p.setComment(s.Comment)
+               p.print(s.EndPos)
 
        case *ast.ValueSpec:
+               if n != 1 {
+                       p.internalError("expected n = 1; got", n)
+               }
                p.setComment(s.Doc)
                p.identList(s.Names, doIndent, multiLine) // always present
-               if n == 1 {
-                       if s.Type != nil {
-                               p.print(blank)
-                               p.expr(s.Type, multiLine)
-                       }
-                       if s.Values != nil {
-                               p.print(blank, token.ASSIGN)
-                               p.exprList(token.NoPos, s.Values, 1, blankStart|commaSep, multiLine, token.NoPos)
-                       }
-                       p.setComment(s.Comment)
-
-               } else {
-                       extraTabs := 3
-                       if s.Type != nil {
-                               p.print(vtab)
-                               p.expr(s.Type, multiLine)
-                               extraTabs--
-                       }
-                       if s.Values != nil {
-                               p.print(vtab, token.ASSIGN)
-                               p.exprList(token.NoPos, s.Values, 1, blankStart|commaSep, multiLine, token.NoPos)
-                               extraTabs--
-                       }
-                       if s.Comment != nil {
-                               for ; extraTabs > 0; extraTabs-- {
-                                       p.print(vtab)
-                               }
-                               p.setComment(s.Comment)
-                       }
+               if s.Type != nil {
+                       p.print(blank)
+                       p.expr(s.Type, multiLine)
                }
+               if s.Values != nil {
+                       p.print(blank, token.ASSIGN)
+                       p.exprList(token.NoPos, s.Values, 1, blankStart|commaSep, multiLine, token.NoPos)
+               }
+               p.setComment(s.Comment)
 
        case *ast.TypeSpec:
                p.setComment(s.Doc)
@@ -1293,7 +1312,6 @@ func (p *printer) spec(spec ast.Spec, n int, doIndent bool, multiLine *bool) {
        }
 }
 
-
 // Sets multiLine to true if the declaration spans multiple lines.
 func (p *printer) genDecl(d *ast.GenDecl, multiLine *bool) {
        p.setComment(d.Doc)
@@ -1302,15 +1320,29 @@ func (p *printer) genDecl(d *ast.GenDecl, multiLine *bool) {
        if d.Lparen.IsValid() {
                // group of parenthesized declarations
                p.print(d.Lparen, token.LPAREN)
-               if len(d.Specs) > 0 {
+               if n := len(d.Specs); n > 0 {
                        p.print(indent, formfeed)
-                       var ml bool
-                       for i, s := range d.Specs {
-                               if i > 0 {
-                                       p.linebreak(p.fset.Position(s.Pos()).Line, 1, ignore, ml)
+                       if n > 1 && (d.Tok == token.CONST || d.Tok == token.VAR) {
+                               // two or more grouped const/var declarations:
+                               // determine if the type column must be kept
+                               keepType := keepTypeColumn(d.Specs)
+                               var ml bool
+                               for i, s := range d.Specs {
+                                       if i > 0 {
+                                               p.linebreak(p.fset.Position(s.Pos()).Line, 1, ignore, ml)
+                                       }
+                                       ml = false
+                                       p.valueSpec(s.(*ast.ValueSpec), keepType[i], false, &ml)
+                               }
+                       } else {
+                               var ml bool
+                               for i, s := range d.Specs {
+                                       if i > 0 {
+                                               p.linebreak(p.fset.Position(s.Pos()).Line, 1, ignore, ml)
+                                       }
+                                       ml = false
+                                       p.spec(s, n, false, &ml)
                                }
-                               ml = false
-                               p.spec(s, len(d.Specs), false, &ml)
                        }
                        p.print(unindent, formfeed)
                        *multiLine = true
@@ -1323,20 +1355,29 @@ func (p *printer) genDecl(d *ast.GenDecl, multiLine *bool) {
        }
 }
 
-
 // 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.
 //
 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
-       // nodeSize computation must be indendent of particular
+       p.nodeSizes[n] = size
+
+       // nodeSize computation must be independent 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 {
@@ -1346,11 +1387,11 @@ func (p *printer) nodeSize(n ast.Node, maxSize int) (size int) {
                        }
                }
                size = buf.Len() // n fits
+               p.nodeSizes[n] = size
        }
        return
 }
 
-
 func (p *printer) isOneLineFunc(b *ast.BlockStmt, headerSize int) bool {
        pos1 := b.Pos()
        pos2 := b.Rbrace
@@ -1374,18 +1415,12 @@ func (p *printer) isOneLineFunc(b *ast.BlockStmt, headerSize int) bool {
        return headerSize+bodySize <= maxSize
 }
 
-
 // Sets multiLine to true if the function body spans multiple lines.
 func (p *printer) funcBody(b *ast.BlockStmt, headerSize int, isLit bool, multiLine *bool) {
        if b == nil {
                return
        }
 
-       p.nesting++
-       defer func() {
-               p.nesting--
-       }()
-
        if p.isOneLineFunc(b, headerSize) {
                sep := vtab
                if isLit {
@@ -1411,7 +1446,6 @@ func (p *printer) funcBody(b *ast.BlockStmt, headerSize int, isLit bool, multiLi
        *multiLine = true
 }
 
-
 // distance returns the column difference between from and to if both
 // are on the same line; if they are on different lines (or unknown)
 // the result is infinity.
@@ -1423,7 +1457,6 @@ func (p *printer) distance(from0 token.Pos, to token.Position) int {
        return infinity
 }
 
-
 // Sets multiLine to true if the declaration spans multiple lines.
 func (p *printer) funcDecl(d *ast.FuncDecl, multiLine *bool) {
        p.setComment(d.Doc)
@@ -1437,7 +1470,6 @@ func (p *printer) funcDecl(d *ast.FuncDecl, multiLine *bool) {
        p.funcBody(d.Body, p.distance(d.Pos(), p.pos), false, multiLine)
 }
 
-
 // Sets multiLine to true if the declaration spans multiple lines.
 func (p *printer) decl(decl ast.Decl, multiLine *bool) {
        switch d := decl.(type) {
@@ -1452,7 +1484,6 @@ func (p *printer) decl(decl ast.Decl, multiLine *bool) {
        }
 }
 
-
 // ----------------------------------------------------------------------------
 // Files
 
@@ -1467,7 +1498,6 @@ func declToken(decl ast.Decl) (tok token.Token) {
        return
 }
 
-
 func (p *printer) file(src *ast.File) {
        p.setComment(src.Doc)
        p.print(src.Pos(), token.PACKAGE, blank)