undeclared name error when referencing external types
c9s opened this issue · 5 comments
The main reason is that ParseExpr
doesn't parse the import declaration, hence the external package type is unknown.
parser.parseFile
: https://github.com/golang/go/blob/master/src/go/parser/parser.go#L2456
parser.ParseExpr
: https://github.com/golang/go/blob/master/src/go/parser/interface.go#L174
The import decl is handled outside of ParseFile
and ParseExpr
: https://github.com/golang/go/blob/master/src/go/build/build.go#L729
It seems that we need to add the package decl into the check manually when using types.Eval:
type Checker struct {
// package information
// (initialized by NewChecker, valid for the life-time of checker)
conf *Config
fset *token.FileSet
pkg *Package
*Info
objMap map[Object]*declInfo // maps package-level object to declaration info
// ... cut
}
OK, here is another update for the internal code.
types.Eval
initialize a Checker
, and it can handle the import decl using DefaultImporter
(which is defined in gcimporter
. however, in typewriter
, we only pass the type name, so the checker doesn't know which package to import.
The Importer
is defined in types.Config
.
Here is some snippets of injecting import package symbols (from go/types/resolver.go
):
var pkgImports = make(map[*Package]bool)
for _, imp := range pkg.imports {
pkgImports[imp] = true
}
if importer := check.conf.Importer; importer != nil {
imp, err = importer.Import(path)
if imp == nil && err == nil {
err = fmt.Errorf("Config.Importer.Import(%s) returned nil but no error", path)
}
} else {
err = fmt.Errorf("Config.Importer not installed")
}
if err != nil {
check.errorf(s.Path.Pos(), "could not import %s (%s)", path, err)
continue
}
if !pkgImports[imp] {
pkgImports[imp] = true
if imp != Unsafe {
pkg.imports = append(pkg.imports, imp)
}
}
// local name overrides imported package name
name := imp.name
if s.Name != nil {
name = s.Name.Name
if name == "init" {
check.errorf(s.Name.Pos(), "cannot declare init - must be func")
continue
}
}
obj := NewPkgName(s.Pos(), pkg, name, imp)
if s.Name != nil {
// in a dot-import, the dot represents the package
check.recordDef(s.Name, obj)
} else {
check.recordImplicit(s, obj)
}
// declare imported package object in file scope
check.declare(fileScope, nil, obj)