blob: 7cb8abe53911cec400a16d8fd9c8fbe6a9205c87 [file] [log] [blame]
Alexander Shaposhnikovbf3c84c2016-09-02 02:56:07 +00001//===-- tools/extra/clang-reorder-fields/ReorderFieldsAction.cpp -*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9///
10/// \file
11/// This file contains the definition of the
12/// ReorderFieldsAction::newASTConsumer method
13///
14//===----------------------------------------------------------------------===//
15
16#include "ReorderFieldsAction.h"
17#include "clang/AST/AST.h"
18#include "clang/AST/ASTConsumer.h"
19#include "clang/AST/ASTContext.h"
20#include "clang/AST/Decl.h"
21#include "clang/AST/RecursiveASTVisitor.h"
22#include "clang/ASTMatchers/ASTMatchFinder.h"
23#include "clang/Lex/Lexer.h"
24#include "clang/Tooling/Refactoring.h"
Alexander Shaposhnikovb687fdd2017-07-30 06:43:03 +000025#include "llvm/ADT/SetVector.h"
Alexander Shaposhnikovbf3c84c2016-09-02 02:56:07 +000026#include <algorithm>
27#include <string>
28
29namespace clang {
30namespace reorder_fields {
31using namespace clang::ast_matchers;
Alexander Shaposhnikovb687fdd2017-07-30 06:43:03 +000032using llvm::SmallSetVector;
Alexander Shaposhnikovbf3c84c2016-09-02 02:56:07 +000033
34/// \brief Finds the definition of a record by name.
35///
36/// \returns nullptr if the name is ambiguous or not found.
Alexander Shaposhnikoveaf833c2017-07-20 21:41:20 +000037static const RecordDecl *findDefinition(StringRef RecordName,
38 ASTContext &Context) {
Alexander Shaposhnikovb687fdd2017-07-30 06:43:03 +000039 auto Results =
40 match(recordDecl(hasName(RecordName), isDefinition()).bind("recordDecl"),
41 Context);
Alexander Shaposhnikovbf3c84c2016-09-02 02:56:07 +000042 if (Results.empty()) {
43 llvm::errs() << "Definition of " << RecordName << " not found\n";
44 return nullptr;
45 }
46 if (Results.size() > 1) {
47 llvm::errs() << "The name " << RecordName
48 << " is ambiguous, several definitions found\n";
49 return nullptr;
50 }
Alexander Shaposhnikoveaf833c2017-07-20 21:41:20 +000051 return selectFirst<RecordDecl>("recordDecl", Results);
Alexander Shaposhnikovbf3c84c2016-09-02 02:56:07 +000052}
53
54/// \brief Calculates the new order of fields.
55///
56/// \returns empty vector if the list of fields doesn't match the definition.
57static SmallVector<unsigned, 4>
Alexander Shaposhnikoveaf833c2017-07-20 21:41:20 +000058getNewFieldsOrder(const RecordDecl *Definition,
Alexander Shaposhnikovbf3c84c2016-09-02 02:56:07 +000059 ArrayRef<std::string> DesiredFieldsOrder) {
60 assert(Definition && "Definition is null");
61
62 llvm::StringMap<unsigned> NameToIndex;
63 for (const auto *Field : Definition->fields())
64 NameToIndex[Field->getName()] = Field->getFieldIndex();
65
66 if (DesiredFieldsOrder.size() != NameToIndex.size()) {
67 llvm::errs() << "Number of provided fields doesn't match definition.\n";
68 return {};
69 }
70 SmallVector<unsigned, 4> NewFieldsOrder;
71 for (const auto &Name : DesiredFieldsOrder) {
72 if (!NameToIndex.count(Name)) {
73 llvm::errs() << "Field " << Name << " not found in definition.\n";
74 return {};
75 }
76 NewFieldsOrder.push_back(NameToIndex[Name]);
77 }
78 assert(NewFieldsOrder.size() == NameToIndex.size());
79 return NewFieldsOrder;
80}
81
82// FIXME: error-handling
83/// \brief Replaces one range of source code by another.
84static void
85addReplacement(SourceRange Old, SourceRange New, const ASTContext &Context,
86 std::map<std::string, tooling::Replacements> &Replacements) {
87 StringRef NewText =
88 Lexer::getSourceText(CharSourceRange::getTokenRange(New),
89 Context.getSourceManager(), Context.getLangOpts());
90 tooling::Replacement R(Context.getSourceManager(),
91 CharSourceRange::getTokenRange(Old), NewText,
92 Context.getLangOpts());
93 consumeError(Replacements[R.getFilePath()].add(R));
94}
95
Alexander Shaposhnikovb687fdd2017-07-30 06:43:03 +000096/// \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
100static SmallSetVector<FieldDecl *, 1>
101findMembersUsedInInitExpr(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
Alexander Shaposhnikovbf3c84c2016-09-02 02:56:07 +0000118/// \brief Reorders fields in the definition of a struct/class.
119///
120/// At the moment reodering of fields with
121/// different accesses (public/protected/private) is not supported.
122/// \returns true on success.
123static bool reorderFieldsInDefinition(
Alexander Shaposhnikoveaf833c2017-07-20 21:41:20 +0000124 const RecordDecl *Definition, ArrayRef<unsigned> NewFieldsOrder,
Alexander Shaposhnikovbf3c84c2016-09-02 02:56:07 +0000125 const ASTContext &Context,
126 std::map<std::string, tooling::Replacements> &Replacements) {
127 assert(Definition && "Definition is null");
128
129 SmallVector<const FieldDecl *, 10> Fields;
130 for (const auto *Field : Definition->fields())
131 Fields.push_back(Field);
132
133 // Check that the permutation of the fields doesn't change the accesses
134 for (const auto *Field : Definition->fields()) {
135 const auto FieldIndex = Field->getFieldIndex();
136 if (Field->getAccess() != Fields[NewFieldsOrder[FieldIndex]]->getAccess()) {
137 llvm::errs() << "Currently reodering of fields with different accesses "
138 "is not supported\n";
139 return false;
140 }
141 }
142
143 for (const auto *Field : Definition->fields()) {
144 const auto FieldIndex = Field->getFieldIndex();
145 if (FieldIndex == NewFieldsOrder[FieldIndex])
146 continue;
147 addReplacement(Field->getSourceRange(),
148 Fields[NewFieldsOrder[FieldIndex]]->getSourceRange(),
149 Context, Replacements);
150 }
151 return true;
152}
153
154/// \brief Reorders initializers in a C++ struct/class constructor.
155///
Alexander Shaposhnikovb687fdd2017-07-30 06:43:03 +0000156/// 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.
Alexander Shaposhnikovbf3c84c2016-09-02 02:56:07 +0000159static void reorderFieldsInConstructor(
160 const CXXConstructorDecl *CtorDecl, ArrayRef<unsigned> NewFieldsOrder,
Alexander Shaposhnikovb687fdd2017-07-30 06:43:03 +0000161 ASTContext &Context,
Alexander Shaposhnikovbf3c84c2016-09-02 02:56:07 +0000162 std::map<std::string, tooling::Replacements> &Replacements) {
163 assert(CtorDecl && "Constructor declaration is null");
164 if (CtorDecl->isImplicit() || CtorDecl->getNumCtorInitializers() <= 1)
165 return;
166
167 // The method FunctionDecl::isThisDeclarationADefinition returns false
168 // for a defaulted function unless that function has been implicitly defined.
169 // Thus this assert needs to be after the previous checks.
170 assert(CtorDecl->isThisDeclarationADefinition() && "Not a definition");
171
172 SmallVector<unsigned, 10> NewFieldsPositions(NewFieldsOrder.size());
173 for (unsigned i = 0, e = NewFieldsOrder.size(); i < e; ++i)
174 NewFieldsPositions[NewFieldsOrder[i]] = i;
175
176 SmallVector<const CXXCtorInitializer *, 10> OldWrittenInitializersOrder;
177 SmallVector<const CXXCtorInitializer *, 10> NewWrittenInitializersOrder;
178 for (const auto *Initializer : CtorDecl->inits()) {
Alexander Shaposhnikovb687fdd2017-07-30 06:43:03 +0000179 if (!Initializer->isMemberInitializer() || !Initializer->isWritten())
Alexander Shaposhnikovbf3c84c2016-09-02 02:56:07 +0000180 continue;
Alexander Shaposhnikovb687fdd2017-07-30 06:43:03 +0000181
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
Alexander Shaposhnikovbf3c84c2016-09-02 02:56:07 +0000199 OldWrittenInitializersOrder.push_back(Initializer);
200 NewWrittenInitializersOrder.push_back(Initializer);
201 }
202 auto ByFieldNewPosition = [&](const CXXCtorInitializer *LHS,
203 const CXXCtorInitializer *RHS) {
204 assert(LHS && RHS);
205 return NewFieldsPositions[LHS->getMember()->getFieldIndex()] <
206 NewFieldsPositions[RHS->getMember()->getFieldIndex()];
207 };
208 std::sort(std::begin(NewWrittenInitializersOrder),
209 std::end(NewWrittenInitializersOrder), ByFieldNewPosition);
210 assert(OldWrittenInitializersOrder.size() ==
211 NewWrittenInitializersOrder.size());
212 for (unsigned i = 0, e = NewWrittenInitializersOrder.size(); i < e; ++i)
213 if (OldWrittenInitializersOrder[i] != NewWrittenInitializersOrder[i])
214 addReplacement(OldWrittenInitializersOrder[i]->getSourceRange(),
215 NewWrittenInitializersOrder[i]->getSourceRange(), Context,
216 Replacements);
217}
218
219/// \brief Reorders initializers in the brace initialization of an aggregate.
220///
221/// At the moment partial initialization is not supported.
222/// \returns true on success
223static bool reorderFieldsInInitListExpr(
224 const InitListExpr *InitListEx, ArrayRef<unsigned> NewFieldsOrder,
225 const ASTContext &Context,
226 std::map<std::string, tooling::Replacements> &Replacements) {
227 assert(InitListEx && "Init list expression is null");
Alexander Shaposhnikovb687fdd2017-07-30 06:43:03 +0000228 // We care only about InitListExprs which originate from source code.
Alexander Shaposhnikovbf3c84c2016-09-02 02:56:07 +0000229 // Implicit InitListExprs are created by the semantic analyzer.
230 if (!InitListEx->isExplicit())
231 return true;
Alexander Shaposhnikovb687fdd2017-07-30 06:43:03 +0000232 // The method InitListExpr::getSyntacticForm may return nullptr indicating
233 // that the current initializer list also serves as its syntactic form.
Alexander Shaposhnikovbf3c84c2016-09-02 02:56:07 +0000234 if (const auto *SyntacticForm = InitListEx->getSyntacticForm())
235 InitListEx = SyntacticForm;
236 // If there are no initializers we do not need to change anything.
237 if (!InitListEx->getNumInits())
238 return true;
239 if (InitListEx->getNumInits() != NewFieldsOrder.size()) {
240 llvm::errs() << "Currently only full initialization is supported\n";
241 return false;
242 }
243 for (unsigned i = 0, e = InitListEx->getNumInits(); i < e; ++i)
244 if (i != NewFieldsOrder[i])
Alexander Shaposhnikovb687fdd2017-07-30 06:43:03 +0000245 addReplacement(InitListEx->getInit(i)->getSourceRange(),
246 InitListEx->getInit(NewFieldsOrder[i])->getSourceRange(),
247 Context, Replacements);
Alexander Shaposhnikovbf3c84c2016-09-02 02:56:07 +0000248 return true;
249}
250
251namespace {
252class ReorderingConsumer : public ASTConsumer {
253 StringRef RecordName;
254 ArrayRef<std::string> DesiredFieldsOrder;
255 std::map<std::string, tooling::Replacements> &Replacements;
256
257public:
258 ReorderingConsumer(StringRef RecordName,
259 ArrayRef<std::string> DesiredFieldsOrder,
260 std::map<std::string, tooling::Replacements> &Replacements)
261 : RecordName(RecordName), DesiredFieldsOrder(DesiredFieldsOrder),
262 Replacements(Replacements) {}
263
264 ReorderingConsumer(const ReorderingConsumer &) = delete;
265 ReorderingConsumer &operator=(const ReorderingConsumer &) = delete;
266
267 void HandleTranslationUnit(ASTContext &Context) override {
Alexander Shaposhnikoveaf833c2017-07-20 21:41:20 +0000268 const RecordDecl *RD = findDefinition(RecordName, Context);
Alexander Shaposhnikovbf3c84c2016-09-02 02:56:07 +0000269 if (!RD)
270 return;
271 SmallVector<unsigned, 4> NewFieldsOrder =
272 getNewFieldsOrder(RD, DesiredFieldsOrder);
273 if (NewFieldsOrder.empty())
274 return;
275 if (!reorderFieldsInDefinition(RD, NewFieldsOrder, Context, Replacements))
276 return;
Alexander Shaposhnikovbf3c84c2016-09-02 02:56:07 +0000277
Alexander Shaposhnikoveaf833c2017-07-20 21:41:20 +0000278 // 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),
Alexander Shaposhnikovb687fdd2017-07-30 06:43:03 +0000284 NewFieldsOrder, Context, Replacements);
Alexander Shaposhnikoveaf833c2017-07-20 21:41:20 +0000285
Alexander Shaposhnikovb687fdd2017-07-30 06:43:03 +0000286 // We only need to reorder init list expressions for
Alexander Shaposhnikoveaf833c2017-07-20 21:41:20 +0000287 // plain C structs or C++ aggregate types.
Alexander Shaposhnikovbf3c84c2016-09-02 02:56:07 +0000288 // For other types the order of constructor parameters is used,
289 // which we don't change at the moment.
290 // Now (v0) partial initialization is not supported.
Alexander Shaposhnikoveaf833c2017-07-20 21:41:20 +0000291 if (!CXXRD || CXXRD->isAggregate())
Alexander Shaposhnikovbf3c84c2016-09-02 02:56:07 +0000292 for (auto Result :
293 match(initListExpr(hasType(equalsNode(RD))).bind("initListExpr"),
294 Context))
295 if (!reorderFieldsInInitListExpr(
296 Result.getNodeAs<InitListExpr>("initListExpr"), NewFieldsOrder,
297 Context, Replacements)) {
298 Replacements.clear();
299 return;
300 }
301 }
302};
303} // end anonymous namespace
304
305std::unique_ptr<ASTConsumer> ReorderFieldsAction::newASTConsumer() {
306 return llvm::make_unique<ReorderingConsumer>(RecordName, DesiredFieldsOrder,
307 Replacements);
308}
309
310} // namespace reorder_fields
311} // namespace clang