aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Donovan <adonovan@google.com>2014-02-27 13:21:59 -0500
committerAlan Donovan <adonovan@google.com>2014-02-27 13:21:59 -0500
commitced954c167af947a6d119cd125918ace69e9be23 (patch)
tree8c7833cfdadd67e92f4e48c8119832362e8d6a6a
parentaaca3a4f957bbdb2f3d773f8b253e01907b251b1 (diff)
downloadtools-ced954c167af947a6d119cd125918ace69e9be23.tar.gz
go.tools/go/types: split Info.Objects map into Defs and Uses.
An identifier X in anonymous struct field struct{X} is both a definition of a field (*Var) and reference to a type (*TypeName). Now that we have split the map, we can capture both of these aspects. Interestingly, every client but one was going to extra effort to iterate over just the uses or just the defs; this simplifies them. Also, fix two bug related to tagless switches: - An entry was being recorded in the Object map for a piece of synthetic syntax. - The "true" identifier was being looked up in the current scope, which allowed perverse users to locally redefine it. Now we use the bool (not untyped boolean) constant true, per the consequent clarification of the spec (issue 7404). + tests. Fixes golang/go#7276 LGTM=gri R=gri CC=golang-codereviews https://golang.org/cl/68270044
-rw-r--r--cmd/vet/main.go3
-rw-r--r--cmd/vet/nilfunc.go4
-rw-r--r--cmd/vet/shadow.go2
-rw-r--r--cmd/vet/types.go13
-rw-r--r--go/loader/loader.go3
-rw-r--r--go/loader/pkginfo.go10
-rw-r--r--go/ssa/source_test.go60
-rw-r--r--go/ssa/testdata/objlookup.go4
-rw-r--r--go/types/api.go20
-rw-r--r--go/types/assignments.go4
-rw-r--r--go/types/builtins_test.go6
-rw-r--r--go/types/call.go3
-rw-r--r--go/types/check.go14
-rw-r--r--go/types/decl.go4
-rw-r--r--go/types/expr.go2
-rw-r--r--go/types/issues_test.go8
-rw-r--r--go/types/labels.go6
-rw-r--r--go/types/resolver.go6
-rw-r--r--go/types/resolver_test.go49
-rw-r--r--go/types/stmt.go22
-rw-r--r--go/types/testdata/stmt0.src10
-rw-r--r--go/types/typexpr.go36
-rw-r--r--oracle/implements.go4
-rw-r--r--oracle/referrers.go7
24 files changed, 193 insertions, 107 deletions
diff --git a/cmd/vet/main.go b/cmd/vet/main.go
index b9d8010..9039532 100644
--- a/cmd/vet/main.go
+++ b/cmd/vet/main.go
@@ -204,7 +204,8 @@ func doPackageDir(directory string) {
type Package struct {
path string
- idents map[*ast.Ident]types.Object
+ defs map[*ast.Ident]types.Object
+ uses map[*ast.Ident]types.Object
types map[ast.Expr]types.TypeAndValue
spans map[types.Object]Span
files []*File
diff --git a/cmd/vet/nilfunc.go b/cmd/vet/nilfunc.go
index 3efb2de..922ecc3 100644
--- a/cmd/vet/nilfunc.go
+++ b/cmd/vet/nilfunc.go
@@ -41,9 +41,9 @@ func (f *File) checkNilFuncComparison(e *ast.BinaryExpr) {
var obj types.Object
switch v := e2.(type) {
case *ast.Ident:
- obj = f.pkg.idents[v]
+ obj = f.pkg.uses[v]
case *ast.SelectorExpr:
- obj = f.pkg.idents[v.Sel]
+ obj = f.pkg.uses[v.Sel]
default:
return
}
diff --git a/cmd/vet/shadow.go b/cmd/vet/shadow.go
index afb0fed..1f3c424 100644
--- a/cmd/vet/shadow.go
+++ b/cmd/vet/shadow.go
@@ -189,7 +189,7 @@ func (f *File) checkShadowDecl(d *ast.GenDecl) {
// checkShadowing checks whether the identifier shadows an identifier in an outer scope.
func (f *File) checkShadowing(ident *ast.Ident) {
- obj := f.pkg.idents[ident]
+ obj := f.pkg.defs[ident]
if obj == nil {
return
}
diff --git a/cmd/vet/types.go b/cmd/vet/types.go
index dce66dd..2cf4de4 100644
--- a/cmd/vet/types.go
+++ b/cmd/vet/types.go
@@ -14,7 +14,8 @@ import (
)
func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) error {
- pkg.idents = make(map[*ast.Ident]types.Object)
+ pkg.defs = make(map[*ast.Ident]types.Object)
+ pkg.uses = make(map[*ast.Ident]types.Object)
pkg.spans = make(map[types.Object]Span)
pkg.types = make(map[ast.Expr]types.TypeAndValue)
// By providing a Config with our own error function, it will continue
@@ -23,13 +24,17 @@ func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) error {
Error: func(error) {},
}
info := &types.Info{
- Types: pkg.types,
- Objects: pkg.idents,
+ Types: pkg.types,
+ Defs: pkg.defs,
+ Uses: pkg.uses,
}
typesPkg, err := config.Check(pkg.path, fs, astFiles, info)
pkg.typesPkg = typesPkg
// update spans
- for id, obj := range pkg.idents {
+ for id, obj := range pkg.defs {
+ pkg.growSpan(id, obj)
+ }
+ for id, obj := range pkg.uses {
pkg.growSpan(id, obj)
}
return err
diff --git a/go/loader/loader.go b/go/loader/loader.go
index 16c330a..0048be8 100644
--- a/go/loader/loader.go
+++ b/go/loader/loader.go
@@ -683,7 +683,8 @@ func (imp *importer) createPackage(path string, files ...*ast.File) *PackageInfo
Files: files,
Info: types.Info{
Types: make(map[ast.Expr]types.TypeAndValue),
- Objects: make(map[*ast.Ident]types.Object),
+ Defs: make(map[*ast.Ident]types.Object),
+ Uses: make(map[*ast.Ident]types.Object),
Implicits: make(map[ast.Node]types.Object),
Scopes: make(map[ast.Node]*types.Scope),
Selections: make(map[*ast.SelectorExpr]*types.Selection),
diff --git a/go/loader/pkginfo.go b/go/loader/pkginfo.go
index dba4f0c..167da94 100644
--- a/go/loader/pkginfo.go
+++ b/go/loader/pkginfo.go
@@ -54,10 +54,18 @@ func (info *PackageInfo) ValueOf(e ast.Expr) exact.Value {
}
// ObjectOf returns the typechecker object denoted by the specified id.
+//
+// If id is an anonymous struct field, the field (*types.Var) is
+// returned, not the type (*types.TypeName).
+//
// Precondition: id belongs to the package's ASTs.
//
func (info *PackageInfo) ObjectOf(id *ast.Ident) types.Object {
- return info.Objects[id]
+ obj, ok := info.Defs[id]
+ if ok {
+ return obj
+ }
+ return info.Uses[id]
}
// IsType returns true iff expression e denotes a type.
diff --git a/go/ssa/source_test.go b/go/ssa/source_test.go
index 2f3bcc9..ee371c8 100644
--- a/go/ssa/source_test.go
+++ b/go/ssa/source_test.go
@@ -60,51 +60,49 @@ func TestObjValueLookup(t *testing.T) {
mainPkg.SetDebugMode(true)
mainPkg.Build()
- // Gather all idents and objects in file.
- objs := make(map[types.Object]bool)
- var ids []*ast.Ident
- ast.Inspect(f, func(n ast.Node) bool {
- if id, ok := n.(*ast.Ident); ok {
- ids = append(ids, id)
- if obj := mainInfo.ObjectOf(id); obj != nil {
- objs[obj] = true
- }
- }
- return true
- })
-
- // Check invariants for func and const objects.
- for obj := range objs {
+ var varIds []*ast.Ident
+ var varObjs []*types.Var
+ for id, obj := range mainInfo.Defs {
+ // Check invariants for func and const objects.
switch obj := obj.(type) {
case *types.Func:
checkFuncValue(t, prog, obj)
case *types.Const:
checkConstValue(t, prog, obj)
+
+ case *types.Var:
+ if id.Name == "_" {
+ continue
+ }
+ varIds = append(varIds, id)
+ varObjs = append(varObjs, obj)
+ }
+ }
+ for id, obj := range mainInfo.Uses {
+ if obj, ok := obj.(*types.Var); ok {
+ varIds = append(varIds, id)
+ varObjs = append(varObjs, obj)
}
}
// Check invariants for var objects.
// The result varies based on the specific Ident.
- for _, id := range ids {
- if id.Name == "_" {
+ for i, id := range varIds {
+ obj := varObjs[i]
+ ref, _ := astutil.PathEnclosingInterval(f, id.Pos(), id.Pos())
+ pos := prog.Fset.Position(id.Pos())
+ exp := expectations[fmt.Sprintf("%s:%d", id.Name, pos.Line)]
+ if exp == "" {
+ t.Errorf("%s: no expectation for var ident %s ", pos, id.Name)
continue
}
- if obj, ok := mainInfo.ObjectOf(id).(*types.Var); ok {
- ref, _ := astutil.PathEnclosingInterval(f, id.Pos(), id.Pos())
- pos := prog.Fset.Position(id.Pos())
- exp := expectations[fmt.Sprintf("%s:%d", id.Name, pos.Line)]
- if exp == "" {
- t.Errorf("%s: no expectation for var ident %s ", pos, id.Name)
- continue
- }
- wantAddr := false
- if exp[0] == '&' {
- wantAddr = true
- exp = exp[1:]
- }
- checkVarValue(t, prog, mainPkg, ref, obj, exp, wantAddr)
+ wantAddr := false
+ if exp[0] == '&' {
+ wantAddr = true
+ exp = exp[1:]
}
+ checkVarValue(t, prog, mainPkg, ref, obj, exp, wantAddr)
}
}
diff --git a/go/ssa/testdata/objlookup.go b/go/ssa/testdata/objlookup.go
index f747a2a..d2862d3 100644
--- a/go/ssa/testdata/objlookup.go
+++ b/go/ssa/testdata/objlookup.go
@@ -109,8 +109,8 @@ func main() {
// of (*J).method, so it doesn't help us locate the specific
// ssa.Values here: a bound-method closure and a promotion
// wrapper.
- _ = v11.method // v11::Const
- _ = (*struct{ J }).method
+ _ = v11.method // v11::Const
+ _ = (*struct{ J }).method // J::nil
// These vars are optimised away.
if false {
diff --git a/go/types/api.go b/go/types/api.go
index 43db3ed..c240dce 100644
--- a/go/types/api.go
+++ b/go/types/api.go
@@ -10,7 +10,7 @@
//
// Name resolution maps each identifier (ast.Ident) in the program to the
// language object (Object) it denotes.
-// Use Info.Objects, Info.Implicits for the results of name resolution.
+// Use Info.{Defs,Uses,Implicits} for the results of name resolution.
//
// Constant folding computes the exact constant value (exact.Value) for
// every expression (ast.Expr) that is a compile-time constant.
@@ -130,8 +130,7 @@ type TypeAndValue struct {
type Info struct {
// Types maps expressions to their types, and for constant
// expressions, their values.
- // Identifiers on the lhs of declarations are collected in
- // Objects, not Types.
+ // Identifiers are collected in Defs and Uses, not Types.
//
// For an expression denoting a predeclared built-in function
// the recorded signature is call-site specific. If the call
@@ -139,13 +138,24 @@ type Info struct {
// specific signature. Otherwise, the recorded type is invalid.
Types map[ast.Expr]TypeAndValue
- // Objects maps identifiers to their corresponding objects (including
+ // Defs maps identifiers to the objects they define (including
// package names, dots "." of dot-imports, and blank "_" identifiers).
// For identifiers that do not denote objects (e.g., the package name
// in package clauses, blank identifiers on the lhs of assignments, or
// symbolic variables t in t := x.(type) of type switch headers), the
// corresponding objects are nil.
- Objects map[*ast.Ident]Object
+ //
+ // For an anonymous field, Defs returns the field *Var it defines.
+ //
+ // Invariant: Defs[id] == nil || Defs[id].Pos() == id.Pos()
+ Defs map[*ast.Ident]Object
+
+ // Uses maps identifiers to the objects they denote.
+ //
+ // For an anonymous field, Uses returns the *TypeName it denotes.
+ //
+ // Invariant: Uses[id].Pos() != id.Pos()
+ Uses map[*ast.Ident]Object
// Implicits maps nodes to their implicitly declared objects, if any.
// The following node and object types may appear:
diff --git a/go/types/assignments.go b/go/types/assignments.go
index 8961f0f..ac8aa2c 100644
--- a/go/types/assignments.go
+++ b/go/types/assignments.go
@@ -144,7 +144,7 @@ func (check *checker) assignVar(lhs ast.Expr, x *operand) Type {
// Don't evaluate lhs if it is the blank identifier.
if ident != nil && ident.Name == "_" {
- check.recordObject(ident, nil)
+ check.recordDef(ident, nil)
if !check.assignment(x, nil) {
assert(x.mode == invalid)
x.typ = nil
@@ -285,7 +285,7 @@ func (check *checker) shortVarDecl(pos token.Pos, lhs, rhs []ast.Expr) {
newVars = append(newVars, obj)
}
if obj != nil {
- check.recordObject(ident, obj)
+ check.recordDef(ident, obj)
}
} else {
check.errorf(lhs.Pos(), "cannot declare %s", lhs)
diff --git a/go/types/builtins_test.go b/go/types/builtins_test.go
index c33f63c..ca3bac8 100644
--- a/go/types/builtins_test.go
+++ b/go/types/builtins_test.go
@@ -133,9 +133,9 @@ func testBuiltinSignature(t *testing.T, name, src0, want string) {
}
var conf Config
- objects := make(map[*ast.Ident]Object)
+ uses := make(map[*ast.Ident]Object)
types := make(map[ast.Expr]TypeAndValue)
- _, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Objects: objects, Types: types})
+ _, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Uses: uses, Types: types})
if err != nil {
t.Errorf("%s: %s", src0, err)
return
@@ -172,7 +172,7 @@ func testBuiltinSignature(t *testing.T, name, src0, want string) {
// identifier denoting the expected built-in
switch p := fun.(type) {
case *ast.Ident:
- obj := objects[p]
+ obj := uses[p]
if obj == nil {
t.Errorf("%s: no object found for %s", src0, p)
return
diff --git a/go/types/call.go b/go/types/call.go
index 4cce318..a589510 100644
--- a/go/types/call.go
+++ b/go/types/call.go
@@ -253,7 +253,7 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) {
// selector expressions.
if ident, ok := e.X.(*ast.Ident); ok {
if pkg, _ := check.scope.LookupParent(ident.Name).(*PkgName); pkg != nil {
- check.recordObject(ident, pkg)
+ check.recordUse(ident, pkg)
pkg.used = true
exp := pkg.pkg.scope.Lookup(sel)
if exp == nil {
@@ -268,7 +268,6 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) {
}
check.recordSelection(e, PackageObj, nil, exp, nil, false)
// Simplified version of the code for *ast.Idents:
- // - imported packages use types.Scope and types.Objects
// - imported objects are always fully initialized
switch exp := exp.(type) {
case *Const:
diff --git a/go/types/check.go b/go/types/check.go
index 3b708f3..ef1e2c1 100644
--- a/go/types/check.go
+++ b/go/types/check.go
@@ -258,9 +258,17 @@ func (check *checker) recordCommaOkTypes(x ast.Expr, a [2]Type) {
}
}
-func (check *checker) recordObject(id *ast.Ident, obj Object) {
+func (check *checker) recordDef(id *ast.Ident, obj Object) {
assert(id != nil)
- if m := check.Objects; m != nil {
+ if m := check.Defs; m != nil {
+ m[id] = obj
+ }
+}
+
+func (check *checker) recordUse(id *ast.Ident, obj Object) {
+ assert(id != nil)
+ assert(obj != nil)
+ if m := check.Uses; m != nil {
m[id] = obj
}
}
@@ -274,7 +282,7 @@ func (check *checker) recordImplicit(node ast.Node, obj Object) {
func (check *checker) recordSelection(x *ast.SelectorExpr, kind SelectionKind, recv Type, obj Object, index []int, indirect bool) {
assert(obj != nil && (recv == nil || len(index) > 0))
- check.recordObject(x.Sel, obj)
+ check.recordUse(x.Sel, obj)
// TODO(gri) Should we also call recordTypeAndValue?
if m := check.Selections; m != nil {
m[x] = &Selection{kind, recv, obj, index, indirect}
diff --git a/go/types/decl.go b/go/types/decl.go
index 326818b..60a689c 100644
--- a/go/types/decl.go
+++ b/go/types/decl.go
@@ -27,7 +27,7 @@ func (check *checker) declare(scope *Scope, id *ast.Ident, obj Object) {
return
}
if id != nil {
- check.recordObject(id, obj)
+ check.recordDef(id, obj)
}
}
@@ -253,7 +253,7 @@ func (check *checker) typeDecl(obj *TypeName, typ ast.Expr, def *Named, path []*
continue
}
}
- check.recordObject(check.objMap[m].fdecl.Name, m)
+ check.recordDef(check.objMap[m].fdecl.Name, m)
check.objDecl(m, nil, nil)
// Methods with blank _ names cannot be found.
// Don't add them to the method list.
diff --git a/go/types/expr.go b/go/types/expr.go
index e746825..f0e0c0d 100644
--- a/go/types/expr.go
+++ b/go/types/expr.go
@@ -1023,7 +1023,7 @@ func (check *checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
continue
}
fld := fields[i]
- check.recordObject(key, fld)
+ check.recordUse(key, fld)
// 0 <= i < len(fields)
if visited[i] {
check.errorf(kv.Pos(), "duplicate field name %s in struct literal", key.Name)
diff --git a/go/types/issues_test.go b/go/types/issues_test.go
index 8bca193..cd86ea0 100644
--- a/go/types/issues_test.go
+++ b/go/types/issues_test.go
@@ -130,15 +130,15 @@ type T struct{} // receiver type after method declaration
}
var conf Config
- objects := make(map[*ast.Ident]Object)
- _, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Objects: objects})
+ defs := make(map[*ast.Ident]Object)
+ _, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Defs: defs})
if err != nil {
t.Fatal(err)
}
m := f.Decls[0].(*ast.FuncDecl)
- res1 := objects[m.Name].(*Func).Type().(*Signature).Results().At(0)
- res2 := objects[m.Type.Results.List[0].Names[0]].(*Var)
+ res1 := defs[m.Name].(*Func).Type().(*Signature).Results().At(0)
+ res2 := defs[m.Type.Results.List[0].Names[0]].(*Var)
if res1 != res2 {
t.Errorf("got %s (%p) != %s (%p)", res1, res2, res1, res2)
diff --git a/go/types/labels.go b/go/types/labels.go
index 65b39e4..d854031 100644
--- a/go/types/labels.go
+++ b/go/types/labels.go
@@ -138,7 +138,7 @@ func (check *checker) blockBranches(all *Scope, parent *block, lstmt *ast.Labele
// ok to continue
} else {
b.insert(s)
- check.recordObject(s.Label, lbl)
+ check.recordDef(s.Label, lbl)
}
// resolve matching forward jumps and remove them from fwdJumps
i := 0
@@ -146,7 +146,7 @@ func (check *checker) blockBranches(all *Scope, parent *block, lstmt *ast.Labele
if jmp.Label.Name == name {
// match
lbl.used = true
- check.recordObject(jmp.Label, lbl)
+ check.recordUse(jmp.Label, lbl)
if jumpsOverVarDecl(jmp) {
check.errorf(
jmp.Label.Pos(),
@@ -220,7 +220,7 @@ func (check *checker) blockBranches(all *Scope, parent *block, lstmt *ast.Labele
// record label use
obj := all.Lookup(name)
obj.(*Label).used = true
- check.recordObject(s.Label, obj)
+ check.recordUse(s.Label, obj)
case *ast.AssignStmt:
if s.Tok == token.DEFINE {
diff --git a/go/types/resolver.go b/go/types/resolver.go
index 4b9dcfd..ae27e4b 100644
--- a/go/types/resolver.go
+++ b/go/types/resolver.go
@@ -125,7 +125,7 @@ func (check *checker) resolveFiles(files []*ast.File) {
for _, file := range files {
// The package identifier denotes the current package,
// but there is no corresponding package object.
- check.recordObject(file.Name, nil)
+ check.recordDef(file.Name, nil)
fileScope := NewScope(pkg.scope)
check.recordScope(file, fileScope)
@@ -188,7 +188,7 @@ func (check *checker) resolveFiles(files []*ast.File) {
obj := NewPkgName(s.Pos(), imp, name)
if s.Name != nil {
// in a dot-import, the dot represents the package
- check.recordObject(s.Name, obj)
+ check.recordDef(s.Name, obj)
} else {
check.recordImplicit(s, obj)
}
@@ -308,7 +308,7 @@ func (check *checker) resolveFiles(files []*ast.File) {
if name == "init" {
// don't declare init functions in the package scope - they are invisible
obj.parent = pkg.scope
- check.recordObject(d.Name, obj)
+ check.recordDef(d.Name, obj)
// init functions must have a body
if d.Body == nil {
check.errorf(obj.pos, "missing function body")
diff --git a/go/types/resolver_test.go b/go/types/resolver_test.go
index 5c8a97e..9fc9893 100644
--- a/go/types/resolver_test.go
+++ b/go/types/resolver_test.go
@@ -9,6 +9,7 @@ import (
"go/ast"
"go/parser"
"go/token"
+ "sort"
"testing"
_ "code.google.com/p/go.tools/go/gcimporter"
@@ -29,6 +30,7 @@ var sources = []string{
`
package p
import "fmt"
+ type errorStringer struct { fmt.Stringer; error }
func f() string {
_ = "foo"
return fmt.Sprintf("%d", g())
@@ -42,7 +44,7 @@ var sources = []string{
func h() Mode { return ImportsOnly }
var _, x int = 1, 2
func init() {}
- type T struct{ sync.Mutex; a, b, c int}
+ type T struct{ *sync.Mutex; a, b, c int}
type I interface{ m() }
var _ = T{a: 1, b: 2, c: 3}
func (_ T) m() {}
@@ -55,6 +57,7 @@ var sources = []string{
case int:
_ = x
}
+ switch {} // implicit 'true' tag
}
`,
`
@@ -99,8 +102,9 @@ func TestResolveIdents(t *testing.T) {
// resolve and type-check package AST
var conf Config
- idents := make(map[*ast.Ident]Object)
- _, err := conf.Check("testResolveIdents", fset, files, &Info{Objects: idents})
+ uses := make(map[*ast.Ident]Object)
+ defs := make(map[*ast.Ident]Object)
+ _, err := conf.Check("testResolveIdents", fset, files, &Info{Defs: defs, Uses: uses})
if err != nil {
t.Fatal(err)
}
@@ -117,12 +121,12 @@ func TestResolveIdents(t *testing.T) {
ast.Inspect(f, func(n ast.Node) bool {
if s, ok := n.(*ast.SelectorExpr); ok {
if x, ok := s.X.(*ast.Ident); ok {
- obj := idents[x]
+ obj := uses[x]
if obj == nil {
t.Errorf("%s: unresolved qualified identifier %s", fset.Position(x.Pos()), x.Name)
return false
}
- if _, ok := obj.(*PkgName); ok && idents[s.Sel] == nil {
+ if _, ok := obj.(*PkgName); ok && uses[s.Sel] == nil {
t.Errorf("%s: unresolved selector %s", fset.Position(s.Sel.Pos()), s.Sel.Name)
return false
}
@@ -134,14 +138,30 @@ func TestResolveIdents(t *testing.T) {
})
}
- // check that each identifier in the source is found in the idents map
+ for id, obj := range uses {
+ if obj == nil {
+ t.Errorf("%s: Uses[%s] == nil", fset.Position(id.Pos()), id.Name)
+ }
+ }
+
+ // check that each identifier in the source is found in uses or defs or both
+ var both []string
for _, f := range files {
ast.Inspect(f, func(n ast.Node) bool {
if x, ok := n.(*ast.Ident); ok {
- if _, found := idents[x]; found {
- delete(idents, x)
- } else {
+ var objects int
+ if _, found := uses[x]; found {
+ objects |= 1
+ delete(uses, x)
+ }
+ if _, found := defs[x]; found {
+ objects |= 2
+ delete(defs, x)
+ }
+ if objects == 0 {
t.Errorf("%s: unresolved identifier %s", fset.Position(x.Pos()), x.Name)
+ } else if objects == 3 {
+ both = append(both, x.Name)
}
return false
}
@@ -149,8 +169,17 @@ func TestResolveIdents(t *testing.T) {
})
}
+ // check the expected set of idents that are simultaneously uses and defs
+ sort.Strings(both)
+ if got, want := fmt.Sprint(both), "[Mutex Stringer error]"; got != want {
+ t.Errorf("simultaneous uses/defs = %s, want %s", got, want)
+ }
+
// any left-over identifiers didn't exist in the source
- for x := range idents {
+ for x := range uses {
+ t.Errorf("%s: identifier %s not present in source", fset.Position(x.Pos()), x.Name)
+ }
+ for x := range defs {
t.Errorf("%s: identifier %s not present in source", fset.Position(x.Pos()), x.Name)
}
diff --git a/go/types/stmt.go b/go/types/stmt.go
index f0faa82..d1f94ca 100644
--- a/go/types/stmt.go
+++ b/go/types/stmt.go
@@ -10,6 +10,8 @@ import (
"fmt"
"go/ast"
"go/token"
+
+ "code.google.com/p/go.tools/go/exact"
)
func (check *checker) funcBody(decl *declInfo, name string, sig *Signature, body *ast.BlockStmt) {
@@ -388,14 +390,16 @@ func (check *checker) stmt(ctxt stmtContext, s ast.Stmt) {
check.initStmt(s.Init)
var x operand
- tag := s.Tag
- if tag == nil {
- // use fake true tag value and position it at the opening { of the switch
- ident := &ast.Ident{NamePos: s.Body.Lbrace, Name: "true"}
- check.recordObject(ident, Universe.Lookup("true"))
- tag = ident
+ if s.Tag != nil {
+ check.expr(&x, s.Tag)
+ } else {
+ // spec: "A missing switch expression is
+ // equivalent to the boolean value true."
+ x.mode = constant
+ x.typ = Typ[Bool]
+ x.val = exact.MakeBool(true)
+ x.expr = &ast.Ident{NamePos: s.Body.Lbrace, Name: "true"}
}
- check.expr(&x, tag)
check.multipleDefaults(s.Body.List)
@@ -448,7 +452,7 @@ func (check *checker) stmt(ctxt stmtContext, s ast.Stmt) {
check.invalidAST(s.Pos(), "incorrect form of type switch guard")
return
}
- check.recordObject(lhs, nil) // lhs variable is implicitly declared in each cause clause
+ check.recordDef(lhs, nil) // lhs variable is implicitly declared in each cause clause
rhs = guard.Rhs[0]
@@ -673,7 +677,7 @@ func (check *checker) stmt(ctxt stmtContext, s ast.Stmt) {
// declare new variable
name := ident.Name
obj = NewVar(ident.Pos(), check.pkg, name, nil)
- check.recordObject(ident, obj)
+ check.recordDef(ident, obj)
// _ variables don't count as new variables
if name != "_" {
vars = append(vars, obj)
diff --git a/go/types/testdata/stmt0.src b/go/types/testdata/stmt0.src
index c70ef3e..255c171 100644
--- a/go/types/testdata/stmt0.src
+++ b/go/types/testdata/stmt0.src
@@ -742,3 +742,13 @@ func expression_statements(ch chan int) {
cap /* ERROR "not used" */ (ch)
println /* ERROR "must be called" */
}
+
+func _() {
+ true := "false"
+ _ = true
+ // A tagless switch is equivalent to the bool
+ // constant true, not the identifier 'true'.
+ switch {
+ case "false" /* ERROR "cannot convert" */:
+ }
+} \ No newline at end of file
diff --git a/go/types/typexpr.go b/go/types/typexpr.go
index 3f88142..1cff672 100644
--- a/go/types/typexpr.go
+++ b/go/types/typexpr.go
@@ -32,7 +32,7 @@ func (check *checker) ident(x *operand, e *ast.Ident, def *Named, path []*TypeNa
}
return
}
- check.recordObject(e, obj)
+ check.recordUse(e, obj)
check.objDecl(obj, def, path)
typ := obj.Type()
@@ -480,7 +480,7 @@ func (check *checker) interfaceType(iface *Interface, ityp *ast.InterfaceType, d
iface.methods = append(iface.methods, m)
iface.allMethods = append(iface.allMethods, m)
signatures = append(signatures, f.Type)
- check.recordObject(name, m)
+ check.recordDef(name, m)
}
} else {
// embedded type
@@ -604,7 +604,8 @@ func (check *checker) structType(styp *Struct, e *ast.StructType, path []*TypeNa
// current field typ and tag
var typ Type
var tag string
- add := func(field *ast.Field, ident *ast.Ident, name string, anonymous bool, pos token.Pos) {
+ // anonymous != nil indicates an anonymous field.
+ add := func(field *ast.Field, ident *ast.Ident, anonymous *TypeName, pos token.Pos) {
if tag != "" && tags == nil {
tags = make([]string, len(fields))
}
@@ -612,13 +613,15 @@ func (check *checker) structType(styp *Struct, e *ast.StructType, path []*TypeNa
tags = append(tags, tag)
}
- fld := NewField(pos, check.pkg, name, typ, anonymous)
+ name := ident.Name
+ fld := NewField(pos, check.pkg, name, typ, anonymous != nil)
// spec: "Within a struct, non-blank field names must be unique."
if name == "_" || check.declareInSet(&fset, pos, fld) {
fields = append(fields, fld)
- if ident != nil {
- check.recordObject(ident, fld)
- }
+ check.recordDef(ident, fld)
+ }
+ if anonymous != nil {
+ check.recordUse(ident, anonymous)
}
}
@@ -628,10 +631,11 @@ func (check *checker) structType(styp *Struct, e *ast.StructType, path []*TypeNa
if len(f.Names) > 0 {
// named fields
for _, name := range f.Names {
- add(f, name, name.Name, false, name.Pos())
+ add(f, name, nil, name.Pos())
}
} else {
// anonymous field
+ name := anonymousFieldIdent(f.Type)
pos := f.Type.Pos()
t, isPtr := deref(typ)
switch t := t.(type) {
@@ -645,7 +649,7 @@ func (check *checker) structType(styp *Struct, e *ast.StructType, path []*TypeNa
check.errorf(pos, "anonymous field type cannot be unsafe.Pointer")
continue
}
- add(f, nil, t.name, true, pos)
+ add(f, name, Universe.Lookup(t.name).(*TypeName), pos)
case *Named:
// spec: "An embedded type must be specified as a type name
@@ -667,7 +671,7 @@ func (check *checker) structType(styp *Struct, e *ast.StructType, path []*TypeNa
continue
}
}
- add(f, nil, t.obj.name, true, pos)
+ add(f, name, t.obj, pos)
default:
check.invalidAST(pos, "anonymous field type %s must be named", typ)
@@ -678,3 +682,15 @@ func (check *checker) structType(styp *Struct, e *ast.StructType, path []*TypeNa
styp.fields = fields
styp.tags = tags
}
+
+func anonymousFieldIdent(e ast.Expr) *ast.Ident {
+ switch e := e.(type) {
+ case *ast.Ident:
+ return e
+ case *ast.StarExpr:
+ return anonymousFieldIdent(e.X)
+ case *ast.SelectorExpr:
+ return e.Sel
+ }
+ return nil // invalid anonymous field
+}
diff --git a/oracle/implements.go b/oracle/implements.go
index 2ff69a7..423de54 100644
--- a/oracle/implements.go
+++ b/oracle/implements.go
@@ -39,8 +39,8 @@ func implements(o *Oracle, qpos *QueryPos) (queryResult, error) {
//
var allNamed []types.Type
for _, info := range o.typeInfo {
- for id, obj := range info.Objects {
- if obj, ok := obj.(*types.TypeName); ok && obj.Pos() == id.Pos() {
+ for _, obj := range info.Defs {
+ if obj, ok := obj.(*types.TypeName); ok {
allNamed = append(allNamed, obj.Type())
}
}
diff --git a/oracle/referrers.go b/oracle/referrers.go
index 24b5414..ddab236 100644
--- a/oracle/referrers.go
+++ b/oracle/referrers.go
@@ -29,14 +29,11 @@ func referrers(o *Oracle, qpos *QueryPos) (queryResult, error) {
return nil, fmt.Errorf("no object for identifier")
}
- // Iterate over all go/types' resolver facts for the entire program.
+ // Iterate over all go/types' Uses facts for the entire program.
var refs []*ast.Ident
for _, info := range o.typeInfo {
- for id2, obj2 := range info.Objects {
+ for id2, obj2 := range info.Uses {
if sameObj(obj, obj2) {
- if id2.NamePos == obj.Pos() {
- continue // skip defining ident
- }
refs = append(refs, id2)
}
}