Skip to content

Commit 59e79f0

Browse files
authored
[clang-tidy] Add support for in-class initializers in readability-redundant-member-init (#77206)
Support detecting redundant in-class initializers. Moved from https://reviews.llvm.org/D157262 Fixes: #62525
1 parent fb2cc9b commit 59e79f0

File tree

4 files changed

+109
-23
lines changed

4 files changed

+109
-23
lines changed

clang-tools-extra/clang-tidy/readability/RedundantMemberInitCheck.cpp

Lines changed: 51 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "RedundantMemberInitCheck.h"
10+
#include "../utils/LexerUtils.h"
1011
#include "../utils/Matchers.h"
1112
#include "clang/AST/ASTContext.h"
1213
#include "clang/ASTMatchers/ASTMatchFinder.h"
@@ -18,52 +19,80 @@ using namespace clang::tidy::matchers;
1819

1920
namespace clang::tidy::readability {
2021

22+
static SourceRange
23+
getFullInitRangeInclWhitespaces(SourceRange Range, const SourceManager &SM,
24+
const LangOptions &LangOpts) {
25+
const Token PrevToken =
26+
utils::lexer::getPreviousToken(Range.getBegin(), SM, LangOpts, false);
27+
if (PrevToken.is(tok::unknown))
28+
return Range;
29+
30+
if (PrevToken.isNot(tok::equal))
31+
return {PrevToken.getEndLoc(), Range.getEnd()};
32+
33+
return getFullInitRangeInclWhitespaces(
34+
{PrevToken.getLocation(), Range.getEnd()}, SM, LangOpts);
35+
}
36+
2137
void RedundantMemberInitCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
2238
Options.store(Opts, "IgnoreBaseInCopyConstructors",
2339
IgnoreBaseInCopyConstructors);
2440
}
2541

2642
void RedundantMemberInitCheck::registerMatchers(MatchFinder *Finder) {
43+
auto ConstructorMatcher =
44+
cxxConstructExpr(argumentCountIs(0),
45+
hasDeclaration(cxxConstructorDecl(ofClass(cxxRecordDecl(
46+
unless(isTriviallyDefaultConstructible()))))))
47+
.bind("construct");
48+
2749
Finder->addMatcher(
2850
cxxConstructorDecl(
2951
unless(isDelegatingConstructor()), ofClass(unless(isUnion())),
3052
forEachConstructorInitializer(
31-
cxxCtorInitializer(
32-
withInitializer(
33-
cxxConstructExpr(
34-
hasDeclaration(
35-
cxxConstructorDecl(ofClass(cxxRecordDecl(
36-
unless(isTriviallyDefaultConstructible()))))))
37-
.bind("construct")),
38-
unless(forField(hasType(isConstQualified()))),
39-
unless(forField(hasParent(recordDecl(isUnion())))))
53+
cxxCtorInitializer(withInitializer(ConstructorMatcher),
54+
unless(forField(fieldDecl(
55+
anyOf(hasType(isConstQualified()),
56+
hasParent(recordDecl(isUnion())))))))
4057
.bind("init")))
4158
.bind("constructor"),
4259
this);
60+
61+
Finder->addMatcher(fieldDecl(hasInClassInitializer(ConstructorMatcher),
62+
unless(hasParent(recordDecl(isUnion()))))
63+
.bind("field"),
64+
this);
4365
}
4466

4567
void RedundantMemberInitCheck::check(const MatchFinder::MatchResult &Result) {
46-
const auto *Init = Result.Nodes.getNodeAs<CXXCtorInitializer>("init");
4768
const auto *Construct = Result.Nodes.getNodeAs<CXXConstructExpr>("construct");
69+
70+
if (const auto *Field = Result.Nodes.getNodeAs<FieldDecl>("field")) {
71+
const Expr *Init = Field->getInClassInitializer();
72+
diag(Construct->getExprLoc(), "initializer for member %0 is redundant")
73+
<< Field
74+
<< FixItHint::CreateRemoval(getFullInitRangeInclWhitespaces(
75+
Init->getSourceRange(), *Result.SourceManager, getLangOpts()));
76+
return;
77+
}
78+
79+
const auto *Init = Result.Nodes.getNodeAs<CXXCtorInitializer>("init");
4880
const auto *ConstructorDecl =
4981
Result.Nodes.getNodeAs<CXXConstructorDecl>("constructor");
5082

5183
if (IgnoreBaseInCopyConstructors && ConstructorDecl->isCopyConstructor() &&
5284
Init->isBaseInitializer())
5385
return;
5486

55-
if (Construct->getNumArgs() == 0 ||
56-
Construct->getArg(0)->isDefaultArgument()) {
57-
if (Init->isAnyMemberInitializer()) {
58-
diag(Init->getSourceLocation(), "initializer for member %0 is redundant")
59-
<< Init->getAnyMember()
60-
<< FixItHint::CreateRemoval(Init->getSourceRange());
61-
} else {
62-
diag(Init->getSourceLocation(),
63-
"initializer for base class %0 is redundant")
64-
<< Construct->getType()
65-
<< FixItHint::CreateRemoval(Init->getSourceRange());
66-
}
87+
if (Init->isAnyMemberInitializer()) {
88+
diag(Init->getSourceLocation(), "initializer for member %0 is redundant")
89+
<< Init->getAnyMember()
90+
<< FixItHint::CreateRemoval(Init->getSourceRange());
91+
} else {
92+
diag(Init->getSourceLocation(),
93+
"initializer for base class %0 is redundant")
94+
<< Construct->getType()
95+
<< FixItHint::CreateRemoval(Init->getSourceRange());
6796
}
6897
}
6998

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,10 @@ Changes in existing checks
501501
<clang-tidy/checks/readability/non-const-parameter>` check to ignore
502502
false-positives in initializer list of record.
503503

504+
- Improved :doc:`readability-redundant-member-init
505+
<clang-tidy/checks/readability/redundant-member-init>` check to now also
506+
detect redundant in-class initializers.
507+
504508
- Improved :doc:`readability-simplify-boolean-expr
505509
<clang-tidy/checks/readability/simplify-boolean-expr>` check by adding the
506510
new option `IgnoreMacros` that allows to ignore boolean expressions originating

clang-tools-extra/docs/clang-tidy/checks/readability/redundant-member-init.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,14 @@ Example
1111

1212
.. code-block:: c++
1313

14-
// Explicitly initializing the member s is unnecessary.
14+
// Explicitly initializing the member s and v is unnecessary.
1515
class Foo {
1616
public:
1717
Foo() : s() {}
1818

1919
private:
2020
std::string s;
21+
std::vector<int> v {};
2122
};
2223

2324
Options

clang-tools-extra/test/clang-tidy/checkers/readability/redundant-member-init.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,3 +250,55 @@ struct NF15 {
250250
S s2;
251251
};
252252
};
253+
254+
// Direct in-class initialization with default constructor
255+
struct D1 {
256+
S f1 {};
257+
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: initializer for member 'f1' is redundant
258+
// CHECK-FIXES: S f1;
259+
};
260+
261+
// Direct in-class initialization with constructor with default argument
262+
struct D2 {
263+
T f2 {};
264+
// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: initializer for member 'f2' is redundant
265+
// CHECK-FIXES: T f2;
266+
};
267+
268+
// Direct in-class initialization with default constructor (assign)
269+
struct D3 {
270+
S f3 = {};
271+
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: initializer for member 'f3' is redundant
272+
// CHECK-FIXES: S f3;
273+
};
274+
275+
// Direct in-class initialization with constructor with default argument (assign)
276+
struct D4 {
277+
T f4 = {};
278+
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: initializer for member 'f4' is redundant
279+
// CHECK-FIXES: T f4;
280+
};
281+
282+
// Templated class independent type
283+
template <class V>
284+
struct D5 {
285+
S f5 /*comment*/ = S();
286+
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: initializer for member 'f5' is redundant
287+
// CHECK-FIXES: S f5 /*comment*/;
288+
};
289+
D5<int> d5i;
290+
D5<S> d5s;
291+
292+
struct D6 {
293+
UsesCleanup uc2{};
294+
// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: initializer for member 'uc2' is redundant
295+
// CHECK-FIXES: UsesCleanup uc2;
296+
};
297+
298+
template<typename V>
299+
struct D7 {
300+
V f7;
301+
};
302+
303+
D7<int> d7i;
304+
D7<S> d7s;

0 commit comments

Comments
 (0)