clipperhouse/typewriter

undeclared name error when referencing external types

Opened this issue · 5 comments

c9s commented

This cause the undeclared name error when referencing external types. Described in #8

c9s commented

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

c9s commented

The import decl is handled outside of ParseFile and ParseExpr: https://github.com/golang/go/blob/master/src/go/build/build.go#L729

c9s commented

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
}
c9s commented

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.

c9s commented

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)