package parser
import (
)
const debugResolve = false
func ( *ast.File, *token.File, func(token.Pos, string)) {
:= ast.NewScope(nil)
:= &resolver{
handle: ,
declErr: ,
topScope: ,
pkgScope: ,
depth: 1,
}
for , := range .Decls {
ast.Walk(, )
}
.closeScope()
assert(.topScope == nil, "unbalanced scopes")
assert(.labelScope == nil, "unbalanced label scopes")
:= 0
for , := range .unresolved {
assert(.Obj == unresolved, "object already resolved")
.Obj = .pkgScope.Lookup(.Name)
if .Obj == nil {
.unresolved[] =
++
} else if debugResolve {
:= .Obj.Decl.(interface{ () token.Pos }).()
.trace("resolved %s@%v to package object %v", .Name, .Pos(), )
}
}
.Scope = .pkgScope
.Unresolved = .unresolved[0:]
}
type resolver struct {
handle *token.File
declErr func(token.Pos, string)
pkgScope *ast.Scope
topScope *ast.Scope
unresolved []*ast.Ident
depth int
labelScope *ast.Scope
targetStack [][]*ast.Ident
}
func ( *resolver) ( string, ...any) {
fmt.Println(strings.Repeat(". ", .depth) + .sprintf(, ...))
}
func ( *resolver) ( string, ...any) string {
for , := range {
switch arg := .(type) {
case token.Pos:
[] = .handle.Position()
}
}
return fmt.Sprintf(, ...)
}
func ( *resolver) ( token.Pos) {
if debugResolve {
.trace("opening scope @%v", )
.depth++
}
.topScope = ast.NewScope(.topScope)
}
func ( *resolver) () {
if debugResolve {
.depth--
.trace("closing scope")
}
.topScope = .topScope.Outer
}
func ( *resolver) () {
.labelScope = ast.NewScope(.labelScope)
.targetStack = append(.targetStack, nil)
}
func ( *resolver) () {
:= len(.targetStack) - 1
:= .labelScope
for , := range .targetStack[] {
.Obj = .Lookup(.Name)
if .Obj == nil && .declErr != nil {
.declErr(.Pos(), fmt.Sprintf("label %s undefined", .Name))
}
}
.targetStack = .targetStack[0:]
.labelScope = .labelScope.Outer
}
func ( *resolver) (, any, *ast.Scope, ast.ObjKind, ...*ast.Ident) {
for , := range {
if .Obj != nil {
panic(fmt.Sprintf("%v: identifier %s already declared or resolved", .Pos(), .Name))
}
:= ast.NewObj(, .Name)
.Decl =
.Data =
if , := .(*ast.Ident); ! {
.Obj =
}
if .Name != "_" {
if debugResolve {
.trace("declaring %s@%v", .Name, .Pos())
}
if := .Insert(); != nil && .declErr != nil {
:= ""
if := .Pos(); .IsValid() {
= .sprintf("\n\tprevious declaration at %v", )
}
.declErr(.Pos(), fmt.Sprintf("%s redeclared in this block%s", .Name, ))
}
}
}
}
func ( *resolver) ( *ast.AssignStmt) {
:= 0
for , := range .Lhs {
if , := .(*ast.Ident); {
assert(.Obj == nil, "identifier already declared or resolved")
:= ast.NewObj(ast.Var, .Name)
.Decl =
.Obj =
if .Name != "_" {
if debugResolve {
.trace("declaring %s@%v", .Name, .Pos())
}
if := .topScope.Insert(); != nil {
.Obj =
} else {
++
}
}
}
}
if == 0 && .declErr != nil {
.declErr(.Lhs[0].Pos(), "no new variables on left side of :=")
}
}
var unresolved = new(ast.Object)
func ( *resolver) ( *ast.Ident, bool) {
if .Obj != nil {
panic(.sprintf("%v: identifier %s already declared or resolved", .Pos(), .Name))
}
if .Name == "_" {
return
}
for := .topScope; != nil; = .Outer {
if := .Lookup(.Name); != nil {
if debugResolve {
.trace("resolved %v:%s to %v", .Pos(), .Name, )
}
assert(.Name != "", "obj with no name")
if , := .Decl.(*ast.Ident); ! {
.Obj =
}
return
}
}
if {
.Obj = unresolved
.unresolved = append(.unresolved, )
}
}
func ( *resolver) ( []ast.Expr) {
for , := range {
ast.Walk(, )
}
}
func ( *resolver) ( []ast.Expr) {
for , := range {
:= unparen()
if , := .(*ast.Ident); ! && != nil {
ast.Walk(, )
}
}
}
func ( *resolver) ( []ast.Stmt) {
for , := range {
ast.Walk(, )
}
}
func ( *resolver) ( ast.Node) ast.Visitor {
if debugResolve && != nil {
.trace("node %T@%v", , .Pos())
}
switch n := .(type) {
case *ast.Ident:
.resolve(, true)
case *ast.FuncLit:
.openScope(.Pos())
defer .closeScope()
.walkFuncType(.Type)
.walkBody(.Body)
case *ast.SelectorExpr:
ast.Walk(, .X)
case *ast.StructType:
.openScope(.Pos())
defer .closeScope()
.walkFieldList(.Fields, ast.Var)
case *ast.FuncType:
.openScope(.Pos())
defer .closeScope()
.walkFuncType()
case *ast.CompositeLit:
if .Type != nil {
ast.Walk(, .Type)
}
for , := range .Elts {
if , := .(*ast.KeyValueExpr); != nil {
if , := .Key.(*ast.Ident); != nil {
.resolve(, false)
} else {
ast.Walk(, .Key)
}
ast.Walk(, .Value)
} else {
ast.Walk(, )
}
}
case *ast.InterfaceType:
.openScope(.Pos())
defer .closeScope()
.walkFieldList(.Methods, ast.Fun)
case *ast.LabeledStmt:
.declare(, nil, .labelScope, ast.Lbl, .Label)
ast.Walk(, .Stmt)
case *ast.AssignStmt:
.walkExprs(.Rhs)
if .Tok == token.DEFINE {
.shortVarDecl()
} else {
.walkExprs(.Lhs)
}
case *ast.BranchStmt:
if .Tok != token.FALLTHROUGH && .Label != nil {
:= len(.targetStack) - 1
.targetStack[] = append(.targetStack[], .Label)
}
case *ast.BlockStmt:
.openScope(.Pos())
defer .closeScope()
.walkStmts(.List)
case *ast.IfStmt:
.openScope(.Pos())
defer .closeScope()
if .Init != nil {
ast.Walk(, .Init)
}
ast.Walk(, .Cond)
ast.Walk(, .Body)
if .Else != nil {
ast.Walk(, .Else)
}
case *ast.CaseClause:
.walkExprs(.List)
.openScope(.Pos())
defer .closeScope()
.walkStmts(.Body)
case *ast.SwitchStmt:
.openScope(.Pos())
defer .closeScope()
if .Init != nil {
ast.Walk(, .Init)
}
if .Tag != nil {
if .Init != nil {
.openScope(.Tag.Pos())
defer .closeScope()
}
ast.Walk(, .Tag)
}
if .Body != nil {
.walkStmts(.Body.List)
}
case *ast.TypeSwitchStmt:
if .Init != nil {
.openScope(.Pos())
defer .closeScope()
ast.Walk(, .Init)
}
.openScope(.Assign.Pos())
defer .closeScope()
ast.Walk(, .Assign)
if .Body != nil {
.walkStmts(.Body.List)
}
case *ast.CommClause:
.openScope(.Pos())
defer .closeScope()
if .Comm != nil {
ast.Walk(, .Comm)
}
.walkStmts(.Body)
case *ast.SelectStmt:
if .Body != nil {
.walkStmts(.Body.List)
}
case *ast.ForStmt:
.openScope(.Pos())
defer .closeScope()
if .Init != nil {
ast.Walk(, .Init)
}
if .Cond != nil {
ast.Walk(, .Cond)
}
if .Post != nil {
ast.Walk(, .Post)
}
ast.Walk(, .Body)
case *ast.RangeStmt:
.openScope(.Pos())
defer .closeScope()
ast.Walk(, .X)
var []ast.Expr
if .Key != nil {
= append(, .Key)
}
if .Value != nil {
= append(, .Value)
}
if len() > 0 {
if .Tok == token.DEFINE {
:= &ast.AssignStmt{
Lhs: ,
Tok: token.DEFINE,
TokPos: .TokPos,
Rhs: []ast.Expr{&ast.UnaryExpr{Op: token.RANGE, X: .X}},
}
.walkLHS()
.shortVarDecl()
} else {
.walkExprs()
}
}
ast.Walk(, .Body)
case *ast.GenDecl:
switch .Tok {
case token.CONST, token.VAR:
for , := range .Specs {
:= .(*ast.ValueSpec)
:= ast.Con
if .Tok == token.VAR {
= ast.Var
}
.walkExprs(.Values)
if .Type != nil {
ast.Walk(, .Type)
}
.declare(, , .topScope, , .Names...)
}
case token.TYPE:
for , := range .Specs {
:= .(*ast.TypeSpec)
.declare(, nil, .topScope, ast.Typ, .Name)
if .TypeParams != nil {
.openScope(.Pos())
defer .closeScope()
.walkTParams(.TypeParams)
}
ast.Walk(, .Type)
}
}
case *ast.FuncDecl:
.openScope(.Pos())
defer .closeScope()
.walkRecv(.Recv)
if .Type.TypeParams != nil {
.walkTParams(.Type.TypeParams)
}
.resolveList(.Type.Params)
.resolveList(.Type.Results)
.declareList(.Recv, ast.Var)
.declareList(.Type.Params, ast.Var)
.declareList(.Type.Results, ast.Var)
.walkBody(.Body)
if .Recv == nil && .Name.Name != "init" {
.declare(, nil, .pkgScope, ast.Fun, .Name)
}
default:
return
}
return nil
}
func ( *resolver) ( *ast.FuncType) {
.resolveList(.Params)
.resolveList(.Results)
.declareList(.Params, ast.Var)
.declareList(.Results, ast.Var)
}
func ( *resolver) ( *ast.FieldList) {
if == nil {
return
}
for , := range .List {
if .Type != nil {
ast.Walk(, .Type)
}
}
}
func ( *resolver) ( *ast.FieldList, ast.ObjKind) {
if == nil {
return
}
for , := range .List {
.declare(, nil, .topScope, , .Names...)
}
}
func ( *resolver) ( *ast.FieldList) {
if == nil || len(.List) == 0 {
return
}
:= .List[0].Type
if , := .(*ast.StarExpr); {
= .X
}
var []ast.Expr
var []ast.Expr
switch typ := .(type) {
case *ast.IndexExpr:
= []ast.Expr{.Index}
= append(, .X)
case *ast.IndexListExpr:
= .Indices
= append(, .X)
default:
= append(, )
}
for , := range {
if , := .(*ast.Ident); != nil {
.declare(, nil, .topScope, ast.Typ, )
} else {
= append(, )
}
}
for , := range {
if != nil {
ast.Walk(, )
}
}
for , := range .List[1:] {
if .Type != nil {
ast.Walk(, .Type)
}
}
}
func ( *resolver) ( *ast.FieldList, ast.ObjKind) {
if == nil {
return
}
.resolveList()
.declareList(, )
}
func ( *resolver) ( *ast.FieldList) {
.declareList(, ast.Typ)
.resolveList()
}
func ( *resolver) ( *ast.BlockStmt) {
if == nil {
return
}
.openLabelScope()
defer .closeLabelScope()
.walkStmts(.List)
}