]> Cypherpunks repositories - gostls13.git/commitdiff
go/types, types2: better error message for type parameter field access
authorRobert Griesemer <gri@golang.org>
Sat, 8 Jan 2022 22:39:43 +0000 (14:39 -0800)
committerRobert Griesemer <gri@golang.org>
Mon, 10 Jan 2022 22:48:57 +0000 (22:48 +0000)
Fixes #50516.

Also call DefPredeclaredTestFuncs in TestFixedbugs so it can be
run independently again.

Change-Id: I78d4cc11790b1543a2545a7ab297a223b3d5e3c8
Reviewed-on: https://go-review.googlesource.com/c/go/+/376954
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
src/cmd/compile/internal/types2/call.go
src/cmd/compile/internal/types2/check_test.go
src/cmd/compile/internal/types2/testdata/check/typeparams.go2
src/cmd/compile/internal/types2/testdata/fixedbugs/issue50417.go2
src/cmd/compile/internal/types2/testdata/fixedbugs/issue50516.go2 [new file with mode: 0644]
src/go/types/call.go
src/go/types/check_test.go
src/go/types/testdata/check/typeparams.go2
src/go/types/testdata/fixedbugs/issue50417.go2
src/go/types/testdata/fixedbugs/issue50516.go2 [new file with mode: 0644]

index bd62e825af3d80c014a10bcb1f1cdbb34f3f362d..1c3bf48b08c8b8e4c2efd53f2a518bc21d72dc49 100644 (file)
@@ -542,39 +542,26 @@ func (check *Checker) selector(x *operand, e *syntax.SelectorExpr) {
                        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 bound for %s has no method %s", x.typ, sel)
-                       }
+               if isInterfacePtr(x.typ) {
+                       why = check.sprintf("type %s is pointer to interface, not interface", x.typ)
                } 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.
-               // 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 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)
                goto Error
        }
index 1868ad0c6efa824d36348959603ae13e50425294..7efa512164444d88706f8227ee52fab8638a700a 100644 (file)
@@ -295,10 +295,13 @@ func TestManual(t *testing.T) {
 
 // TODO(gri) go/types has extra TestLongConstants and TestIndexRepresentability tests
 
-func TestCheck(t *testing.T)     { DefPredeclaredTestFuncs(); testDirFiles(t, "testdata/check", 55, false) } // TODO(gri) narrow column tolerance
-func TestSpec(t *testing.T)      { DefPredeclaredTestFuncs(); testDirFiles(t, "testdata/spec", 0, false) }
-func TestExamples(t *testing.T)  { testDirFiles(t, "testdata/examples", 0, false) }
-func TestFixedbugs(t *testing.T) { testDirFiles(t, "testdata/fixedbugs", 0, false) }
+func TestCheck(t *testing.T)    { DefPredeclaredTestFuncs(); testDirFiles(t, "testdata/check", 55, false) } // TODO(gri) narrow column tolerance
+func TestSpec(t *testing.T)     { DefPredeclaredTestFuncs(); testDirFiles(t, "testdata/spec", 0, false) }
+func TestExamples(t *testing.T) { testDirFiles(t, "testdata/examples", 0, false) }
+func TestFixedbugs(t *testing.T) {
+       DefPredeclaredTestFuncs()
+       testDirFiles(t, "testdata/fixedbugs", 0, false)
+}
 
 func testDirFiles(t *testing.T, dir string, colDelta uint, manual bool) {
        testenv.MustHaveGoBuild(t)
index 007157ea0f1d5e6163f760728420a6b261495ae5..ef582415199e0561b6d4d670481908ce3bbad2f0 100644 (file)
@@ -519,13 +519,13 @@ func _[P C[P]] (x P) {
 type I interface {}
 
 func _[P I] (x P) {
-       x.m /* ERROR interface I has no method m */ ()
+       x.m /* ERROR type P has no field or method m */ ()
 }
 
 func _[P interface{}] (x P) {
-       x.m /* ERROR type bound for P has no method m */ ()
+       x.m /* ERROR type P has no field or method m */ ()
 }
 
 func _[P any] (x P) {
-       x.m /* ERROR type bound for P has no method m */ ()
+       x.m /* ERROR type P has no field or method m */ ()
 }
index c05987e5ea195784efb055f782d8f3642eb55fbf..b6454ab0031b1036c8e5a6a3e81a0a6d92a284a8 100644 (file)
@@ -58,7 +58,7 @@ type PSfm *Sfm
 func f3[P interface{ PSfm }](p P) {
         _ = p.f
         p.f = 0
-        p.m /* ERROR type bound for P has no method m */ ()
+        p.m /* ERROR type P has no field or method m */ ()
 }
 
 var _ = f3[PSfm]
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue50516.go2 b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue50516.go2
new file mode 100644 (file)
index 0000000..f73015e
--- /dev/null
@@ -0,0 +1,13 @@
+// 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
+
+func _[P struct{ f int }](x P) {
+       _ = x.g // ERROR type P has no field or method g
+}
+
+func _[P struct{ f int } | struct{ g int }](x P) {
+       _ = x.g // ERROR type P has no field or method g
+}
index a904b3df91aa6245abba4b3a15b9d9d5cb4e75d1..4a31ec25864be9e2e0588fbd71ef95321422495d 100644 (file)
@@ -544,40 +544,27 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
                        goto Error
                }
 
-               if isInterfacePtr(x.typ) {
-                       check.errorf(e.Sel, _InvalidMethodExpr, "%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 bound for %s has no method %s", x.typ, sel)
-                       }
+               if isInterfacePtr(x.typ) {
+                       why = check.sprintf("type %s is pointer to interface, not interface", x.typ)
                } 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.
-               // TODO(gri) This code only looks at the first character but LookupFieldOrMethod should
-               //           have an (internal) mechanism for case-insensitive lookup that we should use
-               //           instead (see types2).
-               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 should
+                       //           have an (internal) mechanism for case-insensitive lookup that we should use
+                       //           instead (see types2).
+                       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, _MissingFieldOrMethod, "%s.%s undefined (%s)", x.expr, sel, why)
                goto Error
        }
