Skip to content

Commit 06b49eb

Browse files
authored
[clang-tidy] improve robustness of the member initializer detection in modernize-use-default-member-init (llvm#159392)
Fixed llvm#156295 Fixed llvm#122480 Fixed llvm#160394 The original ast matcher based member initializer detection is bug prone. In this PR we introduce a new recusively detection which can be easier extended to detect whether we can move the initializer to member
1 parent 816970b commit 06b49eb

File tree

3 files changed

+71
-24
lines changed

3 files changed

+71
-24
lines changed

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

Lines changed: 44 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,57 @@
88

99
#include "UseDefaultMemberInitCheck.h"
1010
#include "clang/AST/ASTContext.h"
11+
#include "clang/AST/Expr.h"
1112
#include "clang/ASTMatchers/ASTMatchFinder.h"
13+
#include "clang/ASTMatchers/ASTMatchers.h"
1214
#include "clang/Lex/Lexer.h"
15+
#include "llvm/ADT/TypeSwitch.h"
1316

1417
using namespace clang::ast_matchers;
1518

1619
namespace clang::tidy::modernize {
1720

21+
static bool isExprAllowedInMemberInit(const Expr *E) {
22+
if (!E)
23+
return false;
24+
return llvm::TypeSwitch<const Expr *, bool>(E)
25+
.Case<IntegerLiteral, FloatingLiteral, CXXBoolLiteralExpr,
26+
CXXNullPtrLiteralExpr, CharacterLiteral, StringLiteral>(
27+
[](const auto *) { return true; })
28+
.Case<ImplicitValueInitExpr>([](const auto *) { return true; })
29+
.Case<ParenExpr>([](const ParenExpr *PE) {
30+
return isExprAllowedInMemberInit(PE->getSubExpr());
31+
})
32+
.Case<UnaryOperator>([](const UnaryOperator *UO) {
33+
return isExprAllowedInMemberInit(UO->getSubExpr());
34+
})
35+
.Case<BinaryOperator>([](const BinaryOperator *BO) {
36+
return isExprAllowedInMemberInit(BO->getLHS()) &&
37+
isExprAllowedInMemberInit(BO->getRHS());
38+
})
39+
.Case<CastExpr>([](const CastExpr *CE) {
40+
return isExprAllowedInMemberInit(CE->getSubExpr());
41+
})
42+
.Case<DeclRefExpr>([](const DeclRefExpr *DRE) {
43+
if (const ValueDecl *D = DRE->getDecl()) {
44+
if (isa<EnumConstantDecl>(D))
45+
return true;
46+
if (const auto *VD = dyn_cast<VarDecl>(D))
47+
return VD->isConstexpr() || VD->getStorageClass() == SC_Static;
48+
}
49+
return false;
50+
})
51+
.Default(false);
52+
}
53+
1854
namespace {
55+
1956
AST_MATCHER_P(InitListExpr, initCountIs, unsigned, N) {
2057
return Node.getNumInits() == N;
2158
}
59+
60+
AST_MATCHER(Expr, allowedInitExpr) { return isExprAllowedInMemberInit(&Node); }
61+
2262
} // namespace
2363

2464
static StringRef getValueOfValueInit(const QualType InitType) {
@@ -206,30 +246,10 @@ void UseDefaultMemberInitCheck::storeOptions(
206246
}
207247

208248
void UseDefaultMemberInitCheck::registerMatchers(MatchFinder *Finder) {
209-
auto NumericLiteral = anyOf(integerLiteral(), floatLiteral());
210-
auto UnaryNumericLiteral = unaryOperator(hasAnyOperatorName("+", "-"),
211-
hasUnaryOperand(NumericLiteral));
212-
213-
auto ConstExprRef = varDecl(anyOf(isConstexpr(), isStaticStorageClass()));
214-
auto ImmutableRef =
215-
declRefExpr(to(decl(anyOf(enumConstantDecl(), ConstExprRef))));
216-
217-
auto BinaryNumericExpr = binaryOperator(
218-
hasOperands(anyOf(NumericLiteral, ImmutableRef, binaryOperator()),
219-
anyOf(NumericLiteral, ImmutableRef, binaryOperator())));
220-
221-
auto InitBase =
222-
anyOf(stringLiteral(), characterLiteral(), NumericLiteral,
223-
UnaryNumericLiteral, cxxBoolLiteral(), cxxNullPtrLiteralExpr(),
224-
implicitValueInitExpr(), ImmutableRef, BinaryNumericExpr);
225-
226-
auto ExplicitCastExpr = castExpr(hasSourceExpression(InitBase));
227-
auto InitMatcher = anyOf(InitBase, ExplicitCastExpr);
228-
229-
auto Init =
230-
anyOf(initListExpr(anyOf(allOf(initCountIs(1), hasInit(0, InitMatcher)),
231-
initCountIs(0), hasType(arrayType()))),
232-
InitBase, ExplicitCastExpr);
249+
auto Init = anyOf(
250+
initListExpr(anyOf(allOf(initCountIs(1), hasInit(0, allowedInitExpr())),
251+
initCountIs(0), hasType(arrayType()))),
252+
allowedInitExpr());
233253

234254
Finder->addMatcher(
235255
cxxConstructorDecl(forEachConstructorInitializer(

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,10 @@ Changes in existing checks
301301
uses of non-standard ``enable_if`` with a signature different from
302302
``std::enable_if`` (such as ``boost::enable_if``).
303303

304+
- Improved :doc:`modernize-use-default-member-init
305+
<clang-tidy/checks/modernize/use-default-member-init>` check to
306+
enhance the robustness of the member initializer detection.
307+
304308
- Improved :doc:`modernize-use-designated-initializers
305309
<clang-tidy/checks/modernize/use-designated-initializers>` check to
306310
suggest using designated initializers for aliased aggregate types.

clang-tools-extra/test/clang-tidy/checkers/modernize/use-default-member-init.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,3 +596,26 @@ class DefaultMemberInitWithArithmetic {
596596
};
597597

598598
} //namespace PR122480
599+
600+
namespace GH156295 {
601+
602+
class NotFix {
603+
NotFix(int v) : x(0 + 0 + (0 * 0 * (((((((v)))) - 20))) + 10)) {}
604+
int x;
605+
};
606+
607+
class ShouldFix {
608+
ShouldFix(int v) : x(0 + 0 + (0 * 0 * (((((((1)))) - 20))) + 10)) {}
609+
int x;
610+
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use default member initializer for 'x' [modernize-use-default-member-init]
611+
// CHECK-FIXES: int x{0 + 0 + (0 * 0 * (((((((1)))) - 20))) + 10)};
612+
};
613+
614+
} // namespace GH156295
615+
616+
namespace GH160394 {
617+
struct A {
618+
A(int i) : f((i & 0x1f) == 1) {}
619+
bool f;
620+
};
621+
} // namespace GH160394

0 commit comments

Comments
 (0)