@@ -55,15 +55,15 @@ func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o
55
55
// not have found it for T (see also go.dev/issue/8590).
56
56
if t , _ := T .(* Named ); t != nil {
57
57
if p , _ := t .Underlying ().(* Pointer ); p != nil {
58
- obj , index , indirect = lookupFieldOrMethod (p , false , pkg , name , false )
58
+ obj , index , indirect = lookupFieldOrMethodImpl (p , false , pkg , name , false )
59
59
if _ , ok := obj .(* Func ); ok {
60
60
return nil , nil , false
61
61
}
62
62
return
63
63
}
64
64
}
65
65
66
- obj , index , indirect = lookupFieldOrMethod (T , addressable , pkg , name , false )
66
+ obj , index , indirect = lookupFieldOrMethodImpl (T , addressable , pkg , name , false )
67
67
68
68
// If we didn't find anything and if we have a type parameter with a core type,
69
69
// see if there is a matching field (but not a method, those need to be declared
@@ -72,7 +72,7 @@ func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o
72
72
const enableTParamFieldLookup = false // see go.dev/issue/51576
73
73
if enableTParamFieldLookup && obj == nil && isTypeParam (T ) {
74
74
if t := coreType (T ); t != nil {
75
- obj , index , indirect = lookupFieldOrMethod (t , addressable , pkg , name , false )
75
+ obj , index , indirect = lookupFieldOrMethodImpl (t , addressable , pkg , name , false )
76
76
if _ , ok := obj .(* Var ); ! ok {
77
77
obj , index , indirect = nil , nil , false // accept fields (variables) only
78
78
}
@@ -81,18 +81,33 @@ func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o
81
81
return
82
82
}
83
83
84
- // lookupFieldOrMethod should only be called by LookupFieldOrMethod and missingMethod.
85
- // If foldCase is true, the lookup for methods will include looking for any method
86
- // which case-folds to the same as 'name' (used for giving helpful error messages).
84
+ // lookupFieldOrMethodImpl is the implementation of LookupFieldOrMethod.
85
+ // Notably, in contrast to LookupFieldOrMethod, it won't find struct fields
86
+ // in base types of defined (*Named) pointer types T. For instance, given
87
+ // the declaration:
88
+ //
89
+ // type T *struct{f int}
90
+ //
91
+ // lookupFieldOrMethodImpl won't find the field f in the defined (*Named) type T
92
+ // (methods on T are not permitted in the first place).
93
+ //
94
+ // Thus, lookupFieldOrMethodImpl should only be called by LookupFieldOrMethod
95
+ // and missingMethod (the latter doesn't care about struct fields).
96
+ //
97
+ // If foldCase is true, method names are considered equal if they are equal
98
+ // with case folding.
87
99
//
88
100
// The resulting object may not be fully type-checked.
89
- func lookupFieldOrMethod (T Type , addressable bool , pkg * Package , name string , foldCase bool ) (obj Object , index []int , indirect bool ) {
101
+ func lookupFieldOrMethodImpl (T Type , addressable bool , pkg * Package , name string , foldCase bool ) (obj Object , index []int , indirect bool ) {
90
102
// WARNING: The code in this function is extremely subtle - do not modify casually!
91
103
92
104
if name == "_" {
93
105
return // blank fields/methods are never found
94
106
}
95
107
108
+ // Importantly, we must not call under before the call to deref below (nor
109
+ // does deref call under), as doing so could incorrectly result in finding
110
+ // methods of the pointer base type when T is a (*Named) pointer type.
96
111
typ , isPtr := deref (T )
97
112
98
113
// *typ where typ is an interface (incl. a type parameter) has no methods.
@@ -356,14 +371,13 @@ func (check *Checker) missingMethod(V, T Type, static bool, equivalent func(x, y
356
371
}
357
372
} else {
358
373
for _ , m = range methods {
359
- // TODO(gri) should this be calling LookupFieldOrMethod instead (and why not)?
360
- obj , _ , _ := lookupFieldOrMethod (V , false , m .pkg , m .name , false )
374
+ obj , _ , _ := lookupFieldOrMethodImpl (V , false , m .pkg , m .name , false )
361
375
362
376
// check if m is on *V, or on V with case-folding
363
377
if obj == nil {
364
378
state = notFound
365
379
// TODO(gri) Instead of NewPointer(V) below, can we just set the "addressable" argument?
366
- obj , _ , _ = lookupFieldOrMethod (NewPointer (V ), false , m .pkg , m .name , false )
380
+ obj , _ , _ = lookupFieldOrMethodImpl (NewPointer (V ), false , m .pkg , m .name , false )
367
381
if obj != nil {
368
382
f , _ = obj .(* Func )
369
383
if f != nil {
@@ -372,7 +386,7 @@ func (check *Checker) missingMethod(V, T Type, static bool, equivalent func(x, y
372
386
// otherwise we found a field, keep state == notFound
373
387
break
374
388
}
375
- obj , _ , _ = lookupFieldOrMethod (V , false , m .pkg , m .name , true /* fold case */ )
389
+ obj , _ , _ = lookupFieldOrMethodImpl (V , false , m .pkg , m .name , true /* fold case */ )
376
390
if obj != nil {
377
391
f , _ = obj .(* Func )
378
392
if f != nil {
@@ -504,7 +518,8 @@ func (check *Checker) newAssertableTo(V, T Type, cause *string) bool {
504
518
return check .implements (T , V , false , cause )
505
519
}
506
520
507
- // deref dereferences typ if it is a *Pointer and returns its base and true.
521
+ // deref dereferences typ if it is a *Pointer (but not a *Named type
522
+ // with an underlying pointer type!) and returns its base and true.
508
523
// Otherwise it returns (typ, false).
509
524
func deref (typ Type ) (Type , bool ) {
510
525
if p , _ := typ .(* Pointer ); p != nil {
0 commit comments