typewriter
typewriter copied to clipboard
undeclared name error when referencing external types
This cause the undeclared name error when referencing external types. Described in #8
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)