Skip to content

Commit 38b0e9b

Browse files
adonovangopherbot
authored andcommitted
x/tools: add explicit Unalias operations
This change is the result of an audit of all type assertions and type switches whose operand is a types.Type. (These were enumerated with an analyzer tool.) If the operand is already the result of a call to Underlying or Unalias, there is nothing to do, but in other cases, explicit Unalias operations were added in order to preserve the existing behavior when go/types starts creating explicit Alias types. This change does not address any desired behavior changes required for the ideal handling of aliases; they will wait for a followup. In a number of places I have added comments matching "TODO.*alias". It may be prudent to split this change by top-level directory, both for ease of review, and of later bisection if needed. During the audit, there appeared to be a recurring need for the following operators: - (*types.Func).Signature (golang/go#65772); - Deref(Type): it's easy to forget to strip off the Alias constructor; - ReceiverName (CL 565075), for destructuring receiver types such as T and *T, in which up to two Aliases might be present. Updates golang/go#65294 Change-Id: I5180b9bae1c9191807026b8e0dc6f15ed4953b9a Reviewed-on: https://go-review.googlesource.com/c/tools/+/565035 LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Robert Findley <[email protected]> Auto-Submit: Alan Donovan <[email protected]>
1 parent a6c03c8 commit 38b0e9b

File tree

53 files changed

+221
-121
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+221
-121
lines changed

cmd/godex/print.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import (
1212
"go/types"
1313
"io"
1414
"math/big"
15+
16+
"golang.org/x/tools/internal/aliases"
1517
)
1618

1719
// TODO(gri) use tabwriter for alignment?
@@ -56,7 +58,7 @@ func (p *printer) printf(format string, args ...interface{}) {
5658
// denoted by obj is not an interface and has methods. Otherwise it returns
5759
// the zero value.
5860
func methodsFor(obj *types.TypeName) (*types.Named, []*types.Selection) {
59-
named, _ := obj.Type().(*types.Named)
61+
named, _ := aliases.Unalias(obj.Type()).(*types.Named)
6062
if named == nil {
6163
// A type name's type can also be the
6264
// exported basic type unsafe.Pointer.

cmd/godex/writetype.go

+9-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@
1212

1313
package main
1414

15-
import "go/types"
15+
import (
16+
"go/types"
17+
18+
"golang.org/x/tools/internal/aliases"
19+
)
1620

1721
func (p *printer) writeType(this *types.Package, typ types.Type) {
1822
p.writeTypeInternal(this, typ, make([]types.Type, 8))
@@ -173,6 +177,10 @@ func (p *printer) writeTypeInternal(this *types.Package, typ types.Type, visited
173177
p.print(")")
174178
}
175179

180+
case *aliases.Alias:
181+
// TODO(adonovan): display something aliasy.
182+
p.writeTypeInternal(this, aliases.Unalias(t), visited)
183+
176184
case *types.Named:
177185
s := "<Named w/o object>"
178186
if obj := t.Obj(); obj != nil {

cmd/guru/describe.go

+8-7
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"golang.org/x/tools/go/ast/astutil"
2020
"golang.org/x/tools/go/loader"
2121
"golang.org/x/tools/go/types/typeutil"
22+
"golang.org/x/tools/internal/aliases"
2223
"golang.org/x/tools/internal/typesinternal"
2324
)
2425

@@ -358,7 +359,7 @@ func appendNames(names []*types.Named, typ types.Type) []*types.Named {
358359
Elem() types.Type
359360
}
360361

361-
switch t := typ.(type) {
362+
switch t := aliases.Unalias(typ).(type) {
362363
case *types.Named:
363364
names = append(names, t)
364365
case *types.Map:
@@ -469,7 +470,7 @@ func describeType(qpos *queryPos, path []ast.Node) (*describeTypeResult, error)
469470
description = "alias of "
470471
} else if obj.Pos() == n.Pos() {
471472
description = "definition of " // (Named type)
472-
} else if _, ok := typ.(*types.Basic); ok {
473+
} else if _, ok := aliases.Unalias(typ).(*types.Basic); ok {
473474
description = "reference to built-in "
474475
} else {
475476
description = "reference to " // (Named type)
@@ -486,7 +487,7 @@ func describeType(qpos *queryPos, path []ast.Node) (*describeTypeResult, error)
486487
description = description + "type " + qpos.typeString(typ)
487488

488489
// Show sizes for structs and named types (it's fairly obvious for others).
489-
switch typ.(type) {
490+
switch aliases.Unalias(typ).(type) {
490491
case *types.Named, *types.Struct:
491492
szs := types.StdSizes{WordSize: 8, MaxAlign: 8} // assume amd64
492493
description = fmt.Sprintf("%s (size %d, align %d)", description,
@@ -576,7 +577,7 @@ func (r *describeTypeResult) PrintPlain(printf printfFunc) {
576577
printf(r.node, "%s", r.description)
577578

578579
// Show the underlying type for a reference to a named type.
579-
if nt, ok := r.typ.(*types.Named); ok && r.node.Pos() != nt.Obj().Pos() {
580+
if nt, ok := aliases.Unalias(r.typ).(*types.Named); ok && r.node.Pos() != nt.Obj().Pos() {
580581
// TODO(adonovan): improve display of complex struct/interface types.
581582
printf(nt.Obj(), "defined as %s", r.qpos.typeString(nt.Underlying()))
582583
}
@@ -585,7 +586,7 @@ func (r *describeTypeResult) PrintPlain(printf printfFunc) {
585586
if len(r.methods) == 0 {
586587
// Only report null result for type kinds
587588
// capable of bearing methods.
588-
switch r.typ.(type) {
589+
switch aliases.Unalias(r.typ).(type) {
589590
case *types.Interface, *types.Struct, *types.Named:
590591
printf(r.node, "No methods.")
591592
}
@@ -596,7 +597,7 @@ func (r *describeTypeResult) PrintPlain(printf printfFunc) {
596597

597598
func (r *describeTypeResult) JSON(fset *token.FileSet) []byte {
598599
var namePos, nameDef string
599-
if nt, ok := r.typ.(*types.Named); ok {
600+
if nt, ok := aliases.Unalias(r.typ).(*types.Named); ok {
600601
namePos = fset.Position(nt.Obj().Pos()).String()
601602
nameDef = nt.Underlying().String()
602603
}
@@ -727,7 +728,7 @@ func formatMember(obj types.Object, maxname int) string {
727728
}
728729
var typestr string
729730
// Abbreviate long aggregate type names.
730-
switch typ := typ.(type) {
731+
switch typ := aliases.Unalias(typ).(type) {
731732
case *types.Interface:
732733
if typ.NumMethods() > 1 {
733734
typestr = "interface{...}"

cmd/guru/implements.go

+8-7
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"golang.org/x/tools/cmd/guru/serial"
1717
"golang.org/x/tools/go/loader"
1818
"golang.org/x/tools/go/types/typeutil"
19+
"golang.org/x/tools/internal/aliases"
1920
"golang.org/x/tools/refactor/importgraph"
2021
)
2122

@@ -157,7 +158,7 @@ func implements(q *Query) error {
157158
}
158159

159160
var pos interface{} = qpos
160-
if nt, ok := deref(T).(*types.Named); ok {
161+
if nt, ok := aliases.Unalias(deref(T)).(*types.Named); ok {
161162
pos = nt.Obj()
162163
}
163164

@@ -230,7 +231,7 @@ func (r *implementsResult) PrintPlain(printf printfFunc) {
230231
for i, sub := range r.to {
231232
if !isInterface(sub) {
232233
if r.method == nil {
233-
printf(deref(sub).(*types.Named).Obj(), "\t%s %s type %s",
234+
printf(aliases.Unalias(deref(sub)).(*types.Named).Obj(), "\t%s %s type %s",
234235
relation, typeKind(sub), r.qpos.typeString(sub))
235236
} else {
236237
meth(r.toMethod[i])
@@ -240,7 +241,7 @@ func (r *implementsResult) PrintPlain(printf printfFunc) {
240241
for i, sub := range r.to {
241242
if isInterface(sub) {
242243
if r.method == nil {
243-
printf(sub.(*types.Named).Obj(), "\t%s %s type %s",
244+
printf(aliases.Unalias(sub).(*types.Named).Obj(), "\t%s %s type %s",
244245
relation, typeKind(sub), r.qpos.typeString(sub))
245246
} else {
246247
meth(r.toMethod[i])
@@ -251,7 +252,7 @@ func (r *implementsResult) PrintPlain(printf printfFunc) {
251252
relation = "implements"
252253
for i, super := range r.from {
253254
if r.method == nil {
254-
printf(super.(*types.Named).Obj(), "\t%s %s",
255+
printf(aliases.Unalias(super).(*types.Named).Obj(), "\t%s %s",
255256
relation, r.qpos.typeString(super))
256257
} else {
257258
meth(r.fromMethod[i])
@@ -270,7 +271,7 @@ func (r *implementsResult) PrintPlain(printf printfFunc) {
270271
}
271272
for i, super := range r.from {
272273
if r.method == nil {
273-
printf(super.(*types.Named).Obj(), "\t%s %s",
274+
printf(aliases.Unalias(super).(*types.Named).Obj(), "\t%s %s",
274275
relation, r.qpos.typeString(super))
275276
} else {
276277
meth(r.fromMethod[i])
@@ -288,7 +289,7 @@ func (r *implementsResult) PrintPlain(printf printfFunc) {
288289

289290
for i, psuper := range r.fromPtr {
290291
if r.method == nil {
291-
printf(psuper.(*types.Named).Obj(), "\t%s %s",
292+
printf(aliases.Unalias(psuper).(*types.Named).Obj(), "\t%s %s",
292293
relation, r.qpos.typeString(psuper))
293294
} else {
294295
meth(r.fromPtrMethod[i])
@@ -332,7 +333,7 @@ func makeImplementsTypes(tt []types.Type, fset *token.FileSet) []serial.Implemen
332333

333334
func makeImplementsType(T types.Type, fset *token.FileSet) serial.ImplementsType {
334335
var pos token.Pos
335-
if nt, ok := deref(T).(*types.Named); ok { // implementsResult.t may be non-named
336+
if nt, ok := aliases.Unalias(deref(T)).(*types.Named); ok { // implementsResult.t may be non-named
336337
pos = nt.Obj().Pos()
337338
}
338339
return serial.ImplementsType{

go/analysis/passes/composite/composite.go

+11-7
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"golang.org/x/tools/go/analysis"
1616
"golang.org/x/tools/go/analysis/passes/inspect"
1717
"golang.org/x/tools/go/ast/inspector"
18+
"golang.org/x/tools/internal/aliases"
1819
"golang.org/x/tools/internal/typeparams"
1920
)
2021

@@ -71,7 +72,7 @@ func run(pass *analysis.Pass) (interface{}, error) {
7172
return
7273
}
7374
var structuralTypes []types.Type
74-
switch typ := typ.(type) {
75+
switch typ := aliases.Unalias(typ).(type) {
7576
case *types.TypeParam:
7677
terms, err := typeparams.StructuralTerms(typ)
7778
if err != nil {
@@ -84,7 +85,8 @@ func run(pass *analysis.Pass) (interface{}, error) {
8485
structuralTypes = append(structuralTypes, typ)
8586
}
8687
for _, typ := range structuralTypes {
87-
under := deref(typ.Underlying())
88+
// TODO(adonovan): this operation is questionable.
89+
under := aliases.Unalias(deref(typ.Underlying()))
8890
strct, ok := under.(*types.Struct)
8991
if !ok {
9092
// skip non-struct composite literals
@@ -142,9 +144,11 @@ func run(pass *analysis.Pass) (interface{}, error) {
142144
return nil, nil
143145
}
144146

147+
// Note: this is not the usual deref operator!
148+
// It strips off all Pointer constructors (and their Aliases).
145149
func deref(typ types.Type) types.Type {
146150
for {
147-
ptr, ok := typ.(*types.Pointer)
151+
ptr, ok := aliases.Unalias(typ).(*types.Pointer)
148152
if !ok {
149153
break
150154
}
@@ -153,18 +157,18 @@ func deref(typ types.Type) types.Type {
153157
return typ
154158
}
155159

160+
// isLocalType reports whether typ belongs to the same package as pass.
161+
// TODO(adonovan): local means "internal to a function"; rename to isSamePackageType.
156162
func isLocalType(pass *analysis.Pass, typ types.Type) bool {
157-
switch x := typ.(type) {
163+
switch x := aliases.Unalias(typ).(type) {
158164
case *types.Struct:
159165
// struct literals are local types
160166
return true
161167
case *types.Pointer:
162168
return isLocalType(pass, x.Elem())
163-
case *types.Named:
169+
case interface{ Obj() *types.TypeName }: // *Named or *TypeParam (aliases were removed already)
164170
// names in package foo are local to foo_test too
165171
return strings.TrimSuffix(x.Obj().Pkg().Path(), "_test") == strings.TrimSuffix(pass.Pkg.Path(), "_test")
166-
case *types.TypeParam:
167-
return strings.TrimSuffix(x.Obj().Pkg().Path(), "_test") == strings.TrimSuffix(pass.Pkg.Path(), "_test")
168172
}
169173
return false
170174
}

go/analysis/passes/copylock/copylock.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
1919
"golang.org/x/tools/go/ast/astutil"
2020
"golang.org/x/tools/go/ast/inspector"
21+
"golang.org/x/tools/internal/aliases"
2122
"golang.org/x/tools/internal/typeparams"
2223
)
2324

@@ -255,7 +256,7 @@ func lockPath(tpkg *types.Package, typ types.Type, seen map[types.Type]bool) typ
255256
}
256257
seen[typ] = true
257258

258-
if tpar, ok := typ.(*types.TypeParam); ok {
259+
if tpar, ok := aliases.Unalias(typ).(*types.TypeParam); ok {
259260
terms, err := typeparams.StructuralTerms(tpar)
260261
if err != nil {
261262
return nil // invalid type

go/analysis/passes/deepequalerrors/deepequalerrors.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -102,8 +102,7 @@ func containsError(typ types.Type) bool {
102102
return true
103103
}
104104
}
105-
case *types.Named,
106-
*aliases.Alias:
105+
case *types.Named, *aliases.Alias:
107106
return check(t.Underlying())
108107

109108
// We list the remaining valid type kinds for completeness.

go/analysis/passes/httpresponse/httpresponse.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"golang.org/x/tools/go/analysis/passes/inspect"
1515
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
1616
"golang.org/x/tools/go/ast/inspector"
17+
"golang.org/x/tools/internal/aliases"
1718
"golang.org/x/tools/internal/typesinternal"
1819
)
1920

@@ -136,7 +137,7 @@ func isHTTPFuncOrMethodOnClient(info *types.Info, expr *ast.CallExpr) bool {
136137
if analysisutil.IsNamedType(typ, "net/http", "Client") {
137138
return true // method on http.Client.
138139
}
139-
ptr, ok := typ.(*types.Pointer)
140+
ptr, ok := aliases.Unalias(typ).(*types.Pointer)
140141
return ok && analysisutil.IsNamedType(ptr.Elem(), "net/http", "Client") // method on *http.Client.
141142
}
142143

go/analysis/passes/ifaceassert/parameterized.go

+5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package ifaceassert
77
import (
88
"go/types"
99

10+
"golang.org/x/tools/internal/aliases"
1011
"golang.org/x/tools/internal/typeparams"
1112
)
1213

@@ -94,6 +95,10 @@ func (w *tpWalker) isParameterized(typ types.Type) (res bool) {
9495
case *types.Chan:
9596
return w.isParameterized(t.Elem())
9697

98+
case *aliases.Alias:
99+
// TODO(adonovan): think about generic aliases.
100+
return w.isParameterized(aliases.Unalias(t))
101+
97102
case *types.Named:
98103
list := t.TypeArgs()
99104
for i, n := 0, list.Len(); i < n; i++ {

go/analysis/passes/internal/analysisutil/util.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"go/types"
1515
"os"
1616

17+
"golang.org/x/tools/internal/aliases"
1718
"golang.org/x/tools/internal/analysisinternal"
1819
)
1920

@@ -115,7 +116,7 @@ func Imports(pkg *types.Package, path string) bool {
115116
// This function avoids allocating the concatenation of "pkg.Name",
116117
// which is important for the performance of syntax matching.
117118
func IsNamedType(t types.Type, pkgPath string, names ...string) bool {
118-
n, ok := t.(*types.Named)
119+
n, ok := aliases.Unalias(t).(*types.Named)
119120
if !ok {
120121
return false
121122
}

go/analysis/passes/printf/printf.go

+5-2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
2525
"golang.org/x/tools/go/ast/inspector"
2626
"golang.org/x/tools/go/types/typeutil"
27+
"golang.org/x/tools/internal/aliases"
2728
"golang.org/x/tools/internal/typeparams"
2829
)
2930

@@ -959,6 +960,8 @@ func isStringer(sig *types.Signature) bool {
959960
// It is almost always a mistake to print a function value.
960961
func isFunctionValue(pass *analysis.Pass, e ast.Expr) bool {
961962
if typ := pass.TypesInfo.Types[e].Type; typ != nil {
963+
// Don't call Underlying: a named func type with a String method is ok.
964+
// TODO(adonovan): it would be more precise to check isStringer.
962965
_, ok := typ.(*types.Signature)
963966
return ok
964967
}
@@ -1010,7 +1013,7 @@ func checkPrint(pass *analysis.Pass, call *ast.CallExpr, fn *types.Func) {
10101013
// Skip checking functions with unknown type.
10111014
return
10121015
}
1013-
if sig, ok := typ.(*types.Signature); ok {
1016+
if sig, ok := typ.Underlying().(*types.Signature); ok {
10141017
if !sig.Variadic() {
10151018
// Skip checking non-variadic functions.
10161019
return
@@ -1020,7 +1023,7 @@ func checkPrint(pass *analysis.Pass, call *ast.CallExpr, fn *types.Func) {
10201023

10211024
typ := params.At(firstArg).Type()
10221025
typ = typ.(*types.Slice).Elem()
1023-
it, ok := typ.(*types.Interface)
1026+
it, ok := aliases.Unalias(typ).(*types.Interface)
10241027
if !ok || !it.Empty() {
10251028
// Skip variadic functions accepting non-interface{} args.
10261029
return

go/analysis/passes/printf/types.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"go/types"
1111

1212
"golang.org/x/tools/go/analysis"
13+
"golang.org/x/tools/internal/aliases"
1314
"golang.org/x/tools/internal/typeparams"
1415
)
1516

@@ -72,7 +73,7 @@ func (m *argMatcher) match(typ types.Type, topLevel bool) bool {
7273
return true
7374
}
7475

75-
if typ, _ := typ.(*types.TypeParam); typ != nil {
76+
if typ, _ := aliases.Unalias(typ).(*types.TypeParam); typ != nil {
7677
// Avoid infinite recursion through type parameters.
7778
if m.seen[typ] {
7879
return true
@@ -275,7 +276,7 @@ func (m *argMatcher) match(typ types.Type, topLevel bool) bool {
275276
}
276277

277278
func isConvertibleToString(typ types.Type) bool {
278-
if bt, ok := typ.(*types.Basic); ok && bt.Kind() == types.UntypedNil {
279+
if bt, ok := aliases.Unalias(typ).(*types.Basic); ok && bt.Kind() == types.UntypedNil {
279280
// We explicitly don't want untyped nil, which is
280281
// convertible to both of the interfaces below, as it
281282
// would just panic anyway.

0 commit comments

Comments
 (0)