[Clang] Emit a diagnostic note at the class declaration when the method definition does not match any declaration. (#110638)

Fixes #110558.

In this patch, we will emit a diagnostic note pointing to the class
declaration when a method definition does not match any declaration.
This approach, similar to what GCC does, makes the diagnostic more
user-friendly.

---------

Co-authored-by: Vlad Serebrennikov <serebrennikov.vladislav@gmail.com>
This commit is contained in:
c8ef
2024-10-02 22:40:06 +08:00
committed by GitHub
parent 9c697b3a02
commit 8282c58d9b
25 changed files with 92 additions and 51 deletions

View File

@@ -376,6 +376,8 @@ Improvements to Clang's diagnostics
- Clang now diagnoses when a ``requires`` expression has a local parameter of void type, aligning with the function parameter (#GH109831).
- Clang now emits a diagnostic note at the class declaration when the method definition does not match any declaration (#GH110638).
Improvements to Clang's time-trace
----------------------------------

View File

@@ -9086,9 +9086,14 @@ static NamedDecl *DiagnoseInvalidRedeclaration(
SemaRef.Diag(NewFD->getLocation(), DiagMsg)
<< Name << NewDC << IsDefinition << NewFD->getLocation();
bool NewFDisConst = false;
if (CXXMethodDecl *NewMD = dyn_cast<CXXMethodDecl>(NewFD))
NewFDisConst = NewMD->isConst();
CXXMethodDecl *NewMD = dyn_cast<CXXMethodDecl>(NewFD);
if (NewMD && DiagMsg == diag::err_member_decl_does_not_match) {
CXXRecordDecl *RD = NewMD->getParent();
SemaRef.Diag(RD->getLocation(), diag::note_defined_here)
<< RD->getName() << RD->getLocation();
}
bool NewFDisConst = NewMD && NewMD->isConst();
for (SmallVectorImpl<std::pair<FunctionDecl *, unsigned> >::iterator
NearMatch = NearMatches.begin(), NearMatchEnd = NearMatches.end();

View File

@@ -164,11 +164,12 @@ namespace {
#if __cplusplus < 201402L
namespace ImplicitConstexprDef {
struct A {
struct A { // #defined-here
void f(); // expected-note {{member declaration does not match because it is not const qualified}}
};
constexpr void A::f() { } // expected-warning {{'constexpr' non-static member function will not be implicitly 'const' in C++14; add 'const' to avoid a change in behavior}}
// expected-error@-1 {{out-of-line definition of 'f' does not match any declaration in 'ImplicitConstexprDef::A'}}
// expected-note@#defined-here {{defined here}}
}
#endif

View File

@@ -1,8 +1,9 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s
struct A { };
struct A { }; // #defined-here
A::A (enum { e1 }) {} // expected-error{{cannot be defined in a parameter}}
void A::f(enum { e2 }) {} // expected-error{{cannot be defined in a parameter}}
enum { e3 } A::g() { } // expected-error{{cannot be defined in the result type}} \
// expected-error{{out-of-line definition}}
// expected-note@#defined-here{{defined here}}

View File

@@ -96,7 +96,7 @@ namespace unconstrained {
// expected-error@-1{{no matching}}
template<typename T>
struct S {
struct S { // #defined-here
constexpr auto f1(auto x, T t) -> decltype(x + t);
template<typename U>
@@ -110,6 +110,7 @@ namespace unconstrained {
template<typename U>
constexpr auto S<T>::f2(auto x, U u, T t) -> decltype(x + u + t) { return x + u + t; }
// expected-error@-1 {{out-of-line definition of 'f2' does not match any declaration in 'S<T>'}}
// expected-note@#defined-here {{S defined here}}
template<typename T>
template<typename U>

View File

@@ -102,12 +102,13 @@ namespace MultilevelSpecialization {
// default argument -- how far back do we look when determining whether a
// parameter was expanded from a pack?
// -- zygoloid 2020-06-02
template<typename ...T> struct B {
template<typename ...T> struct B { // #cwg2233-B
template <T... V> void f(int i = 0, int (&... arr)[V]);
};
template<> template<int a, int b>
void B<int, int>::f(int i, int (&arr1)[a], int (&arr2)[b]) {}
// since-cxx11-error@-1 {{out-of-line definition of 'f' does not match any declaration in 'cwg2233::MultilevelSpecialization::B<int, int>'}}
// since-cxx11-note@#cwg2233-B {{defined here}}
template<> template<>
void B<int, int>::f<1, 1>(int i, int (&arr1a)[1], int (&arr2a)[1]) {}
}

View File

@@ -597,9 +597,10 @@ namespace cwg336 { // cwg336: yes
void mf2();
};
};
template<> template<class X> class A<int>::B {};
template<> template<class X> class A<int>::B {}; // #cwg336-B
template<> template<> template<class T> void A<int>::B<double>::mf1(T t) {}
// expected-error@-1 {{out-of-line definition of 'mf1' does not match any declaration in 'cwg336::Pre::A<int>::B<double>'}}
// expected-note@#cwg336-B {{defined here}}
template<class Y> template<> void A<Y>::B<double>::mf2() {}
// expected-error@-1 {{nested name specifier 'A<Y>::B<double>::' for declaration does not refer into a class, class template or class template partial specialization}}
}
@@ -758,7 +759,7 @@ namespace cwg347 { // cwg347: yes
void g();
};
struct derived : base {};
struct derived : base {}; // #cwg347-derived
struct derived::nested {};
// expected-error@-1 {{no struct named 'nested' in 'cwg347::derived'}}
@@ -766,8 +767,10 @@ namespace cwg347 { // cwg347: yes
// expected-error@-1 {{no member named 'n' in 'cwg347::derived'}}
void derived::f() {}
// expected-error@-1 {{out-of-line definition of 'f' does not match any declaration in 'cwg347::derived'}}
// expected-note@#cwg347-derived {{defined here}}
void derived::g() {}
// expected-error@-1 {{out-of-line definition of 'g' does not match any declaration in 'cwg347::derived'}}
// expected-note@#cwg347-derived {{defined here}}
}
// cwg348: na
@@ -1009,18 +1012,20 @@ namespace cwg355 { struct ::cwg355_S s; }
// cwg356: na
namespace cwg357 { // cwg357: yes
template<typename T> struct A {
template<typename T> struct A { // #cwg357-A
void f() const; // #cwg357-f
};
template<typename T> void A<T>::f() {}
// expected-error@-1 {{out-of-line definition of 'f' does not match any declaration in 'A<T>'}}
// expected-note@#cwg357-A {{defined here}}
// expected-note@#cwg357-f {{member declaration does not match because it is const qualified}}
struct B {
struct B { // #cwg357-B
template<typename T> void f();
};
template<typename T> void B::f() const {}
// expected-error@-1 {{out-of-line definition of 'f' does not match any declaration in 'cwg357::B'}}
// expected-note@#cwg357-B {{defined here}}
}
namespace cwg358 { // cwg358: yes

View File

@@ -24,9 +24,10 @@ struct C {
template<typename T> constexpr C(T t) : v(t) {}
int v;
};
struct D : C {
struct D : C { // #defined-here
using C::C;
};
static_assert(D(123).v == 123, "");
template<typename T> constexpr D::D(T t) : C(t) {} // expected-error {{does not match any declaration in 'D'}}
// expected-note@#defined-here {{defined here}}

View File

@@ -47,7 +47,7 @@ int AA::A() { return sizeof(T); }
namespace diag {
template <unsigned N>
struct TA {
struct TA { // #defined-here
template <template <unsigned> class TT> requires TT<N>::happy
int A();
};
@@ -55,5 +55,6 @@ struct TA {
template <unsigned N>
template <template <unsigned> class TT> int TA<N>::A() { return sizeof(TT<N>); }
// expected-error@-1{{out-of-line definition of 'A' does not match any declaration in 'TA<N>'}}
// expected-note@#defined-here{{defined here}}
} // end namespace diag

View File

@@ -69,7 +69,7 @@ namespace SearchClassBetweenTemplateParameterLists {
template<typename T> struct A {
using AA = void;
template<typename U> struct B {
template<typename U> struct B { // #defined-here
using BB = void;
void f(U);
void g(U);
@@ -127,19 +127,19 @@ namespace SearchClassBetweenTemplateParameterLists {
template<typename T> template<typename BB>
void A<T>::B<BB>::g(BB) { // expected-error {{does not match}}
BB bb; // expected-error {{incomplete type}}
}
} // expected-note@#defined-here {{defined here}}
// error, 'AA' found in (4)
template<typename AA> template<typename U>
void A<AA>::B<U>::h(AA) { // expected-error {{does not match}}
AA aa; // expected-error {{incomplete type}}
}
} // expected-note@#defined-here {{defined here}}
// error, 'BB' found in (2)
template<typename BB> template<typename U>
void A<BB>::B<U>::i(BB) { // expected-error {{does not match}}
BB bb; // expected-error {{incomplete type}}
}
} // expected-note@#defined-here {{defined here}}
// OK, 'BB' found in (1)
template<typename T> template<typename U> template<typename BB>
@@ -151,7 +151,7 @@ namespace SearchClassBetweenTemplateParameterLists {
template<typename T> template<typename BB> template<typename V>
void A<T>::B<BB>::k(V) { // expected-error {{does not match}}
BB bb; // expected-error {{incomplete type}}
}
} // expected-note@#defined-here {{defined here}}
int CC;
template <typename> struct C;

View File

@@ -31,7 +31,7 @@ constexpr void A::f1<long>(); // since-cxx14-error {{no function template matche
// members of a class template explicitly specialized for an implicitly
// instantiated specialization of that template.
template<typename T>
struct B {
struct B { // #defined-here
void g0(); // since-cxx14-note {{previous declaration is here}}
// cxx11-note@-1 {{member declaration does not match because it is not const qualified}}
@@ -49,11 +49,13 @@ template<>
constexpr void B<short>::g0(); // since-cxx14-error {{constexpr declaration of 'g0' follows non-constexpr declaration}}
// cxx11-error@-1 {{out-of-line declaration of 'g0' does not match any declaration in 'B<short>'}}
// cxx11-warning@-2 {{'constexpr' non-static member function will not be implicitly 'const' in C++14; add 'const'}}
// expected-note@#defined-here {{defined here}}
template<>
constexpr void B<short>::g1(); // since-cxx14-error {{out-of-line declaration of 'g1' does not match any declaration in 'B<short>'}}
// cxx11-error@-1 {{constexpr declaration of 'g1' follows non-constexpr declaration}}
// cxx11-warning@-2 {{'constexpr' non-static member function will not be implicitly 'const' in C++14; add 'const'}}
// expected-note@#defined-here {{defined here}}
template<>
template<typename U>
@@ -66,5 +68,3 @@ template<typename U>
constexpr void B<long>::h1(); // since-cxx14-error {{out-of-line declaration of 'h1' does not match any declaration in 'B<long>'}}
// cxx11-error@-1 {{constexpr declaration of 'h1' follows non-constexpr declaration}}
// cxx11-warning@-2 {{'constexpr' non-static member function will not be implicitly 'const' in C++14; add 'const'}}

View File

@@ -8,7 +8,7 @@ namespace N0 {
concept D = I < 8;
template<int I>
struct A {
struct A { // #defined-here
constexpr static int f() { return 0; }
constexpr static int f() requires C<I> && D<I> { return 1; }
constexpr static int f() requires C<I> { return 2; }
@@ -56,6 +56,7 @@ namespace N0 {
template<>
constexpr int A<0>::h() { return 2; } // expected-error {{out-of-line definition of 'h' does not match any declaration in 'N0::A<0>'}}
// expected-note@#defined-here {{defined here}}
static_assert(A<5>::h() == 0);
static_assert(A<4>::h() == 1);

View File

@@ -7,13 +7,14 @@ template<class T1> class A {
};
template<> template<class X>
class A<long>::B { };
class A<long>::B { }; // #defined-here
template<> template<> template<class T>
void A<int>::B<double>::mf1(T t) { }
template<> template<> template<class T>
void A<long>::B<double>::mf1(T t) { } // expected-error{{does not match}}
// expected-note@#defined-here{{defined here}}
// FIXME: This diagnostic could probably be better.
template<class Y> template<>

View File

@@ -1,12 +1,14 @@
// RUN: %clang_cc1 -verify %s
// RUN: not %clang_cc1 -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
class Foo {
class Foo { // #defined-here
int get() const; // expected-note {{because it is const qualified}}
void set(int); // expected-note {{because it is not const qualified}}
};
// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:15-[[@LINE+1]]:15}:" const"
int Foo::get() {} // expected-error {{does not match any declaration}}
// expected-note@#defined-here {{defined here}}
// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:20-[[@LINE+1]]:26}:""
void Foo::set(int) const {} // expected-error {{does not match any declaration}}
// expected-note@#defined-here {{defined here}}

View File

@@ -100,9 +100,10 @@ class F {
#endif
namespace ctor_error {
class Foo {};
class Foo {}; // #defined-here
// By [class.qual]p2, this is a constructor declaration.
Foo::Foo (F) = F(); // expected-error{{does not match any declaration in 'ctor_error::Foo'}}
// expected-note@#defined-here{{defined here}}
class Ctor { // expected-note{{not complete until the closing '}'}}
Ctor(f)(int); // ok

View File

@@ -180,14 +180,15 @@ void constexpr_test() {
static_assert(foo() == 2, "Should call 'default' in a constexpr context");
}
struct BadOutOfLine {
struct BadOutOfLine { // #defined-here
int __attribute__((target("sse4.2"))) foo(int);
int __attribute__((target("default"))) foo(int);
};
int __attribute__((target("sse4.2"))) BadOutOfLine::foo(int) { return 0; }
int __attribute__((target("default"))) BadOutOfLine::foo(int) { return 1; }
// expected-error@+3 {{out-of-line definition of 'foo' does not match any declaration in 'BadOutOfLine'}}
// expected-note@-3 {{member declaration nearly matches}}
// expected-note@-3 {{member declaration nearly matches}}
// expected-error@+4 {{out-of-line definition of 'foo' does not match any declaration in 'BadOutOfLine'}}
// expected-note@#defined-here {{defined here}}
// expected-note@-4 {{member declaration nearly matches}}
// expected-note@-4 {{member declaration nearly matches}}
int __attribute__((target("arch=atom"))) BadOutOfLine::foo(int) { return 1; }

View File

@@ -97,13 +97,14 @@ using ::Decl;
__attribute__((target_version("jscvt"))) void Decl();
} // namespace Nms
class Out {
class Out { // #defined-here
int __attribute__((target_version("bti"))) func(void);
int __attribute__((target_version("ssbs2"))) func(void);
};
int __attribute__((target_version("bti"))) Out::func(void) { return 1; }
int __attribute__((target_version("ssbs2"))) Out::func(void) { return 2; }
// expected-error@+3 {{out-of-line definition of 'func' does not match any declaration in 'Out'}}
// expected-note@-3 {{member declaration nearly matches}}
// expected-note@-3 {{member declaration nearly matches}}
// expected-error@+4 {{out-of-line definition of 'func' does not match any declaration in 'Out'}}
// expected-note@-2 {{member declaration nearly matches}}
// expected-note@-4 {{member declaration nearly matches}}
// expected-note@#defined-here {{defined here}}
int __attribute__((target_version("rng"))) Out::func(void) { return 3; }

View File

@@ -5,7 +5,7 @@ int surrogate(int);
struct Incomplete; // expected-note{{forward declaration of 'Incomplete'}} \
// expected-note {{forward declaration of 'Incomplete'}}
struct X {
struct X { // expected-note{{defined here}}
X() = default; // expected-note{{candidate constructor not viable: requires 0 arguments, but 1 was provided}}
X(const X&) = default; // expected-note{{candidate constructor not viable: no known conversion from 'bool' to 'const X' for 1st argument}}
X(bool b) __attribute__((enable_if(b, "chosen when 'b' is true"))); // expected-note{{candidate disabled: chosen when 'b' is true}}

View File

@@ -61,7 +61,7 @@ void B::Notypocorrection(int) { // expected-error {{out-of-line definition of 'N
}
struct X { int f(); };
struct Y : public X {};
struct Y : public X {}; // expected-note {{defined here}}
int Y::f() { return 3; } // expected-error {{out-of-line definition of 'f' does not match any declaration in 'Y'}}
namespace test1 {
@@ -70,7 +70,7 @@ struct Foo {
};
}
class Bar {
class Bar { // expected-note {{defined here}}
void f(test1::Foo::Inner foo) const; // expected-note {{member declaration does not match because it is const qualified}}
};
@@ -80,7 +80,8 @@ void Bar::f(Foo::Inner foo) { // expected-error {{out-of-line definition of 'f'
(void)foo;
}
class Crash {
class Crash { // expected-note {{defined here}}
// expected-note@-1 {{defined here}}
public:
void GetCart(int count) const;
};
@@ -89,7 +90,8 @@ void Crash::cart(int count) const {} // expected-error {{out-of-line definition
// ...while this one crashed clang
void Crash::chart(int count) const {} // expected-error {{out-of-line definition of 'chart' does not match any declaration in 'Crash'}}
class TestConst {
class TestConst { // expected-note {{defined here}}
// expected-note@-1 {{defined here}}
public:
int getit() const; // expected-note {{member declaration does not match because it is const qualified}}
void setit(int); // expected-note {{member declaration does not match because it is not const qualified}}
@@ -102,7 +104,7 @@ int TestConst::getit() { // expected-error {{out-of-line definition of 'getit' d
void TestConst::setit(int) const { // expected-error {{out-of-line definition of 'setit' does not match any declaration in 'TestConst'}}
}
struct J { int typo() const; };
struct J { int typo() const; }; // expected-note {{defined here}}
int J::typo_() { return 3; } // expected-error {{out-of-line definition of 'typo_' does not match any declaration in 'J'}}
// Ensure we correct the redecl of Foo::isGood to Bar::Foo::isGood and not
@@ -126,7 +128,7 @@ bool Foo::isGood() { // expected-error {{out-of-line definition of 'isGood' does
void Foo::beEvil() {} // expected-error {{out-of-line definition of 'beEvil' does not match any declaration in namespace 'redecl_typo::Foo'; did you mean 'BeEvil'?}}
}
struct CVQualFun {
struct CVQualFun { // expected-note {{defined here}}
void func(int a, int &b); // expected-note {{type of 2nd parameter of member declaration does not match definition ('int &' vs 'int')}}
};

View File

@@ -70,21 +70,23 @@ void use_g() { g<6>(&"hello"); } // expected-error {{ambiguous}}
namespace GH51416 {
template <class T>
struct A {
struct A { // #defined-here-A
void spam(decltype([] {}));
};
template <class T>
void A<T>::spam(decltype([] {})) // expected-error{{out-of-line definition of 'spam' does not match}}
// expected-note@#defined-here-A{{defined here}}
{}
struct B {
struct B { // #defined-here-B
template <class T>
void spam(decltype([] {}));
};
template <class T>
void B::spam(decltype([] {})) {} // expected-error{{out-of-line definition of 'spam' does not match}}
// expected-note@#defined-here-B{{defined here}}
} // namespace GH51416

View File

@@ -25,7 +25,7 @@ int A::C::cx = 17;
static int A::C::cx2 = 17; // expected-error{{'static' can}}
class C2 {
class C2 { // #defined-here-C2
void m(); // expected-note{{member declaration does not match because it is not const qualified}}
void f(const int& parm); // expected-note{{type of 1st parameter of member declaration does not match definition ('const int &' vs 'int')}}
@@ -36,8 +36,10 @@ class C2 {
};
void C2::m() const { } // expected-error{{out-of-line definition of 'm' does not match any declaration in 'C2'}}
// expected-note@#defined-here-C2{{defined here}}
void C2::f(int) { } // expected-error{{out-of-line definition of 'f' does not match any declaration in 'C2'}}
// expected-note@#defined-here-C2{{defined here}}
void C2::m() {
x = 0;
@@ -122,12 +124,13 @@ namespace E {
}
class Operators {
class Operators { // #defined-here-Operators
Operators operator+(const Operators&) const; // expected-note{{member declaration does not match because it is const qualified}}
operator bool();
};
Operators Operators::operator+(const Operators&) { // expected-error{{out-of-line definition of 'operator+' does not match any declaration in 'Operators'}}
// expected-note@#defined-here-Operators{{defined here}}
Operators ops;
return ops;
}
@@ -149,9 +152,10 @@ void A::f() {} // expected-error-re{{out-of-line definition of 'f' does not matc
void A::g(const int&) { } // expected-error{{out-of-line definition of 'g' does not match any declaration in namespace 'A'}}
struct Struct { };
struct Struct { }; // #defined-here-Struct
void Struct::f() { } // expected-error{{out-of-line definition of 'f' does not match any declaration in 'Struct'}}
// expected-note@#defined-here-Struct{{defined here}}
void global_func(int);
void global_func2(int);

View File

@@ -6,7 +6,11 @@ namespace N2 {
namespace N1 {
class C1 {};
struct S2 {
struct S2 { // expected-note {{defined here}}
// expected-note@-1 {{defined here}}
// expected-note@-2 {{defined here}}
// expected-note@-3 {{defined here}}
// expected-note@-4 {{defined here}}
void func(S1*); // expected-note {{type of 1st parameter of member declaration does not match definition ('S1 *' (aka 'N2::S1 *') vs 'S1 *' (aka 'N2::N1::S1 *'))}}
void func(C1&, unsigned, const S1*); // expected-note {{type of 3rd parameter of member declaration does not match definition ('const S1 *' (aka 'const N2::S1 *') vs 'const S1 *' (aka 'const N2::N1::S1 *'))}}
void func(const S1*, unsigned); //expected-note {{type of 1st parameter of member declaration does not match definition ('const S1 *' vs 'S1')}}

View File

@@ -173,6 +173,7 @@ class Parent {
};
class Child: public Parent {};
void Child::add_types(int value) {} // expected-error{{out-of-line definition of 'add_types' does not match any declaration in 'Child'}}
// expected-note@-2{{defined here}}
// Fix the callback based filtering of typo corrections within
// Sema::ActOnIdExpression by Parser::ParseCastExpression to allow type names as

View File

@@ -516,7 +516,7 @@ concept something_interesting = requires {
};
template <class T>
struct X {
struct X { // #defined-here
void foo() requires requires { requires is_not_same_v<T, int>; };
void bar(decltype(requires { requires is_not_same_v<T, int>; }));
};
@@ -524,7 +524,8 @@ struct X {
template <class T>
void X<T>::foo() requires requires { requires something_interesting<T>; } {}
// expected-error@-1{{definition of 'foo' does not match any declaration}}
// expected-note@*{{}}
// expected-note@#defined-here{{defined here}}
// expected-note@-8{{member declaration nearly matches}}
template <class T>
void X<T>::foo() requires requires { requires is_not_same_v<T, int>; } {} // ok
@@ -532,6 +533,7 @@ void X<T>::foo() requires requires { requires is_not_same_v<T, int>; } {} // ok
template <class T>
void X<T>::bar(decltype(requires { requires something_interesting<T>; })) {}
// expected-error@-1{{definition of 'bar' does not match any declaration}}
// expected-note@#defined-here{{defined here}}
template <class T>
void X<T>::bar(decltype(requires { requires is_not_same_v<T, int>; })) {}

View File

@@ -47,7 +47,7 @@ namespace PR16225 {
namespace test1 {
template <typename> class ArraySlice {};
class Foo;
class NonTemplateClass {
class NonTemplateClass { // #defined-here
void MemberFunction(ArraySlice<Foo>, int);
template <class T> void MemberFuncTemplate(ArraySlice<T>, int);
};
@@ -61,7 +61,8 @@ namespace test1 {
// expected-error@+1 {{member 'UndeclaredMethod' used before its declaration}}
UndeclaredMethod(resource_data);
}
// expected-error@+2 {{out-of-line definition of 'UndeclaredMethod' does not match any declaration}}
// expected-note@+1 {{member is declared here}}
// expected-error@+3 {{out-of-line definition of 'UndeclaredMethod' does not match any declaration}}
// expected-note@+2 {{member is declared here}}
// expected-note@#defined-here {{defined here}}
void NonTemplateClass::UndeclaredMethod() {}
}