22
22
#include " clang/ASTMatchers/ASTMatchFinder.h"
23
23
#include " clang/Lex/Lexer.h"
24
24
#include " clang/Tooling/Refactoring.h"
25
+ #include " llvm/ADT/SetVector.h"
25
26
#include < algorithm>
26
27
#include < string>
27
28
28
29
namespace clang {
29
30
namespace reorder_fields {
30
31
using namespace clang ::ast_matchers;
32
+ using llvm::SmallSetVector;
31
33
32
34
// / \brief Finds the definition of a record by name.
33
35
// /
34
36
// / \returns nullptr if the name is ambiguous or not found.
35
- static const CXXRecordDecl *findDefinition (StringRef RecordName,
36
- ASTContext &Context) {
37
- auto Results = match (
38
- recordDecl (hasName (RecordName), isDefinition ()).bind (" cxxRecordDecl " ),
39
- Context);
37
+ static const RecordDecl *findDefinition (StringRef RecordName,
38
+ ASTContext &Context) {
39
+ auto Results =
40
+ match ( recordDecl (hasName (RecordName), isDefinition ()).bind (" recordDecl " ),
41
+ Context);
40
42
if (Results.empty ()) {
41
43
llvm::errs () << " Definition of " << RecordName << " not found\n " ;
42
44
return nullptr ;
@@ -46,14 +48,14 @@ static const CXXRecordDecl *findDefinition(StringRef RecordName,
46
48
<< " is ambiguous, several definitions found\n " ;
47
49
return nullptr ;
48
50
}
49
- return selectFirst<CXXRecordDecl >(" cxxRecordDecl " , Results);
51
+ return selectFirst<RecordDecl >(" recordDecl " , Results);
50
52
}
51
53
52
54
// / \brief Calculates the new order of fields.
53
55
// /
54
56
// / \returns empty vector if the list of fields doesn't match the definition.
55
57
static SmallVector<unsigned , 4 >
56
- getNewFieldsOrder (const CXXRecordDecl *Definition,
58
+ getNewFieldsOrder (const RecordDecl *Definition,
57
59
ArrayRef<std::string> DesiredFieldsOrder) {
58
60
assert (Definition && " Definition is null" );
59
61
@@ -91,13 +93,35 @@ addReplacement(SourceRange Old, SourceRange New, const ASTContext &Context,
91
93
consumeError (Replacements[R.getFilePath ()].add (R));
92
94
}
93
95
96
+ // / \brief Find all member fields used in the given init-list initializer expr
97
+ // / that belong to the same record
98
+ // /
99
+ // / \returns a set of field declarations, empty if none were present
100
+ static SmallSetVector<FieldDecl *, 1 >
101
+ findMembersUsedInInitExpr (const CXXCtorInitializer *Initializer,
102
+ ASTContext &Context) {
103
+ SmallSetVector<FieldDecl *, 1 > Results;
104
+ // Note that this does not pick up member fields of base classes since
105
+ // for those accesses Sema::PerformObjectMemberConversion always inserts an
106
+ // UncheckedDerivedToBase ImplicitCastExpr between the this expr and the
107
+ // object expression
108
+ auto FoundExprs =
109
+ match (findAll (memberExpr (hasObjectExpression (cxxThisExpr ())).bind (" ME" )),
110
+ *Initializer->getInit (), Context);
111
+ for (BoundNodes &BN : FoundExprs)
112
+ if (auto *MemExpr = BN.getNodeAs <MemberExpr>(" ME" ))
113
+ if (auto *FD = dyn_cast<FieldDecl>(MemExpr->getMemberDecl ()))
114
+ Results.insert (FD);
115
+ return Results;
116
+ }
117
+
94
118
// / \brief Reorders fields in the definition of a struct/class.
95
119
// /
96
120
// / At the moment reodering of fields with
97
121
// / different accesses (public/protected/private) is not supported.
98
122
// / \returns true on success.
99
123
static bool reorderFieldsInDefinition (
100
- const CXXRecordDecl *Definition, ArrayRef<unsigned > NewFieldsOrder,
124
+ const RecordDecl *Definition, ArrayRef<unsigned > NewFieldsOrder,
101
125
const ASTContext &Context,
102
126
std::map<std::string, tooling::Replacements> &Replacements) {
103
127
assert (Definition && " Definition is null" );
@@ -129,11 +153,12 @@ static bool reorderFieldsInDefinition(
129
153
130
154
// / \brief Reorders initializers in a C++ struct/class constructor.
131
155
// /
132
- // / A constructor can have initializers for an arbitrary subset of the class's fields.
133
- // / Thus, we need to ensure that we reorder just the initializers that are present.
156
+ // / A constructor can have initializers for an arbitrary subset of the class's
157
+ // / fields. Thus, we need to ensure that we reorder just the initializers that
158
+ // / are present.
134
159
static void reorderFieldsInConstructor (
135
160
const CXXConstructorDecl *CtorDecl, ArrayRef<unsigned > NewFieldsOrder,
136
- const ASTContext &Context,
161
+ ASTContext &Context,
137
162
std::map<std::string, tooling::Replacements> &Replacements) {
138
163
assert (CtorDecl && " Constructor declaration is null" );
139
164
if (CtorDecl->isImplicit () || CtorDecl->getNumCtorInitializers () <= 1 )
@@ -151,8 +176,26 @@ static void reorderFieldsInConstructor(
151
176
SmallVector<const CXXCtorInitializer *, 10 > OldWrittenInitializersOrder;
152
177
SmallVector<const CXXCtorInitializer *, 10 > NewWrittenInitializersOrder;
153
178
for (const auto *Initializer : CtorDecl->inits ()) {
154
- if (!Initializer->isWritten ())
179
+ if (!Initializer->isMemberInitializer () || !Initializer-> isWritten ())
155
180
continue ;
181
+
182
+ // Warn if this reordering violates initialization expr dependencies.
183
+ const FieldDecl *ThisM = Initializer->getMember ();
184
+ const auto UsedMembers = findMembersUsedInInitExpr (Initializer, Context);
185
+ for (const FieldDecl *UM : UsedMembers) {
186
+ if (NewFieldsPositions[UM->getFieldIndex ()] >
187
+ NewFieldsPositions[ThisM->getFieldIndex ()]) {
188
+ DiagnosticsEngine &DiagEngine = Context.getDiagnostics ();
189
+ auto Description = (" reordering field " + UM->getName () + " after " +
190
+ ThisM->getName () + " makes " + UM->getName () +
191
+ " uninitialized when used in init expression" )
192
+ .str ();
193
+ unsigned ID = DiagEngine.getDiagnosticIDs ()->getCustomDiagID (
194
+ DiagnosticIDs::Warning, Description);
195
+ DiagEngine.Report (Initializer->getSourceLocation (), ID);
196
+ }
197
+ }
198
+
156
199
OldWrittenInitializersOrder.push_back (Initializer);
157
200
NewWrittenInitializersOrder.push_back (Initializer);
158
201
}
@@ -182,12 +225,12 @@ static bool reorderFieldsInInitListExpr(
182
225
const ASTContext &Context,
183
226
std::map<std::string, tooling::Replacements> &Replacements) {
184
227
assert (InitListEx && " Init list expression is null" );
185
- // We care only about InitListExprs which originate from source code.
228
+ // We care only about InitListExprs which originate from source code.
186
229
// Implicit InitListExprs are created by the semantic analyzer.
187
230
if (!InitListEx->isExplicit ())
188
231
return true ;
189
- // The method InitListExpr::getSyntacticForm may return nullptr indicating that
190
- // the current initializer list also serves as its syntactic form.
232
+ // The method InitListExpr::getSyntacticForm may return nullptr indicating
233
+ // that the current initializer list also serves as its syntactic form.
191
234
if (const auto *SyntacticForm = InitListEx->getSyntacticForm ())
192
235
InitListEx = SyntacticForm;
193
236
// If there are no initializers we do not need to change anything.
@@ -199,10 +242,9 @@ static bool reorderFieldsInInitListExpr(
199
242
}
200
243
for (unsigned i = 0 , e = InitListEx->getNumInits (); i < e; ++i)
201
244
if (i != NewFieldsOrder[i])
202
- addReplacement (
203
- InitListEx->getInit (i)->getSourceRange (),
204
- InitListEx->getInit (NewFieldsOrder[i])->getSourceRange (), Context,
205
- Replacements);
245
+ addReplacement (InitListEx->getInit (i)->getSourceRange (),
246
+ InitListEx->getInit (NewFieldsOrder[i])->getSourceRange (),
247
+ Context, Replacements);
206
248
return true ;
207
249
}
208
250
@@ -223,7 +265,7 @@ class ReorderingConsumer : public ASTConsumer {
223
265
ReorderingConsumer &operator =(const ReorderingConsumer &) = delete ;
224
266
225
267
void HandleTranslationUnit (ASTContext &Context) override {
226
- const CXXRecordDecl *RD = findDefinition (RecordName, Context);
268
+ const RecordDecl *RD = findDefinition (RecordName, Context);
227
269
if (!RD)
228
270
return ;
229
271
SmallVector<unsigned , 4 > NewFieldsOrder =
@@ -232,16 +274,21 @@ class ReorderingConsumer : public ASTConsumer {
232
274
return ;
233
275
if (!reorderFieldsInDefinition (RD, NewFieldsOrder, Context, Replacements))
234
276
return ;
235
- for (const auto *C : RD->ctors ())
236
- if (const auto *D = dyn_cast<CXXConstructorDecl>(C->getDefinition ()))
237
- reorderFieldsInConstructor (cast<const CXXConstructorDecl>(D),
238
- NewFieldsOrder, Context, Replacements);
239
277
240
- // We only need to reorder init list expressions for aggregate types.
278
+ // CXXRD will be nullptr if C code (not C++) is being processed.
279
+ const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD);
280
+ if (CXXRD)
281
+ for (const auto *C : CXXRD->ctors ())
282
+ if (const auto *D = dyn_cast<CXXConstructorDecl>(C->getDefinition ()))
283
+ reorderFieldsInConstructor (cast<const CXXConstructorDecl>(D),
284
+ NewFieldsOrder, Context, Replacements);
285
+
286
+ // We only need to reorder init list expressions for
287
+ // plain C structs or C++ aggregate types.
241
288
// For other types the order of constructor parameters is used,
242
289
// which we don't change at the moment.
243
290
// Now (v0) partial initialization is not supported.
244
- if (RD ->isAggregate ())
291
+ if (!CXXRD || CXXRD ->isAggregate ())
245
292
for (auto Result :
246
293
match (initListExpr (hasType (equalsNode (RD))).bind (" initListExpr" ),
247
294
Context))
0 commit comments