blob: 0b3a77401f432f2d54094793c59e2d4c2d3cbc36 [file] [log] [blame]
// Copyright 2013 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 types
import (
"fmt"
"go/ast"
"go/token"
"strconv"
)
func (check *checker) declareObj(scope, altScope *Scope, obj Object, dotImport token.Pos) {
alt := scope.Insert(obj)
if alt == nil && altScope != nil {
// see if there is a conflicting declaration in altScope
alt = altScope.Lookup(obj.Name())
}
if alt != nil {
prevDecl := ""
// for dot-imports, local declarations are declared first - swap messages
if dotImport.IsValid() {
if pos := alt.Pos(); pos.IsValid() {
check.errorf(pos, fmt.Sprintf("%s redeclared in this block by dot-import at %s",
obj.Name(), check.fset.Position(dotImport)))
return
}
// get by w/o other position
check.errorf(dotImport, fmt.Sprintf("dot-import redeclares %s", obj.Name()))
return
}
if pos := alt.Pos(); pos.IsValid() {
prevDecl = fmt.Sprintf("\n\tother declaration at %s", check.fset.Position(pos))
}
check.errorf(obj.Pos(), fmt.Sprintf("%s redeclared in this block%s", obj.Name(), prevDecl))
}
}
func (check *checker) resolveIdent(scope *Scope, ident *ast.Ident) bool {
for ; scope != nil; scope = scope.Outer {
if obj := scope.Lookup(ident.Name); obj != nil {
check.register(ident, obj)
return true
}
}
return false
}
func (check *checker) resolve(importer Importer) (methods []*ast.FuncDecl) {
pkg := check.pkg
// complete package scope
i := 0
for _, file := range check.files {
// package names must match
switch name := file.Name.Name; {
case pkg.name == "":
pkg.name = name
case name != pkg.name:
check.errorf(file.Package, "package %s; expected %s", name, pkg.name)
continue // ignore this file
}
// keep this file
check.files[i] = file
i++
// the package identifier denotes the current package
check.register(file.Name, pkg)
// insert top-level file objects in package scope
// (the parser took care of declaration errors)
for _, decl := range file.Decls {
switch d := decl.(type) {
case *ast.BadDecl:
// ignore
case *ast.GenDecl:
if d.Tok == token.CONST {
check.assocInitvals(d)
}
for _, spec := range d.Specs {
switch s := spec.(type) {
case *ast.ImportSpec:
// handled separately below
case *ast.ValueSpec:
for _, name := range s.Names {
if name.Name == "_" {
continue
}
pkg.scope.Insert(check.lookup(name))
}
case *ast.TypeSpec:
if s.Name.Name == "_" {
continue
}
pkg.scope.Insert(check.lookup(s.Name))
default:
check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s)
}
}
case *ast.FuncDecl:
if d.Recv != nil {
// collect method
methods = append(methods, d)
continue
}
if d.Name.Name == "_" || d.Name.Name == "init" {
continue // blank (_) and init functions are inaccessible
}
pkg.scope.Insert(check.lookup(d.Name))
default:
check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d)
}
}
}
check.files = check.files[0:i]
// complete file scopes with imports and resolve identifiers
for _, file := range check.files {
// build file scope by processing all imports
importErrors := false
fileScope := &Scope{Outer: pkg.scope}
for _, spec := range file.Imports {
if importer == nil {
importErrors = true
continue
}
path, _ := strconv.Unquote(spec.Path.Value)
imp, err := importer(pkg.imports, path)
if err != nil {
check.errorf(spec.Path.Pos(), "could not import %s (%s)", path, err)
importErrors = true
continue
}
// TODO(gri) If a local package name != "." is provided,
// global identifier resolution could proceed even if the
// import failed. Consider adjusting the logic here a bit.
// local name overrides imported package name
name := imp.name
if spec.Name != nil {
name = spec.Name.Name
}
// add import to file scope
if name == "." {
// merge imported scope with file scope
for _, obj := range imp.scope.Entries {
// gcimported package scopes contain non-exported
// objects such as types used in partially exported
// objects - do not accept them
if ast.IsExported(obj.Name()) {
check.declareObj(fileScope, pkg.scope, obj, spec.Pos())
}
}
// TODO(gri) consider registering the "." identifier
// if we have Context.Ident callbacks for say blank
// (_) identifiers
// check.register(spec.Name, pkg)
} else if name != "_" {
// declare imported package object in file scope
// (do not re-use imp in the file scope but create
// a new object instead; the Decl field is different
// for different files)
obj := &Package{name: name, scope: imp.scope, spec: spec}
check.declareObj(fileScope, pkg.scope, obj, token.NoPos)
}
}
// resolve identifiers
if importErrors {
// don't use the universe scope without correct imports
// (objects in the universe may be shadowed by imports;
// with missing imports, identifiers might get resolved
// incorrectly to universe objects)
pkg.scope.Outer = nil
}
i := 0
for _, ident := range file.Unresolved {
if !check.resolveIdent(fileScope, ident) {
check.errorf(ident.Pos(), "undeclared name: %s", ident.Name)
file.Unresolved[i] = ident
i++
}
}
file.Unresolved = file.Unresolved[0:i]
pkg.scope.Outer = Universe // reset outer scope (is nil if there were importErrors)
}
return
}