1
0
mirror of https://github.com/golang/go synced 2024-11-19 08:14:40 -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:
Robert Griesemer 2022-01-08 14:39:43 -08:00
parent 6019a52d4d
commit 91edf2b7f2
10 changed files with 79 additions and 73 deletions

View 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
}

View 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)

View 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 */ ()
}

View 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]

View 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
}

View 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
}

View 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)

View 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 */ ()
}

View 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]

View 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
}