diff --git a/src/go/parser/interface.go b/src/go/parser/interface.go index 123faaee77..8f306adaab 100644 --- a/src/go/parser/interface.go +++ b/src/go/parser/interface.go @@ -215,15 +215,7 @@ func ParseExprFrom(fset *token.FileSet, filename string, src interface{}, mode M // parse expr p.init(fset, filename, text, mode) - // Set up pkg-level scopes to avoid nil-pointer errors. - // This is not needed for a correct expression x as the - // parser will be ok with a nil topScope, but be cautious - // in case of an erroneous x. - p.openScope() - p.pkgScope = p.topScope expr = p.parseRhsOrType() - p.closeScope() - assert(p.topScope == nil, "unbalanced scopes") // If a semicolon was inserted, consume it; // report an error if there's more tokens. diff --git a/src/go/parser/parser.go b/src/go/parser/parser.go index 0a69515be1..93ab4a4600 100644 --- a/src/go/parser/parser.go +++ b/src/go/parser/parser.go @@ -58,16 +58,7 @@ type parser struct { exprLev int // < 0: in control clause, >= 0: in expression inRhs bool // if set, the parser is parsing a rhs expression - // Ordinary identifier scopes - 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 scopes - // (maintained by open/close LabelScope) - labelScope *ast.Scope // label scope for current function - targetStack [][]*ast.Ident // stack of unresolved labels + imports []*ast.ImportSpec // list of imports } func (p *parser) init(fset *token.FileSet, filename string, src []byte, mode Mode) { @@ -81,134 +72,9 @@ func (p *parser) init(fset *token.FileSet, filename string, src []byte, mode Mod p.mode = mode p.trace = mode&Trace != 0 // for convenience (p.trace is used frequently) - p.next() } -// ---------------------------------------------------------------------------- -// Scoping support - -func (p *parser) openScope() { - p.topScope = ast.NewScope(p.topScope) -} - -func (p *parser) closeScope() { - p.topScope = p.topScope.Outer -} - -func (p *parser) openLabelScope() { - p.labelScope = ast.NewScope(p.labelScope) - p.targetStack = append(p.targetStack, nil) -} - -func (p *parser) closeLabelScope() { - // resolve labels - n := len(p.targetStack) - 1 - scope := p.labelScope - for _, ident := range p.targetStack[n] { - ident.Obj = scope.Lookup(ident.Name) - if ident.Obj == nil && p.mode&DeclarationErrors != 0 { - p.error(ident.Pos(), fmt.Sprintf("label %s undefined", ident.Name)) - } - } - // pop label scope - p.targetStack = p.targetStack[0:n] - p.labelScope = p.labelScope.Outer -} - -func (p *parser) declare(decl, data interface{}, scope *ast.Scope, kind ast.ObjKind, idents ...*ast.Ident) { - for _, ident := range idents { - assert(ident.Obj == nil, "identifier already declared or resolved") - obj := ast.NewObj(kind, ident.Name) - // remember the corresponding declaration for redeclaration - // errors and global variable resolution/typechecking phase - obj.Decl = decl - obj.Data = data - ident.Obj = obj - if ident.Name != "_" { - if alt := scope.Insert(obj); alt != nil && p.mode&DeclarationErrors != 0 { - prevDecl := "" - if pos := alt.Pos(); pos.IsValid() { - prevDecl = fmt.Sprintf("\n\tprevious declaration at %s", p.file.Position(pos)) - } - p.error(ident.Pos(), fmt.Sprintf("%s redeclared in this block%s", ident.Name, prevDecl)) - } - } - } -} - -func (p *parser) shortVarDecl(decl *ast.AssignStmt) { - // Go spec: A short variable declaration may redeclare variables - // provided they were originally declared in the same block with - // the same type, and at least one of the non-blank variables is new. - n := 0 // number of new variables - for _, x := range decl.Lhs { - if ident, isIdent := x.(*ast.Ident); isIdent { - assert(ident.Obj == nil, "identifier already declared or resolved") - obj := ast.NewObj(ast.Var, ident.Name) - // remember corresponding assignment for other tools - obj.Decl = decl - ident.Obj = obj - if ident.Name != "_" { - if alt := p.topScope.Insert(obj); alt != nil { - ident.Obj = alt // redeclaration - } else { - n++ // new declaration - } - } - } else { - p.errorExpected(x.Pos(), "identifier on left side of :=") - } - } - if n == 0 && p.mode&DeclarationErrors != 0 { - p.error(decl.Lhs[0].Pos(), "no new variables on left side of :=") - } -} - -// 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) - -// If x is an identifier, tryResolve attempts to resolve x by looking up -// the object it denotes. If no object is found and collectUnresolved is -// set, x is marked as unresolved and collected in the list of unresolved -// identifiers. -// -func (p *parser) tryResolve(x ast.Expr, collectUnresolved bool) { - // nothing to do if x is not an identifier or the blank identifier - ident, _ := x.(*ast.Ident) - if ident == nil { - return - } - // Don't use assert here, to avoid needless formatting of the message below. - if ident.Obj != nil { - panic(fmt.Sprintf("identifier %s already declared or resolved", ident.Name)) - } - if ident.Name == "_" { - return - } - // try to resolve the identifier - for s := p.topScope; s != nil; s = s.Outer { - if obj := s.Lookup(ident.Name); obj != nil { - ident.Obj = obj - return - } - } - // 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 - if collectUnresolved { - ident.Obj = unresolved - p.unresolved = append(p.unresolved, ident) - } -} - -func (p *parser) resolve(x ast.Expr) { - p.tryResolve(x, true) -} - // ---------------------------------------------------------------------------- // Parsing support @@ -580,50 +446,24 @@ func (p *parser) parseIdentList() (list []*ast.Ident) { // Common productions // If lhs is set, result list elements which are identifiers are not resolved. -func (p *parser) parseExprList(lhs bool) (list []ast.Expr) { +func (p *parser) parseExprList() (list []ast.Expr) { if p.trace { defer un(trace(p, "ExpressionList")) } - list = append(list, p.checkExpr(p.parseExpr(lhs))) + list = append(list, p.checkExpr(p.parseExpr())) for p.tok == token.COMMA { p.next() - list = append(list, p.checkExpr(p.parseExpr(lhs))) + list = append(list, p.checkExpr(p.parseExpr())) } return } -func (p *parser) parseLhsList() []ast.Expr { +func (p *parser) parseList(inRhs bool) []ast.Expr { old := p.inRhs - p.inRhs = false - list := p.parseExprList(true) - switch p.tok { - case token.DEFINE: - // lhs of a short variable declaration - // but doesn't enter scope until later. - 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) - } - } - p.inRhs = old - return list -} - -func (p *parser) parseRhsList() []ast.Expr { - old := p.inRhs - p.inRhs = true - list := p.parseExprList(false) + p.inRhs = inRhs + list := p.parseExprList() p.inRhs = old return list } @@ -636,7 +476,7 @@ func (p *parser) parseType() ast.Expr { defer un(trace(p, "Type")) } - typ := p.tryType() + typ := p.tryIdentOrType() if typ == nil { pos := p.pos @@ -669,13 +509,11 @@ func (p *parser) parseTypeName(ident *ast.Ident) ast.Expr { if ident == nil { ident = p.parseIdent() - // don't resolve ident yet - it may be a parameter or field name } if p.tok == token.PERIOD { // ident is a package name p.next() - p.resolve(ident) sel := p.parseIdent() return &ast.SelectorExpr{X: ident, Sel: sel} } @@ -740,7 +578,7 @@ func (p *parser) parseArrayFieldOrTypeInstance(x *ast.Ident) (*ast.Ident, ast.Ex // x [P]E or x[P] if len(args) == 1 { - elt := p.tryType() + elt := p.tryIdentOrType() if elt != nil { // x [P]E return x, &ast.ArrayType{Lbrack: lbrack, Len: args[0], Elt: elt} @@ -760,7 +598,7 @@ func (p *parser) parseArrayFieldOrTypeInstance(x *ast.Ident) (*ast.Ident, ast.Ex return nil, &ast.IndexExpr{X: x, Lbrack: lbrack, Index: &ast.ListExpr{ElemList: args}, Rbrack: rbrack} } -func (p *parser) parseFieldDecl(scope *ast.Scope) *ast.Field { +func (p *parser) parseFieldDecl() *ast.Field { if p.trace { defer un(trace(p, "FieldDecl")) } @@ -776,8 +614,6 @@ func (p *parser) parseFieldDecl(scope *ast.Scope) *ast.Field { typ = name if p.tok == token.PERIOD { typ = p.parseQualifiedIdent(name) - } else { - p.resolve(typ) } } else { // name1, name2, ... T @@ -814,7 +650,6 @@ func (p *parser) parseFieldDecl(scope *ast.Scope) *ast.Field { p.expectSemi() // call before accessing p.linecomment field := &ast.Field{Doc: doc, Names: names, Type: typ, Tag: tag, Comment: p.lineComment} - p.declare(field, nil, scope, ast.Var, names...) return field } @@ -825,13 +660,12 @@ 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(scope)) + list = append(list, p.parseFieldDecl()) } rbrace := p.expect(token.RBRACE) @@ -927,7 +761,7 @@ func (p *parser) parseParamDecl(name *ast.Ident) (f field) { return } -func (p *parser) parseParameterList(scope *ast.Scope, name0 *ast.Ident, closing token.Token, parseParamDecl func(*ast.Ident) field, tparams bool) (params []*ast.Field) { +func (p *parser) parseParameterList(name0 *ast.Ident, closing token.Token, parseParamDecl func(*ast.Ident) field, tparams bool) (params []*ast.Field) { if p.trace { defer un(trace(p, "ParameterList")) } @@ -968,7 +802,6 @@ func (p *parser) parseParameterList(scope *ast.Scope, name0 *ast.Ident, closing for i := 0; i < len(list); i++ { par := &list[i] if typ := par.name; typ != nil { - p.resolve(typ) par.typ = typ par.name = nil } @@ -1022,9 +855,6 @@ func (p *parser) parseParameterList(scope *ast.Scope, name0 *ast.Ident, closing addParams := func() { assert(typ != nil, "nil type in named parameter list") field := &ast.Field{Names: names, Type: typ} - // Go spec: The scope of an identifier denoting a function - // parameter or result variable is the function body. - p.declare(field, nil, scope, ast.Var, names...) params = append(params, field) names = nil } @@ -1043,7 +873,7 @@ func (p *parser) parseParameterList(scope *ast.Scope, name0 *ast.Ident, closing return } -func (p *parser) parseParameters(scope *ast.Scope, acceptTParams bool) (tparams, params *ast.FieldList) { +func (p *parser) parseParameters(acceptTParams bool) (tparams, params *ast.FieldList) { if p.trace { defer un(trace(p, "Parameters")) } @@ -1052,7 +882,7 @@ func (p *parser) parseParameters(scope *ast.Scope, acceptTParams bool) (tparams, opening := p.pos p.next() // [T any](params) syntax - list := p.parseParameterList(scope, nil, token.RBRACK, p.parseParamDecl, true) + list := p.parseParameterList(nil, token.RBRACK, p.parseParamDecl, true) rbrack := p.expect(token.RBRACK) tparams = &ast.FieldList{Opening: opening, List: list, Closing: rbrack} // Type parameter lists must not be empty. @@ -1066,7 +896,7 @@ func (p *parser) parseParameters(scope *ast.Scope, acceptTParams bool) (tparams, var fields []*ast.Field if p.tok != token.RPAREN { - fields = p.parseParameterList(scope, nil, token.RPAREN, p.parseParamDecl, false) + fields = p.parseParameterList(nil, token.RPAREN, p.parseParamDecl, false) } rparen := p.expect(token.RPAREN) @@ -1075,17 +905,17 @@ func (p *parser) parseParameters(scope *ast.Scope, acceptTParams bool) (tparams, return } -func (p *parser) parseResult(scope *ast.Scope) *ast.FieldList { +func (p *parser) parseResult() *ast.FieldList { if p.trace { defer un(trace(p, "Result")) } if p.tok == token.LPAREN { - _, results := p.parseParameters(scope, false) + _, results := p.parseParameters(false) return results } - typ := p.tryType() + typ := p.tryIdentOrType() if typ != nil { list := make([]*ast.Field, 1) list[0] = &ast.Field{Type: typ} @@ -1095,23 +925,22 @@ func (p *parser) parseResult(scope *ast.Scope) *ast.FieldList { return nil } -func (p *parser) parseFuncType() (*ast.FuncType, *ast.Scope) { +func (p *parser) parseFuncType() *ast.FuncType { if p.trace { defer un(trace(p, "FuncType")) } pos := p.expect(token.FUNC) - scope := ast.NewScope(p.topScope) // function scope - tparams, params := p.parseParameters(scope, true) + tparams, params := p.parseParameters(true) if tparams != nil { p.error(tparams.Pos(), "function type cannot have type parameters") } - results := p.parseResult(scope) + results := p.parseResult() - return &ast.FuncType{Func: pos, Params: params, Results: results}, scope + return &ast.FuncType{Func: pos, Params: params, Results: results} } -func (p *parser) parseMethodSpec(scope *ast.Scope) *ast.Field { +func (p *parser) parseMethodSpec() *ast.Field { if p.trace { defer un(trace(p, "MethodSpec")) } @@ -1127,17 +956,16 @@ func (p *parser) parseMethodSpec(scope *ast.Scope) *ast.Field { lbrack := p.pos p.next() p.exprLev++ - x := p.parseExpr(true) // we don't know yet if we're a lhs or rhs expr + x := p.parseExpr() p.exprLev-- if name0, _ := x.(*ast.Ident); name0 != nil && p.tok != token.COMMA && p.tok != token.RBRACK { // generic method m[T any] - scope := ast.NewScope(nil) // method scope - list := p.parseParameterList(scope, name0, token.RBRACK, p.parseParamDecl, true) + list := p.parseParameterList(name0, token.RBRACK, p.parseParamDecl, true) rbrack := p.expect(token.RBRACK) tparams := &ast.FieldList{Opening: lbrack, List: list, Closing: rbrack} // TODO(rfindley) refactor to share code with parseFuncType. - _, params := p.parseParameters(scope, false) - results := p.parseResult(scope) + _, params := p.parseParameters(false) + results := p.parseResult() idents = []*ast.Ident{ident} typ = &ast.FuncType{Func: token.NoPos, TParams: tparams, Params: params, Results: results} } else { @@ -1161,15 +989,13 @@ func (p *parser) parseMethodSpec(scope *ast.Scope) *ast.Field { case p.tok == token.LPAREN: // ordinary method // TODO(rfindley) refactor to share code with parseFuncType. - scope := ast.NewScope(nil) // method scope - _, params := p.parseParameters(scope, false) - results := p.parseResult(scope) + _, params := p.parseParameters(false) + results := p.parseResult() idents = []*ast.Ident{ident} typ = &ast.FuncType{Func: token.NoPos, Params: params, Results: results} default: // embedded type typ = x - p.resolve(typ) } } else { // embedded, possibly instantiated type @@ -1182,7 +1008,6 @@ func (p *parser) parseMethodSpec(scope *ast.Scope) *ast.Field { p.expectSemi() // call before accessing p.linecomment spec := &ast.Field{Doc: doc, Names: idents, Type: typ, Comment: p.lineComment} - p.declare(spec, nil, scope, ast.Fun, idents...) return spec } @@ -1194,11 +1019,10 @@ 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 || p.mode&parseTypeParams != 0 && p.tok == token.TYPE { if p.tok == token.IDENT { - list = append(list, p.parseMethodSpec(scope)) + list = append(list, p.parseMethodSpec()) } else { // all types in a type list share the same field name "type" // (since type is a keyword, a Go program cannot have that field name) @@ -1287,7 +1111,6 @@ func (p *parser) parseTypeInstance(typ ast.Expr) ast.Expr { return &ast.IndexExpr{X: typ, Lbrack: opening, Index: &ast.ListExpr{ElemList: list}, Rbrack: closing} } -// If the result is an identifier, it is not resolved. func (p *parser) tryIdentOrType() ast.Expr { switch p.tok { case token.IDENT: @@ -1307,7 +1130,7 @@ func (p *parser) tryIdentOrType() ast.Expr { case token.MUL: return p.parsePointerType() case token.FUNC: - typ, _ := p.parseFuncType() + typ := p.parseFuncType() return typ case token.INTERFACE: return p.parseInterfaceType() @@ -1327,14 +1150,6 @@ func (p *parser) tryIdentOrType() ast.Expr { return nil } -func (p *parser) tryType() ast.Expr { - typ := p.tryIdentOrType() - if typ != nil { - p.resolve(typ) - } - return typ -} - // ---------------------------------------------------------------------------- // Blocks @@ -1350,17 +1165,13 @@ func (p *parser) parseStmtList() (list []ast.Stmt) { return } -func (p *parser) parseBody(scope *ast.Scope) *ast.BlockStmt { +func (p *parser) parseBody() *ast.BlockStmt { if p.trace { defer un(trace(p, "Body")) } lbrace := p.expect(token.LBRACE) - p.topScope = scope // open function scope - p.openLabelScope() list := p.parseStmtList() - p.closeLabelScope() - p.closeScope() rbrace := p.expect2(token.RBRACE) return &ast.BlockStmt{Lbrace: lbrace, List: list, Rbrace: rbrace} @@ -1372,9 +1183,7 @@ func (p *parser) parseBlockStmt() *ast.BlockStmt { } lbrace := p.expect(token.LBRACE) - p.openScope() list := p.parseStmtList() - p.closeScope() rbrace := p.expect2(token.RBRACE) return &ast.BlockStmt{Lbrace: lbrace, List: list, Rbrace: rbrace} @@ -1388,14 +1197,14 @@ func (p *parser) parseFuncTypeOrLit() ast.Expr { defer un(trace(p, "FuncTypeOrLit")) } - typ, scope := p.parseFuncType() + typ := p.parseFuncType() if p.tok != token.LBRACE { // function type only return typ } p.exprLev++ - body := p.parseBody(scope) + body := p.parseBody() p.exprLev-- return &ast.FuncLit{Type: typ, Body: body} @@ -1403,9 +1212,8 @@ 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(lhs bool) ast.Expr { +func (p *parser) parseOperand() ast.Expr { if p.trace { defer un(trace(p, "Operand")) } @@ -1413,9 +1221,6 @@ func (p *parser) parseOperand(lhs bool) ast.Expr { switch p.tok { case token.IDENT: x := p.parseIdent() - if !lhs { - p.resolve(x) - } return x case token.INT, token.FLOAT, token.IMAG, token.CHAR, token.STRING: @@ -1590,7 +1395,7 @@ func (p *parser) parseCallOrConversion(fun ast.Expr) *ast.CallExpr { return &ast.CallExpr{Fun: fun, Lparen: lparen, Args: list, Ellipsis: ellipsis, Rparen: rparen} } -func (p *parser) parseValue(keyOk bool) ast.Expr { +func (p *parser) parseValue() ast.Expr { if p.trace { defer un(trace(p, "Element")) } @@ -1599,35 +1404,7 @@ func (p *parser) parseValue(keyOk bool) ast.Expr { return p.parseLiteralValue(nil) } - // Because the parser doesn't know the composite literal type, it cannot - // know if a key that's an identifier is a struct field name or a name - // denoting a value. The former is not resolved by the parser or the - // resolver. - // - // Instead, _try_ to resolve such a key if possible. If it resolves, - // it a) has correctly resolved, or b) incorrectly resolved because - // the key is a struct field with a name matching another identifier. - // In the former case we are done, and in the latter case we don't - // care because the type checker will do a separate field lookup. - // - // If the key does not resolve, it a) must be defined at the top - // level in another file of the same package, the universe scope, or be - // undeclared; or b) it is a struct field. In the former case, the type - // checker can do a top-level lookup, and in the latter case it will do - // a separate field lookup. - x := p.checkExpr(p.parseExpr(keyOk)) - if keyOk { - if p.tok == token.COLON { - // Try to resolve the key but don't collect it - // as unresolved identifier if it fails so that - // we don't get (possibly false) errors about - // undeclared names. - p.tryResolve(x, false) - } else { - // not a key - p.resolve(x) - } - } + x := p.checkExpr(p.parseExpr()) return x } @@ -1637,11 +1414,11 @@ func (p *parser) parseElement() ast.Expr { defer un(trace(p, "Element")) } - x := p.parseValue(true) + x := p.parseValue() if p.tok == token.COLON { colon := p.pos p.next() - x = &ast.KeyValueExpr{Key: x, Colon: colon, Value: p.parseValue(false)} + x = &ast.KeyValueExpr{Key: x, Colon: colon, Value: p.parseValue()} } return x @@ -1736,20 +1513,16 @@ func (p *parser) checkExprOrType(x ast.Expr) ast.Expr { return x } -// If lhs is set and the result is an identifier, it is not resolved. -func (p *parser) parsePrimaryExpr(lhs bool) (x ast.Expr) { +func (p *parser) parsePrimaryExpr() (x ast.Expr) { if p.trace { defer un(trace(p, "PrimaryExpr")) } - x = p.parseOperand(lhs) + x = p.parseOperand() for { switch p.tok { case token.PERIOD: p.next() - if lhs { - p.resolve(x) - } switch p.tok { case token.IDENT: x = p.parseSelector(p.checkExprOrType(x)) @@ -1770,14 +1543,8 @@ func (p *parser) parsePrimaryExpr(lhs bool) (x ast.Expr) { x = &ast.SelectorExpr{X: x, Sel: sel} } case token.LBRACK: - if lhs { - p.resolve(x) - } x = p.parseIndexOrSliceOrInstance(p.checkExpr(x)) case token.LPAREN: - if lhs { - p.resolve(x) - } x = p.parseCallOrConversion(p.checkExprOrType(x)) case token.LBRACE: // operand may have returned a parenthesized complit @@ -1804,21 +1571,14 @@ func (p *parser) parsePrimaryExpr(lhs bool) (x ast.Expr) { p.error(t.Pos(), "cannot parenthesize type in composite literal") // already progressed, no need to advance } - if lhs { - // An error has already been reported above, but try to resolve the 'T' - // in (T){...} anyway. - p.resolve(t) - } x = p.parseLiteralValue(x) default: return } - lhs = false // no need to try to resolve again } } -// If lhs is set and the result is an identifier, it is not resolved. -func (p *parser) parseUnaryExpr(lhs bool) ast.Expr { +func (p *parser) parseUnaryExpr() ast.Expr { if p.trace { defer un(trace(p, "UnaryExpr")) } @@ -1827,7 +1587,7 @@ func (p *parser) parseUnaryExpr(lhs bool) ast.Expr { case token.ADD, token.SUB, token.NOT, token.XOR, token.AND: pos, op := p.pos, p.tok p.next() - x := p.parseUnaryExpr(false) + x := p.parseUnaryExpr() return &ast.UnaryExpr{OpPos: pos, Op: op, X: p.checkExpr(x)} case token.ARROW: @@ -1849,7 +1609,7 @@ func (p *parser) parseUnaryExpr(lhs bool) ast.Expr { // <- (chan type) => (<-chan type) // <- (chan<- type) => (<-chan (<-type)) - x := p.parseUnaryExpr(false) + x := p.parseUnaryExpr() // determine which case we have if typ, ok := x.(*ast.ChanType); ok { @@ -1880,11 +1640,11 @@ func (p *parser) parseUnaryExpr(lhs bool) ast.Expr { // pointer type or unary "*" expression pos := p.pos p.next() - x := p.parseUnaryExpr(false) + x := p.parseUnaryExpr() return &ast.StarExpr{Star: pos, X: p.checkExprOrType(x)} } - return p.parsePrimaryExpr(lhs) + return p.parsePrimaryExpr() } func (p *parser) tokPrec() (token.Token, int) { @@ -1895,44 +1655,38 @@ func (p *parser) tokPrec() (token.Token, int) { return tok, tok.Precedence() } -// If lhs is set and the result is an identifier, it is not resolved. -func (p *parser) parseBinaryExpr(lhs bool, prec1 int) ast.Expr { +func (p *parser) parseBinaryExpr(prec1 int) ast.Expr { if p.trace { defer un(trace(p, "BinaryExpr")) } - x := p.parseUnaryExpr(lhs) + x := p.parseUnaryExpr() for { op, oprec := p.tokPrec() if oprec < prec1 { return x } pos := p.expect(op) - if lhs { - p.resolve(x) - lhs = false - } - y := p.parseBinaryExpr(false, oprec+1) + y := p.parseBinaryExpr(oprec + 1) x = &ast.BinaryExpr{X: p.checkExpr(x), OpPos: pos, Op: op, Y: p.checkExpr(y)} } } -// If lhs is set and the result is an identifier, it is not resolved. // The result may be a type or even a raw type ([...]int). Callers must // check the result (using checkExpr or checkExprOrType), depending on // context. -func (p *parser) parseExpr(lhs bool) ast.Expr { +func (p *parser) parseExpr() ast.Expr { if p.trace { defer un(trace(p, "Expression")) } - return p.parseBinaryExpr(lhs, token.LowestPrec+1) + return p.parseBinaryExpr(token.LowestPrec + 1) } func (p *parser) parseRhs() ast.Expr { old := p.inRhs p.inRhs = true - x := p.checkExpr(p.parseExpr(false)) + x := p.checkExpr(p.parseExpr()) p.inRhs = old return x } @@ -1940,7 +1694,7 @@ func (p *parser) parseRhs() ast.Expr { func (p *parser) parseRhsOrType() ast.Expr { old := p.inRhs p.inRhs = true - x := p.checkExprOrType(p.parseExpr(false)) + x := p.checkExprOrType(p.parseExpr()) p.inRhs = old return x } @@ -1964,7 +1718,7 @@ func (p *parser) parseSimpleStmt(mode int) (ast.Stmt, bool) { defer un(trace(p, "SimpleStmt")) } - x := p.parseLhsList() + x := p.parseList(false) switch p.tok { case @@ -1983,11 +1737,11 @@ func (p *parser) parseSimpleStmt(mode int) (ast.Stmt, bool) { y = []ast.Expr{&ast.UnaryExpr{OpPos: pos, Op: token.RANGE, X: p.parseRhs()}} isRange = true } else { - y = p.parseRhsList() + y = p.parseList(true) } as := &ast.AssignStmt{Lhs: x, TokPos: pos, Tok: tok, Rhs: y} if tok == token.DEFINE { - p.shortVarDecl(as) + p.checkAssignStmt(as) } return as, isRange } @@ -2007,7 +1761,6 @@ func (p *parser) parseSimpleStmt(mode int) (ast.Stmt, bool) { // in which it is declared and excludes the body of any nested // function. stmt := &ast.LabeledStmt{Label: label, Colon: colon, Stmt: p.parseStmt()} - p.declare(stmt, nil, p.labelScope, ast.Lbl, label) return stmt, false } // The label declaration typically starts at x[0].Pos(), but the label @@ -2037,6 +1790,14 @@ func (p *parser) parseSimpleStmt(mode int) (ast.Stmt, bool) { return &ast.ExprStmt{X: x[0]}, false } +func (p *parser) checkAssignStmt(as *ast.AssignStmt) { + for _, x := range as.Lhs { + if _, isIdent := x.(*ast.Ident); !isIdent { + p.errorExpected(x.Pos(), "identifier on left side of :=") + } + } +} + 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 { @@ -2088,7 +1849,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.parseRhsList() + x = p.parseList(true) } p.expectSemi() @@ -2104,9 +1865,6 @@ func (p *parser) parseBranchStmt(tok token.Token) *ast.BranchStmt { var label *ast.Ident if tok != token.FALLTHROUGH && p.tok == token.IDENT { label = p.parseIdent() - // add to list of unresolved targets - n := len(p.targetStack) - 1 - p.targetStack[n] = append(p.targetStack[n], label) } p.expectSemi() @@ -2197,8 +1955,6 @@ func (p *parser) parseIfStmt() *ast.IfStmt { } pos := p.expect(token.IF) - p.openScope() - defer p.closeScope() init, cond := p.parseIfHeader() body := p.parseBlockStmt() @@ -2249,16 +2005,14 @@ func (p *parser) parseCaseClause(typeSwitch bool) *ast.CaseClause { if typeSwitch { list = p.parseTypeList() } else { - list = p.parseRhsList() + list = p.parseList(true) } } else { p.expect(token.DEFAULT) } colon := p.expect(token.COLON) - p.openScope() body := p.parseStmtList() - p.closeScope() return &ast.CaseClause{Case: pos, List: list, Colon: colon, Body: body} } @@ -2295,8 +2049,6 @@ func (p *parser) parseSwitchStmt() ast.Stmt { } pos := p.expect(token.SWITCH) - p.openScope() - defer p.closeScope() var s1, s2 ast.Stmt if p.tok != token.LBRACE { @@ -2322,8 +2074,6 @@ func (p *parser) parseSwitchStmt() ast.Stmt { // // If we don't have a type switch, s2 must be an expression. // Having the extra nested but empty scope won't affect it. - p.openScope() - defer p.closeScope() s2, _ = p.parseSimpleStmt(basic) } } @@ -2352,12 +2102,11 @@ func (p *parser) parseCommClause() *ast.CommClause { defer un(trace(p, "CommClause")) } - p.openScope() pos := p.pos var comm ast.Stmt if p.tok == token.CASE { p.next() - lhs := p.parseLhsList() + lhs := p.parseList(false) if p.tok == token.ARROW { // SendStmt if len(lhs) > 1 { @@ -2382,7 +2131,7 @@ func (p *parser) parseCommClause() *ast.CommClause { rhs := p.parseRhs() as := &ast.AssignStmt{Lhs: lhs, TokPos: pos, Tok: tok, Rhs: []ast.Expr{rhs}} if tok == token.DEFINE { - p.shortVarDecl(as) + p.checkAssignStmt(as) } comm = as } else { @@ -2400,7 +2149,6 @@ func (p *parser) parseCommClause() *ast.CommClause { colon := p.expect(token.COLON) body := p.parseStmtList() - p.closeScope() return &ast.CommClause{Case: pos, Comm: comm, Colon: colon, Body: body} } @@ -2429,8 +2177,6 @@ func (p *parser) parseForStmt() ast.Stmt { } pos := p.expect(token.FOR) - p.openScope() - defer p.closeScope() var s1, s2, s3 ast.Stmt var isRange bool @@ -2627,12 +2373,12 @@ func (p *parser) parseValueSpec(doc *ast.CommentGroup, _ token.Pos, keyword toke pos := p.pos idents := p.parseIdentList() - typ := p.tryType() + typ := p.tryIdentOrType() var values []ast.Expr // always permit optional initialization for more tolerant parsing if p.tok == token.ASSIGN { p.next() - values = p.parseRhsList() + values = p.parseList(true) } p.expectSemi() // call before accessing p.linecomment @@ -2647,10 +2393,6 @@ func (p *parser) parseValueSpec(doc *ast.CommentGroup, _ token.Pos, keyword toke } } - // Go spec: The scope of a constant or variable identifier declared inside - // a function begins at the end of the ConstSpec or VarSpec and ends at - // the end of the innermost containing block. - // (Global identifiers are resolved in a separate phase after parsing.) spec := &ast.ValueSpec{ Doc: doc, Names: idents, @@ -2658,18 +2400,11 @@ func (p *parser) parseValueSpec(doc *ast.CommentGroup, _ token.Pos, keyword toke Values: values, Comment: p.lineComment, } - kind := ast.Con - if keyword == token.VAR { - kind = ast.Var - } - p.declare(spec, iota, p.topScope, kind, idents...) - return spec } func (p *parser) parseGenericType(spec *ast.TypeSpec, openPos token.Pos, name0 *ast.Ident, closeTok token.Token) { - p.openScope() - list := p.parseParameterList(p.topScope, name0, closeTok, p.parseParamDecl, true) + list := p.parseParameterList(name0, closeTok, p.parseParamDecl, true) closePos := p.expect(closeTok) spec.TParams = &ast.FieldList{Opening: openPos, List: list, Closing: closePos} // Type alias cannot have type parameters. Accept them for robustness but complain. @@ -2678,7 +2413,6 @@ func (p *parser) parseGenericType(spec *ast.TypeSpec, openPos token.Pos, name0 * p.next() } spec.Type = p.parseType() - p.closeScope() } func (p *parser) parseTypeSpec(doc *ast.CommentGroup, _ token.Pos, _ token.Token, _ int) ast.Spec { @@ -2687,13 +2421,7 @@ func (p *parser) parseTypeSpec(doc *ast.CommentGroup, _ token.Pos, _ token.Token } ident := p.parseIdent() - - // Go spec: The scope of a type identifier declared inside a function begins - // at the identifier in the TypeSpec and ends at the end of the innermost - // containing block. - // (Global identifiers are resolved in a separate phase after parsing.) spec := &ast.TypeSpec{Doc: doc, Name: ident} - p.declare(spec, nil, p.topScope, ast.Typ, ident) switch p.tok { case token.LBRACK: @@ -2702,7 +2430,7 @@ func (p *parser) parseTypeSpec(doc *ast.CommentGroup, _ token.Pos, _ token.Token if p.tok == token.IDENT { // array type or generic type [T any] p.exprLev++ - x := p.parseExpr(true) // we don't know yet if we're a lhs or rhs expr + x := p.parseExpr() p.exprLev-- if name0, _ := x.(*ast.Ident); p.mode&parseTypeParams != 0 && name0 != nil && p.tok != token.RBRACK { // generic type [T any]; @@ -2776,28 +2504,27 @@ func (p *parser) parseFuncDecl() *ast.FuncDecl { doc := p.leadComment pos := p.expect(token.FUNC) - scope := ast.NewScope(p.topScope) // function scope var recv *ast.FieldList if p.tok == token.LPAREN { - _, recv = p.parseParameters(scope, false) + _, recv = p.parseParameters(false) } ident := p.parseIdent() - tparams, params := p.parseParameters(scope, true) - results := p.parseResult(scope) + tparams, params := p.parseParameters(true) + results := p.parseResult() var body *ast.BlockStmt if p.tok == token.LBRACE { - body = p.parseBody(scope) + body = p.parseBody() p.expectSemi() } else if p.tok == token.SEMICOLON { p.next() if p.tok == token.LBRACE { // opening { of function declaration on next line p.error(p.pos, "unexpected semicolon or newline before {") - body = p.parseBody(scope) + body = p.parseBody() p.expectSemi() } } else { @@ -2816,18 +2543,6 @@ func (p *parser) parseFuncDecl() *ast.FuncDecl { }, Body: body, } - if recv == nil { - // Go spec: The scope of an identifier denoting a constant, type, - // variable, or function (but not method) declared at top level - // (outside any function) is the package block. - // - // init() functions cannot be referred to and there may - // be more than one - don't put them in the pkgScope - if ident.Name != "init" { - p.declare(decl, nil, p.pkgScope, ast.Fun, ident) - } - } - return decl } @@ -2888,8 +2603,6 @@ func (p *parser) parseFile() *ast.File { return nil } - p.openScope() - p.pkgScope = p.topScope var decls []ast.Decl if p.mode&PackageClauseOnly == 0 { // import decls @@ -2904,30 +2617,20 @@ func (p *parser) parseFile() *ast.File { } } } - p.closeScope() - assert(p.topScope == nil, "unbalanced scopes") - assert(p.labelScope == nil, "unbalanced label scopes") - // resolve global identifiers within the same file - i := 0 - for _, ident := range p.unresolved { - // i <= index for current ident - 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++ - } + f := &ast.File{ + Doc: doc, + Package: pos, + Name: ident, + Decls: decls, + Imports: p.imports, + Comments: p.comments, } + var declErr func(token.Pos, string) + if p.mode&DeclarationErrors != 0 { + declErr = p.error + } + resolveFile(f, p.file, declErr) - return &ast.File{ - Doc: doc, - Package: pos, - Name: ident, - Decls: decls, - Scope: p.pkgScope, - Imports: p.imports, - Unresolved: p.unresolved[0:i], - Comments: p.comments, - } + return f } diff --git a/src/go/parser/resolver.go b/src/go/parser/resolver.go new file mode 100644 index 0000000000..dd77b685e3 --- /dev/null +++ b/src/go/parser/resolver.go @@ -0,0 +1,505 @@ +// Copyright 2021 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 parser + +import ( + "fmt" + "go/ast" + "go/token" +) + +const debugResolve = false + +// resolveFile walks the given file to resolve identifiers within the file +// scope, updating ast.Ident.Obj fields with declaration information. +// +// If declErr is non-nil, it is used to report declaration errors during +// resolution. tok is used to format position in error messages. +func resolveFile(file *ast.File, handle *token.File, declErr func(token.Pos, string)) { + topScope := ast.NewScope(nil) + r := &resolver{ + handle: handle, + declErr: declErr, + topScope: topScope, + pkgScope: topScope, + } + + for _, decl := range file.Decls { + ast.Walk(r, decl) + } + + r.closeScope() + assert(r.topScope == nil, "unbalanced scopes") + assert(r.labelScope == nil, "unbalanced label scopes") + + // resolve global identifiers within the same file + i := 0 + for _, ident := range r.unresolved { + // i <= index for current ident + assert(ident.Obj == unresolved, "object already resolved") + ident.Obj = r.pkgScope.Lookup(ident.Name) // also removes unresolved sentinel + if ident.Obj == nil { + r.unresolved[i] = ident + i++ + } else if debugResolve { + pos := ident.Obj.Decl.(interface{ Pos() token.Pos }).Pos() + r.dump("resolved %s@%v to package object %v", ident.Name, ident.Pos(), pos) + } + } + file.Scope = r.pkgScope + file.Unresolved = r.unresolved[0:i] +} + +type resolver struct { + handle *token.File + declErr func(token.Pos, string) + + // Ordinary identifier scopes + pkgScope *ast.Scope // pkgScope.Outer == nil + topScope *ast.Scope // top-most scope; may be pkgScope + unresolved []*ast.Ident // unresolved identifiers + + // Label scopes + // (maintained by open/close LabelScope) + labelScope *ast.Scope // label scope for current function + targetStack [][]*ast.Ident // stack of unresolved labels +} + +func (r *resolver) dump(format string, args ...interface{}) { + fmt.Println(">>> " + r.sprintf(format, args...)) +} + +func (r *resolver) sprintf(format string, args ...interface{}) string { + for i, arg := range args { + switch arg := arg.(type) { + case token.Pos: + args[i] = r.handle.Position(arg) + } + } + return fmt.Sprintf(format, args...) +} + +func (r *resolver) openScope(pos token.Pos) { + if debugResolve { + r.dump("opening scope @%v", pos) + } + r.topScope = ast.NewScope(r.topScope) +} + +func (r *resolver) closeScope() { + if debugResolve { + r.dump("closing scope") + } + r.topScope = r.topScope.Outer +} + +func (r *resolver) openLabelScope() { + r.labelScope = ast.NewScope(r.labelScope) + r.targetStack = append(r.targetStack, nil) +} + +func (r *resolver) closeLabelScope() { + // resolve labels + n := len(r.targetStack) - 1 + scope := r.labelScope + for _, ident := range r.targetStack[n] { + ident.Obj = scope.Lookup(ident.Name) + if ident.Obj == nil && r.declErr != nil { + r.declErr(ident.Pos(), fmt.Sprintf("label %s undefined", ident.Name)) + } + } + // pop label scope + r.targetStack = r.targetStack[0:n] + r.labelScope = r.labelScope.Outer +} + +func (r *resolver) declare(decl, data interface{}, scope *ast.Scope, kind ast.ObjKind, idents ...*ast.Ident) { + for _, ident := range idents { + // "type" is used for type lists in interfaces, and is otherwise an invalid + // identifier. The 'type' identifier is also artificially duplicated in the + // type list, so could cause panics below if we were to proceed. + if ident.Name == "type" { + continue + } + assert(ident.Obj == nil, "identifier already declared or resolved") + obj := ast.NewObj(kind, ident.Name) + // remember the corresponding declaration for redeclaration + // errors and global variable resolution/typechecking phase + obj.Decl = decl + obj.Data = data + ident.Obj = obj + if ident.Name != "_" { + if debugResolve { + r.dump("declaring %s@%v", ident.Name, ident.Pos()) + } + if alt := scope.Insert(obj); alt != nil && r.declErr != nil { + prevDecl := "" + if pos := alt.Pos(); pos.IsValid() { + prevDecl = fmt.Sprintf("\n\tprevious declaration at %s", r.handle.Position(pos)) + } + r.declErr(ident.Pos(), fmt.Sprintf("%s redeclared in this block%s", ident.Name, prevDecl)) + } + } + } +} + +func (r *resolver) shortVarDecl(decl *ast.AssignStmt) { + // Go spec: A short variable declaration may redeclare variables + // provided they were originally declared in the same block with + // the same type, and at least one of the non-blank variables is new. + n := 0 // number of new variables + for _, x := range decl.Lhs { + if ident, isIdent := x.(*ast.Ident); isIdent { + assert(ident.Obj == nil, "identifier already declared or resolved") + obj := ast.NewObj(ast.Var, ident.Name) + // remember corresponding assignment for other tools + obj.Decl = decl + ident.Obj = obj + if ident.Name != "_" { + if debugResolve { + r.dump("declaring %s@%v", ident.Name, ident.Pos()) + } + if alt := r.topScope.Insert(obj); alt != nil { + ident.Obj = alt // redeclaration + } else { + n++ // new declaration + } + } + } + } + if n == 0 && r.declErr != nil { + r.declErr(decl.Lhs[0].Pos(), "no new variables on left side of :=") + } +} + +// 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) + +// If x is an identifier, resolve attempts to resolve x by looking up +// the object it denotes. If no object is found and collectUnresolved is +// set, x is marked as unresolved and collected in the list of unresolved +// identifiers. +// +func (r *resolver) resolve(ident *ast.Ident, collectUnresolved bool) { + if ident.Obj != nil { + panic(fmt.Sprintf("%s: identifier %s already declared or resolved", r.handle.Position(ident.Pos()), ident.Name)) + } + // '_' and 'type' should never refer to existing declarations: '_' because it + // has special handling in the spec, and 'type' because it is a keyword, and + // only valid in an interface type list. + if ident.Name == "_" || ident.Name == "type" { + return + } + for s := r.topScope; s != nil; s = s.Outer { + if obj := s.Lookup(ident.Name); obj != nil { + assert(obj.Name != "", "obj with no name") + ident.Obj = obj + return + } + } + // 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 + if collectUnresolved { + ident.Obj = unresolved + r.unresolved = append(r.unresolved, ident) + } +} + +func (r *resolver) walkExprs(list []ast.Expr) { + for _, node := range list { + ast.Walk(r, node) + } +} + +func (r *resolver) walkLHS(list []ast.Expr) { + for _, expr := range list { + expr := unparen(expr) + if _, ok := expr.(*ast.Ident); !ok && expr != nil { + ast.Walk(r, expr) + } + } +} + +func (r *resolver) walkStmts(list []ast.Stmt) { + for _, stmt := range list { + ast.Walk(r, stmt) + } +} + +func (r *resolver) Visit(node ast.Node) ast.Visitor { + if debugResolve && node != nil { + r.dump("node %T@%v", node, node.Pos()) + } + + switch n := node.(type) { + + // Expressions. + case *ast.Ident: + r.resolve(n, true) + + case *ast.FuncLit: + functionScope := ast.NewScope(r.topScope) + r.walkFuncType(functionScope, n.Type) + r.walkBody(functionScope, n.Body) + + case *ast.SelectorExpr: + ast.Walk(r, n.X) + // Note: don't try to resolve n.Sel, as we don't support qualified + // resolution. + + case *ast.StructType: + scope := ast.NewScope(nil) + r.walkFieldList(scope, n.Fields, ast.Var) + + case *ast.FuncType: + scope := ast.NewScope(r.topScope) + r.walkFuncType(scope, n) + + case *ast.CompositeLit: + if n.Type != nil { + ast.Walk(r, n.Type) + } + for _, e := range n.Elts { + if kv, _ := e.(*ast.KeyValueExpr); kv != nil { + // See issue #45160: try to resolve composite lit keys, but don't + // collect them as unresolved if resolution failed. This replicates + // existing behavior when resolving during parsing. + if ident, _ := kv.Key.(*ast.Ident); ident != nil { + r.resolve(ident, false) + } else { + ast.Walk(r, kv.Key) + } + ast.Walk(r, kv.Value) + } else { + ast.Walk(r, e) + } + } + + case *ast.InterfaceType: + scope := ast.NewScope(nil) + r.walkFieldList(scope, n.Methods, ast.Fun) + + // Statements + case *ast.LabeledStmt: + r.declare(n, nil, r.labelScope, ast.Lbl, n.Label) + ast.Walk(r, n.Stmt) + + case *ast.AssignStmt: + r.walkExprs(n.Rhs) + if n.Tok == token.DEFINE { + r.shortVarDecl(n) + } else { + r.walkExprs(n.Lhs) + } + + case *ast.BranchStmt: + // add to list of unresolved targets + if n.Tok != token.FALLTHROUGH && n.Label != nil { + depth := len(r.targetStack) - 1 + r.targetStack[depth] = append(r.targetStack[depth], n.Label) + } + + case *ast.BlockStmt: + r.openScope(n.Pos()) + defer r.closeScope() + r.walkStmts(n.List) + + case *ast.IfStmt: + r.openScope(n.Pos()) + defer r.closeScope() + if n.Init != nil { + ast.Walk(r, n.Init) + } + ast.Walk(r, n.Cond) + ast.Walk(r, n.Body) + if n.Else != nil { + ast.Walk(r, n.Else) + } + + case *ast.CaseClause: + r.walkExprs(n.List) + r.openScope(n.Pos()) + defer r.closeScope() + r.walkStmts(n.Body) + + case *ast.SwitchStmt: + r.openScope(n.Pos()) + defer r.closeScope() + if n.Init != nil { + ast.Walk(r, n.Init) + } + if n.Tag != nil { + // The scope below reproduces some unnecessary behavior of the parser, + // opening an extra scope in case this is a type switch. It's not needed + // for expression switches. + // TODO: remove this once we've matched the parser resolution exactly. + if n.Init != nil { + r.openScope(n.Tag.Pos()) + defer r.closeScope() + } + ast.Walk(r, n.Tag) + } + if n.Body != nil { + r.walkStmts(n.Body.List) + } + + case *ast.TypeSwitchStmt: + if n.Init != nil { + r.openScope(n.Pos()) + defer r.closeScope() + ast.Walk(r, n.Init) + } + r.openScope(n.Assign.Pos()) + defer r.closeScope() + ast.Walk(r, n.Assign) + // s.Body consists only of case clauses, so does not get its own + // scope. + if n.Body != nil { + r.walkStmts(n.Body.List) + } + + case *ast.CommClause: + r.openScope(n.Pos()) + defer r.closeScope() + if n.Comm != nil { + ast.Walk(r, n.Comm) + } + r.walkStmts(n.Body) + + case *ast.SelectStmt: + // as for switch statements, select statement bodies don't get their own + // scope. + if n.Body != nil { + r.walkStmts(n.Body.List) + } + + case *ast.ForStmt: + r.openScope(n.Pos()) + defer r.closeScope() + if n.Init != nil { + ast.Walk(r, n.Init) + } + if n.Cond != nil { + ast.Walk(r, n.Cond) + } + if n.Post != nil { + ast.Walk(r, n.Post) + } + ast.Walk(r, n.Body) + + case *ast.RangeStmt: + r.openScope(n.Pos()) + defer r.closeScope() + ast.Walk(r, n.X) + var lhs []ast.Expr + if n.Key != nil { + lhs = append(lhs, n.Key) + } + if n.Value != nil { + lhs = append(lhs, n.Value) + } + if len(lhs) > 0 { + if n.Tok == token.DEFINE { + // Note: we can't exactly match the behavior of object resolution + // during the parsing pass here, as it uses the position of the RANGE + // token for the RHS OpPos. That information is not contained within + // the AST. + as := &ast.AssignStmt{ + Lhs: lhs, + Tok: token.DEFINE, + TokPos: n.TokPos, + Rhs: []ast.Expr{&ast.UnaryExpr{Op: token.RANGE, X: n.X}}, + } + // TODO(rFindley): this walkLHS reproduced the parser resolution, but + // is it necessary? By comparison, for a normal AssignStmt we don't + // walk the LHS in case there is an invalid identifier list. + r.walkLHS(lhs) + r.shortVarDecl(as) + } else { + r.walkExprs(lhs) + } + } + ast.Walk(r, n.Body) + + // Declarations + case *ast.GenDecl: + switch n.Tok { + case token.CONST, token.VAR: + for i, spec := range n.Specs { + spec := spec.(*ast.ValueSpec) + kind := ast.Con + if n.Tok == token.VAR { + kind = ast.Var + } + r.walkExprs(spec.Values) + if spec.Type != nil { + ast.Walk(r, spec.Type) + } + r.declare(spec, i, r.topScope, kind, spec.Names...) + } + case token.TYPE: + for _, spec := range n.Specs { + spec := spec.(*ast.TypeSpec) + // Go spec: The scope of a type identifier declared inside a function begins + // at the identifier in the TypeSpec and ends at the end of the innermost + // containing block. + r.declare(spec, nil, r.topScope, ast.Typ, spec.Name) + if spec.TParams != nil { + r.openScope(spec.Pos()) + defer r.closeScope() + r.walkFieldList(r.topScope, spec.TParams, ast.Typ) + } + ast.Walk(r, spec.Type) + } + } + + case *ast.FuncDecl: + scope := ast.NewScope(r.topScope) + r.walkFieldList(scope, n.Recv, ast.Var) + r.walkFuncType(scope, n.Type) + r.walkBody(scope, n.Body) + if n.Recv == nil && n.Name.Name != "init" { + r.declare(n, nil, r.pkgScope, ast.Fun, n.Name) + } + + default: + return r + } + + return nil +} + +func (r *resolver) walkFuncType(scope *ast.Scope, typ *ast.FuncType) { + r.walkFieldList(scope, typ.TParams, ast.Typ) + r.walkFieldList(scope, typ.Params, ast.Var) + r.walkFieldList(scope, typ.Results, ast.Var) +} + +func (r *resolver) walkFieldList(scope *ast.Scope, list *ast.FieldList, kind ast.ObjKind) { + if list == nil { + return + } + for _, f := range list.List { + if f.Type != nil { + ast.Walk(r, f.Type) + } + r.declare(f, nil, scope, kind, f.Names...) + } +} + +func (r *resolver) walkBody(scope *ast.Scope, body *ast.BlockStmt) { + if body == nil { + return + } + r.topScope = scope // open function scope + defer r.closeScope() + r.openLabelScope() + defer r.closeLabelScope() + r.walkStmts(body.List) +}