[clang] Add -Wsuggest-override

This patch adds `-Wsuggest-override`, which allows for more aggressive enforcement of modern C++ best practices, as well as better compatibility with gcc, which has had its own `-Wsuggest-override` since version 5.1.

Clang already has `-Winconsistent-missing-override`, which only warns in the case where there is at least one function already marked `override` in a class. This warning strengthens that warning by suggesting the `override` keyword regardless of whether it is already present anywhere.

The text between suggest-override and inconsistent-missing-override is now shared, using `TextSubstitution` for the entire diagnostic text.

Reviewed By: dblaikie

Differential Revision: https://reviews.llvm.org/D82728
This commit is contained in:
Logan Smith 2020-07-12 15:38:37 -07:00 committed by David Blaikie
parent c73f425f84
commit 111167895d
6 changed files with 107 additions and 22 deletions

View File

@ -280,9 +280,12 @@ def CXX98CompatPedantic : DiagGroup<"c++98-compat-pedantic",
def CXX11Narrowing : DiagGroup<"c++11-narrowing">;
def CXX11WarnOverrideDestructor :
def CXX11WarnInconsistentOverrideDestructor :
DiagGroup<"inconsistent-missing-destructor-override">;
def CXX11WarnOverrideMethod : DiagGroup<"inconsistent-missing-override">;
def CXX11WarnInconsistentOverrideMethod :
DiagGroup<"inconsistent-missing-override">;
def CXX11WarnSuggestOverrideDestructor : DiagGroup<"suggest-destructor-override">;
def CXX11WarnSuggestOverride : DiagGroup<"suggest-override">;
// Original name of this warning in Clang
def : DiagGroup<"c++0x-narrowing", [CXX11Narrowing]>;

View File

@ -2367,12 +2367,22 @@ def override_keyword_hides_virtual_member_function : Error<
"%select{function|functions}1">;
def err_function_marked_override_not_overriding : Error<
"%0 marked 'override' but does not override any member functions">;
def warn_destructor_marked_not_override_overriding : Warning <
"%0 overrides a destructor but is not marked 'override'">,
InGroup<CXX11WarnOverrideDestructor>, DefaultIgnore;
def warn_function_marked_not_override_overriding : Warning <
"%0 overrides a member function but is not marked 'override'">,
InGroup<CXX11WarnOverrideMethod>;
def warn_destructor_marked_not_override_overriding : TextSubstitution <
"%0 overrides a destructor but is not marked 'override'">;
def warn_function_marked_not_override_overriding : TextSubstitution <
"%0 overrides a member function but is not marked 'override'">;
def warn_inconsistent_destructor_marked_not_override_overriding : Warning <
"%sub{warn_destructor_marked_not_override_overriding}0">,
InGroup<CXX11WarnInconsistentOverrideDestructor>, DefaultIgnore;
def warn_inconsistent_function_marked_not_override_overriding : Warning <
"%sub{warn_function_marked_not_override_overriding}0">,
InGroup<CXX11WarnInconsistentOverrideMethod>;
def warn_suggest_destructor_marked_not_override_overriding : Warning <
"%sub{warn_destructor_marked_not_override_overriding}0">,
InGroup<CXX11WarnSuggestOverrideDestructor>, DefaultIgnore;
def warn_suggest_function_marked_not_override_overriding : Warning <
"%sub{warn_function_marked_not_override_overriding}0">,
InGroup<CXX11WarnSuggestOverride>, DefaultIgnore;
def err_class_marked_final_used_as_base : Error<
"base %0 is marked '%select{final|sealed}1'">;
def warn_abstract_final_class : Warning<

View File

@ -6965,7 +6965,7 @@ public:
/// DiagnoseAbsenceOfOverrideControl - Diagnose if 'override' keyword was
/// not used in the declaration of an overriding method.
void DiagnoseAbsenceOfOverrideControl(NamedDecl *D);
void DiagnoseAbsenceOfOverrideControl(NamedDecl *D, bool Inconsistent);
/// CheckForFunctionMarkedFinal - Checks whether a virtual member function
/// overrides a virtual member function marked 'final', according to

