Commit 42e6c03f authored by Robert Griesemer's avatar Robert Griesemer

go/parser: disallow for statements w/ illegal range clauses

R=rsc
CC=golang-dev
https://golang.org/cl/4908047
parent 0f7bc92b
......@@ -1366,7 +1366,18 @@ func (p *parser) parseRhsOrType() ast.Expr {
// ----------------------------------------------------------------------------
// Statements
func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt {
// Parsing modes for parseSimpleStmt.
const (
basic = iota
labelOk
rangeOk
)
// parseSimpleStmt returns true as 2nd result if it parsed the assignment
// of a range clause (with mode == rangeOk). The returned statement is an
// assignment with a right-hand side that is a single unary expression of
// the form "range x". No guarantees are given for the left-hand side.
func (p *parser) parseSimpleStmt(mode int) (ast.Stmt, bool) {
if p.trace {
defer un(trace(p, "SimpleStmt"))
}
......@@ -1383,14 +1394,16 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt {
pos, tok := p.pos, p.tok
p.next()
var y []ast.Expr
if p.tok == token.RANGE && (tok == token.DEFINE || tok == token.ASSIGN) {
isRange := false
if mode == rangeOk && p.tok == token.RANGE && (tok == token.DEFINE || tok == token.ASSIGN) {
pos := p.pos
p.next()
y = []ast.Expr{&ast.UnaryExpr{pos, token.RANGE, p.parseRhs()}}
isRange = true
} else {
y = p.parseRhsList()
}
return &ast.AssignStmt{x, pos, tok, y}
return &ast.AssignStmt{x, pos, tok, y}, isRange
}
if len(x) > 1 {
......@@ -1403,13 +1416,13 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt {
// labeled statement
colon := p.pos
p.next()
if label, isIdent := x[0].(*ast.Ident); labelOk && isIdent {
if label, isIdent := x[0].(*ast.Ident); mode == labelOk && isIdent {
// Go spec: The scope of a label is the body of the function
// in which it is declared and excludes the body of any nested
// function.
stmt := &ast.LabeledStmt{label, colon, p.parseStmt()}
p.declare(stmt, nil, p.labelScope, ast.Lbl, label)
return stmt
return stmt, false
}
// The label declaration typically starts at x[0].Pos(), but the label
// declaration may be erroneous due to a token after that position (and
......@@ -1418,24 +1431,24 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt {
// before the ':' that caused the problem. Thus, use the (latest) colon
// position for error reporting.
p.error(colon, "illegal label declaration")
return &ast.BadStmt{x[0].Pos(), colon + 1}
return &ast.BadStmt{x[0].Pos(), colon + 1}, false
case token.ARROW:
// send statement
arrow := p.pos
p.next() // consume "<-"
y := p.parseRhs()
return &ast.SendStmt{x[0], arrow, y}
return &ast.SendStmt{x[0], arrow, y}, false
case token.INC, token.DEC:
// increment or decrement
s := &ast.IncDecStmt{x[0], p.pos, p.tok}
p.next() // consume "++" or "--"
return s
return s, false
}
// expression
return &ast.ExprStmt{x[0]}
return &ast.ExprStmt{x[0]}, false
}
func (p *parser) parseCallExpr() *ast.CallExpr {
......@@ -1540,7 +1553,7 @@ func (p *parser) parseIfStmt() *ast.IfStmt {
p.next()
x = p.parseRhs()
} else {
s = p.parseSimpleStmt(false)
s, _ = p.parseSimpleStmt(basic)
if p.tok == token.SEMICOLON {
p.next()
x = p.parseRhs()
......@@ -1631,14 +1644,14 @@ func (p *parser) parseSwitchStmt() ast.Stmt {
prevLev := p.exprLev
p.exprLev = -1
if p.tok != token.SEMICOLON {
s2 = p.parseSimpleStmt(false)
s2, _ = p.parseSimpleStmt(basic)
}
if p.tok == token.SEMICOLON {
p.next()
s1 = s2
s2 = nil
if p.tok != token.LBRACE {
s2 = p.parseSimpleStmt(false)
s2, _ = p.parseSimpleStmt(basic)
}
}
p.exprLev = prevLev
......@@ -1751,22 +1764,23 @@ func (p *parser) parseForStmt() ast.Stmt {
defer p.closeScope()
var s1, s2, s3 ast.Stmt
var isRange bool
if p.tok != token.LBRACE {
prevLev := p.exprLev
p.exprLev = -1
if p.tok != token.SEMICOLON {
s2 = p.parseSimpleStmt(false)
s2, isRange = p.parseSimpleStmt(rangeOk)
}
if p.tok == token.SEMICOLON {
if !isRange && p.tok == token.SEMICOLON {
p.next()
s1 = s2
s2 = nil
if p.tok != token.SEMICOLON {
s2 = p.parseSimpleStmt(false)
s2, _ = p.parseSimpleStmt(basic)
}
p.expectSemi()
if p.tok != token.LBRACE {
s3 = p.parseSimpleStmt(false)
s3, _ = p.parseSimpleStmt(basic)
}
}
p.exprLev = prevLev
......@@ -1775,12 +1789,8 @@ func (p *parser) parseForStmt() ast.Stmt {
body := p.parseBlockStmt()
p.expectSemi()
if as, isAssign := s2.(*ast.AssignStmt); isAssign {
// possibly a for statement with a range clause; check assignment operator
if as.Tok != token.ASSIGN && as.Tok != token.DEFINE {
p.errorExpected(as.TokPos, "'=' or ':='")
return &ast.BadStmt{pos, body.End()}
}
if isRange {
as := s2.(*ast.AssignStmt)
// check lhs
var key, value ast.Expr
switch len(as.Lhs) {
......@@ -1792,18 +1802,10 @@ func (p *parser) parseForStmt() ast.Stmt {
p.errorExpected(as.Lhs[0].Pos(), "1 or 2 expressions")
return &ast.BadStmt{pos, body.End()}
}
// check rhs
if len(as.Rhs) != 1 {
p.errorExpected(as.Rhs[0].Pos(), "1 expression")
return &ast.BadStmt{pos, body.End()}
}
if rhs, isUnary := as.Rhs[0].(*ast.UnaryExpr); isUnary && rhs.Op == token.RANGE {
// rhs is range expression
// (any short variable declaration was handled by parseSimpleStmt above)
return &ast.RangeStmt{pos, key, value, as.TokPos, as.Tok, rhs.X, body}
}
p.errorExpected(s2.Pos(), "range clause")
return &ast.BadStmt{pos, body.End()}
// parseSimpleStmt returned a right-hand side that
// is a single unary expression of the form "range x"
x := as.Rhs[0].(*ast.UnaryExpr).X
return &ast.RangeStmt{pos, key, value, as.TokPos, as.Tok, x, body}
}
// regular for statement
......@@ -1823,7 +1825,7 @@ func (p *parser) parseStmt() (s ast.Stmt) {
token.IDENT, token.INT, token.FLOAT, token.CHAR, token.STRING, token.FUNC, token.LPAREN, // operand
token.LBRACK, token.STRUCT, // composite type
token.MUL, token.AND, token.ARROW, token.ADD, token.SUB, token.XOR: // unary operators
s = p.parseSimpleStmt(true)
s, _ = p.parseSimpleStmt(labelOk)
// because of the required look-ahead, labeled statements are
// parsed by parseSimpleStmt - don't expect a semicolon after
// them
......
......@@ -21,6 +21,11 @@ var illegalInputs = []interface{}{
`package p; func f() { if ; /* should have condition */ {} };`,
`package p; func f() { if f(); /* should have condition */ {} };`,
`package p; const c; /* should have constant value */`,
`package p; func f() { if _ = range x; true {} };`,
`package p; func f() { switch _ = range x; true {} };`,
`package p; func f() { for _ = range x ; ; {} };`,
`package p; func f() { for ; ; _ = range x {} };`,
`package p; func f() { for ; _ = range x ; {} };`,
`package p; var a = [1]int; /* illegal expression */`,
`package p; var a = [...]int; /* illegal expression */`,
`package p; var a = struct{} /* illegal expression */`,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment