// TODO: use the version from ssa or go/types func intuitiveMethodSet(T types.Type) []*types.Selection { var result []*types.Selection mset := types.NewMethodSet(T) if _, ok := T.Underlying().(*types.Interface); ok { for i, n := 0, mset.Len(); i < n; i++ { result = append(result, mset.At(i)) } } else { pmset := types.NewMethodSet(types.NewPointer(T)) for i, n := 0, pmset.Len(); i < n; i++ { meth := pmset.At(i) if m := mset.Lookup(meth.Obj.GetPkg(), meth.Obj.GetName()); m != nil { meth = m } result = append(result, meth) } } return result }
// TODO(adonovan): // - test use of explicit hasher across two maps. // - test hashcodes are consistent with equals for a range of types // (e.g. all types generated by type-checking some body of real code). import ( "testing" "github.com/gordonklaus/flux/go/types" "golang.org/x/tools/go/types/typemap" ) var ( tStr = types.Typ[types.String] // string tPStr1 = types.NewPointer(tStr) // *string tPStr2 = types.NewPointer(tStr) // *string, again tInt = types.Typ[types.Int] // int tChanInt1 = types.NewChan(types.RecvOnly, tInt) // <-chan int tChanInt2 = types.NewChan(types.RecvOnly, tInt) // <-chan int, again ) func checkEqualButNotIdentical(t *testing.T, x, y types.Type, comment string) { if !types.IsIdentical(x, y) { t.Errorf("%s: not equal: %s, %s", comment, x, y) } if x == y { t.Errorf("%s: identical: %v, %v", comment, x, y) } }
func (r *reader) typ(x ast.Expr) types.Type { switch x := x.(type) { case *ast.Ident: if t, ok := r.scope.LookupParent(x.Name).(*types.TypeName); ok { return t.Type } return types.NewNamed(types.NewTypeName(0, r.pkg, x.Name, nil), types.Typ[types.Invalid], nil) //unknown(t.Obj) == true case *ast.SelectorExpr: pkg := r.scope.LookupParent(name(x.X)).(*types.PkgName).Pkg if t, ok := pkg.Scope().Lookup(x.Sel.Name).(*types.TypeName); ok { return t.Type } return types.NewNamed(types.NewTypeName(0, r.pkg, x.Sel.Name, nil), types.Typ[types.Invalid], nil) //unknown(t.Obj) == true case *ast.StarExpr: return types.NewPointer(r.typ(x.X)) case *ast.ArrayType: elem := r.typ(x.Elt) if x.Len != nil { // TODO: x.Len return types.NewArray(elem, 0) } return types.NewSlice(elem) case *ast.Ellipsis: return types.NewSlice(r.typ(x.Elt)) case *ast.MapType: return types.NewMap(r.typ(x.Key), r.typ(x.Value)) case *ast.ChanType: dir := types.SendRecv if x.Dir&ast.SEND == 0 { dir = types.RecvOnly } if x.Dir&ast.RECV == 0 { dir = types.SendOnly } return types.NewChan(dir, r.typ(x.Value)) case *ast.FuncType: var params, results []*types.Var for _, f := range x.Params.List { t := r.typ(f.Type) if f.Names == nil { params = append(params, types.NewParam(0, r.pkg, "", t)) } for _, n := range f.Names { params = append(params, types.NewParam(0, r.pkg, n.Name, t)) } } variadic := false if x.Results != nil { for _, f := range x.Results.List { t := r.typ(f.Type) if f.Names == nil { results = append(results, types.NewParam(0, r.pkg, "", t)) } for _, n := range f.Names { results = append(results, types.NewParam(0, r.pkg, n.Name, t)) } _, variadic = f.Type.(*ast.Ellipsis) } } return types.NewSignature(nil, nil, params, results, variadic) case *ast.StructType: var fields []*types.Var if x.Fields != nil { for _, f := range x.Fields.List { t := r.typ(f.Type) if f.Names == nil { fields = append(fields, types.NewField(0, r.pkg, "", t, true)) } for _, n := range f.Names { fields = append(fields, types.NewField(0, r.pkg, n.Name, t, false)) } } } return types.NewStruct(fields, nil) case *ast.InterfaceType: var methods []*types.Func var embeddeds []*types.Named if x.Methods != nil { for _, f := range x.Methods.List { switch t := r.typ(f.Type).(type) { case *types.Signature: methods = append(methods, types.NewFunc(0, r.pkg, f.Names[0].Name, t)) case *types.Named: embeddeds = append(embeddeds, t) } } } return types.NewInterface(methods, embeddeds) } panic("unreachable") }