]> Cypherpunks repositories - gostls13.git/commitdiff
go/parser: disallow for statements w/ illegal range clauses
authorRobert Griesemer <gri@golang.org>
Wed, 17 Aug 2011 17:45:30 +0000 (10:45 -0700)
committerRobert Griesemer <gri@golang.org>
Wed, 17 Aug 2011 17:45:30 +0000 (10:45 -0700)
R=rsc
CC=golang-dev
https://golang.org/cl/4908047

src/pkg/go/parser/parser.go
src/pkg/go/parser/parser_test.go

index c9e6f9096348be8d8772d34e5fa8f8ef284f58fa..9c14d16673242fb2973924abd1f7c34a66bb703a 100644 (file)
@@ -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
index 58156a38aa050d1f6842ec3c07beee0bbaaaae30..39a78e5156e79b8e64babc4fdf1c16777652aaf3 100644 (file)
@@ -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 */`,