]> Cypherpunks repositories - gostls13.git/commitdiff
go/parser: resolve identifiers properly
authorRobert Griesemer <gri@golang.org>
Thu, 24 Mar 2011 18:45:52 +0000 (11:45 -0700)
committerRobert Griesemer <gri@golang.org>
Thu, 24 Mar 2011 18:45:52 +0000 (11:45 -0700)
Correctly distinguish between lhs and rhs identifiers
and resolve/declare them accordingly.

Collect field and method names in respective scopes
(will be available after some minor AST API changes).

Also collect imports since it's useful to have that
list directly w/o having to re-traverse the AST
(will also be available after some minor AST API changes).

No external API changes in this CL.

R=rsc, rog
CC=golang-dev
https://golang.org/cl/4271061

src/pkg/go/parser/interface.go
src/pkg/go/parser/parser.go

index 6f35b495efa15234c2a1b06e505031e44ae5edca..cca251b81fee2891d01d65f906711626a31eb2af 100644 (file)
@@ -69,7 +69,7 @@ func ParseExpr(fset *token.FileSet, filename string, src interface{}) (ast.Expr,
 
        var p parser
        p.init(fset, filename, data, 0)
-       x := p.parseExpr()
+       x := p.parseRhs()
        if p.tok == token.SEMICOLON {
                p.next() // consume automatically inserted semicolon, if any
        }
index b0e8c8ad7a8e86f6674e083962d40cd4b835b4f2..d2916d93a28f0c6fd1c41cfd90430b3955d9f593 100644 (file)
@@ -55,9 +55,10 @@ type parser struct {
        exprLev int // < 0: in control clause, >= 0: in expression
 
        // Ordinary identifer scopes
-       pkgScope   *ast.Scope   // pkgScope.Outer == nil
-       topScope   *ast.Scope   // top-most scope; may be pkgScope
-       unresolved []*ast.Ident // unresolved global identifiers
+       pkgScope   *ast.Scope        // pkgScope.Outer == nil
+       topScope   *ast.Scope        // top-most scope; may be pkgScope
+       unresolved []*ast.Ident      // unresolved identifiers
+       imports    []*ast.ImportSpec // list of imports
 
        // Label scope
        // (maintained by open/close LabelScope)
@@ -141,6 +142,7 @@ func (p *parser) closeLabelScope() {
 
 func (p *parser) declare(decl interface{}, scope *ast.Scope, kind ast.ObjKind, idents ...*ast.Ident) {
        for _, ident := range idents {
+               assert(ident.Obj == nil, "identifier already declared or resolved")
                if ident.Name != "_" {
                        obj := ast.NewObj(kind, ident.Name)
                        // remember the corresponding declaration for redeclaration
@@ -166,6 +168,7 @@ func (p *parser) shortVarDecl(idents []*ast.Ident) {
        // the same type, and at least one of the non-blank variables is new.
        n := 0 // number of new variables
        for _, ident := range idents {
+               assert(ident.Obj == nil, "identifier already declared or resolved")
                if ident.Name != "_" {
                        obj := ast.NewObj(ast.Var, ident.Name)
                        // short var declarations cannot have redeclaration errors
@@ -184,7 +187,19 @@ func (p *parser) shortVarDecl(idents []*ast.Ident) {
 }
 
 
-func (p *parser) resolve(ident *ast.Ident) {
+// The unresolved object is a sentinel to mark identifiers that have been added
+// to the list of unresolved identifiers. The sentinel is only used for verifying
+// internal consistency.
+var unresolved = new(ast.Object)
+
+
+func (p *parser) resolve(x ast.Expr) {
+       // nothing to do if x is not an identifier or the blank identifier
+       ident, _ := x.(*ast.Ident)
+       if ident == nil {
+               return
+       }
+       assert(ident.Obj == nil, "identifier already declared or resolved")
        if ident.Name == "_" {
                return
        }
@@ -195,10 +210,12 @@ func (p *parser) resolve(ident *ast.Ident) {
                        return
                }
        }
-       // collect unresolved global identifiers; ignore the others
-       if p.topScope == p.pkgScope {
-               p.unresolved = append(p.unresolved, ident)
-       }
+       // all local scopes are known, so any unresolved identifier
+       // must be found either in the file scope, package scope
+       // (perhaps in another file), or universe scope --- collect
+       // them so that they can be resolved later
+       ident.Obj = unresolved
+       p.unresolved = append(p.unresolved, ident)
 }
 
 
@@ -388,6 +405,13 @@ func (p *parser) expectSemi() {
 }
 
 
+func assert(cond bool, msg string) {
+       if !cond {
+               panic("go/parser internal error: " + msg)
+       }
+}
+
+
 // ----------------------------------------------------------------------------
 // Identifiers
 
@@ -422,21 +446,51 @@ func (p *parser) parseIdentList() (list []*ast.Ident) {
 // ----------------------------------------------------------------------------
 // Common productions
 
-func (p *parser) parseExprList() (list []ast.Expr) {
+// If lhs is set, result list elements which are identifiers are not resolved.
+func (p *parser) parseExprList(lhs bool) (list []ast.Expr) {
        if p.trace {
                defer un(trace(p, "ExpressionList"))
        }
 
-       list = append(list, p.parseExpr())
+       list = append(list, p.parseExpr(lhs))
        for p.tok == token.COMMA {
                p.next()
-               list = append(list, p.parseExpr())
+               list = append(list, p.parseExpr(lhs))
        }
 
        return
 }
 
 
+func (p *parser) parseLhsList() []ast.Expr {
+       list := p.parseExprList(true)
+       switch p.tok {
+       case token.DEFINE:
+               // lhs of a short variable declaration
+               p.shortVarDecl(p.makeIdentList(list))
+       case token.COLON:
+               // lhs of a label declaration or a communication clause of a select
+               // statement (parseLhsList is not called when parsing the case clause
+               // of a switch statement):
+               // - labels are declared by the caller of parseLhsList
+               // - for communication clauses, if there is a stand-alone identifier
+               //   followed by a colon, we have a syntax error; there is no need
+               //   to resolve the identifier in that case
+       default:
+               // identifiers must be declared elsewhere
+               for _, x := range list {
+                       p.resolve(x)
+               }
+       }
+       return list
+}
+
+
+func (p *parser) parseRhsList() []ast.Expr {
+       return p.parseExprList(false)
+}
+
+
 // ----------------------------------------------------------------------------
 // Types
 
@@ -458,31 +512,24 @@ func (p *parser) parseType() ast.Expr {
 }
 
 
-func (p *parser) parseQualifiedIdent() ast.Expr {
+// If the result is an identifier, it is not resolved.
+func (p *parser) parseTypeName() ast.Expr {
        if p.trace {
-               defer un(trace(p, "QualifiedIdent"))
+               defer un(trace(p, "TypeName"))
        }
 
        ident := p.parseIdent()
-       p.resolve(ident)
-       var x ast.Expr = ident
+       // don't resolve ident yet - it may be a parameter or field name
+
        if p.tok == token.PERIOD {
-               // first identifier is a package identifier
+               // ident is a package name
                p.next()
+               p.resolve(ident)
                sel := p.parseIdent()
-               x = &ast.SelectorExpr{x, sel}
+               return &ast.SelectorExpr{ident, sel}
        }
 
-       return x
-}
-
-
-func (p *parser) parseTypeName() ast.Expr {
-       if p.trace {
-               defer un(trace(p, "TypeName"))
-       }
-
-       return p.parseQualifiedIdent()
+       return ident
 }
 
 
@@ -497,7 +544,7 @@ func (p *parser) parseArrayType(ellipsisOk bool) ast.Expr {
                len = &ast.Ellipsis{p.pos, nil}
                p.next()
        } else if p.tok != token.RBRACK {
-               len = p.parseExpr()
+               len = p.parseRhs()
        }
        p.expect(token.RBRACK)
        elt := p.parseType()
@@ -521,7 +568,7 @@ func (p *parser) makeIdentList(list []ast.Expr) []*ast.Ident {
 }
 
 
-func (p *parser) parseFieldDecl() *ast.Field {
+func (p *parser) parseFieldDecl(scope *ast.Scope) *ast.Field {
        if p.trace {
                defer un(trace(p, "FieldDecl"))
        }
@@ -546,6 +593,7 @@ func (p *parser) parseFieldDecl() *ast.Field {
        } else {
                // ["*"] TypeName (AnonymousField)
                typ = list[0] // we always have at least one element
+               p.resolve(typ)
                if n := len(list); n > 1 || !isTypeName(deref(typ)) {
                        pos := typ.Pos()
                        p.errorExpected(pos, "anonymous field")
@@ -555,7 +603,10 @@ func (p *parser) parseFieldDecl() *ast.Field {
 
        p.expectSemi() // call before accessing p.linecomment
 
-       return &ast.Field{doc, idents, typ, tag, p.lineComment}
+       field := &ast.Field{doc, idents, typ, tag, p.lineComment}
+       p.declare(field, scope, ast.Var, idents...)
+
+       return field
 }
 
 
@@ -566,15 +617,17 @@ func (p *parser) parseStructType() *ast.StructType {
 
        pos := p.expect(token.STRUCT)
        lbrace := p.expect(token.LBRACE)
+       scope := ast.NewScope(nil) // struct scope
        var list []*ast.Field
        for p.tok == token.IDENT || p.tok == token.MUL || p.tok == token.LPAREN {
                // a field declaration cannot start with a '(' but we accept
                // it here for more robust parsing and better error messages
                // (parseFieldDecl will check and complain if necessary)
-               list = append(list, p.parseFieldDecl())
+               list = append(list, p.parseFieldDecl(scope))
        }
        rbrace := p.expect(token.RBRACE)
 
+       // TODO(gri): store struct scope in AST
        return &ast.StructType{pos, &ast.FieldList{lbrace, list, rbrace}, false}
 }
 
@@ -595,7 +648,7 @@ func (p *parser) tryVarType(isParam bool) ast.Expr {
        if isParam && p.tok == token.ELLIPSIS {
                pos := p.pos
                p.next()
-               typ := p.tryType() // don't use parseType so we can provide better error message
+               typ := p.tryIdentOrType(isParam) // don't use parseType so we can provide better error message
                if typ == nil {
                        p.error(pos, "'...' parameter is missing type")
                        typ = &ast.BadExpr{pos, p.pos}
@@ -605,7 +658,7 @@ func (p *parser) tryVarType(isParam bool) ast.Expr {
                }
                return &ast.Ellipsis{pos, typ}
        }
-       return p.tryType()
+       return p.tryIdentOrType(false)
 }
 
 
@@ -641,6 +694,9 @@ func (p *parser) parseVarList(isParam bool) (list []ast.Expr, typ ast.Expr) {
 
        // if we had a list of identifiers, it must be followed by a type
        typ = p.tryVarType(isParam)
+       if typ != nil {
+               p.resolve(typ)
+       }
 
        return
 }
@@ -682,6 +738,7 @@ func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params [
                // Type { "," Type } (anonymous parameters)
                params = make([]*ast.Field, len(list))
                for i, x := range list {
+                       p.resolve(x)
                        params[i] = &ast.Field{Type: x}
                }
        }
@@ -751,7 +808,7 @@ func (p *parser) parseFuncType() (*ast.FuncType, *ast.Scope) {
 }
 
 
-func (p *parser) parseMethodSpec() *ast.Field {
+func (p *parser) parseMethodSpec(scope *ast.Scope) *ast.Field {
        if p.trace {
                defer un(trace(p, "MethodSpec"))
        }
@@ -759,7 +816,7 @@ func (p *parser) parseMethodSpec() *ast.Field {
        doc := p.leadComment
        var idents []*ast.Ident
        var typ ast.Expr
-       x := p.parseQualifiedIdent()
+       x := p.parseTypeName()
        if ident, isIdent := x.(*ast.Ident); isIdent && p.tok == token.LPAREN {
                // method
                idents = []*ast.Ident{ident}
@@ -772,7 +829,10 @@ func (p *parser) parseMethodSpec() *ast.Field {
        }
        p.expectSemi() // call before accessing p.linecomment
 
-       return &ast.Field{doc, idents, typ, nil, p.lineComment}
+       spec := &ast.Field{doc, idents, typ, nil, p.lineComment}
+       p.declare(spec, scope, ast.Fun, idents...)
+
+       return spec
 }
 
 
@@ -783,12 +843,14 @@ func (p *parser) parseInterfaceType() *ast.InterfaceType {
 
        pos := p.expect(token.INTERFACE)
        lbrace := p.expect(token.LBRACE)
+       scope := ast.NewScope(nil) // interface scope
        var list []*ast.Field
        for p.tok == token.IDENT {
-               list = append(list, p.parseMethodSpec())
+               list = append(list, p.parseMethodSpec(scope))
        }
        rbrace := p.expect(token.RBRACE)
 
+       // TODO(gri): store interface scope in AST
        return &ast.InterfaceType{pos, &ast.FieldList{lbrace, list, rbrace}, false}
 }
 
@@ -832,7 +894,8 @@ func (p *parser) parseChanType() *ast.ChanType {
 }
 
 
-func (p *parser) tryRawType(ellipsisOk bool) ast.Expr {
+// If the result is an identifier, it is not resolved.
+func (p *parser) tryIdentOrType(ellipsisOk bool) ast.Expr {
        switch p.tok {
        case token.IDENT:
                return p.parseTypeName()
@@ -864,7 +927,13 @@ func (p *parser) tryRawType(ellipsisOk bool) ast.Expr {
 }
 
 
-func (p *parser) tryType() ast.Expr { return p.tryRawType(false) }
+func (p *parser) tryType() ast.Expr {
+       typ := p.tryIdentOrType(false)
+       if typ != nil {
+               p.resolve(typ)
+       }
+       return typ
+}
 
 
 // ----------------------------------------------------------------------------
@@ -939,17 +1008,20 @@ func (p *parser) parseFuncTypeOrLit() ast.Expr {
 
 // parseOperand may return an expression or a raw type (incl. array
 // types of the form [...]T. Callers must verify the result.
+// If lhs is set and the result is an identifier, it is not resolved.
 //
-func (p *parser) parseOperand() ast.Expr {
+func (p *parser) parseOperand(lhs bool) ast.Expr {
        if p.trace {
                defer un(trace(p, "Operand"))
        }
 
        switch p.tok {
        case token.IDENT:
-               ident := p.parseIdent()
-               p.resolve(ident)
-               return ident
+               x := p.parseIdent()
+               if !lhs {
+                       p.resolve(x)
+               }
+               return x
 
        case token.INT, token.FLOAT, token.IMAG, token.CHAR, token.STRING:
                x := &ast.BasicLit{p.pos, p.tok, p.lit()}
@@ -960,7 +1032,7 @@ func (p *parser) parseOperand() ast.Expr {
                lparen := p.pos
                p.next()
                p.exprLev++
-               x := p.parseExpr()
+               x := p.parseRhs()
                p.exprLev--
                rparen := p.expect(token.RPAREN)
                return &ast.ParenExpr{lparen, x, rparen}
@@ -969,9 +1041,11 @@ func (p *parser) parseOperand() ast.Expr {
                return p.parseFuncTypeOrLit()
 
        default:
-               t := p.tryRawType(true) // could be type for composite literal or conversion
-               if t != nil {
-                       return t
+               if typ := p.tryIdentOrType(true); typ != nil {
+                       // could be type for composite literal or conversion
+                       _, isIdent := typ.(*ast.Ident)
+                       assert(!isIdent, "type cannot be identifier")
+                       return typ
                }
        }
 
@@ -982,19 +1056,22 @@ func (p *parser) parseOperand() ast.Expr {
 }
 
 
-func (p *parser) parseSelectorOrTypeAssertion(x ast.Expr) ast.Expr {
+func (p *parser) parseSelector(x ast.Expr) ast.Expr {
        if p.trace {
-               defer un(trace(p, "SelectorOrTypeAssertion"))
+               defer un(trace(p, "Selector"))
        }
 
-       p.expect(token.PERIOD)
-       if p.tok == token.IDENT {
-               // selector
-               sel := p.parseIdent()
-               return &ast.SelectorExpr{x, sel}
+       sel := p.parseIdent()
+
+       return &ast.SelectorExpr{x, sel}
+}
+
+
+func (p *parser) parseTypeAssertion(x ast.Expr) ast.Expr {
+       if p.trace {
+               defer un(trace(p, "TypeAssertion"))
        }
 
-       // type assertion
        p.expect(token.LPAREN)
        var typ ast.Expr
        if p.tok == token.TYPE {
@@ -1019,13 +1096,13 @@ func (p *parser) parseIndexOrSlice(x ast.Expr) ast.Expr {
        var low, high ast.Expr
        isSlice := false
        if p.tok != token.COLON {
-               low = p.parseExpr()
+               low = p.parseRhs()
        }
        if p.tok == token.COLON {
                isSlice = true
                p.next()
                if p.tok != token.RBRACK {
-                       high = p.parseExpr()
+                       high = p.parseRhs()
                }
        }
        p.exprLev--
@@ -1048,7 +1125,7 @@ func (p *parser) parseCallOrConversion(fun ast.Expr) *ast.CallExpr {
        var list []ast.Expr
        var ellipsis token.Pos
        for p.tok != token.RPAREN && p.tok != token.EOF && !ellipsis.IsValid() {
-               list = append(list, p.parseExpr())
+               list = append(list, p.parseRhs())
                if p.tok == token.ELLIPSIS {
                        ellipsis = p.pos
                        p.next()
@@ -1074,7 +1151,7 @@ func (p *parser) parseElement(keyOk bool) ast.Expr {
                return p.parseLiteralValue(nil)
        }
 
-       x := p.parseExpr()
+       x := p.parseRhs()
        if keyOk && p.tok == token.COLON {
                colon := p.pos
                p.next()
@@ -1231,23 +1308,47 @@ func (p *parser) checkExprOrType(x ast.Expr) ast.Expr {
 }
 
 
-func (p *parser) parsePrimaryExpr() ast.Expr {
+// If lhs is set and the result is an identifier, it is not resolved.
+func (p *parser) parsePrimaryExpr(lhs bool) ast.Expr {
        if p.trace {
                defer un(trace(p, "PrimaryExpr"))
        }
 
-       x := p.parseOperand()
+       x := p.parseOperand(lhs)
 L:
        for {
                switch p.tok {
                case token.PERIOD:
-                       x = p.parseSelectorOrTypeAssertion(p.checkExpr(x))
+                       p.next()
+                       if lhs {
+                               p.resolve(x)
+                       }
+                       switch p.tok {
+                       case token.IDENT:
+                               x = p.parseSelector(p.checkExpr(x))
+                       case token.LPAREN:
+                               x = p.parseTypeAssertion(p.checkExpr(x))
+                       default:
+                               pos := p.pos
+                               p.next() // make progress
+                               p.errorExpected(pos, "selector or type assertion")
+                               x = &ast.BadExpr{pos, p.pos}
+                       }
                case token.LBRACK:
+                       if lhs {
+                               p.resolve(x)
+                       }
                        x = p.parseIndexOrSlice(p.checkExpr(x))
                case token.LPAREN:
+                       if lhs {
+                               p.resolve(x)
+                       }
                        x = p.parseCallOrConversion(p.checkExprOrType(x))
                case token.LBRACE:
                        if isLiteralType(x) && (p.exprLev >= 0 || !isTypeName(x)) {
+                               if lhs {
+                                       p.resolve(x)
+                               }
                                x = p.parseLiteralValue(x)
                        } else {
                                break L
@@ -1255,13 +1356,15 @@ L:
                default:
                        break L
                }
+               lhs = false // no need to try to resolve again
        }
 
        return x
 }
 
 
-func (p *parser) parseUnaryExpr() ast.Expr {
+// If lhs is set and the result is an identifier, it is not resolved.
+func (p *parser) parseUnaryExpr(lhs bool) ast.Expr {
        if p.trace {
                defer un(trace(p, "UnaryExpr"))
        }
@@ -1270,7 +1373,7 @@ func (p *parser) parseUnaryExpr() ast.Expr {
        case token.ADD, token.SUB, token.NOT, token.XOR, token.AND, token.RANGE:
                pos, op := p.pos, p.tok
                p.next()
-               x := p.parseUnaryExpr()
+               x := p.parseUnaryExpr(false)
                return &ast.UnaryExpr{pos, op, p.checkExpr(x)}
 
        case token.ARROW:
@@ -1283,32 +1386,37 @@ func (p *parser) parseUnaryExpr() ast.Expr {
                        return &ast.ChanType{pos, ast.RECV, value}
                }
 
-               x := p.parseUnaryExpr()
+               x := p.parseUnaryExpr(false)
                return &ast.UnaryExpr{pos, token.ARROW, p.checkExpr(x)}
 
        case token.MUL:
                // pointer type or unary "*" expression
                pos := p.pos
                p.next()
-               x := p.parseUnaryExpr()
+               x := p.parseUnaryExpr(false)
                return &ast.StarExpr{pos, p.checkExprOrType(x)}
        }
 
-       return p.parsePrimaryExpr()
+       return p.parsePrimaryExpr(lhs)
 }
 
 
-func (p *parser) parseBinaryExpr(prec1 int) ast.Expr {
+// If lhs is set and the result is an identifier, it is not resolved.
+func (p *parser) parseBinaryExpr(lhs bool, prec1 int) ast.Expr {
        if p.trace {
                defer un(trace(p, "BinaryExpr"))
        }
 
-       x := p.parseUnaryExpr()
+       x := p.parseUnaryExpr(lhs)
        for prec := p.tok.Precedence(); prec >= prec1; prec-- {
                for p.tok.Precedence() == prec {
                        pos, op := p.pos, p.tok
                        p.next()
-                       y := p.parseBinaryExpr(prec + 1)
+                       if lhs {
+                               p.resolve(x)
+                               lhs = false
+                       }
+                       y := p.parseBinaryExpr(false, prec+1)
                        x = &ast.BinaryExpr{p.checkExpr(x), pos, op, p.checkExpr(y)}
                }
        }
@@ -1317,14 +1425,20 @@ func (p *parser) parseBinaryExpr(prec1 int) ast.Expr {
 }
 
 
+// If lhs is set and the result is an identifier, it is not resolved.
 // TODO(gri): parseExpr may return a type or even a raw type ([..]int) -
 //            should reject when a type/raw type is obviously not allowed
-func (p *parser) parseExpr() ast.Expr {
+func (p *parser) parseExpr(lhs bool) ast.Expr {
        if p.trace {
                defer un(trace(p, "Expression"))
        }
 
-       return p.parseBinaryExpr(token.LowestPrec + 1)
+       return p.parseBinaryExpr(lhs, token.LowestPrec+1)
+}
+
+
+func (p *parser) parseRhs() ast.Expr {
+       return p.parseExpr(false)
 }
 
 
@@ -1336,7 +1450,7 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt {
                defer un(trace(p, "SimpleStmt"))
        }
 
-       x := p.parseExprList()
+       x := p.parseLhsList()
 
        switch p.tok {
        case
@@ -1347,10 +1461,7 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt {
                // assignment statement
                pos, tok := p.pos, p.tok
                p.next()
-               y := p.parseExprList()
-               if tok == token.DEFINE {
-                       p.shortVarDecl(p.makeIdentList(x))
-               }
+               y := p.parseRhsList()
                return &ast.AssignStmt{x, pos, tok, y}
        }
 
@@ -1379,7 +1490,7 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt {
                // send statement
                arrow := p.pos
                p.next() // consume "<-"
-               y := p.parseExpr()
+               y := p.parseRhs()
                return &ast.SendStmt{x[0], arrow, y}
 
        case token.INC, token.DEC:
@@ -1395,7 +1506,7 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt {
 
 
 func (p *parser) parseCallExpr() *ast.CallExpr {
-       x := p.parseExpr()
+       x := p.parseRhs()
        if call, isCall := x.(*ast.CallExpr); isCall {
                return call
        }
@@ -1445,7 +1556,7 @@ func (p *parser) parseReturnStmt() *ast.ReturnStmt {
        p.expect(token.RETURN)
        var x []ast.Expr
        if p.tok != token.SEMICOLON && p.tok != token.RBRACE {
-               x = p.parseExprList()
+               x = p.parseRhsList()
        }
        p.expectSemi()
 
@@ -1500,12 +1611,12 @@ func (p *parser) parseIfStmt() *ast.IfStmt {
                p.exprLev = -1
                if p.tok == token.SEMICOLON {
                        p.next()
-                       x = p.parseExpr()
+                       x = p.parseRhs()
                } else {
                        s = p.parseSimpleStmt(false)
                        if p.tok == token.SEMICOLON {
                                p.next()
-                               x = p.parseExpr()
+                               x = p.parseRhs()
                        } else {
                                x = p.makeExpr(s)
                                s = nil
@@ -1552,7 +1663,7 @@ func (p *parser) parseCaseClause(exprSwitch bool) *ast.CaseClause {
        if p.tok == token.CASE {
                p.next()
                if exprSwitch {
-                       list = p.parseExprList()
+                       list = p.parseRhsList()
                } else {
                        list = p.parseTypeList()
                }
@@ -1639,7 +1750,7 @@ func (p *parser) parseCommClause() *ast.CommClause {
        var comm ast.Stmt
        if p.tok == token.CASE {
                p.next()
-               lhs := p.parseExprList()
+               lhs := p.parseLhsList()
                if p.tok == token.ARROW {
                        // SendStmt
                        if len(lhs) > 1 {
@@ -1648,7 +1759,7 @@ func (p *parser) parseCommClause() *ast.CommClause {
                        }
                        arrow := p.pos
                        p.next()
-                       rhs := p.parseExpr()
+                       rhs := p.parseRhs()
                        comm = &ast.SendStmt{lhs[0], arrow, rhs}
                } else {
                        // RecvStmt
@@ -1663,10 +1774,7 @@ func (p *parser) parseCommClause() *ast.CommClause {
                                        lhs = lhs[0:2]
                                }
                                p.next()
-                               rhs = p.parseExpr()
-                               if tok == token.DEFINE {
-                                       p.shortVarDecl(p.makeIdentList(lhs))
-                               }
+                               rhs = p.parseRhs()
                        } else {
                                // rhs must be single receive operation
                                if len(lhs) > 1 {
@@ -1873,7 +1981,11 @@ func parseImportSpec(p *parser, doc *ast.CommentGroup, _ int) ast.Spec {
        }
        p.expectSemi() // call before accessing p.linecomment
 
-       return &ast.ImportSpec{doc, ident, path, p.lineComment}
+       // collect imports
+       spec := &ast.ImportSpec{doc, ident, path, p.lineComment}
+       p.imports = append(p.imports, spec)
+
+       return spec
 }
 
 
@@ -1887,7 +1999,7 @@ func parseConstSpec(p *parser, doc *ast.CommentGroup, iota int) ast.Spec {
        var values []ast.Expr
        if typ != nil || p.tok == token.ASSIGN || iota == 0 {
                p.expect(token.ASSIGN)
-               values = p.parseExprList()
+               values = p.parseRhsList()
        }
        p.expectSemi() // call before accessing p.linecomment
 
@@ -1932,7 +2044,7 @@ func parseVarSpec(p *parser, doc *ast.CommentGroup, _ int) ast.Spec {
        var values []ast.Expr
        if typ == nil || p.tok == token.ASSIGN {
                p.expect(token.ASSIGN)
-               values = p.parseExprList()
+               values = p.parseRhsList()
        }
        p.expectSemi() // call before accessing p.linecomment
 
@@ -2120,20 +2232,20 @@ func (p *parser) parseFile() *ast.File {
                }
        }
 
-       if p.topScope != p.pkgScope {
-               panic("internal error: imbalanced scopes")
-       }
+       assert(p.topScope == p.pkgScope, "imbalanced scopes")
 
        // resolve global identifiers within the same file
        i := 0
        for _, ident := range p.unresolved {
                // i <= index for current ident
-               ident.Obj = p.pkgScope.Lookup(ident.Name)
+               assert(ident.Obj == unresolved, "object already resolved")
+               ident.Obj = p.pkgScope.Lookup(ident.Name) // also removes unresolved sentinel
                if ident.Obj == nil {
                        p.unresolved[i] = ident
                        i++
                }
        }
 
+       // TODO(gri): store p.imports in AST
        return &ast.File{doc, pos, ident, decls, p.pkgScope, p.unresolved[0:i], p.comments}
 }