View File

@ -3045,7 +3045,7 @@ void Sema::CheckOverrideControl(NamedDecl *D) {
<< MD->getDeclName();
}
void Sema::DiagnoseAbsenceOfOverrideControl(NamedDecl *D) {
void Sema::DiagnoseAbsenceOfOverrideControl(NamedDecl *D, bool Inconsistent) {
if (D->isInvalidDecl() || D->hasAttr<OverrideAttr>())
return;
CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(D);
@ -3061,12 +3061,22 @@ void Sema::DiagnoseAbsenceOfOverrideControl(NamedDecl *D) {
return;
if (MD->size_overridden_methods() > 0) {
unsigned DiagID = isa<CXXDestructorDecl>(MD)
? diag::warn_destructor_marked_not_override_overriding
: diag::warn_function_marked_not_override_overriding;
Diag(MD->getLocation(), DiagID) << MD->getDeclName();
const CXXMethodDecl *OMD = *MD->begin_overridden_methods();
Diag(OMD->getLocation(), diag::note_overridden_virtual_function);
auto EmitDiag = [&](unsigned DiagInconsistent, unsigned DiagSuggest) {
unsigned DiagID =
Inconsistent && !Diags.isIgnored(DiagInconsistent, MD->getLocation())
? DiagInconsistent
: DiagSuggest;
Diag(MD->getLocation(), DiagID) << MD->getDeclName();
const CXXMethodDecl *OMD = *MD->begin_overridden_methods();
Diag(OMD->getLocation(), diag::note_overridden_virtual_function);
};
if (isa<CXXDestructorDecl>(MD))
EmitDiag(
diag::warn_inconsistent_destructor_marked_not_override_overriding,
diag::warn_suggest_destructor_marked_not_override_overriding);
else
EmitDiag(diag::warn_inconsistent_function_marked_not_override_overriding,
diag::warn_suggest_function_marked_not_override_overriding);
}
}
@ -6749,13 +6759,10 @@ void Sema::CheckCompletedCXXClass(Scope *S, CXXRecordDecl *Record) {
}
}
if (HasMethodWithOverrideControl &&
HasOverridingMethodWithoutOverrideControl) {
// At least one method has the 'override' control declared.
// Diagnose all other overridden methods which do not have 'override'
// specified on them.
if (HasOverridingMethodWithoutOverrideControl) {
bool HasInconsistentOverrideControl = HasMethodWithOverrideControl;
for (auto *M : Record->methods())
DiagnoseAbsenceOfOverrideControl(M);
DiagnoseAbsenceOfOverrideControl(M, HasInconsistentOverrideControl);
}
// Check the defaulted secondary comparisons after any other member functions.

View File

@ -0,0 +1,27 @@
// RUN: %clang_cc1 -fsyntax-only -std=c++11 %s -verify -Wsuggest-destructor-override
struct A {
~A();
virtual void run();
};
struct B : public A {
~B();
};
struct C {
virtual void run();
virtual ~C(); // expected-note 2{{overridden virtual function is here}}
};
struct D : public C {
void run();
~D();
// expected-warning@-1 {{'~D' overrides a destructor but is not marked 'override'}}
};
struct E : public C {
void run();
virtual ~E();
// expected-warning@-1 {{'~E' overrides a destructor but is not marked 'override'}}
};

View File

@ -0,0 +1,38 @@
// RUN: %clang_cc1 -fsyntax-only -std=c++11 %s -verify -Wsuggest-override
struct A {
~A();
void run();
};
struct B : public A {
~B();
void run();
};
struct C {
virtual void run(); // expected-note 2{{overridden virtual function is here}}
virtual ~C();
};
struct D : public C {
void run();
// expected-warning@-1 {{'run()' overrides a member function but is not marked 'override'}}
~D();
};
struct E : public C {
virtual void run();
// expected-warning@-1 {{'run()' overrides a member function but is not marked 'override'}}
virtual ~E();
};
struct F : public C {
void run() override;
~F() override;
};
struct G : public C {
void run() final;
~G() final;
};