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
This commit is contained in:
Alan Donovan 2014-02-27 13:21:59 -05:00
parent aaca3a4f95
commit ced954c167
24 changed files with 193 additions and 107 deletions

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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

View File

@ -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),

View File

@ -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.

View File

@ -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)
}
}

View File

@ -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 {

View File

@ -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:

View File

@ -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)

View File

@ -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

View File

@ -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:

View File

@ -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}

View File

@ -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.

View File

@ -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)

View File

@ -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)

View File

@ -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 {

View File

@ -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")

View File

@ -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)
}

View File

@ -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)

View File

@ -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" */:
}
}

View File

@ -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
}

View File

@ -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())
}
}

View File

@ -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)
}
}