mirror of
https://github.com/golang/go
synced 2024-11-19 05:54:44 -07:00
go/types, types2: better error message for type parameter field access
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>
This commit is contained in:
parent
6019a52d4d
commit
91edf2b7f2
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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 */ ()
|
||||
}
|
||||
|
@ -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]
|
||||
|
13
src/cmd/compile/internal/types2/testdata/fixedbugs/issue50516.go2
vendored
Normal file
13
src/cmd/compile/internal/types2/testdata/fixedbugs/issue50516.go2
vendored
Normal file
@ -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
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
6
src/go/types/testdata/check/typeparams.go2
vendored
6
src/go/types/testdata/check/typeparams.go2
vendored
@ -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 */ ()
|
||||
}
|
||||
|
@ -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]
|
||||
|
13
src/go/types/testdata/fixedbugs/issue50516.go2
vendored
Normal file
13
src/go/types/testdata/fixedbugs/issue50516.go2
vendored
Normal file
@ -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
|
||||
}
|
Loading…
Reference in New Issue
Block a user