summaryrefslogtreecommitdiff
path: root/src/pkg/go/parser
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/go/parser')
-rw-r--r--src/pkg/go/parser/error_test.go21
-rw-r--r--src/pkg/go/parser/interface.go7
-rw-r--r--src/pkg/go/parser/parser.go75
-rw-r--r--src/pkg/go/parser/parser_test.go15
-rw-r--r--src/pkg/go/parser/short_test.go25
5 files changed, 110 insertions, 33 deletions
diff --git a/src/pkg/go/parser/error_test.go b/src/pkg/go/parser/error_test.go
index d4d4f909d..8506077ce 100644
--- a/src/pkg/go/parser/error_test.go
+++ b/src/pkg/go/parser/error_test.go
@@ -59,8 +59,11 @@ func getPos(filename string, offset int) token.Pos {
// ERROR comments must be of the form /* ERROR "rx" */ and rx is
// a regular expression that matches the expected error message.
+// The special form /* ERROR HERE "rx" */ must be used for error
+// messages that appear immediately after a token, rather than at
+// a token's position.
//
-var errRx = regexp.MustCompile(`^/\* *ERROR *"([^"]*)" *\*/$`)
+var errRx = regexp.MustCompile(`^/\* *ERROR *(HERE)? *"([^"]*)" *\*/$`)
// expectedErrors collects the regular expressions of ERROR comments found
// in files and returns them as a map of error positions to error messages.
@@ -74,6 +77,7 @@ func expectedErrors(t *testing.T, filename string, src []byte) map[token.Pos]str
// not match the position information collected by the parser
s.Init(getFile(filename), src, nil, scanner.ScanComments)
var prev token.Pos // position of last non-comment, non-semicolon token
+ var here token.Pos // position immediately after the token at position prev
for {
pos, tok, lit := s.Scan()
@@ -82,11 +86,22 @@ func expectedErrors(t *testing.T, filename string, src []byte) map[token.Pos]str
return errors
case token.COMMENT:
s := errRx.FindStringSubmatch(lit)
- if len(s) == 2 {
- errors[prev] = string(s[1])
+ if len(s) == 3 {
+ pos := prev
+ if s[1] == "HERE" {
+ pos = here
+ }
+ errors[pos] = string(s[2])
}
default:
prev = pos
+ var l int // token length
+ if tok.IsLiteral() {
+ l = len(lit)
+ } else {
+ l = len(tok.String())
+ }
+ here = prev + token.Pos(l)
}
}
}
diff --git a/src/pkg/go/parser/interface.go b/src/pkg/go/parser/interface.go
index 0f83ca931..57da4ddcd 100644
--- a/src/pkg/go/parser/interface.go
+++ b/src/pkg/go/parser/interface.go
@@ -182,6 +182,13 @@ func ParseExpr(x string) (ast.Expr, error) {
p.closeScope()
assert(p.topScope == nil, "unbalanced scopes")
+ // If a semicolon was inserted, consume it;
+ // report an error if there's more tokens.
+ if p.tok == token.SEMICOLON {
+ p.next()
+ }
+ p.expect(token.EOF)
+
if p.errors.Len() > 0 {
p.errors.Sort()
return nil, p.errors.Err()
diff --git a/src/pkg/go/parser/parser.go b/src/pkg/go/parser/parser.go
index c4523318f..00dd532b2 100644
--- a/src/pkg/go/parser/parser.go
+++ b/src/pkg/go/parser/parser.go
@@ -492,6 +492,26 @@ func syncDecl(p *parser) {
}
}
+// safePos returns a valid file position for a given position: If pos
+// is valid to begin with, safePos returns pos. If pos is out-of-range,
+// safePos returns the EOF position.
+//
+// This is hack to work around "artificial" end positions in the AST which
+// are computed by adding 1 to (presumably valid) token positions. If the
+// token positions are invalid due to parse errors, the resulting end position
+// may be past the file's EOF position, which would lead to panics if used
+// later on.
+//
+func (p *parser) safePos(pos token.Pos) (res token.Pos) {
+ defer func() {
+ if recover() != nil {
+ res = token.Pos(p.file.Base() + p.file.Size()) // EOF position
+ }
+ }()
+ _ = p.file.Offset(pos) // trigger a panic if position is out-of-range
+ return pos
+}
+
// ----------------------------------------------------------------------------
// Identifiers
@@ -679,7 +699,7 @@ func (p *parser) parseFieldDecl(scope *ast.Scope) *ast.Field {
if n := len(list); n > 1 || !isTypeName(deref(typ)) {
pos := typ.Pos()
p.errorExpected(pos, "anonymous field")
- typ = &ast.BadExpr{From: pos, To: list[n-1].End()}
+ typ = &ast.BadExpr{From: pos, To: p.safePos(list[n-1].End())}
}
}
@@ -1168,16 +1188,19 @@ func (p *parser) parseIndexOrSlice(x ast.Expr) ast.Expr {
defer un(trace(p, "IndexOrSlice"))
}
+ const N = 3 // change the 3 to 2 to disable 3-index slices
lbrack := p.expect(token.LBRACK)
p.exprLev++
- var index [3]ast.Expr // change the 3 to 2 to disable slice expressions w/ cap
+ var index [N]ast.Expr
+ var colons [N - 1]token.Pos
if p.tok != token.COLON {
index[0] = p.parseRhs()
}
ncolons := 0
- for p.tok == token.COLON && ncolons < len(index)-1 {
- p.next()
+ for p.tok == token.COLON && ncolons < len(colons) {
+ colons[ncolons] = p.pos
ncolons++
+ p.next()
if p.tok != token.COLON && p.tok != token.RBRACK && p.tok != token.EOF {
index[ncolons] = p.parseRhs()
}
@@ -1187,7 +1210,21 @@ func (p *parser) parseIndexOrSlice(x ast.Expr) ast.Expr {
if ncolons > 0 {
// slice expression
- return &ast.SliceExpr{X: x, Lbrack: lbrack, Low: index[0], High: index[1], Max: index[2], Slice3: ncolons == 2, Rbrack: rbrack}
+ slice3 := false
+ if ncolons == 2 {
+ slice3 = true
+ // Check presence of 2nd and 3rd index here rather than during type-checking
+ // to prevent erroneous programs from passing through gofmt (was issue 7305).
+ if index[1] == nil {
+ p.error(colons[0], "2nd index required in 3-index slice")
+ index[1] = &ast.BadExpr{From: colons[0] + 1, To: colons[1]}
+ }
+ if index[2] == nil {
+ p.error(colons[1], "3rd index required in 3-index slice")
+ index[2] = &ast.BadExpr{From: colons[1] + 1, To: rbrack}
+ }
+ }
+ return &ast.SliceExpr{X: x, Lbrack: lbrack, Low: index[0], High: index[1], Max: index[2], Slice3: slice3, Rbrack: rbrack}
}
return &ast.IndexExpr{X: x, Lbrack: lbrack, Index: index[0], Rbrack: rbrack}
@@ -1320,7 +1357,7 @@ func (p *parser) checkExpr(x ast.Expr) ast.Expr {
default:
// all other nodes are not proper expressions
p.errorExpected(x.Pos(), "expression")
- x = &ast.BadExpr{From: x.Pos(), To: x.End()}
+ x = &ast.BadExpr{From: x.Pos(), To: p.safePos(x.End())}
}
return x
}
@@ -1383,7 +1420,7 @@ func (p *parser) checkExprOrType(x ast.Expr) ast.Expr {
case *ast.ArrayType:
if len, isEllipsis := t.Len.(*ast.Ellipsis); isEllipsis {
p.error(len.Pos(), "expected array length, found '...'")
- x = &ast.BadExpr{From: x.Pos(), To: x.End()}
+ x = &ast.BadExpr{From: x.Pos(), To: p.safePos(x.End())}
}
}
@@ -1669,14 +1706,14 @@ func (p *parser) parseSimpleStmt(mode int) (ast.Stmt, bool) {
return &ast.ExprStmt{X: x[0]}, false
}
-func (p *parser) parseCallExpr() *ast.CallExpr {
+func (p *parser) parseCallExpr(callType string) *ast.CallExpr {
x := p.parseRhsOrType() // could be a conversion: (some type)(x)
if call, isCall := x.(*ast.CallExpr); isCall {
return call
}
if _, isBad := x.(*ast.BadExpr); !isBad {
// only report error if it's a new one
- p.errorExpected(x.Pos(), "function/method call")
+ p.error(p.safePos(x.End()), fmt.Sprintf("function must be invoked in %s statement", callType))
}
return nil
}
@@ -1687,7 +1724,7 @@ func (p *parser) parseGoStmt() ast.Stmt {
}
pos := p.expect(token.GO)
- call := p.parseCallExpr()
+ call := p.parseCallExpr("go")
p.expectSemi()
if call == nil {
return &ast.BadStmt{From: pos, To: pos + 2} // len("go")
@@ -1702,7 +1739,7 @@ func (p *parser) parseDeferStmt() ast.Stmt {
}
pos := p.expect(token.DEFER)
- call := p.parseCallExpr()
+ call := p.parseCallExpr("defer")
p.expectSemi()
if call == nil {
return &ast.BadStmt{From: pos, To: pos + 5} // len("defer")
@@ -1745,15 +1782,15 @@ func (p *parser) parseBranchStmt(tok token.Token) *ast.BranchStmt {
return &ast.BranchStmt{TokPos: pos, Tok: tok, Label: label}
}
-func (p *parser) makeExpr(s ast.Stmt) ast.Expr {
+func (p *parser) makeExpr(s ast.Stmt, kind string) ast.Expr {
if s == nil {
return nil
}
if es, isExpr := s.(*ast.ExprStmt); isExpr {
return p.checkExpr(es.X)
}
- p.error(s.Pos(), "expected condition, found simple statement")
- return &ast.BadExpr{From: s.Pos(), To: s.End()}
+ p.error(s.Pos(), fmt.Sprintf("expected %s, found simple statement (missing parentheses around composite literal?)", kind))
+ return &ast.BadExpr{From: s.Pos(), To: p.safePos(s.End())}
}
func (p *parser) parseIfStmt() *ast.IfStmt {
@@ -1779,7 +1816,7 @@ func (p *parser) parseIfStmt() *ast.IfStmt {
p.next()
x = p.parseRhs()
} else {
- x = p.makeExpr(s)
+ x = p.makeExpr(s, "boolean expression")
s = nil
}
}
@@ -1910,7 +1947,7 @@ func (p *parser) parseSwitchStmt() ast.Stmt {
return &ast.TypeSwitchStmt{Switch: pos, Init: s1, Assign: s2, Body: body}
}
- return &ast.SwitchStmt{Switch: pos, Init: s1, Tag: p.makeExpr(s2), Body: body}
+ return &ast.SwitchStmt{Switch: pos, Init: s1, Tag: p.makeExpr(s2, "switch expression"), Body: body}
}
func (p *parser) parseCommClause() *ast.CommClause {
@@ -2035,7 +2072,7 @@ func (p *parser) parseForStmt() ast.Stmt {
key = as.Lhs[0]
default:
p.errorExpected(as.Lhs[0].Pos(), "1 or 2 expressions")
- return &ast.BadStmt{From: pos, To: body.End()}
+ return &ast.BadStmt{From: pos, To: p.safePos(body.End())}
}
// parseSimpleStmt returned a right-hand side that
// is a single unary expression of the form "range x"
@@ -2055,7 +2092,7 @@ func (p *parser) parseForStmt() ast.Stmt {
return &ast.ForStmt{
For: pos,
Init: s1,
- Cond: p.makeExpr(s2),
+ Cond: p.makeExpr(s2, "boolean or range expression"),
Post: s3,
Body: body,
}
@@ -2282,7 +2319,7 @@ func (p *parser) parseReceiver(scope *ast.Scope) *ast.FieldList {
p.errorExpected(base.Pos(), "(unqualified) identifier")
}
par.List = []*ast.Field{
- {Type: &ast.BadExpr{From: recv.Pos(), To: recv.End()}},
+ {Type: &ast.BadExpr{From: recv.Pos(), To: p.safePos(recv.End())}},
}
}
diff --git a/src/pkg/go/parser/parser_test.go b/src/pkg/go/parser/parser_test.go
index 0a34b7e50..2797ea518 100644
--- a/src/pkg/go/parser/parser_test.go
+++ b/src/pkg/go/parser/parser_test.go
@@ -78,7 +78,7 @@ func TestParseExpr(t *testing.T) {
}
// sanity check
if _, ok := x.(*ast.BinaryExpr); !ok {
- t.Errorf("ParseExpr(%s): got %T, expected *ast.BinaryExpr", src, x)
+ t.Errorf("ParseExpr(%s): got %T, want *ast.BinaryExpr", src, x)
}
// a valid type expression
@@ -89,17 +89,24 @@ func TestParseExpr(t *testing.T) {
}
// sanity check
if _, ok := x.(*ast.StructType); !ok {
- t.Errorf("ParseExpr(%s): got %T, expected *ast.StructType", src, x)
+ t.Errorf("ParseExpr(%s): got %T, want *ast.StructType", src, x)
}
// an invalid expression
src = "a + *"
_, err = ParseExpr(src)
if err == nil {
- t.Fatalf("ParseExpr(%s): %v", src, err)
+ t.Fatalf("ParseExpr(%s): got no error", src)
+ }
+
+ // a valid expression followed by extra tokens is invalid
+ src = "a[i] := x"
+ _, err = ParseExpr(src)
+ if err == nil {
+ t.Fatalf("ParseExpr(%s): got no error", src)
}
- // it must not crash
+ // ParseExpr must not crash
for _, src := range valids {
ParseExpr(src)
}
diff --git a/src/pkg/go/parser/short_test.go b/src/pkg/go/parser/short_test.go
index 0ef0c560c..b79406099 100644
--- a/src/pkg/go/parser/short_test.go
+++ b/src/pkg/go/parser/short_test.go
@@ -48,14 +48,14 @@ var invalids = []string{
`package p; func f() { if { /* ERROR "expected operand" */ } };`,
`package p; func f() { if ; { /* ERROR "expected operand" */ } };`,
`package p; func f() { if f(); { /* ERROR "expected operand" */ } };`,
- `package p; func f() { if _ /* ERROR "expected condition" */ = range x; true {} };`,
- `package p; func f() { switch _ /* ERROR "expected condition" */ = range x; true {} };`,
+ `package p; func f() { if _ /* ERROR "expected boolean expression" */ = range x; true {} };`,
+ `package p; func f() { switch _ /* ERROR "expected switch expression" */ = range x; true {} };`,
`package p; func f() { for _ = range x ; /* ERROR "expected '{'" */ ; {} };`,
`package p; func f() { for ; ; _ = range /* ERROR "expected operand" */ x {} };`,
- `package p; func f() { for ; _ /* ERROR "expected condition" */ = range x ; {} };`,
- `package p; func f() { switch t /* ERROR "expected condition" */ = t.(type) {} };`,
- `package p; func f() { switch t /* ERROR "expected condition" */ , t = t.(type) {} };`,
- `package p; func f() { switch t /* ERROR "expected condition" */ = t.(type), t {} };`,
+ `package p; func f() { for ; _ /* ERROR "expected boolean or range expression" */ = range x ; {} };`,
+ `package p; func f() { switch t /* ERROR "expected switch expression" */ = t.(type) {} };`,
+ `package p; func f() { switch t /* ERROR "expected switch expression" */ , t = t.(type) {} };`,
+ `package p; func f() { switch t /* ERROR "expected switch expression" */ = t.(type), t {} };`,
`package p; var a = [ /* ERROR "expected expression" */ 1]int;`,
`package p; var a = [ /* ERROR "expected expression" */ ...]int;`,
`package p; var a = struct /* ERROR "expected expression" */ {}`,
@@ -76,8 +76,19 @@ var invalids = []string{
`package p; func f() { _ = x = /* ERROR "expected '=='" */ 0 {}};`,
`package p; func f() { _ = 1 == func()int { var x bool; x = x = /* ERROR "expected '=='" */ true; return x }() };`,
`package p; func f() { var s []int; _ = s[] /* ERROR "expected operand" */ };`,
- `package p; func f() { var s []int; _ = s[::: /* ERROR "expected ']'" */ ] };`,
+ `package p; func f() { var s []int; _ = s[i:j: /* ERROR "3rd index required" */ ] };`,
+ `package p; func f() { var s []int; _ = s[i: /* ERROR "2nd index required" */ :k] };`,
+ `package p; func f() { var s []int; _ = s[i: /* ERROR "2nd index required" */ :] };`,
+ `package p; func f() { var s []int; _ = s[: /* ERROR "2nd index required" */ :] };`,
+ `package p; func f() { var s []int; _ = s[: /* ERROR "2nd index required" */ ::] };`,
`package p; func f() { var s []int; _ = s[i:j:k: /* ERROR "expected ']'" */ l] };`,
+ `package p; func f() { for x /* ERROR "boolean or range expression" */ = []string {} }`,
+ `package p; func f() { for x /* ERROR "boolean or range expression" */ := []string {} }`,
+ `package p; func f() { for i /* ERROR "boolean or range expression" */ , x = []string {} }`,
+ `package p; func f() { for i /* ERROR "boolean or range expression" */ , x := []string {} }`,
+ `package p; func f() { go f /* ERROR HERE "function must be invoked" */ }`,
+ `package p; func f() { defer func() {} /* ERROR HERE "function must be invoked" */ }`,
+ `package p; func f() { go func() { func() { f(x func /* ERROR "expected '\)'" */ (){}) } } }`,
}
func TestInvalid(t *testing.T) {