Skip to content

Commit d4bb72b

Browse files
committed
cmd/internal/gc: improve "type *X has no field or method M" message
Try to provide hints for common areas, either *interface were interface would have been better, and note incorrect capitalization (but don't be more ambitious than that, at least not today). Added code and test for cases ptrInterface.ExistingMethod ptrInterface.unexportedMethod ptrInterface.MissingMethod ptrInterface.withwRongcASEdMethod interface.withwRongcASEdMethod ptrStruct.withwRongcASEdMethod struct.withwRongcASEdMethod also included tests for related errors to check for unintentional changes and consistent wording. Somewhat simplified from previous versions to avoid second- guessing user errors, yet also biased to point out most-likely root cause. Fixes #10700 Change-Id: I16693e93cc8d8ca195e7742a222d640c262105b4 Reviewed-on: https://go-review.googlesource.com/9731 Reviewed-by: Russ Cox <[email protected]>
1 parent fa89673 commit d4bb72b

File tree

5 files changed

+115
-18
lines changed

5 files changed

+115
-18
lines changed

src/cmd/internal/gc/typecheck.go

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -915,11 +915,26 @@ OpSwitch:
915915
return
916916
}
917917

918-
if !lookdot(n, t, 0) {
919-
if lookdot(n, t, 1) {
918+
if lookdot(n, t, 0) == nil {
919+
// Legitimate field or method lookup failed, try to explain the error
920+
switch {
921+
case isnilinter(t):
922+
Yyerror("%v undefined (type %v is interface with no methods)", n, n.Left.Type)
923+
924+
case Isptr[t.Etype] && Isinter(t.Type):
925+
// Pointer to interface is almost always a mistake.
926+
Yyerror("%v undefined (type %v is pointer to interface, not interface)", n, n.Left.Type)
927+
928+
case lookdot(n, t, 1) != nil:
929+
// Field or method matches by name, but it is not exported.
920930
Yyerror("%v undefined (cannot refer to unexported field or method %v)", n, n.Right.Sym)
921-
} else {
922-
Yyerror("%v undefined (type %v has no field or method %v)", n, n.Left.Type, n.Right.Sym)
931+
932+
default:
933+
if mt := lookdot(n, t, 2); mt != nil { // Case-insensitive lookup.
934+
Yyerror("%v undefined (type %v has no field or method %v, but does have %v)", n, n.Left.Type, n.Right.Sym, mt.Sym)
935+
} else {
936+
Yyerror("%v undefined (type %v has no field or method %v)", n, n.Left.Type, n.Right.Sym)
937+
}
923938
}
924939
n.Type = nil
925940
return
@@ -2391,6 +2406,9 @@ func lookdot1(errnode *Node, s *Sym, t *Type, f *Type, dostrcmp int) *Type {
23912406
if dostrcmp != 0 && f.Sym.Name == s.Name {
23922407
return f
23932408
}
2409+
if dostrcmp == 2 && strings.EqualFold(f.Sym.Name, s.Name) {
2410+
return f
2411+
}
23942412
if f.Sym != s {
23952413
continue
23962414
}
@@ -2461,7 +2479,7 @@ func derefall(t *Type) *Type {
24612479
return t
24622480
}
24632481

2464-
func lookdot(n *Node, t *Type, dostrcmp int) bool {
2482+
func lookdot(n *Node, t *Type, dostrcmp int) *Type {
24652483
s := n.Right.Sym
24662484

24672485
dowidth(t)
@@ -2481,6 +2499,10 @@ func lookdot(n *Node, t *Type, dostrcmp int) bool {
24812499
}
24822500

24832501
if f1 != nil {
2502+
if dostrcmp > 1 {
2503+
// Already in the process of diagnosing an error.
2504+
return f1
2505+
}
24842506
if f2 != nil {
24852507
Yyerror("%v is both field and method", n.Right.Sym)
24862508
}
@@ -2500,10 +2522,14 @@ func lookdot(n *Node, t *Type, dostrcmp int) bool {
25002522
n.Op = ODOTINTER
25012523
}
25022524

2503-
return true
2525+
return f1
25042526
}
25052527

25062528
if f2 != nil {
2529+
if dostrcmp > 1 {
2530+
// Already in the process of diagnosing an error.
2531+
return f2
2532+
}
25072533
tt := n.Left.Type
25082534
dowidth(tt)
25092535
rcvr := getthisx(f2.Type).Type.Type
@@ -2543,7 +2569,7 @@ func lookdot(n *Node, t *Type, dostrcmp int) bool {
25432569
// It is invalid to automatically dereference a named pointer type when selecting a method.
25442570
// Make n->left == ll to clarify error message.
25452571
n.Left = ll
2546-
return false
2572+
return nil
25472573
}
25482574
}
25492575

@@ -2554,10 +2580,10 @@ func lookdot(n *Node, t *Type, dostrcmp int) bool {
25542580
// print("lookdot found [%p] %T\n", f2->type, f2->type);
25552581
n.Op = ODOTMETH
25562582

2557-
return true
2583+
return f2
25582584
}
25592585

2560-
return false
2586+
return nil
25612587
}
25622588

25632589
func nokeys(l *NodeList) bool {
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Copyright 2015 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package other
6+
7+
type Exported interface {
8+
Do()
9+
secret()
10+
}

test/fixedbugs/issue10700.dir/test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// errorcheck -0 -m -l
2+
3+
// Copyright 2015 The Go Authors. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
package main
8+
9+
import "./other"
10+
11+
type Imported interface {
12+
Do()
13+
}
14+
15+
type HasAMethod struct {
16+
x int
17+
}
18+
19+
func (me *HasAMethod) Do() {
20+
println(me.x)
21+
}
22+
23+
func InMyCode(x *Imported, y *HasAMethod, z *other.Exported) {
24+
x.Do() // ERROR "x\.Do undefined \(type \*Imported is pointer to interface, not interface\)"
25+
x.do() // ERROR "x\.do undefined \(type \*Imported is pointer to interface, not interface\)"
26+
(*x).Do()
27+
x.Dont() // ERROR "x\.Dont undefined \(type \*Imported is pointer to interface, not interface\)"
28+
(*x).Dont() // ERROR "\(\*x\)\.Dont undefined \(type Imported has no field or method Dont\)"
29+
30+
y.Do()
31+
y.do() // ERROR "y\.do undefined \(type \*HasAMethod has no field or method do, but does have Do\)"
32+
(*y).Do()
33+
(*y).do() // ERROR "\(\*y\)\.do undefined \(type HasAMethod has no field or method do, but does have Do\)"
34+
y.Dont() // ERROR "y\.Dont undefined \(type \*HasAMethod has no field or method Dont\)"
35+
(*y).Dont() // ERROR "\(\*y\)\.Dont undefined \(type HasAMethod has no field or method Dont\)"
36+
37+
z.Do() // ERROR "z\.Do undefined \(type \*other\.Exported is pointer to interface, not interface\)"
38+
z.do() // ERROR "z\.do undefined \(type \*other\.Exported is pointer to interface, not interface\)"
39+
(*z).Do()
40+
(*z).do() // ERROR "\(\*z\)\.do undefined \(type other.Exported has no field or method do, but does have Do\)"
41+
z.Dont() // ERROR "z\.Dont undefined \(type \*other\.Exported is pointer to interface, not interface\)"
42+
(*z).Dont() // ERROR "\(\*z\)\.Dont undefined \(type other\.Exported has no field or method Dont\)"
43+
z.secret() // ERROR "z\.secret undefined \(type \*other\.Exported is pointer to interface, not interface\)"
44+
(*z).secret() // ERROR "\(\*z\)\.secret undefined \(cannot refer to unexported field or method secret\)"
45+
46+
}
47+
48+
func main() {
49+
}

test/fixedbugs/issue10700.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// errorcheckdir
2+
3+
// Copyright 2015 The Go Authors. All rights reserved.
4+
// Use of this source code is governed by a BSD-style
5+
// license that can be found in the LICENSE file.
6+
7+
package ignored

test/interface/embed2.go

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,25 @@ import "os"
1212

1313
const Value = 1e12
1414

15-
type Inter interface { M() int64 }
15+
type Inter interface {
16+
M() int64
17+
}
1618

1719
type T int64
20+
1821
func (t T) M() int64 { return int64(t) }
22+
1923
var t = T(Value)
2024
var pt = &t
2125
var ti Inter = t
2226
var pti = &ti
2327

24-
type S struct { Inter }
25-
var s = S{ ti }
28+
type S struct{ Inter }
29+
30+
var s = S{ti}
2631
var ps = &s
2732

28-
type SP struct { *Inter } // ERROR "interface"
33+
type SP struct{ *Inter } // ERROR "interface"
2934

3035
var i Inter
3136
var pi = &i
@@ -43,25 +48,25 @@ func main() {
4348
check("t.M()", t.M())
4449
check("pt.M()", pt.M())
4550
check("ti.M()", ti.M())
46-
check("pti.M()", pti.M()) // ERROR "method"
51+
check("pti.M()", pti.M()) // ERROR "pointer to interface, not interface"
4752
check("s.M()", s.M())
4853
check("ps.M()", ps.M())
4954

5055
i = t
5156
check("i = t; i.M()", i.M())
52-
check("i = t; pi.M()", pi.M()) // ERROR "method"
57+
check("i = t; pi.M()", pi.M()) // ERROR "pointer to interface, not interface"
5358

5459
i = pt
5560
check("i = pt; i.M()", i.M())
56-
check("i = pt; pi.M()", pi.M()) // ERROR "method"
61+
check("i = pt; pi.M()", pi.M()) // ERROR "pointer to interface, not interface"
5762

5863
i = s
5964
check("i = s; i.M()", i.M())
60-
check("i = s; pi.M()", pi.M()) // ERROR "method"
65+
check("i = s; pi.M()", pi.M()) // ERROR "pointer to interface, not interface"
6166

6267
i = ps
6368
check("i = ps; i.M()", i.M())
64-
check("i = ps; pi.M()", pi.M()) // ERROR "method"
69+
check("i = ps; pi.M()", pi.M()) // ERROR "pointer to interface, not interface"
6570

6671
if !ok {
6772
println("BUG: interface10")

0 commit comments

Comments
 (0)