index e296d13be9150b9546e85cb00ff7e1a9d7d6fa0c..81ea81ca4ed0ca798435212d86a8cea9e960ac0d 100644 (file)
@@ -376,10 +376,13 @@ func TestIssue47243_TypedRHS(t *testing.T) {
        testFiles(t, &StdSizes{4, 4}, []string{"p.go"}, [][]byte{[]byte(src)}, false, nil)
 }
 
-func TestCheck(t *testing.T)     { DefPredeclaredTestFuncs(); testDirFiles(t, "testdata/check", false) }
-func TestSpec(t *testing.T)      { DefPredeclaredTestFuncs(); testDirFiles(t, "testdata/spec", false) }
-func TestExamples(t *testing.T)  { testDirFiles(t, "testdata/examples", false) }
-func TestFixedbugs(t *testing.T) { testDirFiles(t, "testdata/fixedbugs", false) }
+func TestCheck(t *testing.T)    { DefPredeclaredTestFuncs(); testDirFiles(t, "testdata/check", false) }
+func TestSpec(t *testing.T)     { DefPredeclaredTestFuncs(); testDirFiles(t, "testdata/spec", false) }
+func TestExamples(t *testing.T) { testDirFiles(t, "testdata/examples", false) }
+func TestFixedbugs(t *testing.T) {
+       DefPredeclaredTestFuncs()
+       testDirFiles(t, "testdata/fixedbugs", false)
+}
 
 func testDirFiles(t *testing.T, dir string, manual bool) {
        testenv.MustHaveGoBuild(t)
index e3aca4ccb0321564f7fbf4207fd64c669875636c..6d63d598d90862be679d2fbce0fb56fa9ac9d312 100644 (file)
@@ -518,13 +518,13 @@ func _[P C[P]] (x P) {
 type I interface {}
 
 func _[P I] (x P) {
-       x.m /* ERROR interface I has no method m */ ()
+       x.m /* ERROR type P has no field or method m */ ()
 }
 
 func _[P interface{}] (x P) {
-       x.m /* ERROR type bound for P has no method m */ ()
+       x.m /* ERROR type P has no field or method m */ ()
 }
 
 func _[P any] (x P) {
-       x.m /* ERROR type bound for P has no method m */ ()
+       x.m /* ERROR type P has no field or method m */ ()
 }
index c05987e5ea195784efb055f782d8f3642eb55fbf..b6454ab0031b1036c8e5a6a3e81a0a6d92a284a8 100644 (file)
@@ -58,7 +58,7 @@ type PSfm *Sfm
 func f3[P interface{ PSfm }](p P) {
         _ = p.f
         p.f = 0
-        p.m /* ERROR type bound for P has no method m */ ()
+        p.m /* ERROR type P has no field or method m */ ()
 }
 
 var _ = f3[PSfm]
diff --git a/src/go/types/testdata/fixedbugs/issue50516.go2 b/src/go/types/testdata/fixedbugs/issue50516.go2
new file mode 100644 (file)
index 0000000..f73015e
--- /dev/null
@@ -0,0 +1,13 @@
+// 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
+
+func _[P struct{ f int }](x P) {
+       _ = x.g // ERROR type P has no field or method g
+}
+
+func _[P struct{ f int } | struct{ g int }](x P) {
+       _ = x.g // ERROR type P has no field or method g
+}