diff --git a/src/cmd/compile/internal/syntax/nodes.go b/src/cmd/compile/internal/syntax/nodes.go index b788efb3ec..efcf64717c 100644 --- a/src/cmd/compile/internal/syntax/nodes.go +++ b/src/cmd/compile/internal/syntax/nodes.go @@ -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 index 0000000000..6b4119c70b --- /dev/null +++ b/src/cmd/compile/internal/syntax/nodes_test.go @@ -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 +} diff --git a/src/cmd/compile/internal/syntax/parser.go b/src/cmd/compile/internal/syntax/parser.go index d7f542e609..585765e556 100644 --- a/src/cmd/compile/internal/syntax/parser.go +++ b/src/cmd/compile/internal/syntax/parser.go @@ -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) } diff --git a/test/escape_closure.go b/test/escape_closure.go index e9cf776afb..fc35cb59cf 100644 --- a/test/escape_closure.go +++ b/test/escape_closure.go @@ -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"