aboutsummaryrefslogtreecommitdiff
path: root/src/cmd
diff options
context:
space:
mode:
authorRobert Griesemer <gri@golang.org>2022-01-08 13:01:37 -0800
committerRobert Griesemer <gri@golang.org>2022-01-10 22:48:40 +0000
commit6019a52d4dab7c243ee9088c3522c821c0c95cfa (patch)
tree6214fff11c99576b245dc5a1efed0e83972b98d2 /src/cmd
parent1f411e9b6d8849014653c89a9df77b8aadd082e6 (diff)
downloadgo-6019a52d4dab7c243ee9088c3522c821c0c95cfa.tar.gz
go-6019a52d4dab7c243ee9088c3522c821c0c95cfa.zip
go/types, types2: better error message when using *interface instead of interface
- detect *interface case and report specific error - replaced switch with sequence of if's for more clarity - fixed isInterfacePtr: it applies to all interfaces, incl. type parameters - reviewed/fixed all uses of isInterfacePtr - adjusted error messages to be consistently of the format "type %s is pointer to interface, not interface" Fixes #48312. Change-Id: Ic3c8cfcf93ad57ecdb60f6a727cce9e1aa4afb5d Reviewed-on: https://go-review.googlesource.com/c/go/+/376914 Trust: Robert Griesemer <gri@golang.org> Reviewed-by: Robert Findley <rfindley@google.com>
Diffstat (limited to 'src/cmd')
-rw-r--r--src/cmd/compile/internal/types2/call.go64
-rw-r--r--src/cmd/compile/internal/types2/lookup.go10
-rw-r--r--src/cmd/compile/internal/types2/operand.go2
-rw-r--r--src/cmd/compile/internal/types2/testdata/check/methodsets.src12
-rw-r--r--src/cmd/compile/internal/types2/testdata/fixedbugs/issue47747.go24
-rw-r--r--src/cmd/compile/internal/types2/testdata/fixedbugs/issue48312.go220
6 files changed, 71 insertions, 41 deletions
diff --git a/src/cmd/compile/internal/types2/call.go b/src/cmd/compile/internal/types2/call.go
index d93805e9c7..bd62e825af 100644
--- a/src/cmd/compile/internal/types2/call.go
+++ b/src/cmd/compile/internal/types2/call.go
@@ -531,41 +531,51 @@ func (check *Checker) selector(x *operand, e *syntax.SelectorExpr) {
obj, index, indirect = LookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, sel)
if obj == nil {
- switch {
- case index != nil:
+ if index != nil {
// TODO(gri) should provide actual type where the conflict happens
check.errorf(e.Sel, "ambiguous selector %s.%s", x.expr, sel)
- case indirect:
+ goto Error
+ }
+
+ if indirect {
check.errorf(e.Sel, "cannot call pointer method %s on %s", sel, x.typ)
- default:
- var why string
- if tpar, _ := x.typ.(*TypeParam); tpar != nil {
- // Type parameter bounds don't specify fields, so don't mention "field".
- if tname := tpar.iface().obj; tname != nil {
- why = check.sprintf("interface %s has no method %s", tname.name, sel)
- } else {
- why = check.sprintf("type bound for %s has no method %s", x.typ, sel)
- }
+ goto Error
+ }
+
+ if isInterfacePtr(x.typ) {
+ check.errorf(e.Sel, "%s.%s undefined (type %s is pointer to interface, not interface)", x.expr, sel, x.typ)
+ goto Error
+ }
+
+ var why string
+ if tpar, _ := x.typ.(*TypeParam); tpar != nil {
+ // Type parameter bounds don't specify fields, so don't mention "field".
+ // TODO(gri) Type constraints may have accessible fields now. Revisit this.
+ if tname := tpar.iface().obj; tname != nil {
+ why = check.sprintf("interface %s has no method %s", tname.name, sel)
} else {
- why = check.sprintf("type %s has no field or method %s", x.typ, sel)
+ why = check.sprintf("type bound for %s has no method %s", x.typ, sel)
}
+ } else {
+ why = check.sprintf("type %s has no field or method %s", x.typ, sel)
+ }
- // Check if capitalization of sel matters and provide better error message in that case.
- if len(sel) > 0 {
- var changeCase string
- if r := rune(sel[0]); unicode.IsUpper(r) {
- changeCase = string(unicode.ToLower(r)) + sel[1:]
- } else {
- changeCase = string(unicode.ToUpper(r)) + sel[1:]
- }
- if obj, _, _ = LookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, changeCase); obj != nil {
- why += ", but does have " + changeCase
- }
+ // Check if capitalization of sel matters and provide better error message in that case.
+ // TODO(gri) This code only looks at the first character but LookupFieldOrMethod has an
+ // (internal) mechanism for case-insensitive lookup. Should use that instead.
+ if len(sel) > 0 {
+ var changeCase string
+ if r := rune(sel[0]); unicode.IsUpper(r) {
+ changeCase = string(unicode.ToLower(r)) + sel[1:]
+ } else {
+ changeCase = string(unicode.ToUpper(r)) + sel[1:]
+ }
+ if obj, _, _ = LookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, changeCase); obj != nil {
+ why += ", but does have " + changeCase
}
-
- check.errorf(e.Sel, "%s.%s undefined (%s)", x.expr, sel, why)
-
}
+
+ check.errorf(e.Sel, "%s.%s undefined (%s)", x.expr, sel, why)
goto Error
}
diff --git a/src/cmd/compile/internal/types2/lookup.go b/src/cmd/compile/internal/types2/lookup.go
index 0cce3fdc3f..aa1ab8ac98 100644
--- a/src/cmd/compile/internal/types2/lookup.go
+++ b/src/cmd/compile/internal/types2/lookup.go
@@ -448,12 +448,12 @@ func (check *Checker) missingMethodReason(V, T Type, m, wrongType *Func) string
// an extra formatting option for types2.Type that doesn't print out
// 'func'.
r = strings.Replace(r, "^^func", "", -1)
- } else if IsInterface(T) && !isTypeParam(T) {
+ } else if IsInterface(T) {
if isInterfacePtr(V) {
- r = fmt.Sprintf("(%s is pointer to interface, not interface)", V)
+ r = fmt.Sprintf("(type %s is pointer to interface, not interface)", V)
}
- } else if isInterfacePtr(T) && !isTypeParam(T) {
- r = fmt.Sprintf("(%s is pointer to interface, not interface)", T)
+ } else if isInterfacePtr(T) {
+ r = fmt.Sprintf("(type %s is pointer to interface, not interface)", T)
}
if r == "" {
r = fmt.Sprintf("(missing %s)", mname)
@@ -463,7 +463,7 @@ func (check *Checker) missingMethodReason(V, T Type, m, wrongType *Func) string
func isInterfacePtr(T Type) bool {
p, _ := under(T).(*Pointer)
- return p != nil && IsInterface(p.base) && !isTypeParam(p.base)
+ return p != nil && IsInterface(p.base)
}
// assertableTo reports whether a value of type V can be asserted to have type T.
diff --git a/src/cmd/compile/internal/types2/operand.go b/src/cmd/compile/internal/types2/operand.go
index f6bd0291ec..69e3a0a832 100644
--- a/src/cmd/compile/internal/types2/operand.go
+++ b/src/cmd/compile/internal/types2/operand.go
@@ -317,7 +317,7 @@ func (x *operand) assignableTo(check *Checker, T Type, reason *string) (bool, er
if check != nil && check.conf.CompilerErrorMessages {
if isInterfacePtr(Tu) {
if reason != nil {
- *reason = check.sprintf("%s does not implement %s (%s is pointer to interface, not interface)", x.typ, T, T)
+ *reason = check.sprintf("%s does not implement %s (type %s is pointer to interface, not interface)", x.typ, T, T)
}
return false, _InvalidIfaceAssign
}
diff --git a/src/cmd/compile/internal/types2/testdata/check/methodsets.src b/src/cmd/compile/internal/types2/testdata/check/methodsets.src
index 9fb10deb9a..b0eb14cf50 100644
--- a/src/cmd/compile/internal/types2/testdata/check/methodsets.src
+++ b/src/cmd/compile/internal/types2/testdata/check/methodsets.src
@@ -196,9 +196,9 @@ func issue5918() {
_ func(error) string = error.Error
perr = &err
- _ = perr.Error /* ERROR "no field or method" */ ()
- _ func() string = perr.Error /* ERROR "no field or method" */
- _ func(*error) string = (*error).Error /* ERROR "no field or method" */
+ _ = perr.Error /* ERROR "type \*error is pointer to interface, not interface" */ ()
+ _ func() string = perr.Error /* ERROR "type \*error is pointer to interface, not interface" */
+ _ func(*error) string = (*error).Error /* ERROR "type \*error is pointer to interface, not interface" */
)
type T *interface{ m() int }
@@ -207,8 +207,8 @@ func issue5918() {
_ = (*x).m()
_ = (*x).m
- _ = x.m /* ERROR "no field or method" */ ()
- _ = x.m /* ERROR "no field or method" */
- _ = T.m /* ERROR "no field or method" */
+ _ = x.m /* ERROR "type T is pointer to interface, not interface" */ ()
+ _ = x.m /* ERROR "type T is pointer to interface, not interface" */
+ _ = T.m /* ERROR "type T is pointer to interface, not interface" */
)
}
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47747.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47747.go2
index 6a2e787bf9..edde497f5a 100644
--- a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47747.go2
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue47747.go2
@@ -20,7 +20,7 @@ func _[P interface{ m() }](x P) {
x.m()
// (&x).m doesn't exist because &x is of type *P
// and pointers to type parameters don't have methods
- (&x).m /* ERROR \*P has no field or method m */ ()
+ (&x).m /* ERROR type \*P is pointer to interface, not interface */ ()
}
@@ -29,7 +29,7 @@ type T2 interface{ m() }
func _(x *T2) {
// x.m doesn't exists because x is of type *T2
// and pointers to interfaces don't have methods
- x.m /* ERROR \*T2 has no field or method m */()
+ x.m /* ERROR type \*T2 is pointer to interface, not interface */()
}
// Test case 1 from issue
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48312.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48312.go2
new file mode 100644
index 0000000000..6e5911d0aa
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue48312.go2
@@ -0,0 +1,20 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+type T interface{ m() }
+type P *T
+
+func _(p *T) {
+ p.m /* ERROR type \*T is pointer to interface, not interface */ ()
+}
+
+func _(p P) {
+ p.m /* ERROR type P is pointer to interface, not interface */ ()
+}
+
+func _[P T](p *P) {
+ p.m /* ERROR type \*P is pointer to interface, not interface */ ()
+}