diff --git a/cmd/godex/print.go b/cmd/godex/print.go index 1bb5214edfd..da3b2f04e0b 100644 --- a/cmd/godex/print.go +++ b/cmd/godex/print.go @@ -12,6 +12,8 @@ import ( "go/types" "io" "math/big" + + "golang.org/x/tools/internal/aliases" ) // TODO(gri) use tabwriter for alignment? @@ -56,7 +58,7 @@ func (p *printer) printf(format string, args ...interface{}) { // denoted by obj is not an interface and has methods. Otherwise it returns // the zero value. func methodsFor(obj *types.TypeName) (*types.Named, []*types.Selection) { - named, _ := obj.Type().(*types.Named) + named, _ := aliases.Unalias(obj.Type()).(*types.Named) if named == nil { // A type name's type can also be the // exported basic type unsafe.Pointer. diff --git a/cmd/godex/writetype.go b/cmd/godex/writetype.go index 5cbe1b12c84..6ae365d13a3 100644 --- a/cmd/godex/writetype.go +++ b/cmd/godex/writetype.go @@ -12,7 +12,11 @@ package main -import "go/types" +import ( + "go/types" + + "golang.org/x/tools/internal/aliases" +) func (p *printer) writeType(this *types.Package, typ types.Type) { p.writeTypeInternal(this, typ, make([]types.Type, 8)) @@ -173,6 +177,10 @@ func (p *printer) writeTypeInternal(this *types.Package, typ types.Type, visited p.print(")") } + case *aliases.Alias: + // TODO(adonovan): display something aliasy. + p.writeTypeInternal(this, aliases.Unalias(t), visited) + case *types.Named: s := "" if obj := t.Obj(); obj != nil { diff --git a/cmd/guru/describe.go b/cmd/guru/describe.go index 273d5e2d73c..e85bc385feb 100644 --- a/cmd/guru/describe.go +++ b/cmd/guru/describe.go @@ -19,6 +19,7 @@ import ( "golang.org/x/tools/go/ast/astutil" "golang.org/x/tools/go/loader" "golang.org/x/tools/go/types/typeutil" + "golang.org/x/tools/internal/aliases" "golang.org/x/tools/internal/typesinternal" ) @@ -358,7 +359,7 @@ func appendNames(names []*types.Named, typ types.Type) []*types.Named { Elem() types.Type } - switch t := typ.(type) { + switch t := aliases.Unalias(typ).(type) { case *types.Named: names = append(names, t) case *types.Map: @@ -469,7 +470,7 @@ func describeType(qpos *queryPos, path []ast.Node) (*describeTypeResult, error) description = "alias of " } else if obj.Pos() == n.Pos() { description = "definition of " // (Named type) - } else if _, ok := typ.(*types.Basic); ok { + } else if _, ok := aliases.Unalias(typ).(*types.Basic); ok { description = "reference to built-in " } else { description = "reference to " // (Named type) @@ -486,7 +487,7 @@ func describeType(qpos *queryPos, path []ast.Node) (*describeTypeResult, error) description = description + "type " + qpos.typeString(typ) // Show sizes for structs and named types (it's fairly obvious for others). - switch typ.(type) { + switch aliases.Unalias(typ).(type) { case *types.Named, *types.Struct: szs := types.StdSizes{WordSize: 8, MaxAlign: 8} // assume amd64 description = fmt.Sprintf("%s (size %d, align %d)", description, @@ -576,7 +577,7 @@ func (r *describeTypeResult) PrintPlain(printf printfFunc) { printf(r.node, "%s", r.description) // Show the underlying type for a reference to a named type. - if nt, ok := r.typ.(*types.Named); ok && r.node.Pos() != nt.Obj().Pos() { + if nt, ok := aliases.Unalias(r.typ).(*types.Named); ok && r.node.Pos() != nt.Obj().Pos() { // TODO(adonovan): improve display of complex struct/interface types. printf(nt.Obj(), "defined as %s", r.qpos.typeString(nt.Underlying())) } @@ -585,7 +586,7 @@ func (r *describeTypeResult) PrintPlain(printf printfFunc) { if len(r.methods) == 0 { // Only report null result for type kinds // capable of bearing methods. - switch r.typ.(type) { + switch aliases.Unalias(r.typ).(type) { case *types.Interface, *types.Struct, *types.Named: printf(r.node, "No methods.") } @@ -596,7 +597,7 @@ func (r *describeTypeResult) PrintPlain(printf printfFunc) { func (r *describeTypeResult) JSON(fset *token.FileSet) []byte { var namePos, nameDef string - if nt, ok := r.typ.(*types.Named); ok { + if nt, ok := aliases.Unalias(r.typ).(*types.Named); ok { namePos = fset.Position(nt.Obj().Pos()).String() nameDef = nt.Underlying().String() } @@ -727,7 +728,7 @@ func formatMember(obj types.Object, maxname int) string { } var typestr string // Abbreviate long aggregate type names. - switch typ := typ.(type) { + switch typ := aliases.Unalias(typ).(type) { case *types.Interface: if typ.NumMethods() > 1 { typestr = "interface{...}" diff --git a/cmd/guru/implements.go b/cmd/guru/implements.go index 9e4d0dba6ee..48ab19116ae 100644 --- a/cmd/guru/implements.go +++ b/cmd/guru/implements.go @@ -16,6 +16,7 @@ import ( "golang.org/x/tools/cmd/guru/serial" "golang.org/x/tools/go/loader" "golang.org/x/tools/go/types/typeutil" + "golang.org/x/tools/internal/aliases" "golang.org/x/tools/refactor/importgraph" ) @@ -157,7 +158,7 @@ func implements(q *Query) error { } var pos interface{} = qpos - if nt, ok := deref(T).(*types.Named); ok { + if nt, ok := aliases.Unalias(deref(T)).(*types.Named); ok { pos = nt.Obj() } @@ -230,7 +231,7 @@ func (r *implementsResult) PrintPlain(printf printfFunc) { for i, sub := range r.to { if !isInterface(sub) { if r.method == nil { - printf(deref(sub).(*types.Named).Obj(), "\t%s %s type %s", + printf(aliases.Unalias(deref(sub)).(*types.Named).Obj(), "\t%s %s type %s", relation, typeKind(sub), r.qpos.typeString(sub)) } else { meth(r.toMethod[i]) @@ -240,7 +241,7 @@ func (r *implementsResult) PrintPlain(printf printfFunc) { for i, sub := range r.to { if isInterface(sub) { if r.method == nil { - printf(sub.(*types.Named).Obj(), "\t%s %s type %s", + printf(aliases.Unalias(sub).(*types.Named).Obj(), "\t%s %s type %s", relation, typeKind(sub), r.qpos.typeString(sub)) } else { meth(r.toMethod[i]) @@ -251,7 +252,7 @@ func (r *implementsResult) PrintPlain(printf printfFunc) { relation = "implements" for i, super := range r.from { if r.method == nil { - printf(super.(*types.Named).Obj(), "\t%s %s", + printf(aliases.Unalias(super).(*types.Named).Obj(), "\t%s %s", relation, r.qpos.typeString(super)) } else { meth(r.fromMethod[i]) @@ -270,7 +271,7 @@ func (r *implementsResult) PrintPlain(printf printfFunc) { } for i, super := range r.from { if r.method == nil { - printf(super.(*types.Named).Obj(), "\t%s %s", + printf(aliases.Unalias(super).(*types.Named).Obj(), "\t%s %s", relation, r.qpos.typeString(super)) } else { meth(r.fromMethod[i]) @@ -288,7 +289,7 @@ func (r *implementsResult) PrintPlain(printf printfFunc) { for i, psuper := range r.fromPtr { if r.method == nil { - printf(psuper.(*types.Named).Obj(), "\t%s %s", + printf(aliases.Unalias(psuper).(*types.Named).Obj(), "\t%s %s", relation, r.qpos.typeString(psuper)) } else { meth(r.fromPtrMethod[i]) @@ -332,7 +333,7 @@ func makeImplementsTypes(tt []types.Type, fset *token.FileSet) []serial.Implemen func makeImplementsType(T types.Type, fset *token.FileSet) serial.ImplementsType { var pos token.Pos - if nt, ok := deref(T).(*types.Named); ok { // implementsResult.t may be non-named + if nt, ok := aliases.Unalias(deref(T)).(*types.Named); ok { // implementsResult.t may be non-named pos = nt.Obj().Pos() } return serial.ImplementsType{ diff --git a/go/analysis/passes/composite/composite.go b/go/analysis/passes/composite/composite.go index 847063bb326..6b126f897d8 100644 --- a/go/analysis/passes/composite/composite.go +++ b/go/analysis/passes/composite/composite.go @@ -15,6 +15,7 @@ import ( "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis/passes/inspect" "golang.org/x/tools/go/ast/inspector" + "golang.org/x/tools/internal/aliases" "golang.org/x/tools/internal/typeparams" ) @@ -71,7 +72,7 @@ func run(pass *analysis.Pass) (interface{}, error) { return } var structuralTypes []types.Type - switch typ := typ.(type) { + switch typ := aliases.Unalias(typ).(type) { case *types.TypeParam: terms, err := typeparams.StructuralTerms(typ) if err != nil { @@ -84,7 +85,8 @@ func run(pass *analysis.Pass) (interface{}, error) { structuralTypes = append(structuralTypes, typ) } for _, typ := range structuralTypes { - under := deref(typ.Underlying()) + // TODO(adonovan): this operation is questionable. + under := aliases.Unalias(deref(typ.Underlying())) strct, ok := under.(*types.Struct) if !ok { // skip non-struct composite literals @@ -142,9 +144,11 @@ func run(pass *analysis.Pass) (interface{}, error) { return nil, nil } +// Note: this is not the usual deref operator! +// It strips off all Pointer constructors (and their Aliases). func deref(typ types.Type) types.Type { for { - ptr, ok := typ.(*types.Pointer) + ptr, ok := aliases.Unalias(typ).(*types.Pointer) if !ok { break } @@ -153,18 +157,18 @@ func deref(typ types.Type) types.Type { return typ } +// isLocalType reports whether typ belongs to the same package as pass. +// TODO(adonovan): local means "internal to a function"; rename to isSamePackageType. func isLocalType(pass *analysis.Pass, typ types.Type) bool { - switch x := typ.(type) { + switch x := aliases.Unalias(typ).(type) { case *types.Struct: // struct literals are local types return true case *types.Pointer: return isLocalType(pass, x.Elem()) - case *types.Named: + case interface{ Obj() *types.TypeName }: // *Named or *TypeParam (aliases were removed already) // names in package foo are local to foo_test too return strings.TrimSuffix(x.Obj().Pkg().Path(), "_test") == strings.TrimSuffix(pass.Pkg.Path(), "_test") - case *types.TypeParam: - return strings.TrimSuffix(x.Obj().Pkg().Path(), "_test") == strings.TrimSuffix(pass.Pkg.Path(), "_test") } return false } diff --git a/go/analysis/passes/copylock/copylock.go b/go/analysis/passes/copylock/copylock.go index 6cbbc7e8140..8f39159c0f0 100644 --- a/go/analysis/passes/copylock/copylock.go +++ b/go/analysis/passes/copylock/copylock.go @@ -18,6 +18,7 @@ import ( "golang.org/x/tools/go/analysis/passes/internal/analysisutil" "golang.org/x/tools/go/ast/astutil" "golang.org/x/tools/go/ast/inspector" + "golang.org/x/tools/internal/aliases" "golang.org/x/tools/internal/typeparams" ) @@ -255,7 +256,7 @@ func lockPath(tpkg *types.Package, typ types.Type, seen map[types.Type]bool) typ } seen[typ] = true - if tpar, ok := typ.(*types.TypeParam); ok { + if tpar, ok := aliases.Unalias(typ).(*types.TypeParam); ok { terms, err := typeparams.StructuralTerms(tpar) if err != nil { return nil // invalid type diff --git a/go/analysis/passes/deepequalerrors/deepequalerrors.go b/go/analysis/passes/deepequalerrors/deepequalerrors.go index 5e17bd1ab90..95cd9a061eb 100644 --- a/go/analysis/passes/deepequalerrors/deepequalerrors.go +++ b/go/analysis/passes/deepequalerrors/deepequalerrors.go @@ -102,8 +102,7 @@ func containsError(typ types.Type) bool { return true } } - case *types.Named, - *aliases.Alias: + case *types.Named, *aliases.Alias: return check(t.Underlying()) // We list the remaining valid type kinds for completeness. diff --git a/go/analysis/passes/httpresponse/httpresponse.go b/go/analysis/passes/httpresponse/httpresponse.go index 8cb046e16da..047ae07cca1 100644 --- a/go/analysis/passes/httpresponse/httpresponse.go +++ b/go/analysis/passes/httpresponse/httpresponse.go @@ -14,6 +14,7 @@ import ( "golang.org/x/tools/go/analysis/passes/inspect" "golang.org/x/tools/go/analysis/passes/internal/analysisutil" "golang.org/x/tools/go/ast/inspector" + "golang.org/x/tools/internal/aliases" "golang.org/x/tools/internal/typesinternal" ) @@ -136,7 +137,7 @@ func isHTTPFuncOrMethodOnClient(info *types.Info, expr *ast.CallExpr) bool { if analysisutil.IsNamedType(typ, "net/http", "Client") { return true // method on http.Client. } - ptr, ok := typ.(*types.Pointer) + ptr, ok := aliases.Unalias(typ).(*types.Pointer) return ok && analysisutil.IsNamedType(ptr.Elem(), "net/http", "Client") // method on *http.Client. } diff --git a/go/analysis/passes/ifaceassert/parameterized.go b/go/analysis/passes/ifaceassert/parameterized.go index 12507f9967f..a077d440246 100644 --- a/go/analysis/passes/ifaceassert/parameterized.go +++ b/go/analysis/passes/ifaceassert/parameterized.go @@ -7,6 +7,7 @@ package ifaceassert import ( "go/types" + "golang.org/x/tools/internal/aliases" "golang.org/x/tools/internal/typeparams" ) @@ -94,6 +95,10 @@ func (w *tpWalker) isParameterized(typ types.Type) (res bool) { case *types.Chan: return w.isParameterized(t.Elem()) + case *aliases.Alias: + // TODO(adonovan): think about generic aliases. + return w.isParameterized(aliases.Unalias(t)) + case *types.Named: list := t.TypeArgs() for i, n := 0, list.Len(); i < n; i++ { diff --git a/go/analysis/passes/internal/analysisutil/util.go b/go/analysis/passes/internal/analysisutil/util.go index 3f01b3b55dc..89291602a5b 100644 --- a/go/analysis/passes/internal/analysisutil/util.go +++ b/go/analysis/passes/internal/analysisutil/util.go @@ -14,6 +14,7 @@ import ( "go/types" "os" + "golang.org/x/tools/internal/aliases" "golang.org/x/tools/internal/analysisinternal" ) @@ -115,7 +116,7 @@ func Imports(pkg *types.Package, path string) bool { // This function avoids allocating the concatenation of "pkg.Name", // which is important for the performance of syntax matching. func IsNamedType(t types.Type, pkgPath string, names ...string) bool { - n, ok := t.(*types.Named) + n, ok := aliases.Unalias(t).(*types.Named) if !ok { return false } diff --git a/go/analysis/passes/printf/printf.go b/go/analysis/passes/printf/printf.go index 070654f0124..32350192583 100644 --- a/go/analysis/passes/printf/printf.go +++ b/go/analysis/passes/printf/printf.go @@ -24,6 +24,7 @@ import ( "golang.org/x/tools/go/analysis/passes/internal/analysisutil" "golang.org/x/tools/go/ast/inspector" "golang.org/x/tools/go/types/typeutil" + "golang.org/x/tools/internal/aliases" "golang.org/x/tools/internal/typeparams" ) @@ -959,6 +960,8 @@ func isStringer(sig *types.Signature) bool { // It is almost always a mistake to print a function value. func isFunctionValue(pass *analysis.Pass, e ast.Expr) bool { if typ := pass.TypesInfo.Types[e].Type; typ != nil { + // Don't call Underlying: a named func type with a String method is ok. + // TODO(adonovan): it would be more precise to check isStringer. _, ok := typ.(*types.Signature) return ok } @@ -1010,7 +1013,7 @@ func checkPrint(pass *analysis.Pass, call *ast.CallExpr, fn *types.Func) { // Skip checking functions with unknown type. return } - if sig, ok := typ.(*types.Signature); ok { + if sig, ok := typ.Underlying().(*types.Signature); ok { if !sig.Variadic() { // Skip checking non-variadic functions. return @@ -1020,7 +1023,7 @@ func checkPrint(pass *analysis.Pass, call *ast.CallExpr, fn *types.Func) { typ := params.At(firstArg).Type() typ = typ.(*types.Slice).Elem() - it, ok := typ.(*types.Interface) + it, ok := aliases.Unalias(typ).(*types.Interface) if !ok || !it.Empty() { // Skip variadic functions accepting non-interface{} args. return diff --git a/go/analysis/passes/printf/types.go b/go/analysis/passes/printf/types.go index ab98e569980..017c8a247ec 100644 --- a/go/analysis/passes/printf/types.go +++ b/go/analysis/passes/printf/types.go @@ -10,6 +10,7 @@ import ( "go/types" "golang.org/x/tools/go/analysis" + "golang.org/x/tools/internal/aliases" "golang.org/x/tools/internal/typeparams" ) @@ -72,7 +73,7 @@ func (m *argMatcher) match(typ types.Type, topLevel bool) bool { return true } - if typ, _ := typ.(*types.TypeParam); typ != nil { + if typ, _ := aliases.Unalias(typ).(*types.TypeParam); typ != nil { // Avoid infinite recursion through type parameters. if m.seen[typ] { return true @@ -275,7 +276,7 @@ func (m *argMatcher) match(typ types.Type, topLevel bool) bool { } func isConvertibleToString(typ types.Type) bool { - if bt, ok := typ.(*types.Basic); ok && bt.Kind() == types.UntypedNil { + if bt, ok := aliases.Unalias(typ).(*types.Basic); ok && bt.Kind() == types.UntypedNil { // We explicitly don't want untyped nil, which is // convertible to both of the interfaces below, as it // would just panic anyway. diff --git a/go/analysis/passes/shift/shift.go b/go/analysis/passes/shift/shift.go index 61f16501bb3..d01eb1eebe5 100644 --- a/go/analysis/passes/shift/shift.go +++ b/go/analysis/passes/shift/shift.go @@ -21,6 +21,7 @@ import ( "golang.org/x/tools/go/analysis/passes/inspect" "golang.org/x/tools/go/analysis/passes/internal/analysisutil" "golang.org/x/tools/go/ast/inspector" + "golang.org/x/tools/internal/aliases" "golang.org/x/tools/internal/typeparams" ) @@ -99,7 +100,7 @@ func checkLongShift(pass *analysis.Pass, node ast.Node, x, y ast.Expr) { return } var structuralTypes []types.Type - switch t := t.(type) { + switch t := aliases.Unalias(t).(type) { case *types.TypeParam: terms, err := typeparams.StructuralTerms(t) if err != nil { diff --git a/go/analysis/passes/stringintconv/string.go b/go/analysis/passes/stringintconv/string.go index 005e2e54b7d..16a4b3e5516 100644 --- a/go/analysis/passes/stringintconv/string.go +++ b/go/analysis/passes/stringintconv/string.go @@ -60,10 +60,12 @@ func describe(typ, inType types.Type, inName string) string { } func typeName(typ types.Type) string { - if v, _ := typ.(interface{ Name() string }); v != nil { + typ = aliases.Unalias(typ) + // TODO(adonovan): don't discard alias type, return its name. + if v, _ := typ.(*types.Basic); v != nil { return v.Name() } - if v, _ := typ.(interface{ Obj() *types.TypeName }); v != nil { + if v, _ := typ.(interface{ Obj() *types.TypeName }); v != nil { // Named, TypeParam return v.Obj().Name() } return "" diff --git a/go/analysis/passes/testinggoroutine/testinggoroutine.go b/go/analysis/passes/testinggoroutine/testinggoroutine.go index dc5307a15d0..828f95bc862 100644 --- a/go/analysis/passes/testinggoroutine/testinggoroutine.go +++ b/go/analysis/passes/testinggoroutine/testinggoroutine.go @@ -17,6 +17,7 @@ import ( "golang.org/x/tools/go/ast/astutil" "golang.org/x/tools/go/ast/inspector" "golang.org/x/tools/go/types/typeutil" + "golang.org/x/tools/internal/aliases" ) //go:embed doc.go @@ -270,7 +271,7 @@ func forbiddenMethod(info *types.Info, call *ast.CallExpr) (*types.Var, *types.S func formatMethod(sel *types.Selection, fn *types.Func) string { var ptr string rtype := sel.Recv() - if p, ok := rtype.(*types.Pointer); ok { + if p, ok := aliases.Unalias(rtype).(*types.Pointer); ok { ptr = "*" rtype = p.Elem() } diff --git a/go/analysis/passes/tests/tests.go b/go/analysis/passes/tests/tests.go index 6db12f3cb9a..39d0d9e429e 100644 --- a/go/analysis/passes/tests/tests.go +++ b/go/analysis/passes/tests/tests.go @@ -252,6 +252,8 @@ func validateFuzzArgs(pass *analysis.Pass, params *types.Tuple, expr ast.Expr) b } func isTestingType(typ types.Type, testingType string) bool { + // No Unalias here: I doubt "go test" recognizes + // "type A = *testing.T; func Test(A) {}" as a test. ptr, ok := typ.(*types.Pointer) if !ok { return false diff --git a/go/analysis/passes/timeformat/timeformat.go b/go/analysis/passes/timeformat/timeformat.go index eb84502bd99..4a6c6b8bc6c 100644 --- a/go/analysis/passes/timeformat/timeformat.go +++ b/go/analysis/passes/timeformat/timeformat.go @@ -107,7 +107,7 @@ func badFormatAt(info *types.Info, e ast.Expr) int { return -1 } - t, ok := tv.Type.(*types.Basic) + t, ok := tv.Type.(*types.Basic) // sic, no unalias if !ok || t.Info()&types.IsString == 0 { return -1 } diff --git a/go/analysis/passes/unsafeptr/unsafeptr.go b/go/analysis/passes/unsafeptr/unsafeptr.go index 32e71ef979d..14e4a6c1e4b 100644 --- a/go/analysis/passes/unsafeptr/unsafeptr.go +++ b/go/analysis/passes/unsafeptr/unsafeptr.go @@ -17,6 +17,7 @@ import ( "golang.org/x/tools/go/analysis/passes/internal/analysisutil" "golang.org/x/tools/go/ast/astutil" "golang.org/x/tools/go/ast/inspector" + "golang.org/x/tools/internal/aliases" ) //go:embed doc.go @@ -88,7 +89,7 @@ func isSafeUintptr(info *types.Info, x ast.Expr) bool { // by the time we get to the conversion at the end. // For now approximate by saying that *Header is okay // but Header is not. - pt, ok := info.Types[x.X].Type.(*types.Pointer) + pt, ok := aliases.Unalias(info.Types[x.X].Type).(*types.Pointer) if ok && isReflectHeader(pt.Elem()) { return true } diff --git a/go/analysis/passes/unusedwrite/unusedwrite.go b/go/analysis/passes/unusedwrite/unusedwrite.go index cc484620dcc..a01cbb8f83a 100644 --- a/go/analysis/passes/unusedwrite/unusedwrite.go +++ b/go/analysis/passes/unusedwrite/unusedwrite.go @@ -125,10 +125,7 @@ func isDeadStore(store *ssa.Store, obj ssa.Value, addr ssa.Instruction) bool { // isStructOrArray returns whether the underlying type is struct or array. func isStructOrArray(tp types.Type) bool { - if named, ok := tp.(*types.Named); ok { - tp = named.Underlying() - } - switch tp.(type) { + switch tp.Underlying().(type) { case *types.Array: return true case *types.Struct: @@ -146,7 +143,7 @@ func hasStructOrArrayType(v ssa.Value) bool { // func (t T) f() { ...} // the receiver object is of type *T: // t0 = local T (t) *T - if tp, ok := alloc.Type().(*types.Pointer); ok { + if tp, ok := aliases.Unalias(alloc.Type()).(*types.Pointer); ok { return isStructOrArray(tp.Elem()) } return false @@ -162,14 +159,12 @@ func hasStructOrArrayType(v ssa.Value) bool { func getFieldName(tp types.Type, index int) string { // TODO(adonovan): use // stp, ok := typeparams.Deref(tp).Underlying().(*types.Struct); ok { - // when Deref is defined. + // when Deref is defined. But see CL 565456 for a better fix. + if pt, ok := aliases.Unalias(tp).(*types.Pointer); ok { tp = pt.Elem() } - if named, ok := aliases.Unalias(tp).(*types.Named); ok { - tp = named.Underlying() - } - if stp, ok := tp.(*types.Struct); ok { + if stp, ok := tp.Underlying().(*types.Struct); ok { return stp.Field(index).Name() } return fmt.Sprintf("%d", index) diff --git a/go/callgraph/rta/rta.go b/go/callgraph/rta/rta.go index 72b383dabe7..3c8dc41cd38 100644 --- a/go/callgraph/rta/rta.go +++ b/go/callgraph/rta/rta.go @@ -375,7 +375,7 @@ func (r *rta) interfaces(C types.Type) []*types.Interface { // and update the 'implements' relation. r.interfaceTypes.Iterate(func(I types.Type, v interface{}) { iinfo := v.(*interfaceTypeInfo) - if I := I.(*types.Interface); implements(cinfo, iinfo) { + if I := aliases.Unalias(I).(*types.Interface); implements(cinfo, iinfo) { iinfo.implementations = append(iinfo.implementations, C) cinfo.implements = append(cinfo.implements, I) } @@ -457,7 +457,7 @@ func (r *rta) addRuntimeType(T types.Type, skip bool) { // Each package maintains its own set of types it has visited. var n *types.Named - switch T := T.(type) { + switch T := aliases.Unalias(T).(type) { case *types.Named: n = T case *types.Pointer: diff --git a/go/callgraph/vta/graph.go b/go/callgraph/vta/graph.go index 4b5a65b3336..0abeab01bb1 100644 --- a/go/callgraph/vta/graph.go +++ b/go/callgraph/vta/graph.go @@ -12,6 +12,7 @@ import ( "golang.org/x/tools/go/callgraph" "golang.org/x/tools/go/ssa" "golang.org/x/tools/go/types/typeutil" + "golang.org/x/tools/internal/aliases" "golang.org/x/tools/internal/typeparams" ) @@ -410,7 +411,7 @@ func (b *builder) tassert(a *ssa.TypeAssert) { // The case where a is register so there // is a flow from a.X to a[0]. Here, a[0] is represented as an // indexedLocal: an entry into local tuple register a at index 0. - tup := a.Type().Underlying().(*types.Tuple) + tup := a.Type().(*types.Tuple) t := tup.At(0).Type() local := indexedLocal{val: a, typ: t, index: 0} @@ -421,7 +422,7 @@ func (b *builder) tassert(a *ssa.TypeAssert) { // and t1 where the source is indexed local representing a value // from tuple register t2 at index i and the target is t1. func (b *builder) extract(e *ssa.Extract) { - tup := e.Tuple.Type().Underlying().(*types.Tuple) + tup := e.Tuple.Type().(*types.Tuple) t := tup.At(e.Index).Type() local := indexedLocal{val: e.Tuple, typ: t, index: e.Index} @@ -527,7 +528,7 @@ func (b *builder) next(n *ssa.Next) { if n.IsString { return } - tup := n.Type().Underlying().(*types.Tuple) + tup := n.Type().(*types.Tuple) kt := tup.At(1).Type() vt := tup.At(2).Type() @@ -657,7 +658,7 @@ func addReturnFlows(b *builder, r *ssa.Return, site ssa.Value) { return } - tup := site.Type().Underlying().(*types.Tuple) + tup := site.Type().(*types.Tuple) for i, r := range results { local := indexedLocal{val: site, typ: tup.At(i).Type(), index: i} b.addInFlowEdge(b.nodeFromVal(r), local) @@ -671,7 +672,7 @@ func (b *builder) multiconvert(c *ssa.MultiConvert) { // This is a adaptation of x/exp/typeparams.NormalTerms which x/tools cannot depend on. var terms []*types.Term var err error - switch typ := typ.(type) { + switch typ := aliases.Unalias(typ).(type) { case *types.TypeParam: terms, err = typeparams.StructuralTerms(typ) case *types.Union: @@ -692,7 +693,7 @@ func (b *builder) multiconvert(c *ssa.MultiConvert) { } // isValuePreserving returns true if a conversion from ut_src to // ut_dst is value-preserving, i.e. just a change of type. - // Precondition: neither argument is a named type. + // Precondition: neither argument is a named or alias type. isValuePreserving := func(ut_src, ut_dst types.Type) bool { // Identical underlying types? if types.IdenticalIgnoreTags(ut_dst, ut_src) { @@ -740,7 +741,7 @@ func (b *builder) addInFlowEdge(s, d node) { // Creates const, pointer, global, func, and local nodes based on register instructions. func (b *builder) nodeFromVal(val ssa.Value) node { - if p, ok := val.Type().(*types.Pointer); ok && !types.IsInterface(p.Elem()) && !isFunction(p.Elem()) { + if p, ok := aliases.Unalias(val.Type()).(*types.Pointer); ok && !types.IsInterface(p.Elem()) && !isFunction(p.Elem()) { // Nested pointer to interfaces are modeled as a special // nestedPtrInterface node. if i := interfaceUnderPtr(p.Elem()); i != nil { diff --git a/go/callgraph/vta/graph_test.go b/go/callgraph/vta/graph_test.go index da574d71b53..060f67f7ae6 100644 --- a/go/callgraph/vta/graph_test.go +++ b/go/callgraph/vta/graph_test.go @@ -15,6 +15,7 @@ import ( "golang.org/x/tools/go/callgraph/cha" "golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa/ssautil" + "golang.org/x/tools/internal/aliases" ) func TestNodeInterface(t *testing.T) { @@ -35,7 +36,7 @@ func TestNodeInterface(t *testing.T) { reg := firstRegInstr(main) // t0 := *gl X := pkg.Type("X").Type() gl := pkg.Var("gl") - glPtrType, ok := gl.Type().(*types.Pointer) + glPtrType, ok := aliases.Unalias(gl.Type()).(*types.Pointer) if !ok { t.Fatalf("could not cast gl variable to pointer type") } diff --git a/go/callgraph/vta/utils.go b/go/callgraph/vta/utils.go index 3471aae3a10..ed248d73e0b 100644 --- a/go/callgraph/vta/utils.go +++ b/go/callgraph/vta/utils.go @@ -9,6 +9,7 @@ import ( "golang.org/x/tools/go/callgraph" "golang.org/x/tools/go/ssa" + "golang.org/x/tools/internal/aliases" "golang.org/x/tools/internal/typeparams" ) @@ -24,7 +25,7 @@ func isReferenceNode(n node) bool { return true } - if _, ok := n.Type().(*types.Pointer); ok { + if _, ok := aliases.Unalias(n.Type()).(*types.Pointer); ok { return true } @@ -166,6 +167,7 @@ func siteCallees(c ssa.CallInstruction, callgraph *callgraph.Graph) []*ssa.Funct } func canHaveMethods(t types.Type) bool { + t = aliases.Unalias(t) if _, ok := t.(*types.Named); ok { return true } diff --git a/go/internal/gccgoimporter/parser.go b/go/internal/gccgoimporter/parser.go index 72bbe793c1e..dc40d217a88 100644 --- a/go/internal/gccgoimporter/parser.go +++ b/go/internal/gccgoimporter/parser.go @@ -20,6 +20,8 @@ import ( "strings" "text/scanner" "unicode/utf8" + + "golang.org/x/tools/internal/aliases" ) type parser struct { @@ -241,7 +243,7 @@ func (p *parser) parseName() string { } func deref(typ types.Type) types.Type { - if p, _ := typ.(*types.Pointer); p != nil { + if p, _ := aliases.Unalias(typ).(*types.Pointer); p != nil { typ = p.Elem() } return typ @@ -260,7 +262,7 @@ func (p *parser) parseField(pkg *types.Package) (field *types.Var, tag string) { if aname, ok := p.aliases[n]; ok { name = aname } else { - switch typ := deref(typ).(type) { + switch typ := aliases.Unalias(deref(typ)).(type) { case *types.Basic: name = typ.Name() case *types.Named: @@ -579,7 +581,7 @@ func (p *parser) parseNamedType(nlist []interface{}) types.Type { t := obj.Type() p.update(t, nlist) - nt, ok := t.(*types.Named) + nt, ok := aliases.Unalias(t).(*types.Named) if !ok { // This can happen for unsafe.Pointer, which is a TypeName holding a Basic type. pt := p.parseType(pkg) @@ -1334,7 +1336,7 @@ func (p *parser) parsePackage() *types.Package { } p.fixups = nil for _, typ := range p.typeList { - if it, ok := typ.(*types.Interface); ok { + if it, ok := aliases.Unalias(typ).(*types.Interface); ok { it.Complete() } } diff --git a/go/ssa/builder.go b/go/ssa/builder.go index ecbc3e4f566..95884060317 100644 --- a/go/ssa/builder.go +++ b/go/ssa/builder.go @@ -81,16 +81,15 @@ import ( "os" "sync" + "golang.org/x/tools/internal/aliases" "golang.org/x/tools/internal/typeparams" "golang.org/x/tools/internal/versions" ) -type opaqueType struct { - types.Type - name string -} +type opaqueType struct{ name string } -func (t *opaqueType) String() string { return t.name } +func (t *opaqueType) String() string { return t.name } +func (t *opaqueType) Underlying() types.Type { return t } var ( varOk = newVar("ok", tBool) @@ -103,7 +102,7 @@ var ( tInvalid = types.Typ[types.Invalid] tString = types.Typ[types.String] tUntypedNil = types.Typ[types.UntypedNil] - tRangeIter = &opaqueType{nil, "iter"} // the type of all "range" iterators + tRangeIter = &opaqueType{"iter"} // the type of all "range" iterators tEface = types.NewInterfaceType(nil, nil).Complete() // SSA Value constants. @@ -802,7 +801,7 @@ func (b *builder) expr0(fn *Function, e ast.Expr, tv types.TypeAndValue) Value { if types.IsInterface(rt) { // If v may be an interface type I (after instantiating), // we must emit a check that v is non-nil. - if recv, ok := sel.recv.(*types.TypeParam); ok { + if recv, ok := aliases.Unalias(sel.recv).(*types.TypeParam); ok { // Emit a nil check if any possible instantiation of the // type parameter is an interface type. if typeSetOf(recv).Len() > 0 { @@ -1253,7 +1252,7 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero case *types.Array, *types.Slice: var at *types.Array var array Value - switch t := t.(type) { + switch t := aliases.Unalias(t).(type) { case *types.Slice: at = types.NewArray(t.Elem(), b.arrayLen(fn, e.Elts)) array = emitNew(fn, at, e.Lbrace, "slicelit") diff --git a/go/ssa/builder_test.go b/go/ssa/builder_test.go index a15ab97aca9..680358cc889 100644 --- a/go/ssa/builder_test.go +++ b/go/ssa/builder_test.go @@ -25,6 +25,7 @@ import ( "golang.org/x/tools/go/packages" "golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa/ssautil" + "golang.org/x/tools/internal/aliases" "golang.org/x/tools/internal/testenv" "golang.org/x/tools/txtar" ) @@ -1091,7 +1092,7 @@ func TestIssue58491Rec(t *testing.T) { // Find the local type result instantiated with int. var found bool for _, rt := range p.Prog.RuntimeTypes() { - if n, ok := rt.(*types.Named); ok { + if n, ok := aliases.Unalias(rt).(*types.Named); ok { if u, ok := n.Underlying().(*types.Struct); ok { found = true if got, want := n.String(), "p.result"; got != want { diff --git a/go/ssa/const.go b/go/ssa/const.go index 2a6ac5882a0..e0d79f5ef72 100644 --- a/go/ssa/const.go +++ b/go/ssa/const.go @@ -14,6 +14,7 @@ import ( "strconv" "strings" + "golang.org/x/tools/internal/aliases" "golang.org/x/tools/internal/typeparams" ) @@ -47,7 +48,7 @@ func soleTypeKind(typ types.Type) types.BasicInfo { state := types.IsBoolean | types.IsInteger | types.IsString underIs(typeSetOf(typ), func(t types.Type) bool { var c types.BasicInfo - if t, ok := t.(*types.Basic); ok { + if t, ok := aliases.Unalias(t).(*types.Basic); ok { c = t.Info() } if c&types.IsNumeric != 0 { // int/float/complex @@ -113,7 +114,7 @@ func zeroString(t types.Type, from *types.Package) string { } case *types.Pointer, *types.Slice, *types.Interface, *types.Chan, *types.Map, *types.Signature: return "nil" - case *types.Named: + case *types.Named, *aliases.Alias: return zeroString(t.Underlying(), from) case *types.Array, *types.Struct: return relType(t, from) + "{}" diff --git a/go/ssa/coretype.go b/go/ssa/coretype.go index 88136b43842..3a512830b1f 100644 --- a/go/ssa/coretype.go +++ b/go/ssa/coretype.go @@ -7,6 +7,7 @@ package ssa import ( "go/types" + "golang.org/x/tools/internal/aliases" "golang.org/x/tools/internal/typeparams" ) @@ -49,7 +50,7 @@ func typeSetOf(typ types.Type) termList { // This is a adaptation of x/exp/typeparams.NormalTerms which x/tools cannot depend on. var terms []*types.Term var err error - switch typ := typ.(type) { + switch typ := aliases.Unalias(typ).(type) { case *types.TypeParam: terms, err = typeparams.StructuralTerms(typ) case *types.Union: diff --git a/go/ssa/emit.go b/go/ssa/emit.go index 2a26c9492dc..549c9114d43 100644 --- a/go/ssa/emit.go +++ b/go/ssa/emit.go @@ -12,6 +12,7 @@ import ( "go/token" "go/types" + "golang.org/x/tools/internal/aliases" "golang.org/x/tools/internal/typeparams" ) @@ -184,7 +185,7 @@ func emitCompare(f *Function, op token.Token, x, y Value, pos token.Pos) Value { // isValuePreserving returns true if a conversion from ut_src to // ut_dst is value-preserving, i.e. just a change of type. -// Precondition: neither argument is a named type. +// Precondition: neither argument is a named or alias type. func isValuePreserving(ut_src, ut_dst types.Type) bool { // Identical underlying types? if types.IdenticalIgnoreTags(ut_dst, ut_src) { @@ -283,11 +284,11 @@ func emitConv(f *Function, val Value, typ types.Type) Value { } // Conversion from slice to array or slice to array pointer? - if slice, ok := s.(*types.Slice); ok { + if slice, ok := aliases.Unalias(s).(*types.Slice); ok { var arr *types.Array var ptr bool // Conversion from slice to array pointer? - switch d := d.(type) { + switch d := aliases.Unalias(d).(type) { case *types.Array: arr = d case *types.Pointer: diff --git a/go/ssa/interp/ops.go b/go/ssa/interp/ops.go index 65d6452b783..62b635c20ac 100644 --- a/go/ssa/interp/ops.go +++ b/go/ssa/interp/ops.go @@ -17,6 +17,7 @@ import ( "unsafe" "golang.org/x/tools/go/ssa" + "golang.org/x/tools/internal/aliases" ) // If the target program panics, the interpreter panics with this type. @@ -172,7 +173,7 @@ func asUnsigned(x value) (value, bool) { // zero returns a new "zero" value of the specified type. func zero(t types.Type) value { - switch t := t.(type) { + switch t := aliases.Unalias(t).(type) { case *types.Basic: if t.Kind() == types.UntypedNil { panic("untyped nil has no zero value") diff --git a/go/ssa/interp/reflect.go b/go/ssa/interp/reflect.go index 9f2f9e1e457..7df3ea27d8b 100644 --- a/go/ssa/interp/reflect.go +++ b/go/ssa/interp/reflect.go @@ -18,6 +18,7 @@ import ( "unsafe" "golang.org/x/tools/go/ssa" + "golang.org/x/tools/internal/aliases" ) type opaqueType struct { @@ -119,7 +120,7 @@ func ext۰reflect۰rtype۰NumField(fr *frame, args []value) value { func ext۰reflect۰rtype۰NumIn(fr *frame, args []value) value { // Signature: func (t reflect.rtype) int - return args[0].(rtype).t.(*types.Signature).Params().Len() + return args[0].(rtype).t.Underlying().(*types.Signature).Params().Len() } func ext۰reflect۰rtype۰NumMethod(fr *frame, args []value) value { @@ -129,13 +130,13 @@ func ext۰reflect۰rtype۰NumMethod(fr *frame, args []value) value { func ext۰reflect۰rtype۰NumOut(fr *frame, args []value) value { // Signature: func (t reflect.rtype) int - return args[0].(rtype).t.(*types.Signature).Results().Len() + return args[0].(rtype).t.Underlying().(*types.Signature).Results().Len() } func ext۰reflect۰rtype۰Out(fr *frame, args []value) value { // Signature: func (t reflect.rtype, i int) int i := args[1].(int) - return makeReflectType(rtype{args[0].(rtype).t.(*types.Signature).Results().At(i).Type()}) + return makeReflectType(rtype{args[0].(rtype).t.Underlying().(*types.Signature).Results().At(i).Type()}) } func ext۰reflect۰rtype۰Size(fr *frame, args []value) value { @@ -178,7 +179,7 @@ func ext۰reflect۰Zero(fr *frame, args []value) value { } func reflectKind(t types.Type) reflect.Kind { - switch t := t.(type) { + switch t := aliases.Unalias(t).(type) { case *types.Named: return reflectKind(t.Underlying()) case *types.Basic: diff --git a/go/ssa/interp/value.go b/go/ssa/interp/value.go index 94018b550fc..d35da990ed1 100644 --- a/go/ssa/interp/value.go +++ b/go/ssa/interp/value.go @@ -45,6 +45,7 @@ import ( "golang.org/x/tools/go/ssa" "golang.org/x/tools/go/types/typeutil" + "golang.org/x/tools/internal/aliases" ) type value interface{} @@ -117,7 +118,7 @@ func usesBuiltinMap(t types.Type) bool { switch t := t.(type) { case *types.Basic, *types.Chan, *types.Pointer: return true - case *types.Named: + case *types.Named, *aliases.Alias: return usesBuiltinMap(t.Underlying()) case *types.Interface, *types.Array, *types.Struct: return false diff --git a/go/ssa/methods.go b/go/ssa/methods.go index 4797b39286c..5f46a18484c 100644 --- a/go/ssa/methods.go +++ b/go/ssa/methods.go @@ -11,6 +11,7 @@ import ( "go/types" "golang.org/x/tools/go/types/typeutil" + "golang.org/x/tools/internal/aliases" "golang.org/x/tools/internal/typeparams" ) @@ -209,6 +210,9 @@ func forEachReachable(msets *typeutil.MethodSetCache, T types.Type, f func(types } switch T := T.(type) { + case *aliases.Alias: + visit(aliases.Unalias(T), false) + case *types.Basic: // nop diff --git a/go/ssa/parameterized.go b/go/ssa/parameterized.go index 84db49d392f..74c541107ef 100644 --- a/go/ssa/parameterized.go +++ b/go/ssa/parameterized.go @@ -8,6 +8,7 @@ import ( "go/types" "sync" + "golang.org/x/tools/internal/aliases" "golang.org/x/tools/internal/typeparams" ) @@ -48,6 +49,9 @@ func (w *tpWalker) isParameterizedLocked(typ types.Type) (res bool) { case nil, *types.Basic: // TODO(gri) should nil be handled here? break + case *aliases.Alias: + return w.isParameterizedLocked(aliases.Unalias(t)) + case *types.Array: return w.isParameterizedLocked(t.Elem()) diff --git a/go/ssa/sanity.go b/go/ssa/sanity.go index 22a3c6bc3dc..13bd39fe862 100644 --- a/go/ssa/sanity.go +++ b/go/ssa/sanity.go @@ -349,7 +349,7 @@ func (s *sanity) checkBlock(b *BasicBlock, index int) { // Check that "untyped" types only appear on constant operands. if _, ok := (*op).(*Const); !ok { - if basic, ok := (*op).Type().(*types.Basic); ok { + if basic, ok := (*op).Type().Underlying().(*types.Basic); ok { if basic.Info()&types.IsUntyped != 0 { s.errorf("operand #%d of %s is untyped: %s", i, instr, basic) } diff --git a/go/ssa/subst.go b/go/ssa/subst.go index a9a6d41e813..9f2f2f30008 100644 --- a/go/ssa/subst.go +++ b/go/ssa/subst.go @@ -6,6 +6,8 @@ package ssa import ( "go/types" + + "golang.org/x/tools/internal/aliases" ) // Type substituter for a fixed set of replacement types. @@ -80,6 +82,9 @@ func (subst *subster) typ(t types.Type) (res types.Type) { // fall through if result r will be identical to t, types.Identical(r, t). switch t := t.(type) { + case *aliases.Alias: + return subst.typ(aliases.Unalias(t)) + case *types.TypeParam: r := subst.replacements[t] assert(r != nil, "type param without replacement encountered") @@ -466,7 +471,7 @@ func reaches(t types.Type, c map[types.Type]bool) (res bool) { return true } } - case *types.Named: + case *types.Named, *aliases.Alias: return reaches(t.Underlying(), c) default: panic("unreachable") diff --git a/go/ssa/util.go b/go/ssa/util.go index 915b4e274c1..4d65259ed9c 100644 --- a/go/ssa/util.go +++ b/go/ssa/util.go @@ -17,6 +17,7 @@ import ( "golang.org/x/tools/go/ast/astutil" "golang.org/x/tools/go/types/typeutil" + "golang.org/x/tools/internal/aliases" "golang.org/x/tools/internal/typeparams" "golang.org/x/tools/internal/typesinternal" ) @@ -51,16 +52,19 @@ func isNonTypeParamInterface(t types.Type) bool { // isBasic reports whether t is a basic type. func isBasic(t types.Type) bool { - _, ok := t.(*types.Basic) + _, ok := aliases.Unalias(t).(*types.Basic) return ok } // isString reports whether t is exactly a string type. +// t is assumed to be an Underlying type (not Named or Alias). func isString(t types.Type) bool { - return isBasic(t) && t.(*types.Basic).Info()&types.IsString != 0 + basic, ok := t.(*types.Basic) + return ok && basic.Info()&types.IsString != 0 } // isByteSlice reports whether t is of the form []~bytes. +// t is assumed to be an Underlying type (not Named or Alias). func isByteSlice(t types.Type) bool { if b, ok := t.(*types.Slice); ok { e, _ := b.Elem().Underlying().(*types.Basic) @@ -70,6 +74,7 @@ func isByteSlice(t types.Type) bool { } // isRuneSlice reports whether t is of the form []~runes. +// t is assumed to be an Underlying type (not Named or Alias). func isRuneSlice(t types.Type) bool { if b, ok := t.(*types.Slice); ok { e, _ := b.Elem().Underlying().(*types.Basic) @@ -131,8 +136,9 @@ func fieldOf(typ types.Type, index int) *types.Var { return nil } -// isUntyped returns true for types that are untyped. +// isUntyped reports whether typ is the type of an untyped constant. func isUntyped(typ types.Type) bool { + // No Underlying/Unalias: untyped constant types cannot be Named or Alias. b, ok := typ.(*types.Basic) return ok && b.Info()&types.IsUntyped != 0 } @@ -342,10 +348,10 @@ func (m *typeListMap) hash(ts []types.Type) uint32 { // instantiateMethod instantiates m with targs and returns a canonical representative for this method. func (canon *canonizer) instantiateMethod(m *types.Func, targs []types.Type, ctxt *types.Context) *types.Func { recv := recvType(m) - if p, ok := recv.(*types.Pointer); ok { + if p, ok := aliases.Unalias(recv).(*types.Pointer); ok { recv = p.Elem() } - named := recv.(*types.Named) + named := aliases.Unalias(recv).(*types.Named) inst, err := types.Instantiate(ctxt, named.Origin(), targs, false) if err != nil { panic(err) diff --git a/go/types/internal/play/play.go b/go/types/internal/play/play.go index 8692d51f0b7..382d8ab3645 100644 --- a/go/types/internal/play/play.go +++ b/go/types/internal/play/play.go @@ -32,6 +32,7 @@ import ( "golang.org/x/tools/go/ast/astutil" "golang.org/x/tools/go/packages" "golang.org/x/tools/go/types/typeutil" + "golang.org/x/tools/internal/aliases" "golang.org/x/tools/internal/typeparams" ) @@ -285,7 +286,7 @@ func formatObj(out *strings.Builder, fset *token.FileSet, ref string, obj types. if obj.IsAlias() { kind = "type alias" } - if named, ok := obj.Type().(*types.Named); ok { + if named, ok := aliases.Unalias(obj.Type()).(*types.Named); ok { origin = named.Obj() } } diff --git a/go/types/objectpath/objectpath.go b/go/types/objectpath/objectpath.go index 62d9585a691..6a57ce3b136 100644 --- a/go/types/objectpath/objectpath.go +++ b/go/types/objectpath/objectpath.go @@ -29,10 +29,13 @@ import ( "strconv" "strings" + "golang.org/x/tools/internal/aliases" "golang.org/x/tools/internal/typeparams" "golang.org/x/tools/internal/typesinternal" ) +// TODO(adonovan): think about generic aliases. + // A Path is an opaque name that identifies a types.Object // relative to its package. Conceptually, the name consists of a // sequence of destructuring operations applied to the package scope @@ -224,7 +227,7 @@ func (enc *Encoder) For(obj types.Object) (Path, error) { // Reject obviously non-viable cases. switch obj := obj.(type) { case *types.TypeName: - if _, ok := obj.Type().(*types.TypeParam); !ok { + if _, ok := aliases.Unalias(obj.Type()).(*types.TypeParam); !ok { // With the exception of type parameters, only package-level type names // have a path. return "", fmt.Errorf("no path for %v", obj) @@ -311,7 +314,7 @@ func (enc *Encoder) For(obj types.Object) (Path, error) { } // Inspect declared methods of defined types. - if T, ok := o.Type().(*types.Named); ok { + if T, ok := aliases.Unalias(o.Type()).(*types.Named); ok { path = append(path, opType) // The method index here is always with respect // to the underlying go/types data structures, @@ -440,6 +443,8 @@ func (enc *Encoder) concreteMethod(meth *types.Func) (Path, bool) { // nil, it will be allocated as necessary. func find(obj types.Object, T types.Type, path []byte, seen map[*types.TypeName]bool) []byte { switch T := T.(type) { + case *aliases.Alias: + return find(obj, aliases.Unalias(T), path, seen) case *types.Basic, *types.Named: // Named types belonging to pkg were handled already, // so T must belong to another package. No path. @@ -612,6 +617,7 @@ func Object(pkg *types.Package, p Path) (types.Object, error) { // Inv: t != nil, obj == nil + t = aliases.Unalias(t) switch code { case opElem: hasElem, ok := t.(hasElem) // Pointer, Slice, Array, Chan, Map diff --git a/go/types/typeutil/ui.go b/go/types/typeutil/ui.go index fa55b0a1e65..a0c1a60ac02 100644 --- a/go/types/typeutil/ui.go +++ b/go/types/typeutil/ui.go @@ -6,7 +6,11 @@ package typeutil // This file defines utilities for user interfaces that display types. -import "go/types" +import ( + "go/types" + + "golang.org/x/tools/internal/aliases" +) // IntuitiveMethodSet returns the intuitive method set of a type T, // which is the set of methods you can call on an addressable value of @@ -24,7 +28,7 @@ import "go/types" // The order of the result is as for types.MethodSet(T). func IntuitiveMethodSet(T types.Type, msets *MethodSetCache) []*types.Selection { isPointerToConcrete := func(T types.Type) bool { - ptr, ok := T.(*types.Pointer) + ptr, ok := aliases.Unalias(T).(*types.Pointer) return ok && !types.IsInterface(ptr.Elem()) } diff --git a/internal/analysisinternal/analysis.go b/internal/analysisinternal/analysis.go index b24a0fba9e7..c3022a28625 100644 --- a/internal/analysisinternal/analysis.go +++ b/internal/analysisinternal/analysis.go @@ -13,6 +13,8 @@ import ( "go/token" "go/types" "strconv" + + "golang.org/x/tools/internal/aliases" ) func TypeErrorEndPos(fset *token.FileSet, src []byte, start token.Pos) token.Pos { @@ -28,7 +30,10 @@ func TypeErrorEndPos(fset *token.FileSet, src []byte, start token.Pos) token.Pos } func ZeroValue(f *ast.File, pkg *types.Package, typ types.Type) ast.Expr { - under := typ + // TODO(adonovan): think about generics, and also generic aliases. + under := aliases.Unalias(typ) + // Don't call Underlying unconditionally: although it removed + // Named and Alias, it also removes TypeParam. if n, ok := typ.(*types.Named); ok { under = n.Underlying() } diff --git a/internal/facts/facts_test.go b/internal/facts/facts_test.go index 56eb599cfd9..daebea2ff59 100644 --- a/internal/facts/facts_test.go +++ b/internal/facts/facts_test.go @@ -18,6 +18,7 @@ import ( "golang.org/x/tools/go/analysis/analysistest" "golang.org/x/tools/go/packages" + "golang.org/x/tools/internal/aliases" "golang.org/x/tools/internal/facts" "golang.org/x/tools/internal/testenv" ) @@ -360,7 +361,7 @@ func find(p *types.Package, expr string) types.Object { if err != nil { return nil } - if n, ok := tv.Type.(*types.Named); ok { + if n, ok := aliases.Unalias(tv.Type).(*types.Named); ok { return n.Obj() } return nil diff --git a/internal/facts/imports.go b/internal/facts/imports.go index 1fe63ca6b51..9f706cd954f 100644 --- a/internal/facts/imports.go +++ b/internal/facts/imports.go @@ -6,6 +6,8 @@ package facts import ( "go/types" + + "golang.org/x/tools/internal/aliases" ) // importMap computes the import map for a package by traversing the @@ -45,6 +47,8 @@ func importMap(imports []*types.Package) map[string]*types.Package { addType = func(T types.Type) { switch T := T.(type) { + case *aliases.Alias: + addType(aliases.Unalias(T)) case *types.Basic: // nop case *types.Named: diff --git a/internal/gcimporter/bexport_test.go b/internal/gcimporter/bexport_test.go index 72fa8a2a31e..1a2c8e8dd0a 100644 --- a/internal/gcimporter/bexport_test.go +++ b/internal/gcimporter/bexport_test.go @@ -18,6 +18,7 @@ import ( "strings" "testing" + "golang.org/x/tools/internal/aliases" "golang.org/x/tools/internal/gcimporter" ) @@ -30,6 +31,8 @@ func fileLine(fset *token.FileSet, obj types.Object) string { } func equalType(x, y types.Type) error { + x = aliases.Unalias(x) + y = aliases.Unalias(y) if reflect.TypeOf(x) != reflect.TypeOf(y) { return fmt.Errorf("unequal kinds: %T vs %T", x, y) } diff --git a/internal/gcimporter/gcimporter_test.go b/internal/gcimporter/gcimporter_test.go index 84f99400bcf..81d36bd5ff6 100644 --- a/internal/gcimporter/gcimporter_test.go +++ b/internal/gcimporter/gcimporter_test.go @@ -28,6 +28,7 @@ import ( "testing" "time" + "golang.org/x/tools/internal/aliases" "golang.org/x/tools/internal/goroot" "golang.org/x/tools/internal/testenv" ) @@ -438,7 +439,7 @@ func TestImportedTypes(t *testing.T) { t.Errorf("%s: got %q; want %q", test.name, got, test.want) } - if named, _ := obj.Type().(*types.Named); named != nil { + if named, _ := aliases.Unalias(obj.Type()).(*types.Named); named != nil { verifyInterfaceMethodRecvs(t, named, 0) } } @@ -521,7 +522,7 @@ func verifyInterfaceMethodRecvs(t *testing.T, named *types.Named, level int) { // check embedded interfaces (if they are named, too) for i := 0; i < iface.NumEmbeddeds(); i++ { // embedding of interfaces cannot have cycles; recursion will terminate - if etype, _ := iface.EmbeddedType(i).(*types.Named); etype != nil { + if etype, _ := aliases.Unalias(iface.EmbeddedType(i)).(*types.Named); etype != nil { verifyInterfaceMethodRecvs(t, etype, level+1) } } @@ -541,7 +542,7 @@ func TestIssue5815(t *testing.T) { t.Errorf("no pkg for %s", obj) } if tname, _ := obj.(*types.TypeName); tname != nil { - named := tname.Type().(*types.Named) + named := aliases.Unalias(tname.Type()).(*types.Named) for i := 0; i < named.NumMethods(); i++ { m := named.Method(i) if m.Pkg() == nil { @@ -641,7 +642,7 @@ func TestIssue13898(t *testing.T) { // look for go/types.Object type obj := lookupObj(t, goTypesPkg.Scope(), "Object") - typ, ok := obj.Type().(*types.Named) + typ, ok := aliases.Unalias(obj.Type()).(*types.Named) if !ok { t.Fatalf("go/types.Object type is %v; wanted named type", typ) } diff --git a/internal/gcimporter/iexport.go b/internal/gcimporter/iexport.go index 2ee8c70164f..638fc1d3b86 100644 --- a/internal/gcimporter/iexport.go +++ b/internal/gcimporter/iexport.go @@ -23,6 +23,7 @@ import ( "strings" "golang.org/x/tools/go/types/objectpath" + "golang.org/x/tools/internal/aliases" "golang.org/x/tools/internal/tokeninternal" ) @@ -506,13 +507,13 @@ func (p *iexporter) doDecl(obj types.Object) { case *types.TypeName: t := obj.Type() - if tparam, ok := t.(*types.TypeParam); ok { + if tparam, ok := aliases.Unalias(t).(*types.TypeParam); ok { w.tag('P') w.pos(obj.Pos()) constraint := tparam.Constraint() if p.version >= iexportVersionGo1_18 { implicit := false - if iface, _ := constraint.(*types.Interface); iface != nil { + if iface, _ := aliases.Unalias(constraint).(*types.Interface); iface != nil { implicit = iface.IsImplicit() } w.bool(implicit) @@ -738,6 +739,8 @@ func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) { }() } switch t := t.(type) { + // TODO(adonovan): support types.Alias. + case *types.Named: if targs := t.TypeArgs(); targs.Len() > 0 { w.startType(instanceType) @@ -843,7 +846,7 @@ func (w *exportWriter) doTyp(t types.Type, pkg *types.Package) { for i := 0; i < n; i++ { ft := t.EmbeddedType(i) tPkg := pkg - if named, _ := ft.(*types.Named); named != nil { + if named, _ := aliases.Unalias(ft).(*types.Named); named != nil { w.pos(named.Obj().Pos()) } else { w.pos(token.NoPos) diff --git a/internal/gcimporter/iimport.go b/internal/gcimporter/iimport.go index 778caf6078e..4d50eb8e587 100644 --- a/internal/gcimporter/iimport.go +++ b/internal/gcimporter/iimport.go @@ -22,6 +22,7 @@ import ( "strings" "golang.org/x/tools/go/types/objectpath" + "golang.org/x/tools/internal/aliases" "golang.org/x/tools/internal/typesinternal" ) @@ -523,7 +524,7 @@ func canReuse(def *types.Named, rhs types.Type) bool { if def == nil { return true } - iface, _ := rhs.(*types.Interface) + iface, _ := aliases.Unalias(rhs).(*types.Interface) if iface == nil { return true } @@ -594,7 +595,7 @@ func (r *importReader) obj(name string) { if targs.Len() > 0 { rparams = make([]*types.TypeParam, targs.Len()) for i := range rparams { - rparams[i] = targs.At(i).(*types.TypeParam) + rparams[i] = aliases.Unalias(targs.At(i)).(*types.TypeParam) } } msig := r.signature(recv, rparams, nil) @@ -624,7 +625,7 @@ func (r *importReader) obj(name string) { } constraint := r.typ() if implicit { - iface, _ := constraint.(*types.Interface) + iface, _ := aliases.Unalias(constraint).(*types.Interface) if iface == nil { errorf("non-interface constraint marked implicit") } @@ -831,7 +832,7 @@ func (r *importReader) typ() types.Type { } func isInterface(t types.Type) bool { - _, ok := t.(*types.Interface) + _, ok := aliases.Unalias(t).(*types.Interface) return ok } @@ -1030,7 +1031,7 @@ func (r *importReader) tparamList() []*types.TypeParam { for i := range xs { // Note: the standard library importer is tolerant of nil types here, // though would panic in SetTypeParams. - xs[i] = r.typ().(*types.TypeParam) + xs[i] = aliases.Unalias(r.typ()).(*types.TypeParam) } return xs } diff --git a/internal/gcimporter/ureader_yes.go b/internal/gcimporter/ureader_yes.go index b977435f626..bcf52d931df 100644 --- a/internal/gcimporter/ureader_yes.go +++ b/internal/gcimporter/ureader_yes.go @@ -16,6 +16,7 @@ import ( "sort" "strings" + "golang.org/x/tools/internal/aliases" "golang.org/x/tools/internal/pkgbits" ) @@ -553,7 +554,7 @@ func (pr *pkgReader) objIdx(idx pkgbits.Index) (*types.Package, string) { // If the underlying type is an interface, we need to // duplicate its methods so we can replace the receiver // parameter's type (#49906). - if iface, ok := underlying.(*types.Interface); ok && iface.NumExplicitMethods() != 0 { + if iface, ok := aliases.Unalias(underlying).(*types.Interface); ok && iface.NumExplicitMethods() != 0 { methods := make([]*types.Func, iface.NumExplicitMethods()) for i := range methods { fn := iface.ExplicitMethod(i) diff --git a/internal/typeparams/common.go b/internal/typeparams/common.go index e5e3ed313cd..6679d79f9cf 100644 --- a/internal/typeparams/common.go +++ b/internal/typeparams/common.go @@ -28,6 +28,7 @@ import ( "go/token" "go/types" + "golang.org/x/tools/internal/aliases" "golang.org/x/tools/internal/typesinternal" ) @@ -74,9 +75,9 @@ func PackIndexExpr(x ast.Expr, lbrack token.Pos, indices []ast.Expr, rbrack toke } } -// IsTypeParam reports whether t is a type parameter. +// IsTypeParam reports whether t is a type parameter (or an alias of one). func IsTypeParam(t types.Type) bool { - _, ok := t.(*types.TypeParam) + _, ok := aliases.Unalias(t).(*types.TypeParam) return ok } @@ -155,6 +156,9 @@ func OriginMethod(fn *types.Func) *types.Func { // In this case, GenericAssignableTo reports that instantiations of Container // are assignable to the corresponding instantiation of Interface. func GenericAssignableTo(ctxt *types.Context, V, T types.Type) bool { + V = aliases.Unalias(V) + T = aliases.Unalias(T) + // If V and T are not both named, or do not have matching non-empty type // parameter lists, fall back on types.AssignableTo. diff --git a/internal/typeparams/coretype.go b/internal/typeparams/coretype.go index 048e2161493..e66e9d0f48c 100644 --- a/internal/typeparams/coretype.go +++ b/internal/typeparams/coretype.go @@ -7,6 +7,8 @@ package typeparams import ( "fmt" "go/types" + + "golang.org/x/tools/internal/aliases" ) // CoreType returns the core type of T or nil if T does not have a core type. @@ -110,7 +112,7 @@ func CoreType(T types.Type) types.Type { // _NormalTerms makes no guarantees about the order of terms, except that it // is deterministic. func _NormalTerms(typ types.Type) ([]*types.Term, error) { - switch typ := typ.(type) { + switch typ := aliases.Unalias(typ).(type) { case *types.TypeParam: return StructuralTerms(typ) case *types.Union: diff --git a/internal/typeparams/genericfeatures/features.go b/internal/typeparams/genericfeatures/features.go index e307e677758..e7d0e0e6112 100644 --- a/internal/typeparams/genericfeatures/features.go +++ b/internal/typeparams/genericfeatures/features.go @@ -12,6 +12,7 @@ import ( "strings" "golang.org/x/tools/go/ast/inspector" + "golang.org/x/tools/internal/aliases" ) // Features is a set of flags reporting which features of generic Go code a @@ -92,7 +93,7 @@ func ForPackage(inspect *inspector.Inspector, info *types.Info) Features { }) for _, inst := range info.Instances { - switch inst.Type.(type) { + switch aliases.Unalias(inst.Type).(type) { case *types.Named: direct |= TypeInstantiation case *types.Signature: diff --git a/refactor/rename/check.go b/refactor/rename/check.go index 9f29b98a0a4..ac2c5d4206f 100644 --- a/refactor/rename/check.go +++ b/refactor/rename/check.go @@ -13,6 +13,7 @@ import ( "go/types" "golang.org/x/tools/go/loader" + "golang.org/x/tools/internal/aliases" "golang.org/x/tools/refactor/satisfy" ) @@ -467,9 +468,10 @@ func (r *renamer) checkStructField(from *types.Var) { // type T int // this and // var s struct {T} // this must change too. if from.Anonymous() { - if named, ok := from.Type().(*types.Named); ok { + // TODO(adonovan): think carefully about aliases. + if named, ok := aliases.Unalias(from.Type()).(*types.Named); ok { r.check(named.Obj()) - } else if named, ok := deref(from.Type()).(*types.Named); ok { + } else if named, ok := aliases.Unalias(deref(from.Type())).(*types.Named); ok { r.check(named.Obj()) } } @@ -777,7 +779,7 @@ func (r *renamer) checkMethod(from *types.Func) { var iface string I := recv(imeth).Type() - if named, ok := I.(*types.Named); ok { + if named, ok := aliases.Unalias(I).(*types.Named); ok { pos = named.Obj().Pos() iface = "interface " + named.Obj().Name() } else { @@ -851,7 +853,7 @@ func someUse(info *loader.PackageInfo, obj types.Object) *ast.Ident { func isInterface(T types.Type) bool { return types.IsInterface(T) } func deref(typ types.Type) types.Type { - if p, _ := typ.(*types.Pointer); p != nil { + if p, _ := aliases.Unalias(typ).(*types.Pointer); p != nil { return p.Elem() } return typ diff --git a/refactor/rename/spec.go b/refactor/rename/spec.go index 22a268a7942..69198cb0d96 100644 --- a/refactor/rename/spec.go +++ b/refactor/rename/spec.go @@ -25,6 +25,7 @@ import ( "golang.org/x/tools/go/buildutil" "golang.org/x/tools/go/loader" + "golang.org/x/tools/internal/aliases" ) // A spec specifies an entity to rename. @@ -465,9 +466,9 @@ func findObjects(info *loader.PackageInfo, spec *spec) ([]types.Object, error) { if spec.searchFor == "" { // If it is an embedded field, return the type of the field. if v, ok := obj.(*types.Var); ok && v.Anonymous() { - switch t := v.Type().(type) { + switch t := aliases.Unalias(v.Type()).(type) { case *types.Pointer: - return []types.Object{t.Elem().(*types.Named).Obj()}, nil + return []types.Object{aliases.Unalias(t.Elem()).(*types.Named).Obj()}, nil case *types.Named: return []types.Object{t.Obj()}, nil }