mirror of
https://github.com/golang/go
synced 2024-11-26 14:08:37 -07:00
go/types: Steps towards removing ast.Object from exported API.
- introduced type Method for methods - renamed StructField -> Field - removed ObjList - methods are not sorted anymore in interfaces (for now) R=adonovan CC=golang-dev https://golang.org/cl/7023043
This commit is contained in:
parent
73aaa44c24
commit
c8eb71b057
@ -5,6 +5,8 @@
|
||||
// Package types declares the data structures for representing
|
||||
// Go types and implements typechecking of package files.
|
||||
//
|
||||
// WARNING: THE TYPES API IS SUBJECT TO SIGNIFICANT CHANGE.
|
||||
//
|
||||
package types
|
||||
|
||||
import (
|
||||
|
@ -197,7 +197,7 @@ func typeString(typ Type) string {
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func writeParams(buf *bytes.Buffer, params ObjList, isVariadic bool) {
|
||||
func writeParams(buf *bytes.Buffer, params []*ast.Object, isVariadic bool) {
|
||||
buf.WriteByte('(')
|
||||
for i, par := range params {
|
||||
if i > 0 {
|
||||
@ -287,7 +287,7 @@ func writeType(buf *bytes.Buffer, typ Type) {
|
||||
buf.WriteString("; ")
|
||||
}
|
||||
buf.WriteString(m.Name)
|
||||
writeSignature(buf, m.Type.(*Signature))
|
||||
writeSignature(buf, m.Type)
|
||||
}
|
||||
buf.WriteByte('}')
|
||||
|
||||
|
@ -22,7 +22,7 @@ import (
|
||||
// - clients need access to builtins type information
|
||||
// - API tests are missing (e.g., identifiers should be handled as expressions in callbacks)
|
||||
|
||||
func (check *checker) collectParams(list *ast.FieldList, variadicOk bool) (params ObjList, isVariadic bool) {
|
||||
func (check *checker) collectParams(list *ast.FieldList, variadicOk bool) (params []*ast.Object, isVariadic bool) {
|
||||
if list == nil {
|
||||
return
|
||||
}
|
||||
@ -70,7 +70,7 @@ func (check *checker) collectParams(list *ast.FieldList, variadicOk bool) (param
|
||||
return
|
||||
}
|
||||
|
||||
func (check *checker) collectMethods(list *ast.FieldList) (methods ObjList) {
|
||||
func (check *checker) collectMethods(list *ast.FieldList) (methods []*Method) {
|
||||
if list == nil {
|
||||
return
|
||||
}
|
||||
@ -81,14 +81,13 @@ func (check *checker) collectMethods(list *ast.FieldList) (methods ObjList) {
|
||||
if len(f.Names) > 0 {
|
||||
// methods (the parser ensures that there's only one
|
||||
// and we don't care if a constructed AST has more)
|
||||
if _, ok := typ.(*Signature); !ok {
|
||||
sig, ok := typ.(*Signature)
|
||||
if !ok {
|
||||
check.invalidAST(f.Type.Pos(), "%s is not a method signature", typ)
|
||||
continue
|
||||
}
|
||||
for _, name := range f.Names {
|
||||
obj := name.Obj
|
||||
obj.Type = typ
|
||||
methods = append(methods, obj)
|
||||
methods = append(methods, &Method{name.Name, sig})
|
||||
}
|
||||
} else {
|
||||
// embedded interface
|
||||
@ -101,14 +100,20 @@ func (check *checker) collectMethods(list *ast.FieldList) (methods ObjList) {
|
||||
}
|
||||
}
|
||||
}
|
||||
// check for double declarations
|
||||
methods.Sort()
|
||||
prev := ""
|
||||
for _, obj := range methods {
|
||||
if obj.Name == prev {
|
||||
check.errorf(list.Pos(), "multiple methods named %s", prev)
|
||||
// Check for double declarations.
|
||||
// The parser inserts methods into an interface-local scope, so local
|
||||
// double declarations are reported by the parser already. We need to
|
||||
// check again for conflicts due to embedded interfaces. This will lead
|
||||
// to a 2nd error message if the double declaration was reported before
|
||||
// by the parser.
|
||||
// TODO(gri) clean this up a bit
|
||||
seen := make(map[string]bool)
|
||||
for _, m := range methods {
|
||||
if seen[m.Name] {
|
||||
check.errorf(list.Pos(), "multiple methods named %s", m.Name)
|
||||
return // keep multiple entries, lookup will only return the first entry
|
||||
}
|
||||
seen[m.Name] = true
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -125,7 +130,7 @@ func (check *checker) tag(t *ast.BasicLit) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields []*StructField) {
|
||||
func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields []*Field) {
|
||||
if list == nil {
|
||||
return
|
||||
}
|
||||
@ -135,15 +140,15 @@ func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields [
|
||||
if len(f.Names) > 0 {
|
||||
// named fields
|
||||
for _, name := range f.Names {
|
||||
fields = append(fields, &StructField{name.Name, typ, tag, false})
|
||||
fields = append(fields, &Field{name.Name, typ, tag, false})
|
||||
}
|
||||
} else {
|
||||
// anonymous field
|
||||
switch t := deref(typ).(type) {
|
||||
case *Basic:
|
||||
fields = append(fields, &StructField{t.Name, typ, tag, true})
|
||||
fields = append(fields, &Field{t.Name, typ, tag, true})
|
||||
case *NamedType:
|
||||
fields = append(fields, &StructField{t.Obj.Name, typ, tag, true})
|
||||
fields = append(fields, &Field{t.Obj.Name, typ, tag, true})
|
||||
default:
|
||||
if typ != Typ[Invalid] {
|
||||
check.invalidAST(f.Type.Pos(), "anonymous field type %s must be named", typ)
|
||||
@ -921,7 +926,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
|
||||
arg.Type = x.typ
|
||||
x.mode = value
|
||||
x.typ = &Signature{
|
||||
Params: append(ObjList{arg}, sig.Params...),
|
||||
Params: append([]*ast.Object{arg}, sig.Params...),
|
||||
Results: sig.Results,
|
||||
IsVariadic: sig.IsVariadic,
|
||||
}
|
||||
|
@ -383,8 +383,8 @@ func (p *gcParser) parseName() (name string) {
|
||||
|
||||
// Field = Name Type [ string_lit ] .
|
||||
//
|
||||
func (p *gcParser) parseField() *StructField {
|
||||
var f StructField
|
||||
func (p *gcParser) parseField() *Field {
|
||||
var f Field
|
||||
f.Name = p.parseName()
|
||||
f.Type = p.parseType()
|
||||
if p.tok == scanner.String {
|
||||
@ -406,7 +406,7 @@ func (p *gcParser) parseField() *StructField {
|
||||
// FieldList = Field { ";" Field } .
|
||||
//
|
||||
func (p *gcParser) parseStructType() Type {
|
||||
var fields []*StructField
|
||||
var fields []*Field
|
||||
|
||||
parseField := func() {
|
||||
fields = append(fields, p.parseField())
|
||||
@ -510,12 +510,12 @@ func (p *gcParser) parseSignature() *Signature {
|
||||
// visible in the export data.
|
||||
//
|
||||
func (p *gcParser) parseInterfaceType() Type {
|
||||
var methods ObjList
|
||||
var methods []*Method
|
||||
|
||||
parseMethod := func() {
|
||||
obj := ast.NewObj(ast.Fun, p.parseName())
|
||||
obj.Type = p.parseSignature()
|
||||
methods = append(methods, obj)
|
||||
name := p.parseName()
|
||||
typ := p.parseSignature()
|
||||
methods = append(methods, &Method{name, typ})
|
||||
}
|
||||
|
||||
p.expectKeyword("interface")
|
||||
@ -529,7 +529,6 @@ func (p *gcParser) parseInterfaceType() Type {
|
||||
}
|
||||
p.expect('}')
|
||||
|
||||
methods.Sort()
|
||||
return &Interface{Methods: methods}
|
||||
}
|
||||
|
||||
|
@ -301,10 +301,10 @@ func lookupFieldBreadthFirst(list []embeddedType, name string) (res lookupResult
|
||||
|
||||
case *Interface:
|
||||
// look for a matching method
|
||||
for _, obj := range typ.Methods {
|
||||
if obj.Name == name {
|
||||
assert(obj.Type != nil)
|
||||
if !potentialMatch(e.multiples, value, obj.Type.(Type)) {
|
||||
for _, m := range typ.Methods {
|
||||
if m.Name == name {
|
||||
assert(m.Type != nil)
|
||||
if !potentialMatch(e.multiples, value, m.Type) {
|
||||
return // name collision
|
||||
}
|
||||
}
|
||||
@ -380,9 +380,9 @@ func lookupField(typ Type, name string) (operandMode, Type) {
|
||||
}
|
||||
|
||||
case *Interface:
|
||||
for _, obj := range typ.Methods {
|
||||
if obj.Name == name {
|
||||
return value, obj.Type.(Type)
|
||||
for _, m := range typ.Methods {
|
||||
if m.Name == name {
|
||||
return value, m.Type
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -165,7 +165,7 @@ func isIdentical(x, y Type) bool {
|
||||
// the same names and identical function types. Lower-case method names from
|
||||
// different packages are always different. The order of the methods is irrelevant.
|
||||
if y, ok := y.(*Interface); ok {
|
||||
return identicalTypes(x.Methods, y.Methods) // methods are sorted
|
||||
return identicalMethods(x.Methods, y.Methods) // methods are sorted
|
||||
}
|
||||
|
||||
case *Map:
|
||||
@ -194,17 +194,36 @@ func isIdentical(x, y Type) bool {
|
||||
|
||||
// identicalTypes returns true if both lists a and b have the
|
||||
// same length and corresponding objects have identical types.
|
||||
func identicalTypes(a, b ObjList) bool {
|
||||
if len(a) == len(b) {
|
||||
for i, x := range a {
|
||||
y := b[i]
|
||||
if !isIdentical(x.Type.(Type), y.Type.(Type)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
func identicalTypes(a, b []*ast.Object) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
return false
|
||||
for i, x := range a {
|
||||
y := b[i]
|
||||
if !isIdentical(x.Type.(Type), y.Type.(Type)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// identicalMethods returns true if both lists a and b have the
|
||||
// same length and corresponding methods have identical types.
|
||||
// TODO(gri) make this more efficient
|
||||
func identicalMethods(a, b []*Method) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
m := make(map[string]*Method)
|
||||
for _, x := range a {
|
||||
m[x.Name] = x
|
||||
}
|
||||
for _, y := range b {
|
||||
if x := m[y.Name]; x == nil || !isIdentical(x.Type, y.Type) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// underlying returns the underlying type of typ.
|
||||
@ -257,14 +276,14 @@ func defaultType(typ Type) Type {
|
||||
// it returns the first missing method required by T and whether it
|
||||
// is missing or simply has the wrong type.
|
||||
//
|
||||
func missingMethod(typ Type, T *Interface) (method *ast.Object, wrongType bool) {
|
||||
func missingMethod(typ Type, T *Interface) (method *Method, wrongType bool) {
|
||||
// TODO(gri): distinguish pointer and non-pointer receivers
|
||||
// an interface type implements T if it has no methods with conflicting signatures
|
||||
// Note: This is stronger than the current spec. Should the spec require this?
|
||||
if ityp, _ := underlying(typ).(*Interface); ityp != nil {
|
||||
for _, m := range T.Methods {
|
||||
mode, sig := lookupField(ityp, m.Name) // TODO(gri) no need to go via lookupField
|
||||
if mode != invalid && !isIdentical(sig, m.Type.(Type)) {
|
||||
if mode != invalid && !isIdentical(sig, m.Type) {
|
||||
return m, true
|
||||
}
|
||||
}
|
||||
@ -277,7 +296,7 @@ func missingMethod(typ Type, T *Interface) (method *ast.Object, wrongType bool)
|
||||
if mode == invalid {
|
||||
return m, false
|
||||
}
|
||||
if !isIdentical(sig, m.Type.(Type)) {
|
||||
if !isIdentical(sig, m.Type) {
|
||||
return m, true
|
||||
}
|
||||
}
|
||||
|
2
src/pkg/go/types/testdata/decls0.src
vendored
2
src/pkg/go/types/testdata/decls0.src
vendored
@ -127,7 +127,7 @@ type (
|
||||
I2 interface {
|
||||
m1()
|
||||
}
|
||||
I3 interface {
|
||||
I3 interface { /* ERROR "multiple methods named m1" */
|
||||
m1()
|
||||
m1 /* ERROR "redeclared" */ ()
|
||||
}
|
||||
|
@ -4,13 +4,9 @@
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"sort"
|
||||
)
|
||||
import "go/ast"
|
||||
|
||||
// All types implement the Type interface.
|
||||
// TODO(gri) Eventually determine what common Type functionality should be exported.
|
||||
type Type interface {
|
||||
aType()
|
||||
}
|
||||
@ -95,7 +91,8 @@ type Slice struct {
|
||||
Elt Type
|
||||
}
|
||||
|
||||
type StructField struct {
|
||||
// A Field represents a field of a struct.
|
||||
type Field struct {
|
||||
Name string // unqualified type name for anonymous fields
|
||||
Type Type
|
||||
Tag string
|
||||
@ -105,7 +102,7 @@ type StructField struct {
|
||||
// A Struct represents a struct type struct{...}.
|
||||
type Struct struct {
|
||||
implementsType
|
||||
Fields []*StructField
|
||||
Fields []*Field
|
||||
}
|
||||
|
||||
func (typ *Struct) fieldIndex(name string) int {
|
||||
@ -126,16 +123,16 @@ type Pointer struct {
|
||||
// A Result represents a (multi-value) function call result.
|
||||
type Result struct {
|
||||
implementsType
|
||||
Values ObjList // Signature.Results of the function called
|
||||
Values []*ast.Object // Signature.Results of the function called
|
||||
}
|
||||
|
||||
// A Signature represents a user-defined function type func(...) (...).
|
||||
type Signature struct {
|
||||
implementsType
|
||||
Recv *ast.Object // nil if not a method
|
||||
Params ObjList // (incoming) parameters from left to right; or nil
|
||||
Results ObjList // (outgoing) results from left to right; or nil
|
||||
IsVariadic bool // true if the last parameter's type is of the form ...T
|
||||
Recv *ast.Object // nil if not a method
|
||||
Params []*ast.Object // (incoming) parameters from left to right; or nil
|
||||
Results []*ast.Object // (outgoing) results from left to right; or nil
|
||||
IsVariadic bool // true if the last parameter's type is of the form ...T
|
||||
}
|
||||
|
||||
// builtinId is an id of a builtin function.
|
||||
@ -180,10 +177,16 @@ type builtin struct {
|
||||
isStatement bool // true if the built-in is valid as an expression statement
|
||||
}
|
||||
|
||||
// A Method represents a method of an interface.
|
||||
type Method struct {
|
||||
Name string
|
||||
Type *Signature
|
||||
}
|
||||
|
||||
// An Interface represents an interface type interface{...}.
|
||||
type Interface struct {
|
||||
implementsType
|
||||
Methods ObjList // interface methods sorted by name; or nil
|
||||
Methods []*Method // TODO(gri) consider keeping them in sorted order
|
||||
}
|
||||
|
||||
// A Map represents a map type map[Key]Elt.
|
||||
@ -206,17 +209,6 @@ type NamedType struct {
|
||||
Underlying Type // nil if not fully declared yet; never a *NamedType
|
||||
}
|
||||
|
||||
// An ObjList represents an ordered (in some fashion) list of objects.
|
||||
type ObjList []*ast.Object
|
||||
|
||||
// ObjList implements sort.Interface.
|
||||
func (list ObjList) Len() int { return len(list) }
|
||||
func (list ObjList) Less(i, j int) bool { return list[i].Name < list[j].Name }
|
||||
func (list ObjList) Swap(i, j int) { list[i], list[j] = list[j], list[i] }
|
||||
|
||||
// Sort sorts an object list by object name.
|
||||
func (list ObjList) Sort() { sort.Sort(list) }
|
||||
|
||||
// All concrete types embed implementsType which
|
||||
// ensures that all types implement the Type interface.
|
||||
type implementsType struct{}
|
||||
|
@ -88,10 +88,7 @@ var testTypes = []testEntry{
|
||||
// interfaces
|
||||
dup("interface{}"),
|
||||
dup("interface{m()}"),
|
||||
{`interface{
|
||||
m(int) float32
|
||||
String() string
|
||||
}`, `interface{String() string; m(int) float32}`}, // methods are sorted
|
||||
dup(`interface{m(int) float32; String() string}`),
|
||||
// TODO(gri) add test for interface w/ anonymous field
|
||||
|
||||
// maps
|
||||
|
@ -118,10 +118,9 @@ func init() {
|
||||
{
|
||||
res := ast.NewObj(ast.Var, "")
|
||||
res.Type = Typ[String]
|
||||
err := ast.NewObj(ast.Fun, "Error")
|
||||
err.Type = &Signature{Results: ObjList{res}}
|
||||
err := &Method{"Error", &Signature{Results: []*ast.Object{res}}}
|
||||
obj := def(ast.Typ, "error")
|
||||
obj.Type = &NamedType{Underlying: &Interface{Methods: ObjList{err}}, Obj: obj}
|
||||
obj.Type = &NamedType{Underlying: &Interface{Methods: []*Method{err}}, Obj: obj}
|
||||
}
|
||||
|
||||
// predeclared constants
|
||||
|
Loading…
Reference in New Issue
Block a user