8
8
9
9
#include " PreferMemberInitializerCheck.h"
10
10
#include " clang/AST/ASTContext.h"
11
+ #include " clang/AST/Decl.h"
11
12
#include " clang/ASTMatchers/ASTMatchFinder.h"
12
13
#include " clang/Lex/Lexer.h"
14
+ #include " llvm/ADT/DenseMap.h"
13
15
14
16
using namespace clang ::ast_matchers;
15
17
@@ -54,31 +56,66 @@ static bool shouldBeDefaultMemberInitializer(const Expr *Value) {
54
56
}
55
57
56
58
namespace {
59
+
57
60
AST_MATCHER_P (FieldDecl, indexNotLessThan, unsigned , Index) {
58
61
return Node.getFieldIndex () >= Index;
59
62
}
63
+
64
+ enum class AssignedLevel {
65
+ // Field is not assigned.
66
+ None,
67
+ // Field is assigned.
68
+ Default,
69
+ // Assignment of field has side effect:
70
+ // - assign to reference.
71
+ // FIXME: support other side effect.
72
+ HasSideEffect,
73
+ // Assignment of field has data dependence.
74
+ HasDependence,
75
+ };
76
+
60
77
} // namespace
61
78
79
+ static bool canAdvanceAssignment (AssignedLevel Level) {
80
+ return Level == AssignedLevel::None || Level == AssignedLevel::Default;
81
+ }
82
+
62
83
// Checks if Field is initialised using a field that will be initialised after
63
84
// it.
64
85
// TODO: Probably should guard against function calls that could have side
65
- // effects or if they do reference another field that's initialized before this
66
- // field, but is modified before the assignment.
67
- static bool isSafeAssignment (const FieldDecl *Field, const Expr *Init,
68
- const CXXConstructorDecl *Context) {
86
+ // effects or if they do reference another field that's initialized before
87
+ // this field, but is modified before the assignment.
88
+ static void updateAssignmentLevel (
89
+ const FieldDecl *Field, const Expr *Init, const CXXConstructorDecl *Ctor,
90
+ llvm::DenseMap<const FieldDecl *, AssignedLevel> &AssignedFields) {
91
+ auto It = AssignedFields.find (Field);
92
+ if (It == AssignedFields.end ())
93
+ It = AssignedFields.insert ({Field, AssignedLevel::None}).first ;
94
+
95
+ if (!canAdvanceAssignment (It->second ))
96
+ // fast path for already decided field.
97
+ return ;
98
+
99
+ if (Field->getType ().getCanonicalType ()->isReferenceType ()) {
100
+ // assign to reference type twice cannot be simplified to once.
101
+ It->second = AssignedLevel::HasSideEffect;
102
+ return ;
103
+ }
69
104
70
105
auto MemberMatcher =
71
106
memberExpr (hasObjectExpression (cxxThisExpr ()),
72
107
member (fieldDecl (indexNotLessThan (Field->getFieldIndex ()))));
73
-
74
108
auto DeclMatcher = declRefExpr (
75
- to (varDecl (unless (parmVarDecl ()), hasDeclContext (equalsNode (Context)))));
76
-
77
- return match (expr (anyOf (MemberMatcher, DeclMatcher,
78
- hasDescendant (MemberMatcher),
79
- hasDescendant (DeclMatcher))),
80
- *Init, Field->getASTContext ())
81
- .empty ();
109
+ to (varDecl (unless (parmVarDecl ()), hasDeclContext (equalsNode (Ctor)))));
110
+ const bool HasDependence = !match (expr (anyOf (MemberMatcher, DeclMatcher,
111
+ hasDescendant (MemberMatcher),
112
+ hasDescendant (DeclMatcher))),
113
+ *Init, Field->getASTContext ())
114
+ .empty ();
115
+ if (HasDependence) {
116
+ It->second = AssignedLevel::HasDependence;
117
+ return ;
118
+ }
82
119
}
83
120
84
121
static std::pair<const FieldDecl *, const Expr *>
@@ -99,9 +136,9 @@ isAssignmentToMemberOf(const CXXRecordDecl *Rec, const Stmt *S,
99
136
if (!isa<CXXThisExpr>(ME->getBase ()))
100
137
return std::make_pair (nullptr , nullptr );
101
138
const Expr *Init = BO->getRHS ()->IgnoreParenImpCasts ();
102
- if ( isSafeAssignment ( Field, Init, Ctor))
103
- return std::make_pair (Field, Init);
104
- } else if (const auto *COCE = dyn_cast<CXXOperatorCallExpr>(S)) {
139
+ return std::make_pair ( Field, Init);
140
+ }
141
+ if (const auto *COCE = dyn_cast<CXXOperatorCallExpr>(S)) {
105
142
if (COCE->getOperator () != OO_Equal)
106
143
return std::make_pair (nullptr , nullptr );
107
144
@@ -117,10 +154,8 @@ isAssignmentToMemberOf(const CXXRecordDecl *Rec, const Stmt *S,
117
154
if (!isa<CXXThisExpr>(ME->getBase ()))
118
155
return std::make_pair (nullptr , nullptr );
119
156
const Expr *Init = COCE->getArg (1 )->IgnoreParenImpCasts ();
120
- if (isSafeAssignment (Field, Init, Ctor))
121
- return std::make_pair (Field, Init);
157
+ return std::make_pair (Field, Init);
122
158
}
123
-
124
159
return std::make_pair (nullptr , nullptr );
125
160
}
126
161
@@ -156,6 +191,12 @@ void PreferMemberInitializerCheck::check(
156
191
const CXXRecordDecl *Class = Ctor->getParent ();
157
192
bool FirstToCtorInits = true ;
158
193
194
+ llvm::DenseMap<const FieldDecl *, AssignedLevel> AssignedFields{};
195
+
196
+ for (const CXXCtorInitializer *Init : Ctor->inits ())
197
+ if (FieldDecl *Field = Init->getMember ())
198
+ updateAssignmentLevel (Field, Init->getInit (), Ctor, AssignedFields);
199
+
159
200
for (const Stmt *S : Body->body ()) {
160
201
if (S->getBeginLoc ().isMacroID ()) {
161
202
StringRef MacroName = Lexer::getImmediateMacroName (
@@ -180,6 +221,9 @@ void PreferMemberInitializerCheck::check(
180
221
std::tie (Field, InitValue) = isAssignmentToMemberOf (Class, S, Ctor);
181
222
if (!Field)
182
223
continue ;
224
+ updateAssignmentLevel (Field, InitValue, Ctor, AssignedFields);
225
+ if (!canAdvanceAssignment (AssignedFields[Field]))
226
+ continue ;
183
227
const bool IsInDefaultMemberInitializer =
184
228
IsUseDefaultMemberInitEnabled && getLangOpts ().CPlusPlus11 &&
185
229
Ctor->isDefaultConstructor () &&
0 commit comments