]> Cypherpunks.ru repositories - gostls13.git/commitdiff
cmd/compile/internal/syntax: establish principled position information
authorRobert Griesemer <gri@golang.org>
Tue, 14 Feb 2017 00:00:53 +0000 (16:00 -0800)
committerRobert Griesemer <gri@golang.org>
Wed, 15 Feb 2017 01:33:03 +0000 (01:33 +0000)
Until now, the parser set the position for each Node to the position of
the first token belonging to that node. For compatibility with the now
defunct gc parser, in many places that position information was modified
when the gcCompat flag was set (which it was, by default). Furthermore,
in some places, position information was not set at all.

This change removes the gcCompat flag and all associated code, and sets
position information for all nodes in a more principled way, as proposed
by mdempsky (see #16943 for details). Specifically, the position of a
node may not be at the very beginning of the respective production. For
instance for an Operation `a + b`, the position associated with the node
is the position of the `+`. Thus, for `a + b + c` we now get different
positions for the two additions.

This change does not pass toolstash -cmp because position information
recorded in export data and pcline tables is different. There are no
other functional changes.

Added test suite testing the position of all nodes.

Fixes #16943.

Change-Id: I3fc02bf096bc3b3d7d2fa655dfd4714a1a0eb90c
Reviewed-on: https://go-review.googlesource.com/37017
Run-TryBot: Robert Griesemer <gri@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
src/cmd/compile/internal/syntax/nodes.go
src/cmd/compile/internal/syntax/nodes_test.go [new file with mode: 0644]
src/cmd/compile/internal/syntax/parser.go
test/escape_closure.go

index b788efb3ec3924a1e2165e0a8d129ae5d862d7e3..efcf64717c1f50f57c5271d71e5ab4164fcff105 100644 (file)
@@ -10,9 +10,16 @@ import "cmd/internal/src"
 // Nodes
 
 type Node interface {
+       // Pos() returns the position associated with the node as follows:
+       // 1) The position of a node representing a terminal syntax production
+       //    (Name, BasicLit, etc.) is the position of the respective production
+       //    in the source.
+       // 2) The position of a node representing a non-terminal production
+       //    (IndexExpr, IfStmt, etc.) is the position of a token uniquely
+       //    associated with that production; usually the left-most one
+       //    ('[' for IndexExpr, 'if' for IfStmt, etc.)
        Pos() src.Pos
        aNode()
-       init(p *parser)
 }
 
 type node struct {
@@ -27,11 +34,6 @@ func (n *node) Pos() src.Pos {
 
 func (*node) aNode() {}
 
-// TODO(gri) we may be able to get rid of init here and in Node
-func (n *node) init(p *parser) {
-       n.pos = p.pos()
-}
-
 // ----------------------------------------------------------------------------
 // Files
 
diff --git a/src/cmd/compile/internal/syntax/nodes_test.go b/src/cmd/compile/internal/syntax/nodes_test.go
new file mode 100644 (file)
index 0000000..6b4119c
--- /dev/null
@@ -0,0 +1,321 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package syntax
+
+import (
+       "fmt"
+       "strings"
+       "testing"
+)
+
+// A test is a source code snippet of a particular node type.
+// In the snippet, a '@' indicates the position recorded by
+// the parser when creating the respective node.
+type test struct {
+       nodetyp string
+       snippet string
+}
+
+var decls = []test{
+       // The position of declarations is always the
+       // position of the first token of an individual
+       // declaration, independent of grouping.
+       {"ImportDecl", `import @"math"`},
+       {"ImportDecl", `import @mymath "math"`},
+       {"ImportDecl", `import @. "math"`},
+       {"ImportDecl", `import (@"math")`},
+       {"ImportDecl", `import (@mymath "math")`},
+       {"ImportDecl", `import (@. "math")`},
+
+       {"ConstDecl", `const @x`},
+       {"ConstDecl", `const @x = 0`},
+       {"ConstDecl", `const @x, y, z = 0, 1, 2`},
+       {"ConstDecl", `const (@x)`},
+       {"ConstDecl", `const (@x = 0)`},
+       {"ConstDecl", `const (@x, y, z = 0, 1, 2)`},
+
+       {"TypeDecl", `type @T int`},
+       {"TypeDecl", `type @T = int`},
+       {"TypeDecl", `type (@T int)`},
+       {"TypeDecl", `type (@T = int)`},
+
+       {"VarDecl", `var @x int`},
+       {"VarDecl", `var @x, y, z int`},
+       {"VarDecl", `var @x int = 0`},
+       {"VarDecl", `var @x, y, z int = 1, 2, 3`},
+       {"VarDecl", `var @x = 0`},
+       {"VarDecl", `var @x, y, z = 1, 2, 3`},
+       {"VarDecl", `var (@x int)`},
+       {"VarDecl", `var (@x, y, z int)`},
+       {"VarDecl", `var (@x int = 0)`},
+       {"VarDecl", `var (@x, y, z int = 1, 2, 3)`},
+       {"VarDecl", `var (@x = 0)`},
+       {"VarDecl", `var (@x, y, z = 1, 2, 3)`},
+
+       {"FuncDecl", `func @f() {}`},
+       {"FuncDecl", `func @(T) f() {}`},
+       {"FuncDecl", `func @(x T) f() {}`},
+}
+
+var exprs = []test{
+       // The position of an expression is the position
+       // of the left-most token that identifies the
+       // kind of expression.
+       {"Name", `@x`},
+
+       {"BasicLit", `@0`},
+       {"BasicLit", `@0x123`},
+       {"BasicLit", `@3.1415`},
+       {"BasicLit", `@.2718`},
+       {"BasicLit", `@1i`},
+       {"BasicLit", `@'a'`},
+       {"BasicLit", `@"abc"`},
+       {"BasicLit", "@`abc`"},
+
+       {"CompositeLit", `@{}`},
+       {"CompositeLit", `T@{}`},
+       {"CompositeLit", `struct{x, y int}@{}`},
+
+       {"KeyValueExpr", `"foo"@: true`},
+       {"KeyValueExpr", `"a"@: b`},
+
+       {"FuncLit", `@func (){}`},
+       {"ParenExpr", `@(x)`},
+       {"SelectorExpr", `a@.b`},
+       {"IndexExpr", `a@[i]`},
+
+       {"SliceExpr", `a@[:]`},
+       {"SliceExpr", `a@[i:]`},
+       {"SliceExpr", `a@[:j]`},
+       {"SliceExpr", `a@[i:j]`},
+       {"SliceExpr", `a@[i:j:k]`},
+
+       {"AssertExpr", `x@.(T)`},
+
+       {"Operation", `@*b`},
+       {"Operation", `@+b`},
+       {"Operation", `@-b`},
+       {"Operation", `@!b`},
+       {"Operation", `@^b`},
+       {"Operation", `@&b`},
+       {"Operation", `@<-b`},
+
+       {"Operation", `a @|| b`},
+       {"Operation", `a @&& b`},
+       {"Operation", `a @== b`},
+       {"Operation", `a @+ b`},
+       {"Operation", `a @* b`},
+
+       {"CallExpr", `f@()`},
+       {"CallExpr", `f@(x, y, z)`},
+       {"CallExpr", `obj.f@(1, 2, 3)`},
+       {"CallExpr", `func(x int) int { return x + 1 }@(y)`},
+
+       // ListExpr: tested via multi-value const/var declarations
+}
+
+var types = []test{
+       {"Operation", `@*T`},
+       {"Operation", `@*struct{}`},
+
+       {"ArrayType", `@[10]T`},
+       {"ArrayType", `@[...]T`},
+
+       {"SliceType", `@[]T`},
+       {"DotsType", `@...T`},
+       {"StructType", `@struct{}`},
+       {"InterfaceType", `@interface{}`},
+       {"FuncType", `func@()`},
+       {"MapType", `@map[T]T`},
+
+       {"ChanType", `@chan T`},
+       {"ChanType", `@chan<- T`},
+       {"ChanType", `@<-chan T`},
+}
+
+var fields = []test{
+       {"Field", `@T`},
+       {"Field", `@(T)`},
+       {"Field", `@x T`},
+       {"Field", `@x *(T)`},
+       {"Field", `@x, y, z T`},
+       {"Field", `@x, y, z (*T)`},
+}
+
+var stmts = []test{
+       {"EmptyStmt", `@;`},
+
+       {"LabeledStmt", `L@:`},
+       {"LabeledStmt", `L@: ;`},
+       {"LabeledStmt", `L@: f()`},
+
+       {"BlockStmt", `@{}`},
+
+       // The position of an ExprStmt is the position of the expression.
+       {"ExprStmt", `@<-ch`},
+       {"ExprStmt", `f@()`},
+       {"ExprStmt", `append@(s, 1, 2, 3)`},
+
+       {"SendStmt", `ch @<- x`},
+
+       {"DeclStmt", `@const x = 0`},
+       {"DeclStmt", `@const (x = 0)`},
+       {"DeclStmt", `@type T int`},
+       {"DeclStmt", `@type T = int`},
+       {"DeclStmt", `@type (T1 = int; T2 = float32)`},
+       {"DeclStmt", `@var x = 0`},
+       {"DeclStmt", `@var x, y, z int`},
+       {"DeclStmt", `@var (a, b = 1, 2)`},
+
+       {"AssignStmt", `x @= y`},
+       {"AssignStmt", `a, b, x @= 1, 2, 3`},
+       {"AssignStmt", `x @+= y`},
+       {"AssignStmt", `x @:= y`},
+       {"AssignStmt", `x, ok @:= f()`},
+       {"AssignStmt", `x@++`},
+       {"AssignStmt", `a[i]@--`},
+
+       {"BranchStmt", `@break`},
+       {"BranchStmt", `@break L`},
+       {"BranchStmt", `@continue`},
+       {"BranchStmt", `@continue L`},
+       {"BranchStmt", `@fallthrough`},
+       {"BranchStmt", `@goto L`},
+
+       {"CallStmt", `@defer f()`},
+       {"CallStmt", `@go f()`},
+
+       {"ReturnStmt", `@return`},
+       {"ReturnStmt", `@return x`},
+       {"ReturnStmt", `@return a, b, c`},
+
+       {"IfStmt", `@if cond {}`},
+       {"ForStmt", `@for {}`},
+       {"SwitchStmt", `@switch {}`},
+       {"SelectStmt", `@select {}`},
+}
+
+var ranges = []test{
+       {"RangeClause", `for @range s {}`},
+       {"RangeClause", `for _, i = @range s {}`},
+       {"RangeClause", `for x, i = @range s {}`},
+       {"RangeClause", `for _, i := @range s {}`},
+       {"RangeClause", `for x, i := @range s {}`},
+}
+
+var guards = []test{
+       {"TypeSwitchGuard", `switch x@.(type) {}`},
+       {"TypeSwitchGuard", `switch x := x@.(type) {}`},
+       {"TypeSwitchGuard", `switch a = b; x@.(type) {}`},
+       {"TypeSwitchGuard", `switch a := b; x := x@.(type) {}`},
+}
+
+var cases = []test{
+       {"CaseClause", ` switch { @case x: }`},
+       {"CaseClause", ` switch { @case x, y, z: }`},
+       {"CaseClause", ` switch { @case x == 1, y == 2: }`},
+       {"CaseClause", ` switch { @default: }`},
+}
+
+var comms = []test{
+       {"CommClause", `select { @case <-ch: }`},
+       {"CommClause", `select { @case x <- ch: }`},
+       {"CommClause", `select { @case x = <-ch: }`},
+       {"CommClause", `select { @case x := <-ch: }`},
+       {"CommClause", `select { @case x, ok = <-ch: }`},
+       {"CommClause", `select { @case x, ok := <-ch: }`},
+       {"CommClause", `select { @default: }`},
+}
+
+func TestPos(t *testing.T) {
+       // TODO(gri) Once we have a general tree walker, we can use that to find
+       // the first occurence of the respective node and we don't need to hand-
+       // extract the node for each specific kind of construct.
+
+       testPos(t, decls, "package p; ", "",
+               func(f *File) Node { return f.DeclList[0] },
+       )
+
+       // embed expressions in a composite literal so we can test key:value and naked composite literals
+       testPos(t, exprs, "package p; var _ = T{ ", " }",
+               func(f *File) Node { return f.DeclList[0].(*VarDecl).Values.(*CompositeLit).ElemList[0] },
+       )
+
+       // embed types in a function  signature so we can test ... types
+       testPos(t, types, "package p; func f(", ")",
+               func(f *File) Node { return f.DeclList[0].(*FuncDecl).Type.ParamList[0].Type },
+       )
+
+       testPos(t, fields, "package p; func f(", ")",
+               func(f *File) Node { return f.DeclList[0].(*FuncDecl).Type.ParamList[0] },
+       )
+
+       testPos(t, stmts, "package p; func _() { ", " } ",
+               func(f *File) Node { return f.DeclList[0].(*FuncDecl).Body[0] },
+       )
+
+       testPos(t, ranges, "package p; func _() { ", " } ",
+               func(f *File) Node { return f.DeclList[0].(*FuncDecl).Body[0].(*ForStmt).Init.(*RangeClause) },
+       )
+
+       testPos(t, guards, "package p; func _() { ", " } ",
+               func(f *File) Node { return f.DeclList[0].(*FuncDecl).Body[0].(*SwitchStmt).Tag.(*TypeSwitchGuard) },
+       )
+
+       testPos(t, cases, "package p; func _() { ", " } ",
+               func(f *File) Node { return f.DeclList[0].(*FuncDecl).Body[0].(*SwitchStmt).Body[0] },
+       )
+
+       testPos(t, comms, "package p; func _() { ", " } ",
+               func(f *File) Node { return f.DeclList[0].(*FuncDecl).Body[0].(*SelectStmt).Body[0] },
+       )
+}
+
+func testPos(t *testing.T, list []test, prefix, suffix string, extract func(*File) Node) {
+       for _, test := range list {
+               // complete source, compute @ position, and strip @ from source
+               src, index := stripAt(prefix + test.snippet + suffix)
+               if index < 0 {
+                       t.Errorf("missing @: %s", src)
+                       continue
+               }
+
+               // build syntaxt tree
+               file, err := ParseBytes(nil, []byte(src), nil, nil, 0)
+               if err != nil {
+                       t.Errorf("parse error: %s: %v", src, err)
+                       continue
+               }
+
+               // extract desired node
+               node := extract(file)
+               if typ := typeOf(node); typ != test.nodetyp {
+                       t.Errorf("type error: %s: type = %s, want %s", src, typ, test.nodetyp)
+                       continue
+               }
+
+               // verify node position with expected position as indicated by @
+               if col := int(node.Pos().Col()); col != index {
+                       t.Errorf("pos error: %s: col = %d, want %d", src, col, index)
+                       continue
+               }
+       }
+}
+
+func stripAt(s string) (string, int) {
+       if i := strings.Index(s, "@"); i >= 0 {
+               return s[:i] + s[i+1:], i
+       }
+       return s, -1
+}
+
+func typeOf(n Node) string {
+       const prefix = "*syntax."
+       k := fmt.Sprintf("%T", n)
+       if strings.HasPrefix(k, prefix) {
+               return k[len(prefix):]
+       }
+       return k
+}
index d7f542e609da016d2e5a817e3b78d7e71d59034e..585765e5564875576f8b0a785059029f8a77cf45 100644 (file)
@@ -15,11 +15,6 @@ import (
 const debug = false
 const trace = false
 
-// The old gc parser assigned line numbers very inconsistently depending
-// on when it happened to construct AST nodes. To make transitioning to the
-// new AST easier, we try to mimick the behavior as much as possible.
-const gcCompat = true
-
 type parser struct {
        base *src.PosBase
        errh ErrorHandler
@@ -248,7 +243,7 @@ func (p *parser) file() *File {
        }
 
        f := new(File)
-       f.init(p)
+       f.pos = p.pos()
 
        // PackageClause
        if !p.got(_Package) {
@@ -346,14 +341,14 @@ func (p *parser) importDecl(group *Group) Decl {
        }
 
        d := new(ImportDecl)
-       d.init(p)
+       d.pos = p.pos()
 
        switch p.tok {
        case _Name:
                d.LocalPkgName = p.name()
        case _Dot:
                n := new(Name)
-               n.init(p)
+               n.pos = p.pos()
                n.Value = "."
                d.LocalPkgName = n
                p.next()
@@ -376,7 +371,7 @@ func (p *parser) constDecl(group *Group) Decl {
        }
 
        d := new(ConstDecl)
-       d.init(p)
+       d.pos = p.pos()
 
        d.NameList = p.nameList(p.name())
        if p.tok != _EOF && p.tok != _Semi && p.tok != _Rparen {
@@ -397,7 +392,7 @@ func (p *parser) typeDecl(group *Group) Decl {
        }
 
        d := new(TypeDecl)
-       d.init(p)
+       d.pos = p.pos()
 
        d.Name = p.name()
        d.Alias = p.got(_Assign)
@@ -419,7 +414,7 @@ func (p *parser) varDecl(group *Group) Decl {
        }
 
        d := new(VarDecl)
-       d.init(p)
+       d.pos = p.pos()
 
        d.NameList = p.nameList(p.name())
        if p.got(_Assign) {
@@ -431,9 +426,6 @@ func (p *parser) varDecl(group *Group) Decl {
                }
        }
        d.Group = group
-       if gcCompat {
-               d.init(p)
-       }
 
        return d
 }
@@ -449,7 +441,7 @@ func (p *parser) funcDecl() *FuncDecl {
        }
 
        f := new(FuncDecl)
-       f.init(p)
+       f.pos = p.pos()
 
        badRecv := false
        if p.tok == _Lparen {
@@ -488,9 +480,6 @@ func (p *parser) funcDecl() *FuncDecl {
 
        f.Name = p.name()
        f.Type = p.funcType()
-       if gcCompat {
-               f.node = f.Type.node
-       }
        f.Body = p.funcBody()
 
        f.Pragma = p.pragma
@@ -525,15 +514,12 @@ func (p *parser) binaryExpr(prec int) Expr {
        x := p.unaryExpr()
        for (p.tok == _Operator || p.tok == _Star) && p.prec > prec {
                t := new(Operation)
-               t.init(p)
+               t.pos = p.pos()
                t.Op = p.op
                t.X = x
                tprec := p.prec
                p.next()
                t.Y = p.binaryExpr(tprec)
-               if gcCompat {
-                       t.init(p)
-               }
                x = t
        }
        return x
@@ -550,20 +536,17 @@ func (p *parser) unaryExpr() Expr {
                switch p.op {
                case Mul, Add, Sub, Not, Xor:
                        x := new(Operation)
-                       x.init(p)
+                       x.pos = p.pos()
                        x.Op = p.op
                        p.next()
                        x.X = p.unaryExpr()
-                       if gcCompat {
-                               x.init(p)
-                       }
                        return x
 
                case And:
-                       p.next()
                        x := new(Operation)
-                       x.init(p)
+                       x.pos = p.pos()
                        x.Op = And
+                       p.next()
                        // unaryExpr may have returned a parenthesized composite literal
                        // (see comment in operand) - remove parentheses if any
                        x.X = unparen(p.unaryExpr())
@@ -572,6 +555,7 @@ func (p *parser) unaryExpr() Expr {
 
        case _Arrow:
                // receive op (<-x) or receive-only channel (<-chan E)
+               pos := p.pos()
                p.next()
 
                // If the next token is _Chan we still don't know if it is
@@ -620,7 +604,11 @@ func (p *parser) unaryExpr() Expr {
                }
 
                // x is not a channel type => we have a receive op
-               return &Operation{Op: Recv, X: x}
+               o := new(Operation)
+               o.pos = pos
+               o.Op = Recv
+               o.X = x
+               return o
        }
 
        // TODO(mdempsky): We need parens here so we can report an
@@ -636,7 +624,7 @@ func (p *parser) callStmt() *CallStmt {
        }
 
        s := new(CallStmt)
-       s.init(p)
+       s.pos = p.pos()
        s.Tok = p.tok // _Defer or _Go
        p.next()
 
@@ -672,6 +660,7 @@ func (p *parser) operand(keep_parens bool) Expr {
                return p.oliteral()
 
        case _Lparen:
+               pos := p.pos()
                p.next()
                p.xnest++
                x := p.expr() // expr_or_type
@@ -700,21 +689,27 @@ func (p *parser) operand(keep_parens bool) Expr {
                // in a go/defer statement. In that case, operand is called
                // with keep_parens set.
                if keep_parens {
-                       x = &ParenExpr{X: x}
+                       px := new(ParenExpr)
+                       px.pos = pos
+                       px.X = x
+                       x = px
                }
                return x
 
        case _Func:
+               pos := p.pos()
                p.next()
                t := p.funcType()
                if p.tok == _Lbrace {
                        p.fnest++
                        p.xnest++
+
                        f := new(FuncLit)
-                       f.init(p)
+                       f.pos = pos
                        f.Type = t
                        f.Body = p.funcBody()
                        f.EndLine = p.line
+
                        p.xnest--
                        p.fnest--
                        return f
@@ -767,6 +762,7 @@ func (p *parser) pexpr(keep_parens bool) Expr {
 
 loop:
        for {
+               pos := p.pos()
                switch p.tok {
                case _Dot:
                        p.next()
@@ -774,7 +770,7 @@ loop:
                        case _Name:
                                // pexpr '.' sym
                                t := new(SelectorExpr)
-                               t.init(p)
+                               t.pos = pos
                                t.X = x
                                t.Sel = p.name()
                                x = t
@@ -783,12 +779,12 @@ loop:
                                p.next()
                                if p.got(_Type) {
                                        t := new(TypeSwitchGuard)
-                                       t.init(p)
+                                       t.pos = pos
                                        t.X = x
                                        x = t
                                } else {
                                        t := new(AssertExpr)
-                                       t.init(p)
+                                       t.pos = pos
                                        t.X = x
                                        t.Type = p.expr()
                                        x = t
@@ -799,9 +795,6 @@ loop:
                                p.syntax_error("expecting name or (")
                                p.advance(_Semi, _Rparen)
                        }
-                       if gcCompat && x != nil {
-                               x.init(p)
-                       }
 
                case _Lbrack:
                        p.next()
@@ -813,7 +806,7 @@ loop:
                                if p.got(_Rbrack) {
                                        // x[i]
                                        t := new(IndexExpr)
-                                       t.init(p)
+                                       t.pos = pos
                                        t.X = x
                                        t.Index = i
                                        x = t
@@ -824,7 +817,7 @@ loop:
 
                        // x[i:...
                        t := new(SliceExpr)
-                       t.init(p)
+                       t.pos = pos
                        t.X = x
                        t.Index[0] = i
                        p.want(_Colon)
@@ -909,7 +902,7 @@ func (p *parser) complitexpr() *CompositeLit {
        }
 
        x := new(CompositeLit)
-       x.init(p)
+       x.pos = p.pos()
 
        p.want(_Lbrace)
        p.xnest++
@@ -917,15 +910,13 @@ func (p *parser) complitexpr() *CompositeLit {
        for p.tok != _EOF && p.tok != _Rbrace {
                // value
                e := p.bare_complitexpr()
-               if p.got(_Colon) {
+               if p.tok == _Colon {
                        // key ':' value
                        l := new(KeyValueExpr)
-                       l.init(p)
+                       l.pos = p.pos()
+                       p.next()
                        l.Key = e
                        l.Value = p.bare_complitexpr()
-                       if gcCompat {
-                               l.init(p)
-                       }
                        e = l
                        x.NKeys++
                }
@@ -954,13 +945,17 @@ func (p *parser) type_() Expr {
                return typ
        }
 
-       p.syntax_error("")
+       p.syntax_error("expecting type")
        p.advance()
        return nil
 }
 
-func indirect(typ Expr) Expr {
-       return &Operation{Op: Mul, X: typ}
+func indirect(pos src.Pos, typ Expr) Expr {
+       o := new(Operation)
+       o.pos = pos
+       o.Op = Mul
+       o.X = typ
+       return o
 }
 
 // tryType is like type_ but it returns nil if there was no type
@@ -975,18 +970,19 @@ func (p *parser) tryType() Expr {
                defer p.trace("tryType")()
        }
 
+       pos := p.pos()
        switch p.tok {
        case _Star:
                // ptrtype
                p.next()
-               return indirect(p.type_())
+               return indirect(pos, p.type_())
 
        case _Arrow:
                // recvchantype
                p.next()
                p.want(_Chan)
                t := new(ChanType)
-               t.init(p)
+               t.pos = pos
                t.Dir = RecvOnly
                t.Elem = p.chanElem()
                return t
@@ -1005,14 +1001,14 @@ func (p *parser) tryType() Expr {
                        // []T
                        p.xnest--
                        t := new(SliceType)
-                       t.init(p)
+                       t.pos = pos
                        t.Elem = p.type_()
                        return t
                }
 
                // [n]T
                t := new(ArrayType)
-               t.init(p)
+               t.pos = pos
                if !p.got(_DotDotDot) {
                        t.Len = p.expr()
                }
@@ -1026,7 +1022,7 @@ func (p *parser) tryType() Expr {
                // _Chan _Comm ntype
                p.next()
                t := new(ChanType)
-               t.init(p)
+               t.pos = pos
                if p.got(_Arrow) {
                        t.Dir = SendOnly
                }
@@ -1038,7 +1034,7 @@ func (p *parser) tryType() Expr {
                p.next()
                p.want(_Lbrack)
                t := new(MapType)
-               t.init(p)
+               t.pos = pos
                t.Key = p.type_()
                p.want(_Rbrack)
                t.Value = p.type_()
@@ -1069,12 +1065,10 @@ func (p *parser) funcType() *FuncType {
        }
 
        typ := new(FuncType)
-       typ.init(p)
+       typ.pos = p.pos()
        typ.ParamList = p.paramList()
        typ.ResultList = p.funcResult()
-       if gcCompat {
-               typ.init(p)
-       }
+
        return typ
 }
 
@@ -1097,9 +1091,10 @@ func (p *parser) dotname(name *Name) Expr {
                defer p.trace("dotname")()
        }
 
-       if p.got(_Dot) {
+       if p.tok == _Dot {
                s := new(SelectorExpr)
-               s.init(p)
+               s.pos = p.pos()
+               p.next()
                s.X = name
                s.Sel = p.name()
                return s
@@ -1114,7 +1109,7 @@ func (p *parser) structType() *StructType {
        }
 
        typ := new(StructType)
-       typ.init(p)
+       typ.pos = p.pos()
 
        p.want(_Struct)
        p.want(_Lbrace)
@@ -1136,7 +1131,7 @@ func (p *parser) interfaceType() *InterfaceType {
        }
 
        typ := new(InterfaceType)
-       typ.init(p)
+       typ.pos = p.pos()
 
        p.want(_Interface)
        p.want(_Lbrace)
@@ -1183,9 +1178,10 @@ func (p *parser) funcResult() []*Field {
                return p.paramList()
        }
 
+       pos := p.pos()
        if result := p.tryType(); result != nil {
                f := new(Field)
-               f.init(p)
+               f.pos = pos
                f.Type = result
                return []*Field{f}
        }
@@ -1193,7 +1189,7 @@ func (p *parser) funcResult() []*Field {
        return nil
 }
 
-func (p *parser) addField(styp *StructType, name *Name, typ Expr, tag *BasicLit) {
+func (p *parser) addField(styp *StructType, pos src.Pos, name *Name, typ Expr, tag *BasicLit) {
        if tag != nil {
                for i := len(styp.FieldList) - len(styp.TagList); i > 0; i-- {
                        styp.TagList = append(styp.TagList, nil)
@@ -1202,15 +1198,11 @@ func (p *parser) addField(styp *StructType, name *Name, typ Expr, tag *BasicLit)
        }
 
        f := new(Field)
-       f.init(p)
+       f.pos = pos
        f.Name = name
        f.Type = typ
        styp.FieldList = append(styp.FieldList, f)
 
-       if gcCompat && name != nil {
-               f.node = name.node
-       }
-
        if debug && tag != nil && len(styp.FieldList) != len(styp.TagList) {
                panic("inconsistent struct field list")
        }
@@ -1224,15 +1216,15 @@ func (p *parser) fieldDecl(styp *StructType) {
                defer p.trace("fieldDecl")()
        }
 
-       var name *Name
+       pos := p.pos()
        switch p.tok {
        case _Name:
-               name = p.name()
+               name := p.name()
                if p.tok == _Dot || p.tok == _Literal || p.tok == _Semi || p.tok == _Rbrace {
                        // embed oliteral
                        typ := p.qualifiedName(name)
                        tag := p.oliteral()
-                       p.addField(styp, nil, typ, tag)
+                       p.addField(styp, pos, nil, typ, tag)
                        return
                }
 
@@ -1242,18 +1234,19 @@ func (p *parser) fieldDecl(styp *StructType) {
                tag := p.oliteral()
 
                for _, name := range names {
-                       p.addField(styp, name, typ, tag)
+                       p.addField(styp, name.Pos(), name, typ, tag)
                }
 
        case _Lparen:
                p.next()
                if p.tok == _Star {
                        // '(' '*' embed ')' oliteral
+                       pos := p.pos()
                        p.next()
-                       typ := indirect(p.qualifiedName(nil))
+                       typ := indirect(pos, p.qualifiedName(nil))
                        p.want(_Rparen)
                        tag := p.oliteral()
-                       p.addField(styp, nil, typ, tag)
+                       p.addField(styp, pos, nil, typ, tag)
                        p.syntax_error("cannot parenthesize embedded type")
 
                } else {
@@ -1261,7 +1254,7 @@ func (p *parser) fieldDecl(styp *StructType) {
                        typ := p.qualifiedName(nil)
                        p.want(_Rparen)
                        tag := p.oliteral()
-                       p.addField(styp, nil, typ, tag)
+                       p.addField(styp, pos, nil, typ, tag)
                        p.syntax_error("cannot parenthesize embedded type")
                }
 
@@ -1269,17 +1262,17 @@ func (p *parser) fieldDecl(styp *StructType) {
                p.next()
                if p.got(_Lparen) {
                        // '*' '(' embed ')' oliteral
-                       typ := indirect(p.qualifiedName(nil))
+                       typ := indirect(pos, p.qualifiedName(nil))
                        p.want(_Rparen)
                        tag := p.oliteral()
-                       p.addField(styp, nil, typ, tag)
+                       p.addField(styp, pos, nil, typ, tag)
                        p.syntax_error("cannot parenthesize embedded type")
 
                } else {
                        // '*' embed oliteral
-                       typ := indirect(p.qualifiedName(nil))
+                       typ := indirect(pos, p.qualifiedName(nil))
                        tag := p.oliteral()
-                       p.addField(styp, nil, typ, tag)
+                       p.addField(styp, pos, nil, typ, tag)
                }
 
        default:
@@ -1291,7 +1284,7 @@ func (p *parser) fieldDecl(styp *StructType) {
 func (p *parser) oliteral() *BasicLit {
        if p.tok == _Literal {
                b := new(BasicLit)
-               b.init(p)
+               b.pos = p.pos()
                b.Value = p.lit
                b.Kind = p.kind
                p.next()
@@ -1324,7 +1317,7 @@ func (p *parser) methodDecl() *Field {
                }
 
                f := new(Field)
-               f.init(p)
+               f.pos = name.Pos()
                if p.tok != _Lparen {
                        // packname
                        f.Type = p.qualifiedName(name)
@@ -1336,16 +1329,16 @@ func (p *parser) methodDecl() *Field {
                return f
 
        case _Lparen:
-               p.next()
+               p.syntax_error("cannot parenthesize embedded type")
                f := new(Field)
-               f.init(p)
+               f.pos = p.pos()
+               p.next()
                f.Type = p.qualifiedName(nil)
                p.want(_Rparen)
-               p.syntax_error("cannot parenthesize embedded type")
                return f
 
        default:
-               p.syntax_error("")
+               p.syntax_error("expecting method or interface name")
                p.advance(_Semi, _Rbrace)
                return nil
        }
@@ -1358,7 +1351,7 @@ func (p *parser) paramDecl() *Field {
        }
 
        f := new(Field)
-       f.init(p)
+       f.pos = p.pos()
 
        switch p.tok {
        case _Name:
@@ -1403,7 +1396,7 @@ func (p *parser) dotsType() *DotsType {
        }
 
        t := new(DotsType)
-       t.init(p)
+       t.pos = p.pos()
 
        p.want(_DotDotDot)
        t.Elem = p.tryType()
@@ -1486,7 +1479,7 @@ func (p *parser) simpleStmt(lhs Expr, rangeOk bool) SimpleStmt {
                defer p.trace("simpleStmt")()
        }
 
-       if rangeOk && p.got(_Range) {
+       if rangeOk && p.tok == _Range {
                // _Range expr
                if debug && lhs != nil {
                        panic("invalid call of simpleStmt")
@@ -1500,55 +1493,60 @@ func (p *parser) simpleStmt(lhs Expr, rangeOk bool) SimpleStmt {
 
        if _, ok := lhs.(*ListExpr); !ok && p.tok != _Assign && p.tok != _Define {
                // expr
+               pos := p.pos()
                switch p.tok {
                case _AssignOp:
                        // lhs op= rhs
                        op := p.op
                        p.next()
-                       return p.newAssignStmt(op, lhs, p.expr())
+                       return p.newAssignStmt(pos, op, lhs, p.expr())
 
                case _IncOp:
                        // lhs++ or lhs--
                        op := p.op
                        p.next()
-                       return p.newAssignStmt(op, lhs, ImplicitOne)
+                       return p.newAssignStmt(pos, op, lhs, ImplicitOne)
 
                case _Arrow:
                        // lhs <- rhs
-                       p.next()
                        s := new(SendStmt)
-                       s.init(p)
+                       s.pos = pos
+                       p.next()
                        s.Chan = lhs
                        s.Value = p.expr()
-                       if gcCompat {
-                               s.init(p)
-                       }
                        return s
 
                default:
                        // expr
-                       return &ExprStmt{X: lhs}
+                       s := new(ExprStmt)
+                       if lhs != nil { // be cautious (test/syntax/semi4.go)
+                               s.pos = lhs.Pos()
+                       } else {
+                               s.pos = p.pos()
+                       }
+                       s.X = lhs
+                       return s
                }
        }
 
        // expr_list
+       pos := p.pos()
        switch p.tok {
        case _Assign:
                p.next()
 
-               if rangeOk && p.got(_Range) {
+               if rangeOk && p.tok == _Range {
                        // expr_list '=' _Range expr
                        return p.rangeClause(lhs, false)
                }
 
                // expr_list '=' expr_list
-               return p.newAssignStmt(0, lhs, p.exprList())
+               return p.newAssignStmt(pos, 0, lhs, p.exprList())
 
        case _Define:
-               pos := p.pos()
                p.next()
 
-               if rangeOk && p.got(_Range) {
+               if rangeOk && p.tok == _Range {
                        // expr_list ':=' range expr
                        return p.rangeClause(lhs, true)
                }
@@ -1566,10 +1564,13 @@ func (p *parser) simpleStmt(lhs Expr, rangeOk bool) SimpleStmt {
                                // TODO(mdempsky): Have Expr types implement Stringer?
                                p.error(fmt.Sprintf("invalid variable name %s in type switch", lhs))
                        }
-                       return &ExprStmt{X: x}
+                       s := new(ExprStmt)
+                       s.pos = x.Pos()
+                       s.X = x
+                       return s
                }
 
-               as := p.newAssignStmt(Def, lhs, rhs)
+               as := p.newAssignStmt(pos, Def, lhs, rhs)
                as.pos = pos // TODO(gri) pass this into newAssignStmt
                return as
 
@@ -1582,19 +1583,17 @@ func (p *parser) simpleStmt(lhs Expr, rangeOk bool) SimpleStmt {
 
 func (p *parser) rangeClause(lhs Expr, def bool) *RangeClause {
        r := new(RangeClause)
-       r.init(p)
+       r.pos = p.pos()
+       p.next() // consume _Range
        r.Lhs = lhs
        r.Def = def
        r.X = p.expr()
-       if gcCompat {
-               r.init(p)
-       }
        return r
 }
 
-func (p *parser) newAssignStmt(op Operator, lhs, rhs Expr) *AssignStmt {
+func (p *parser) newAssignStmt(pos src.Pos, op Operator, lhs, rhs Expr) *AssignStmt {
        a := new(AssignStmt)
-       a.init(p)
+       a.pos = pos
        a.Op = op
        a.Lhs = lhs
        a.Rhs = rhs
@@ -1607,7 +1606,7 @@ func (p *parser) labeledStmt(label *Name) Stmt {
        }
 
        s := new(LabeledStmt)
-       s.init(p)
+       s.pos = p.pos()
        s.Label = label
 
        p.want(_Colon)
@@ -1631,7 +1630,7 @@ func (p *parser) blockStmt() *BlockStmt {
        }
 
        s := new(BlockStmt)
-       s.init(p)
+       s.pos = p.pos()
        p.want(_Lbrace)
        s.Body = p.stmtList()
        p.want(_Rbrace)
@@ -1645,7 +1644,7 @@ func (p *parser) declStmt(f func(*Group) Decl) *DeclStmt {
        }
 
        s := new(DeclStmt)
-       s.init(p)
+       s.pos = p.pos()
 
        p.next() // _Const, _Type, or _Var
        s.DeclList = p.appendGroup(nil, f)
@@ -1659,13 +1658,9 @@ func (p *parser) forStmt() Stmt {
        }
 
        s := new(ForStmt)
-       s.init(p)
+       s.pos = p.pos()
 
-       p.want(_For)
        s.Init, s.Cond, s.Post = p.header(_For)
-       if gcCompat {
-               s.init(p)
-       }
        s.Body = p.stmtBody("for clause")
 
        return s
@@ -1689,7 +1684,7 @@ func (p *parser) stmtBody(context string) []Stmt {
 }
 
 func (p *parser) header(keyword token) (init SimpleStmt, cond Expr, post SimpleStmt) {
-       // TODO(gri) move caller's p.want(keyword) here, once we removed gcCompat
+       p.want(keyword)
 
        if p.tok == _Lbrace {
                if keyword == _If {
@@ -1765,15 +1760,9 @@ func (p *parser) ifStmt() *IfStmt {
        }
 
        s := new(IfStmt)
-       s.init(p)
+       s.pos = p.pos()
 
-       p.want(_If)
        s.Init, s.Cond, _ = p.header(_If)
-
-       if gcCompat {
-               s.init(p)
-       }
-
        s.Then = p.stmtBody("if clause")
 
        if p.got(_Else) {
@@ -1796,9 +1785,8 @@ func (p *parser) switchStmt() *SwitchStmt {
                defer p.trace("switchStmt")()
        }
 
-       p.want(_Switch)
        s := new(SwitchStmt)
-       s.init(p)
+       s.pos = p.pos()
 
        s.Init, s.Tag, _ = p.header(_Switch)
 
@@ -1819,10 +1807,10 @@ func (p *parser) selectStmt() *SelectStmt {
                defer p.trace("selectStmt")()
        }
 
-       p.want(_Select)
        s := new(SelectStmt)
-       s.init(p)
+       s.pos = p.pos()
 
+       p.want(_Select)
        if !p.got(_Lbrace) {
                p.syntax_error("missing { after select clause")
                p.advance(_Case, _Default, _Rbrace)
@@ -1841,7 +1829,7 @@ func (p *parser) caseClause() *CaseClause {
        }
 
        c := new(CaseClause)
-       c.init(p)
+       c.pos = p.pos()
 
        switch p.tok {
        case _Case:
@@ -1868,7 +1856,7 @@ func (p *parser) commClause() *CommClause {
        }
 
        c := new(CommClause)
-       c.init(p)
+       c.pos = p.pos()
 
        switch p.tok {
        case _Case:
@@ -1895,9 +1883,6 @@ func (p *parser) commClause() *CommClause {
                p.advance(_Case, _Default, _Rbrace)
        }
 
-       if gcCompat {
-               c.init(p)
-       }
        p.want(_Colon)
        c.Body = p.stmtList()
 
@@ -1966,22 +1951,17 @@ func (p *parser) stmt() Stmt {
                return p.ifStmt()
 
        case _Fallthrough:
-               p.next()
                s := new(BranchStmt)
-               s.init(p)
+               s.pos = p.pos()
+               p.next()
                s.Tok = _Fallthrough
                return s
-               // // will be converted to OFALL
-               // stmt := nod(OXFALL, nil, nil)
-               // stmt.Xoffset = int64(block)
-               // return stmt
 
        case _Break, _Continue:
-               tok := p.tok
-               p.next()
                s := new(BranchStmt)
-               s.init(p)
-               s.Tok = tok
+               s.pos = p.pos()
+               s.Tok = p.tok
+               p.next()
                if p.tok == _Name {
                        s.Label = p.name()
                }
@@ -1991,31 +1971,25 @@ func (p *parser) stmt() Stmt {
                return p.callStmt()
 
        case _Goto:
-               p.next()
                s := new(BranchStmt)
-               s.init(p)
+               s.pos = p.pos()
                s.Tok = _Goto
+               p.next()
                s.Label = p.name()
                return s
-               // stmt := nod(OGOTO, p.new_name(p.name()), nil)
-               // stmt.Sym = dclstack // context, for goto restrictions
-               // return stmt
 
        case _Return:
-               p.next()
                s := new(ReturnStmt)
-               s.init(p)
+               s.pos = p.pos()
+               p.next()
                if p.tok != _Semi && p.tok != _Rbrace {
                        s.Results = p.exprList()
                }
-               if gcCompat {
-                       s.init(p)
-               }
                return s
 
        case _Semi:
                s := new(EmptyStmt)
-               s.init(p)
+               s.pos = p.pos()
                return s
        }
 
@@ -2056,7 +2030,7 @@ func (p *parser) call(fun Expr) *CallExpr {
        // call or conversion
        // convtype '(' expr ocomma ')'
        c := new(CallExpr)
-       c.init(p)
+       c.pos = p.pos()
        c.Fun = fun
 
        p.want(_Lparen)
@@ -2071,9 +2045,6 @@ func (p *parser) call(fun Expr) *CallExpr {
        }
 
        p.xnest--
-       if gcCompat {
-               c.init(p)
-       }
        p.want(_Rparen)
 
        return c
@@ -2086,7 +2057,7 @@ func (p *parser) name() *Name {
        // no tracing to avoid overly verbose output
 
        n := new(Name)
-       n.init(p)
+       n.pos = p.pos()
 
        if p.tok == _Name {
                n.Value = p.lit
@@ -2132,7 +2103,7 @@ func (p *parser) qualifiedName(name *Name) Expr {
                name = p.name()
        default:
                name = new(Name)
-               name.init(p)
+               name.pos = p.pos()
                p.syntax_error("expecting name")
                p.advance(_Dot, _Semi, _Rbrace)
        }
index e9cf776afb87f7f489d874b764659d28a8ed891b..fc35cb59cf331ccf1d61d3d6fd73812da4127e27 100644 (file)
@@ -55,9 +55,9 @@ func ClosureCallArgs4() {
 
 func ClosureCallArgs5() {
        x := 0                     // ERROR "moved to heap: x"
-       sink = func(p *int) *int { // ERROR "leaking param: p to result ~r1" "func literal does not escape"
+       sink = func(p *int) *int { // ERROR "leaking param: p to result ~r1" "func literal does not escape" "\(func literal\)\(&x\) escapes to heap"
                return p
-       }(&x) // ERROR "&x escapes to heap" "\(func literal\)\(&x\) escapes to heap"
+       }(&x) // ERROR "&x escapes to heap"
 }
 
 func ClosureCallArgs6() {
@@ -140,10 +140,10 @@ func ClosureCallArgs14() {
 func ClosureCallArgs15() {
        x := 0                      // ERROR "moved to heap: x"
        p := &x                     // ERROR "moved to heap: p" "&x escapes to heap"
-       sink = func(p **int) *int { // ERROR "leaking param: p to result ~r1 level=1" "func literal does not escape"
+       sink = func(p **int) *int { // ERROR "leaking param: p to result ~r1 level=1" "func literal does not escape" "\(func literal\)\(&p\) escapes to heap"
                return *p
                // BAD: p should not escape here
-       }(&p) // ERROR "&p escapes to heap" "\(func literal\)\(&p\) escapes to heap"
+       }(&p) // ERROR "&p escapes to heap"
 }
 
 func ClosureLeak1(s string) string { // ERROR "ClosureLeak1 s does not escape"