diff --git a/cmd/vet/main.go b/cmd/vet/main.go index b9d80107..9039532d 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 3efb2de3..922ecc37 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 afb0fed0..1f3c4247 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 dce66dd5..2cf4de4a 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 16c330a3..0048be8e 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 dba4f0c7..167da94d 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 2f3bcc9a..ee371c8b 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 f747a2aa..d2862d33 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 43db3ed6..c240dcef 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 8961f0f9..ac8aa2c2 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 c33f63cb..ca3bac88 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 4cce318a..a5895106 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 3b708f3d..ef1e2c1a 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 326818bf..60a689c3 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 e746825c..f0e0c0db 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 8bca1931..cd86ea0a 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 65b39e44..d854031d 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 4b9dcfdf..ae27e4b1 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 5c8a97e9..9fc98931 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 f0faa823..d1f94caa 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 c70ef3e5..255c171d 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 3f881429..1cff6722 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 2ff69a7d..423de543 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 24b5414a..ddab2361 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) } }