mirror of
https://github.com/golang/go
synced 2024-11-23 03:50:03 -07:00
go/types: export QualifiedName.IsSame and NamedType.AstObj
R=adonovan CC=golang-dev https://golang.org/cl/7103047
This commit is contained in:
parent
20130f141f
commit
8b62f54eb7
@ -163,7 +163,7 @@ func (check *checker) object(obj *ast.Object, cycleOk bool) {
|
|||||||
check.valueSpec(spec.Pos(), obj, spec.Names, init.Type, init.Values, iota)
|
check.valueSpec(spec.Pos(), obj, spec.Names, init.Type, init.Values, iota)
|
||||||
|
|
||||||
case ast.Typ:
|
case ast.Typ:
|
||||||
typ := &NamedType{obj: obj}
|
typ := &NamedType{AstObj: obj}
|
||||||
obj.Type = typ // "mark" object so recursion terminates
|
obj.Type = typ // "mark" object so recursion terminates
|
||||||
typ.Underlying = underlying(check.typ(obj.Decl.(*ast.TypeSpec).Type, cycleOk))
|
typ.Underlying = underlying(check.typ(obj.Decl.(*ast.TypeSpec).Type, cycleOk))
|
||||||
// typecheck associated method signatures
|
// typecheck associated method signatures
|
||||||
@ -194,7 +194,7 @@ func (check *checker) object(obj *ast.Object, cycleOk bool) {
|
|||||||
params, _ := check.collectParams(mdecl.Recv, false)
|
params, _ := check.collectParams(mdecl.Recv, false)
|
||||||
sig.Recv = params[0] // the parser/assocMethod ensure there is exactly one parameter
|
sig.Recv = params[0] // the parser/assocMethod ensure there is exactly one parameter
|
||||||
obj.Type = sig
|
obj.Type = sig
|
||||||
methods = append(methods, &Method{QualifiedName{check.pkg, obj.Name}, sig})
|
methods = append(methods, &Method{QualifiedName{nil, obj.Name}, sig})
|
||||||
check.later(obj, sig, mdecl.Body)
|
check.later(obj, sig, mdecl.Body)
|
||||||
}
|
}
|
||||||
typ.Methods = methods
|
typ.Methods = methods
|
||||||
|
@ -313,10 +313,10 @@ func writeType(buf *bytes.Buffer, typ Type) {
|
|||||||
case *NamedType:
|
case *NamedType:
|
||||||
var s string
|
var s string
|
||||||
switch {
|
switch {
|
||||||
case t.obj != nil:
|
|
||||||
s = t.obj.Name
|
|
||||||
case t.Obj != nil:
|
case t.Obj != nil:
|
||||||
s = t.Obj.GetName()
|
s = t.Obj.GetName()
|
||||||
|
case t.AstObj != nil:
|
||||||
|
s = t.AstObj.Name
|
||||||
default:
|
default:
|
||||||
s = "<NamedType w/o object>"
|
s = "<NamedType w/o object>"
|
||||||
}
|
}
|
||||||
|
@ -84,7 +84,7 @@ func (check *checker) collectMethods(list *ast.FieldList) (methods []*Method) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, name := range f.Names {
|
for _, name := range f.Names {
|
||||||
methods = append(methods, &Method{QualifiedName{check.pkg, name.Name}, sig})
|
methods = append(methods, &Method{QualifiedName{nil, name.Name}, sig})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// embedded interface
|
// embedded interface
|
||||||
@ -137,24 +137,24 @@ func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields [
|
|||||||
if len(f.Names) > 0 {
|
if len(f.Names) > 0 {
|
||||||
// named fields
|
// named fields
|
||||||
for _, name := range f.Names {
|
for _, name := range f.Names {
|
||||||
fields = append(fields, &Field{QualifiedName{check.pkg, name.Name}, typ, tag, false})
|
fields = append(fields, &Field{QualifiedName{nil, name.Name}, typ, tag, false})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// anonymous field
|
// anonymous field
|
||||||
switch t := deref(typ).(type) {
|
switch t := deref(typ).(type) {
|
||||||
case *Basic:
|
case *Basic:
|
||||||
fields = append(fields, &Field{QualifiedName{check.pkg, t.Name}, typ, tag, true})
|
fields = append(fields, &Field{QualifiedName{nil, t.Name}, typ, tag, true})
|
||||||
case *NamedType:
|
case *NamedType:
|
||||||
var name string
|
var name string
|
||||||
switch {
|
switch {
|
||||||
case t.obj != nil:
|
|
||||||
name = t.obj.Name
|
|
||||||
case t.Obj != nil:
|
case t.Obj != nil:
|
||||||
name = t.Obj.GetName()
|
name = t.Obj.GetName()
|
||||||
|
case t.AstObj != nil:
|
||||||
|
name = t.AstObj.Name
|
||||||
default:
|
default:
|
||||||
unreachable()
|
unreachable()
|
||||||
}
|
}
|
||||||
fields = append(fields, &Field{QualifiedName{check.pkg, name}, typ, tag, true})
|
fields = append(fields, &Field{QualifiedName{nil, name}, typ, tag, true})
|
||||||
default:
|
default:
|
||||||
if typ != Typ[Invalid] {
|
if typ != Typ[Invalid] {
|
||||||
check.invalidAST(f.Type.Pos(), "anonymous field type %s must be named", typ)
|
check.invalidAST(f.Type.Pos(), "anonymous field type %s must be named", typ)
|
||||||
@ -913,7 +913,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
|
|||||||
if x.mode == invalid {
|
if x.mode == invalid {
|
||||||
goto Error
|
goto Error
|
||||||
}
|
}
|
||||||
mode, typ := lookupField(x.typ, QualifiedName{check.pkg, sel})
|
mode, typ := lookupField(x.typ, QualifiedName{nil, sel})
|
||||||
if mode == invalid {
|
if mode == invalid {
|
||||||
check.invalidOp(e.Pos(), "%s has no single field or method %s", x, sel)
|
check.invalidOp(e.Pos(), "%s has no single field or method %s", x, sel)
|
||||||
goto Error
|
goto Error
|
||||||
|
@ -265,11 +265,11 @@ func lookupFieldBreadthFirst(list []embeddedType, name QualifiedName) (res looku
|
|||||||
visited[typ] = true
|
visited[typ] = true
|
||||||
|
|
||||||
// look for a matching attached method
|
// look for a matching attached method
|
||||||
if typ.obj != nil {
|
if typ.AstObj != nil {
|
||||||
assert(typ.obj.Data == nil) // methods must have been moved to typ.Methods
|
assert(typ.AstObj.Data == nil) // methods must have been moved to typ.Methods
|
||||||
}
|
}
|
||||||
for _, m := range typ.Methods {
|
for _, m := range typ.Methods {
|
||||||
if identicalNames(name, m.QualifiedName) {
|
if name.IsSame(m.QualifiedName) {
|
||||||
assert(m.Type != nil)
|
assert(m.Type != nil)
|
||||||
if !potentialMatch(e.multiples, value, m.Type) {
|
if !potentialMatch(e.multiples, value, m.Type) {
|
||||||
return // name collision
|
return // name collision
|
||||||
@ -281,7 +281,7 @@ func lookupFieldBreadthFirst(list []embeddedType, name QualifiedName) (res looku
|
|||||||
case *Struct:
|
case *Struct:
|
||||||
// look for a matching field and collect embedded types
|
// look for a matching field and collect embedded types
|
||||||
for _, f := range t.Fields {
|
for _, f := range t.Fields {
|
||||||
if identicalNames(name, f.QualifiedName) {
|
if name.IsSame(f.QualifiedName) {
|
||||||
assert(f.Type != nil)
|
assert(f.Type != nil)
|
||||||
if !potentialMatch(e.multiples, variable, f.Type) {
|
if !potentialMatch(e.multiples, variable, f.Type) {
|
||||||
return // name collision
|
return // name collision
|
||||||
@ -305,7 +305,7 @@ func lookupFieldBreadthFirst(list []embeddedType, name QualifiedName) (res looku
|
|||||||
case *Interface:
|
case *Interface:
|
||||||
// look for a matching method
|
// look for a matching method
|
||||||
for _, m := range t.Methods {
|
for _, m := range t.Methods {
|
||||||
if identicalNames(name, m.QualifiedName) {
|
if name.IsSame(m.QualifiedName) {
|
||||||
assert(m.Type != nil)
|
assert(m.Type != nil)
|
||||||
if !potentialMatch(e.multiples, value, m.Type) {
|
if !potentialMatch(e.multiples, value, m.Type) {
|
||||||
return // name collision
|
return // name collision
|
||||||
@ -355,11 +355,11 @@ func lookupField(typ Type, name QualifiedName) (operandMode, Type) {
|
|||||||
typ = deref(typ)
|
typ = deref(typ)
|
||||||
|
|
||||||
if t, ok := typ.(*NamedType); ok {
|
if t, ok := typ.(*NamedType); ok {
|
||||||
if t.obj != nil {
|
if t.AstObj != nil {
|
||||||
assert(t.obj.Data == nil) // methods must have been moved to t.Methods
|
assert(t.AstObj.Data == nil) // methods must have been moved to t.Methods
|
||||||
}
|
}
|
||||||
for _, m := range t.Methods {
|
for _, m := range t.Methods {
|
||||||
if identicalNames(name, m.QualifiedName) {
|
if name.IsSame(m.QualifiedName) {
|
||||||
assert(m.Type != nil)
|
assert(m.Type != nil)
|
||||||
return value, m.Type
|
return value, m.Type
|
||||||
}
|
}
|
||||||
@ -371,7 +371,7 @@ func lookupField(typ Type, name QualifiedName) (operandMode, Type) {
|
|||||||
case *Struct:
|
case *Struct:
|
||||||
var next []embeddedType
|
var next []embeddedType
|
||||||
for _, f := range t.Fields {
|
for _, f := range t.Fields {
|
||||||
if identicalNames(name, f.QualifiedName) {
|
if name.IsSame(f.QualifiedName) {
|
||||||
return variable, f.Type
|
return variable, f.Type
|
||||||
}
|
}
|
||||||
if f.IsAnonymous {
|
if f.IsAnonymous {
|
||||||
@ -388,7 +388,7 @@ func lookupField(typ Type, name QualifiedName) (operandMode, Type) {
|
|||||||
|
|
||||||
case *Interface:
|
case *Interface:
|
||||||
for _, m := range t.Methods {
|
for _, m := range t.Methods {
|
||||||
if identicalNames(name, m.QualifiedName) {
|
if name.IsSame(m.QualifiedName) {
|
||||||
return value, m.Type
|
return value, m.Type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,6 @@
|
|||||||
|
|
||||||
package types
|
package types
|
||||||
|
|
||||||
import "go/ast"
|
|
||||||
|
|
||||||
func isNamed(typ Type) bool {
|
func isNamed(typ Type) bool {
|
||||||
if _, ok := typ.(*Basic); ok {
|
if _, ok := typ.(*Basic); ok {
|
||||||
return ok
|
return ok
|
||||||
@ -131,7 +129,7 @@ func isIdentical(x, y Type) bool {
|
|||||||
if len(x.Fields) == len(y.Fields) {
|
if len(x.Fields) == len(y.Fields) {
|
||||||
for i, f := range x.Fields {
|
for i, f := range x.Fields {
|
||||||
g := y.Fields[i]
|
g := y.Fields[i]
|
||||||
if !identicalNames(f.QualifiedName, g.QualifiedName) ||
|
if !f.QualifiedName.IsSame(g.QualifiedName) ||
|
||||||
!isIdentical(f.Type, g.Type) ||
|
!isIdentical(f.Type, g.Type) ||
|
||||||
f.Tag != g.Tag ||
|
f.Tag != g.Tag ||
|
||||||
f.IsAnonymous != g.IsAnonymous {
|
f.IsAnonymous != g.IsAnonymous {
|
||||||
@ -185,10 +183,10 @@ func isIdentical(x, y Type) bool {
|
|||||||
// in the same type declaration.
|
// in the same type declaration.
|
||||||
if y, ok := y.(*NamedType); ok {
|
if y, ok := y.(*NamedType); ok {
|
||||||
switch {
|
switch {
|
||||||
case x.obj != nil:
|
|
||||||
return x.obj == y.obj
|
|
||||||
case x.Obj != nil:
|
case x.Obj != nil:
|
||||||
return x.Obj == y.Obj
|
return x.Obj == y.Obj
|
||||||
|
case x.AstObj != nil:
|
||||||
|
return x.AstObj == y.AstObj
|
||||||
default:
|
default:
|
||||||
unreachable()
|
unreachable()
|
||||||
}
|
}
|
||||||
@ -198,17 +196,6 @@ func isIdentical(x, y Type) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// identicalNames returns true if the names a and b are equal.
|
|
||||||
func identicalNames(a, b QualifiedName) bool {
|
|
||||||
if a.Name != b.Name {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// a.Name == b.Name
|
|
||||||
// TODO(gri) Guarantee that packages are canonicalized
|
|
||||||
// and then we can compare p == q directly.
|
|
||||||
return ast.IsExported(a.Name) || a.Pkg.Path == b.Pkg.Path
|
|
||||||
}
|
|
||||||
|
|
||||||
// identicalTypes returns true if both lists a and b have the
|
// identicalTypes returns true if both lists a and b have the
|
||||||
// same length and corresponding objects have identical types.
|
// same length and corresponding objects have identical types.
|
||||||
func identicalTypes(a, b []*Var) bool {
|
func identicalTypes(a, b []*Var) bool {
|
||||||
|
@ -91,12 +91,37 @@ type Slice struct {
|
|||||||
Elt Type
|
Elt Type
|
||||||
}
|
}
|
||||||
|
|
||||||
// A QualifiedName is a name qualified with the package the declared the name.
|
// A QualifiedName is a name qualified with the package that declared the name.
|
||||||
type QualifiedName struct {
|
type QualifiedName struct {
|
||||||
Pkg *Package // Pkg.Path == "" for current (non-imported) package
|
Pkg *Package // nil for current (non-imported) package
|
||||||
Name string // unqualified type name for anonymous fields
|
Name string // unqualified type name for anonymous fields
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsSame reports whether p and q are the same.
|
||||||
|
func (p QualifiedName) IsSame(q QualifiedName) bool {
|
||||||
|
// spec:
|
||||||
|
// "Two identifiers are different if they are spelled differently,
|
||||||
|
// or if they appear in different packages and are not exported.
|
||||||
|
// Otherwise, they are the same."
|
||||||
|
if p.Name != q.Name {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// p.Name == q.Name
|
||||||
|
if !ast.IsExported(p.Name) {
|
||||||
|
// TODO(gri) just compare packages once we guarantee that they are canonicalized
|
||||||
|
pp := ""
|
||||||
|
if p.Pkg != nil {
|
||||||
|
pp = p.Pkg.Path
|
||||||
|
}
|
||||||
|
qp := ""
|
||||||
|
if q.Pkg != nil {
|
||||||
|
qp = q.Pkg.Path
|
||||||
|
}
|
||||||
|
return pp == qp
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// A Field represents a field of a struct.
|
// A Field represents a field of a struct.
|
||||||
type Field struct {
|
type Field struct {
|
||||||
QualifiedName
|
QualifiedName
|
||||||
@ -211,9 +236,9 @@ type Chan struct {
|
|||||||
// A NamedType represents a named type as declared in a type declaration.
|
// A NamedType represents a named type as declared in a type declaration.
|
||||||
type NamedType struct {
|
type NamedType struct {
|
||||||
implementsType
|
implementsType
|
||||||
// TODO(gri) remove obj once we have moved away from ast.Objects
|
// TODO(gri) remove AstObj once we have moved away from ast.Objects
|
||||||
obj *ast.Object // corresponding declared object (current package)
|
|
||||||
Obj Object // corresponding declared object (imported package)
|
Obj Object // corresponding declared object (imported package)
|
||||||
|
AstObj *ast.Object // corresponding declared object (current package)
|
||||||
Underlying Type // nil if not fully declared yet; never a *NamedType
|
Underlying Type // nil if not fully declared yet; never a *NamedType
|
||||||
Methods []*Method // TODO(gri) consider keeping them in sorted order
|
Methods []*Method // TODO(gri) consider keeping them in sorted order
|
||||||
}
|
}
|
||||||
|
@ -159,7 +159,7 @@ func def(kind ast.ObjKind, name string, typ Type) *ast.Object {
|
|||||||
obj.Decl = Universe
|
obj.Decl = Universe
|
||||||
obj.Type = typ
|
obj.Type = typ
|
||||||
if typ, ok := typ.(*NamedType); ok {
|
if typ, ok := typ.(*NamedType); ok {
|
||||||
typ.obj = obj
|
typ.AstObj = obj
|
||||||
}
|
}
|
||||||
if Universe.Insert(obj) != nil {
|
if Universe.Insert(obj) != nil {
|
||||||
panic("internal error: double declaration")
|
panic("internal error: double declaration")
|
||||||
|
Loading…
Reference in New Issue
Block a user