Skip to content

Commit df11117

Browse files
author
Mitchell Balan
committed
[clang-tidy] modernize-use-override new option AllowOverrideAndFinal
Summary: In addition to adding `override` wherever possible, clang-tidy's `modernize-use-override` nicely removes `virtual` when `override` or `final` is specified, and further removes override when final is specified. While this is great default behavior, when code needs to be compiled with gcc at high warning levels that include `gcc -Wsuggest-override` or `gcc -Werror=suggest-override`, clang-tidy's removal of the redundant `override` keyword causes gcc to emit a warning or error. This discrepancy / conflict has been noted by others including a comment on Stack Overflow and by Mozilla's Firefox developers. This patch adds an AllowOverrideAndFinal option defaulting to 0 - thus preserving current behavior - that when enabled allows both `override` and `final` to co-exist, while still fixing all other issues. The patch includes a test file verifying all combinations of virtual/override/final, and mentions the new option in the release notes. Reviewers: alexfh, djasper, JonasToth Patch by: poelmanc Subscribers: JonasToth, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D70165
1 parent 1315f4e commit df11117

File tree

5 files changed

+63
-4
lines changed

5 files changed

+63
-4
lines changed

clang-tools-extra/clang-tidy/modernize/UseOverrideCheck.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,13 @@ namespace modernize {
2020
UseOverrideCheck::UseOverrideCheck(StringRef Name, ClangTidyContext *Context)
2121
: ClangTidyCheck(Name, Context),
2222
IgnoreDestructors(Options.get("IgnoreDestructors", false)),
23+
AllowOverrideAndFinal(Options.get("AllowOverrideAndFinal", false)),
2324
OverrideSpelling(Options.get("OverrideSpelling", "override")),
2425
FinalSpelling(Options.get("FinalSpelling", "final")) {}
2526

2627
void UseOverrideCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
2728
Options.store(Opts, "IgnoreDestructors", IgnoreDestructors);
29+
Options.store(Opts, "AllowOverrideAndFinal", AllowOverrideAndFinal);
2830
Options.store(Opts, "OverrideSpelling", OverrideSpelling);
2931
Options.store(Opts, "FinalSpelling", FinalSpelling);
3032
}
@@ -103,7 +105,8 @@ void UseOverrideCheck::check(const MatchFinder::MatchResult &Result) {
103105
bool OnlyVirtualSpecified = HasVirtual && !HasOverride && !HasFinal;
104106
unsigned KeywordCount = HasVirtual + HasOverride + HasFinal;
105107

106-
if (!OnlyVirtualSpecified && KeywordCount == 1)
108+
if ((!OnlyVirtualSpecified && KeywordCount == 1) ||
109+
(!HasVirtual && HasOverride && HasFinal && AllowOverrideAndFinal))
107110
return; // Nothing to do.
108111

109112
std::string Message;
@@ -113,8 +116,9 @@ void UseOverrideCheck::check(const MatchFinder::MatchResult &Result) {
113116
Message = "annotate this function with '%0' or (rarely) '%1'";
114117
} else {
115118
StringRef Redundant =
116-
HasVirtual ? (HasOverride && HasFinal ? "'virtual' and '%0' are"
117-
: "'virtual' is")
119+
HasVirtual ? (HasOverride && HasFinal && !AllowOverrideAndFinal
120+
? "'virtual' and '%0' are"
121+
: "'virtual' is")
118122
: "'%0' is";
119123
StringRef Correct = HasFinal ? "'%1'" : "'%0'";
120124

@@ -211,7 +215,7 @@ void UseOverrideCheck::check(const MatchFinder::MatchResult &Result) {
211215
Diag << FixItHint::CreateInsertion(InsertLoc, ReplacementText);
212216
}
213217

214-
if (HasFinal && HasOverride) {
218+
if (HasFinal && HasOverride && !AllowOverrideAndFinal) {
215219
SourceLocation OverrideLoc = Method->getAttr<OverrideAttr>()->getLocation();
216220
Diag << FixItHint::CreateRemoval(
217221
CharSourceRange::getTokenRange(OverrideLoc, OverrideLoc));

clang-tools-extra/clang-tidy/modernize/UseOverrideCheck.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class UseOverrideCheck : public ClangTidyCheck {
2626

2727
private:
2828
const bool IgnoreDestructors;
29+
const bool AllowOverrideAndFinal;
2930
const std::string OverrideSpelling;
3031
const std::string FinalSpelling;
3132
};

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,12 @@ Improvements to clang-tidy
170170
Finds non-static member functions that can be made ``const``
171171
because the functions don't use ``this`` in a non-const way.
172172

173+
- Improved :doc:`modernize-use-override
174+
<clang-tidy/checks/modernize-use-override>` check.
175+
176+
The check now supports the ``AllowOverrideAndFinal`` option to eliminate
177+
conflicts with ``gcc -Wsuggest-override`` or ``gcc -Werror=suggest-override``.
178+
173179
- The :doc:`readability-redundant-string-init
174180
<clang-tidy/checks/readability-redundant-string-init>` check now supports a
175181
`StringNames` option enabling its application to custom string classes.

clang-tools-extra/docs/clang-tidy/checks/modernize-use-override.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ Options
2727

2828
If set to non-zero, this check will not diagnose destructors. Default is `0`.
2929

30+
.. option:: AllowOverrideAndFinal
31+
32+
If set to non-zero, this check will not diagnose ``override`` as redundant
33+
with ``final``. This is useful when code will be compiled by a compiler with
34+
warning/error checking flags requiring ``override`` explicitly on overriden
35+
members, such as ``gcc -Wsuggest-override``/``gcc -Werror=suggest-override``.
36+
Default is `0`.
37+
3038
.. option:: OverrideSpelling
3139

3240
Specifies a macro to use instead of ``override``. This is useful when
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// RUN: %check_clang_tidy %s modernize-use-override %t -- \
2+
// RUN: -config="{CheckOptions: [{key: modernize-use-override.AllowOverrideAndFinal, value: 1}]}"
3+
4+
struct Base {
5+
virtual ~Base();
6+
virtual void a();
7+
virtual void b();
8+
virtual void c();
9+
virtual void d();
10+
virtual void e();
11+
virtual void f();
12+
virtual void g();
13+
virtual void h();
14+
virtual void i();
15+
};
16+
17+
struct Simple : public Base {
18+
virtual ~Simple();
19+
// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: prefer using 'override' or (rarely) 'final' instead of 'virtual' [modernize-use-override]
20+
// CHECK-FIXES: {{^}} ~Simple() override;
21+
virtual void a() override;
22+
// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 'virtual' is redundant since the function is already declared 'override' [modernize-use-override]
23+
// CHECK-FIXES: {{^}} void a() override;
24+
virtual void b() final;
25+
// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 'virtual' is redundant since the function is already declared 'final' [modernize-use-override]
26+
// CHECK-FIXES: {{^}} void b() final;
27+
virtual void c() final override;
28+
// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 'virtual' is redundant since the function is already declared 'final' [modernize-use-override]
29+
// CHECK-FIXES: {{^}} void c() final override;
30+
virtual void d() override final;
31+
// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 'virtual' is redundant since the function is already declared 'final' [modernize-use-override]
32+
// CHECK-FIXES: {{^}} void d() override final;
33+
void e() final override;
34+
void f() override final;
35+
void g() final;
36+
void h() override;
37+
void i();
38+
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: annotate this function with 'override' or (rarely) 'final' [modernize-use-override]
39+
// CHECK-FIXES: {{^}} void i() override;
40+
};

0 commit comments

Comments
 (0)