From: Robert Griesemer Date: Thu, 3 Feb 2022 05:36:50 +0000 (-0800) Subject: go/types, types2: simplify missingMethodReason X-Git-Tag: go1.18rc1~95 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=ef843ae49c8a8ad6494da294fc46b0e80147e715;p=gostls13.git go/types, types2: simplify missingMethodReason Added a funcString helper so we don't need to rewrite strings with strings.Replace. Use compiler format for error message about wrong method type; this removes another unnecessary variation. Simplify conditions for pointer-to-interface related error: if one of the involved types is an interface pointer, it can't have any methods. Rewrite logic so we don't need all the else-if branches. Adjusted a test case for types2 accordingly. The go/types version of this test case has a different error because the implementation of Checker.typeAssertion is different in the two type checkers (the types2 version gives errors closer to the 1.17 compiler). Change-Id: I19e604d7063c3e31e8290bd78384a9f38bb0d740 Reviewed-on: https://go-review.googlesource.com/c/go/+/382694 Trust: Robert Griesemer Reviewed-by: Robert Findley --- diff --git a/src/cmd/compile/internal/types2/lookup.go b/src/cmd/compile/internal/types2/lookup.go index fc6b34941a..1aeb2beaa0 100644 --- a/src/cmd/compile/internal/types2/lookup.go +++ b/src/cmd/compile/internal/types2/lookup.go @@ -7,6 +7,7 @@ package types2 import ( + "bytes" "strings" ) @@ -364,48 +365,40 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, // missingMethodReason returns a string giving the detailed reason for a missing method m, // where m is missing from V, but required by T. It puts the reason in parentheses, -// and may include more have/want info after that. If non-nil, wrongType is a relevant +// and may include more have/want info after that. If non-nil, alt is a relevant // method that matches in some way. It may have the correct name, but wrong type, or // it may have a pointer receiver, or it may have the correct name except wrong case. -func (check *Checker) missingMethodReason(V, T Type, m, wrongType *Func) string { - var r string +func (check *Checker) missingMethodReason(V, T Type, m, alt *Func) string { var mname string if check.conf.CompilerErrorMessages { mname = m.Name() + " method" } else { mname = "method " + m.Name() } - if wrongType != nil { - if m.Name() != wrongType.Name() { - r = check.sprintf("(missing %s)\n\t\thave %s^^%s\n\t\twant %s^^%s", - mname, wrongType.Name(), wrongType.typ, m.Name(), m.typ) - } else if Identical(m.typ, wrongType.typ) { - r = check.sprintf("(%s has pointer receiver)", mname) - } else { - if check.conf.CompilerErrorMessages { - r = check.sprintf("(wrong type for %s)\n\t\thave %s^^%s\n\t\twant %s^^%s", - mname, wrongType.Name(), wrongType.typ, m.Name(), m.typ) - } else { - r = check.sprintf("(wrong type for %s)\n\thave %s\n\twant %s", - mname, wrongType.typ, m.typ) - } + + if alt != nil { + if m.Name() != alt.Name() { + return check.sprintf("(missing %s)\n\t\thave %s\n\t\twant %s", + mname, check.funcString(alt), check.funcString(m)) } - // This is a hack to print the function type without the leading - // 'func' keyword in the have/want printouts. We could change to have - // an extra formatting option for types2.Type that doesn't print out - // 'func'. - r = strings.Replace(r, "^^func", "", -1) - } else if IsInterface(T) { - if isInterfacePtr(V) { - r = "(" + check.interfacePtrError(V) + ")" + + if Identical(m.typ, alt.typ) { + return check.sprintf("(%s has pointer receiver)", mname) } - } else if isInterfacePtr(T) { - r = "(" + check.interfacePtrError(T) + ")" + + return check.sprintf("(wrong type for %s)\n\t\thave %s\n\t\twant %s", + mname, check.funcString(alt), check.funcString(m)) } - if r == "" { - r = check.sprintf("(missing %s)", mname) + + if isInterfacePtr(V) { + return "(" + check.interfacePtrError(V) + ")" } - return r + + if isInterfacePtr(T) { + return "(" + check.interfacePtrError(T) + ")" + } + + return check.sprintf("(missing %s)", mname) } func isInterfacePtr(T Type) bool { @@ -421,6 +414,13 @@ func (check *Checker) interfacePtrError(T Type) string { return check.sprintf("type %s is pointer to interface, not interface", T) } +// funcString returns a string of the form name + signature for f. +func (check *Checker) funcString(f *Func) string { + buf := bytes.NewBufferString(f.name) + WriteSignature(buf, f.typ.(*Signature), check.qualifier) + return buf.String() +} + // assertableTo reports whether a value of type V can be asserted to have type T. // It returns (nil, false) as affirmative answer. Otherwise it returns a missing // method required by V and whether it is missing or just has the wrong type. diff --git a/src/cmd/compile/internal/types2/testdata/check/issues.src b/src/cmd/compile/internal/types2/testdata/check/issues.src index a19f99b31a..4c49147922 100644 --- a/src/cmd/compile/internal/types2/testdata/check/issues.src +++ b/src/cmd/compile/internal/types2/testdata/check/issues.src @@ -137,7 +137,7 @@ func issue10260() { T1{}.foo /* ERROR cannot call pointer method foo on T1 */ () x.Foo /* ERROR "x.Foo undefined \(type I1 has no field or method Foo, but does have foo\)" */ () - _ = i2. /* ERROR impossible type assertion: i2.\(\*T1\)\n\t\*T1 does not implement I2 \(wrong type for method foo\)\n\thave func\(\)\n\twant func\(x int\) */ (*T1) + _ = i2. /* ERROR impossible type assertion: i2\.\(\*T1\)\n\t\*T1 does not implement I2 \(wrong type for method foo\)\n\t\thave foo\(\)\n\t\twant foo\(x int\) */ (*T1) i1 = i0 /* ERROR cannot use .* missing method foo */ i1 = t0 /* ERROR cannot use .* missing method foo */ diff --git a/src/go/types/lookup.go b/src/go/types/lookup.go index 77e8fe9df5..1b4f953803 100644 --- a/src/go/types/lookup.go +++ b/src/go/types/lookup.go @@ -7,6 +7,7 @@ package types import ( + "bytes" "strings" ) @@ -366,50 +367,40 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, // missingMethodReason returns a string giving the detailed reason for a missing method m, // where m is missing from V, but required by T. It puts the reason in parentheses, -// and may include more have/want info after that. If non-nil, wrongType is a relevant +// and may include more have/want info after that. If non-nil, alt is a relevant // method that matches in some way. It may have the correct name, but wrong type, or -// it may have a pointer receiver. -func (check *Checker) missingMethodReason(V, T Type, m, wrongType *Func) string { - var r string +// it may have a pointer receiver, or it may have the correct name except wrong case. +func (check *Checker) missingMethodReason(V, T Type, m, alt *Func) string { var mname string if compilerErrorMessages { mname = m.Name() + " method" } else { mname = "method " + m.Name() } - if wrongType != nil { - if m.Name() != wrongType.Name() { - // Note: this case can't happen because we don't look for alternative - // method spellings, unlike types2. Keep for symmetry with types2. - r = check.sprintf("(missing %s)\n\t\thave %s^^%s\n\t\twant %s^^%s", - mname, wrongType.Name(), wrongType.typ, m.Name(), m.typ) - } else if Identical(m.typ, wrongType.typ) { - r = check.sprintf("(%s has pointer receiver)", mname) - } else { - if compilerErrorMessages { - r = check.sprintf("(wrong type for %s)\n\t\thave %s^^%s\n\t\twant %s^^%s", - mname, wrongType.Name(), wrongType.typ, m.Name(), m.typ) - } else { - r = check.sprintf("(wrong type for %s)\n\thave %s\n\twant %s", - mname, wrongType.typ, m.typ) - } + + if alt != nil { + if m.Name() != alt.Name() { + return check.sprintf("(missing %s)\n\t\thave %s\n\t\twant %s", + mname, check.funcString(alt), check.funcString(m)) } - // This is a hack to print the function type without the leading - // 'func' keyword in the have/want printouts. We could change to have - // an extra formatting option for types2.Type that doesn't print out - // 'func'. - r = strings.Replace(r, "^^func", "", -1) - } else if IsInterface(T) { - if isInterfacePtr(V) { - r = "(" + check.interfacePtrError(V) + ")" + + if Identical(m.typ, alt.typ) { + return check.sprintf("(%s has pointer receiver)", mname) } - } else if isInterfacePtr(T) { - r = "(" + check.interfacePtrError(T) + ")" + + return check.sprintf("(wrong type for %s)\n\t\thave %s\n\t\twant %s", + mname, check.funcString(alt), check.funcString(m)) } - if r == "" { - r = check.sprintf("(missing %s)", mname) + + if isInterfacePtr(V) { + return "(" + check.interfacePtrError(V) + ")" } - return r + + if isInterfacePtr(T) { + return "(" + check.interfacePtrError(T) + ")" + } + + return check.sprintf("(missing %s)", mname) } func isInterfacePtr(T Type) bool { @@ -425,6 +416,13 @@ func (check *Checker) interfacePtrError(T Type) string { return check.sprintf("type %s is pointer to interface, not interface", T) } +// funcString returns a string of the form name + signature for f. +func (check *Checker) funcString(f *Func) string { + buf := bytes.NewBufferString(f.name) + WriteSignature(buf, f.typ.(*Signature), check.qualifier) + return buf.String() +} + // assertableTo reports whether a value of type V can be asserted to have type T. // It returns (nil, false) as affirmative answer. Otherwise it returns a missing // method required by V and whether it is missing or just has the wrong type.