OSDN Git Service

libgo: Update to weekly.2011-12-06.
[pf3gnuchains/gcc-fork.git] / libgo / go / go / printer / nodes.go
index 2238b6b..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,7 +102,6 @@ func (p *printer) identList(list []*ast.Ident, indent bool, multiLine *bool) {
        p.exprList(token.NoPos, xlist, 1, mode, multiLine, token.NoPos)
 }
 
-
 // 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
@@ -160,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
@@ -227,25 +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
-                               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 {
@@ -281,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 {
@@ -295,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)
@@ -328,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 {
@@ -342,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
@@ -361,18 +353,11 @@ 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, isStruct, isIncomplete bool) {
-       p.nesting++
-       defer func() {
-               p.nesting--
-       }()
-
        lbrace := fields.Opening
        list := fields.List
        rbrace := fields.Closing
@@ -450,8 +435,8 @@ func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool)
                        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
@@ -477,15 +462,14 @@ func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool)
                        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
 
@@ -527,7 +511,7 @@ func walkBinary(e *ast.BinaryExpr) (has4, has5 bool, maxProblem int) {
                }
 
        case *ast.StarExpr:
-               if e.Op.String() == "/" {
+               if e.Op == token.QUO { // `*/`
                        maxProblem = 5
                }
 
@@ -544,7 +528,6 @@ func walkBinary(e *ast.BinaryExpr) (has4, has5 bool, maxProblem int) {
        return
 }
 
-
 func cutoff(e *ast.BinaryExpr, depth int) int {
        has4, has5, maxProblem := walkBinary(e)
        if maxProblem > 0 {
@@ -562,7 +545,6 @@ func cutoff(e *ast.BinaryExpr, depth int) int {
        return 4
 }
 
-
 func diffPrec(expr ast.Expr, prec int) int {
        x, ok := expr.(*ast.BinaryExpr)
        if !ok || prec != x.Op.Precedence() {
@@ -571,7 +553,6 @@ func diffPrec(expr ast.Expr, prec int) int {
        return 0
 }
 
-
 func reduceDepth(depth int) int {
        depth--
        if depth < 1 {
@@ -580,7 +561,6 @@ 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.)
@@ -658,13 +638,11 @@ func (p *printer) binaryExpr(x *ast.BinaryExpr, prec1, cutoff, depth int, multiL
        }
 }
 
-
 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
@@ -704,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) {
@@ -723,7 +700,6 @@ 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, multiLine *bool) {
        p.print(expr.Pos())
@@ -910,19 +886,16 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int, multiLine *bool) {
        return
 }
 
-
 func (p *printer) expr0(x ast.Expr, depth int, multiLine *bool) {
        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, multiLine)
 }
 
-
 // ----------------------------------------------------------------------------
 // Statements
 
@@ -947,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)
@@ -956,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:
@@ -967,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
@@ -994,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
@@ -1029,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())
@@ -1168,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)
@@ -1197,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.
@@ -1212,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)
@@ -1267,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)
@@ -1276,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
@@ -1297,7 +1355,6 @@ 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
@@ -1315,12 +1372,12 @@ func (p *printer) nodeSize(n ast.Node, maxSize int) (size int) {
        size = maxSize + 1 // assume n doesn't fit
        p.nodeSizes[n] = size
 
-       // nodeSize computation must be indendent of particular
+       // 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, p.nodeSizes); err != nil {
+       if err := cfg.fprint(&buf, p.fset, n, p.nodeSizes); err != nil {
                return
        }
        if buf.Len() <= maxSize {
@@ -1335,7 +1392,6 @@ func (p *printer) nodeSize(n ast.Node, maxSize int) (size int) {
        return
 }
 
-
 func (p *printer) isOneLineFunc(b *ast.BlockStmt, headerSize int) bool {
        pos1 := b.Pos()
        pos2 := b.Rbrace
@@ -1359,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 {
@@ -1396,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.
@@ -1408,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)
@@ -1422,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) {
@@ -1437,7 +1484,6 @@ func (p *printer) decl(decl ast.Decl, multiLine *bool) {
        }
 }
 
-
 // ----------------------------------------------------------------------------
 // Files
 
@@ -1452,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)