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) {
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
// 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
}
defer un(trace(p, "Type"))
}
- typ := p.tryType()
+ typ := p.tryIdentOrType()
if typ == nil {
pos := p.pos
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}
}
// 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}
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"))
}
typ = name
if p.tok == token.PERIOD {
typ = p.parseQualifiedIdent(name)
- } else {
- p.resolve(typ)
}
} else {
// name1, name2, ... T
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
}
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)
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"))
}
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
}
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
}
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"))
}
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.
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)
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}
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"))
}
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 {
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
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
}
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)
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:
case token.MUL:
return p.parsePointerType()
case token.FUNC:
- typ, _ := p.parseFuncType()
+ typ := p.parseFuncType()
return typ
case token.INTERFACE:
return p.parseInterfaceType()
return nil
}
-func (p *parser) tryType() ast.Expr {
- typ := p.tryIdentOrType()
- if typ != nil {
- p.resolve(typ)
- }
- return typ
-}
-
// ----------------------------------------------------------------------------
// Blocks
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}
}
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}
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}
// 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"))
}
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:
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"))
}
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
}
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
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))
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
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"))
}
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:
// <- (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 {
// 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) {
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
}
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
}
defer un(trace(p, "SimpleStmt"))
}
- x := p.parseLhsList()
+ x := p.parseList(false)
switch p.tok {
case
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
}
// 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
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 {
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()
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()
}
pos := p.expect(token.IF)
- p.openScope()
- defer p.closeScope()
init, cond := p.parseIfHeader()
body := p.parseBlockStmt()
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}
}
}
pos := p.expect(token.SWITCH)
- p.openScope()
- defer p.closeScope()
var s1, s2 ast.Stmt
if p.tok != token.LBRACE {
//
// 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)
}
}
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 {
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 {
colon := p.expect(token.COLON)
body := p.parseStmtList()
- p.closeScope()
return &ast.CommClause{Case: pos, Comm: comm, Colon: colon, Body: body}
}
}
pos := p.expect(token.FOR)
- p.openScope()
- defer p.closeScope()
var s1, s2, s3 ast.Stmt
var isRange bool
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
}
}
- // 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,
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.
p.next()
}
spec.Type = p.parseType()
- p.closeScope()
}
func (p *parser) parseTypeSpec(doc *ast.CommentGroup, _ token.Pos, _ token.Token, _ int) ast.Spec {
}
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:
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];
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 {
},
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
}
return nil
}
- p.openScope()
- p.pkgScope = p.topScope
var decls []ast.Decl
if p.mode&PackageClauseOnly == 0 {
// import decls
}
}
}
- 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,
}
-
- 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,
+ var declErr func(token.Pos, string)
+ if p.mode&DeclarationErrors != 0 {
+ declErr = p.error
}
+ resolveFile(f, p.file, declErr)
+
+ return f
}
--- /dev/null
+// 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)
+}