t.Fatalf("got %q; want %q", got, want)
}
}
+
+func TestIssue59831(t *testing.T) {
+ // Package a exports a type S with an unexported method m;
+ // the tests check the error messages when m is not found.
+ const asrc = `package a; type S struct{}; func (S) m() {}`
+ apkg := mustTypecheck(asrc, nil, nil)
+
+ // Package b exports a type S with an exported method m;
+ // the tests check the error messages when M is not found.
+ const bsrc = `package b; type S struct{}; func (S) M() {}`
+ bpkg := mustTypecheck(bsrc, nil, nil)
+
+ tests := []struct {
+ imported *Package
+ src, err string
+ }{
+ // tests importing a (or nothing)
+ {apkg, `package a1; import "a"; var _ interface { M() } = a.S{}`,
+ "a.S does not implement interface{M()} (missing method M) have m() want M()"},
+
+ {apkg, `package a2; import "a"; var _ interface { m() } = a.S{}`,
+ "a.S does not implement interface{m()} (unexported method m)"}, // test for issue
+
+ {nil, `package a3; type S struct{}; func (S) m(); var _ interface { M() } = S{}`,
+ "S does not implement interface{M()} (missing method M) have m() want M()"},
+
+ {nil, `package a4; type S struct{}; func (S) m(); var _ interface { m() } = S{}`,
+ ""}, // no error expected
+
+ {nil, `package a5; type S struct{}; func (S) m(); var _ interface { n() } = S{}`,
+ "S does not implement interface{n()} (missing method n)"},
+
+ // tests importing b (or nothing)
+ {bpkg, `package b1; import "b"; var _ interface { m() } = b.S{}`,
+ "b.S does not implement interface{m()} (missing method m) have M() want m()"},
+
+ {bpkg, `package b2; import "b"; var _ interface { M() } = b.S{}`,
+ ""}, // no error expected
+
+ {nil, `package b3; type S struct{}; func (S) M(); var _ interface { M() } = S{}`,
+ ""}, // no error expected
+
+ {nil, `package b4; type S struct{}; func (S) M(); var _ interface { m() } = S{}`,
+ "S does not implement interface{m()} (missing method m) have M() want m()"},
+
+ {nil, `package b5; type S struct{}; func (S) M(); var _ interface { n() } = S{}`,
+ "S does not implement interface{n()} (missing method n)"},
+ }
+
+ for _, test := range tests {
+ // typecheck test source
+ conf := Config{Importer: importHelper{pkg: test.imported}}
+ pkg, err := typecheck(test.src, &conf, nil)
+ if err == nil {
+ if test.err != "" {
+ t.Errorf("package %s: got no error, want %q", pkg.Name(), test.err)
+ }
+ continue
+ }
+ if test.err == "" {
+ t.Errorf("package %s: got %q, want not error", pkg.Name(), err.Error())
+ }
+
+ // flatten reported error message
+ errmsg := strings.ReplaceAll(err.Error(), "\n", " ")
+ errmsg = strings.ReplaceAll(errmsg, "\t", "")
+
+ // verify error message
+ if !strings.Contains(errmsg, test.err) {
+ t.Errorf("package %s: got %q, want %q", pkg.Name(), errmsg, test.err)
+ }
+ }
+}
// and missingMethod (the latter doesn't care about struct fields).
//
// If foldCase is true, method names are considered equal if they are equal
-// with case folding.
+// with case folding, irrespective of which package they are in.
//
// The resulting object may not be fully type-checked.
func lookupFieldOrMethodImpl(T Type, addressable bool, pkg *Package, name string, foldCase bool) (obj Object, index []int, indirect bool) {
ok = iota
notFound
wrongName
+ unexported
wrongSig
ambigSel
ptrRecv
f, _ = obj.(*Func)
if f != nil {
state = wrongName
+ if f.name == m.name {
+ // If the names are equal, f must be unexported
+ // (otherwise the package wouldn't matter).
+ state = unexported
+ }
}
}
break
}
case wrongName:
fs, ms := check.funcString(f, false), check.funcString(m, false)
- *cause = check.sprintf("(missing method %s)\n\t\thave %s\n\t\twant %s",
- m.Name(), fs, ms)
+ *cause = check.sprintf("(missing method %s)\n\t\thave %s\n\t\twant %s", m.Name(), fs, ms)
+ case unexported:
+ *cause = check.sprintf("(unexported method %s)", m.Name())
case wrongSig:
fs, ms := check.funcString(f, false), check.funcString(m, false)
if fs == ms {
}
// lookupMethod returns the index of and method with matching package and name, or (-1, nil).
-// If foldCase is true, method names are considered equal if they are equal with case folding.
+// If foldCase is true, method names are considered equal if they are equal with case folding
+// and their packages are ignored (e.g., pkg1.m, pkg1.M, pkg2.m, and pkg2.M are all equal).
func lookupMethod(methods []*Func, pkg *Package, name string, foldCase bool) (int, *Func) {
if name != "_" {
for i, m := range methods {
- if (m.name == name || foldCase && strings.EqualFold(m.name, name)) && m.sameId(pkg, m.name) {
+ if m.sameId(pkg, name) || foldCase && strings.EqualFold(m.name, name) {
return i, m
}
}
t.Fatalf("got %q; want %q", got, want)
}
}
+
+func TestIssue59831(t *testing.T) {
+ // Package a exports a type S with an unexported method m;
+ // the tests check the error messages when m is not found.
+ const asrc = `package a; type S struct{}; func (S) m() {}`
+ apkg := mustTypecheck(asrc, nil, nil)
+
+ // Package b exports a type S with an exported method m;
+ // the tests check the error messages when M is not found.
+ const bsrc = `package b; type S struct{}; func (S) M() {}`
+ bpkg := mustTypecheck(bsrc, nil, nil)
+
+ tests := []struct {
+ imported *Package
+ src, err string
+ }{
+ // tests importing a (or nothing)
+ {apkg, `package a1; import "a"; var _ interface { M() } = a.S{}`,
+ "a.S does not implement interface{M()} (missing method M) have m() want M()"},
+
+ {apkg, `package a2; import "a"; var _ interface { m() } = a.S{}`,
+ "a.S does not implement interface{m()} (unexported method m)"}, // test for issue
+
+ {nil, `package a3; type S struct{}; func (S) m(); var _ interface { M() } = S{}`,
+ "S does not implement interface{M()} (missing method M) have m() want M()"},
+
+ {nil, `package a4; type S struct{}; func (S) m(); var _ interface { m() } = S{}`,
+ ""}, // no error expected
+
+ {nil, `package a5; type S struct{}; func (S) m(); var _ interface { n() } = S{}`,
+ "S does not implement interface{n()} (missing method n)"},
+
+ // tests importing b (or nothing)
+ {bpkg, `package b1; import "b"; var _ interface { m() } = b.S{}`,
+ "b.S does not implement interface{m()} (missing method m) have M() want m()"},
+
+ {bpkg, `package b2; import "b"; var _ interface { M() } = b.S{}`,
+ ""}, // no error expected
+
+ {nil, `package b3; type S struct{}; func (S) M(); var _ interface { M() } = S{}`,
+ ""}, // no error expected
+
+ {nil, `package b4; type S struct{}; func (S) M(); var _ interface { m() } = S{}`,
+ "S does not implement interface{m()} (missing method m) have M() want m()"},
+
+ {nil, `package b5; type S struct{}; func (S) M(); var _ interface { n() } = S{}`,
+ "S does not implement interface{n()} (missing method n)"},
+ }
+
+ for _, test := range tests {
+ // typecheck test source
+ conf := Config{Importer: importHelper{pkg: test.imported}}
+ pkg, err := typecheck(test.src, &conf, nil)
+ if err == nil {
+ if test.err != "" {
+ t.Errorf("package %s: got no error, want %q", pkg.Name(), test.err)
+ }
+ continue
+ }
+ if test.err == "" {
+ t.Errorf("package %s: got %q, want not error", pkg.Name(), err.Error())
+ }
+
+ // flatten reported error message
+ errmsg := strings.ReplaceAll(err.Error(), "\n", " ")
+ errmsg = strings.ReplaceAll(errmsg, "\t", "")
+
+ // verify error message
+ if !strings.Contains(errmsg, test.err) {
+ t.Errorf("package %s: got %q, want %q", pkg.Name(), errmsg, test.err)
+ }
+ }
+}
// and missingMethod (the latter doesn't care about struct fields).
//
// If foldCase is true, method names are considered equal if they are equal
-// with case folding.
+// with case folding, irrespective of which package they are in.
//
// The resulting object may not be fully type-checked.
func lookupFieldOrMethodImpl(T Type, addressable bool, pkg *Package, name string, foldCase bool) (obj Object, index []int, indirect bool) {
ok = iota
notFound
wrongName
+ unexported
wrongSig
ambigSel
ptrRecv
f, _ = obj.(*Func)
if f != nil {
state = wrongName
+ if f.name == m.name {
+ // If the names are equal, f must be unexported
+ // (otherwise the package wouldn't matter).
+ state = unexported
+ }
}
}
break
}
case wrongName:
fs, ms := check.funcString(f, false), check.funcString(m, false)
- *cause = check.sprintf("(missing method %s)\n\t\thave %s\n\t\twant %s",
- m.Name(), fs, ms)
+ *cause = check.sprintf("(missing method %s)\n\t\thave %s\n\t\twant %s", m.Name(), fs, ms)
+ case unexported:
+ *cause = check.sprintf("(unexported method %s)", m.Name())
case wrongSig:
fs, ms := check.funcString(f, false), check.funcString(m, false)
if fs == ms {
}
// lookupMethod returns the index of and method with matching package and name, or (-1, nil).
-// If foldCase is true, method names are considered equal if they are equal with case folding.
+// If foldCase is true, method names are considered equal if they are equal with case folding
+// and their packages are ignored (e.g., pkg1.m, pkg1.M, pkg2.m, and pkg2.M are all equal).
func lookupMethod(methods []*Func, pkg *Package, name string, foldCase bool) (int, *Func) {
if name != "_" {
for i, m := range methods {
- if (m.name == name || foldCase && strings.EqualFold(m.name, name)) && m.sameId(pkg, m.name) {
+ if m.sameId(pkg, name) || foldCase && strings.EqualFold(m.name, name) {
return i, m
}
}