[SE] Make Kernel movable
[lldb.git] / clang-tools-extra / clang-reorder-fields / ReorderFieldsAction.cpp
1 //===-- 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"
25 #include <algorithm>
26 #include <string>
27
28 namespace clang {
29 namespace reorder_fields {
30 using namespace clang::ast_matchers;
31
32 /// \brief Finds the definition of a record by name.
33 ///
34 /// \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);
40   if (Results.empty()) {
41     llvm::errs() << "Definition of " << RecordName << "  not found\n";
42     return nullptr;
43   }
44   if (Results.size() > 1) {
45     llvm::errs() << "The name " << RecordName
46                  << " is ambiguous, several definitions found\n";
47     return nullptr;
48   }
49   return selectFirst<CXXRecordDecl>("cxxRecordDecl", Results);
50 }
51
52 /// \brief Calculates the new order of fields.
53 ///
54 /// \returns empty vector if the list of fields doesn't match the definition.
55 static SmallVector<unsigned, 4>
56 getNewFieldsOrder(const CXXRecordDecl *Definition,
57                   ArrayRef<std::string> DesiredFieldsOrder) {
58   assert(Definition && "Definition is null");
59
60   llvm::StringMap<unsigned> NameToIndex;
61   for (const auto *Field : Definition->fields())
62     NameToIndex[Field->getName()] = Field->getFieldIndex();
63
64   if (DesiredFieldsOrder.size() != NameToIndex.size()) {
65     llvm::errs() << "Number of provided fields doesn't match definition.\n";
66     return {};
67   }
68   SmallVector<unsigned, 4> NewFieldsOrder;
69   for (const auto &Name : DesiredFieldsOrder) {
70     if (!NameToIndex.count(Name)) {
71       llvm::errs() << "Field " << Name << " not found in definition.\n";
72       return {};
73     }
74     NewFieldsOrder.push_back(NameToIndex[Name]);
75   }
76   assert(NewFieldsOrder.size() == NameToIndex.size());
77   return NewFieldsOrder;
78 }
79
80 // FIXME: error-handling
81 /// \brief Replaces one range of source code by another.
82 static void
83 addReplacement(SourceRange Old, SourceRange New, const ASTContext &Context,
84                std::map<std::string, tooling::Replacements> &Replacements) {
85   StringRef NewText =
86       Lexer::getSourceText(CharSourceRange::getTokenRange(New),
87                            Context.getSourceManager(), Context.getLangOpts());
88   tooling::Replacement R(Context.getSourceManager(),
89                          CharSourceRange::getTokenRange(Old), NewText,
90                          Context.getLangOpts());
91   consumeError(Replacements[R.getFilePath()].add(R));
92 }
93
94 /// \brief Reorders fields in the definition of a struct/class.
95 ///
96 /// At the moment reodering of fields with
97 /// different accesses (public/protected/private) is not supported.
98 /// \returns true on success.
99 static bool reorderFieldsInDefinition(
100     const CXXRecordDecl *Definition, ArrayRef<unsigned> NewFieldsOrder,
101     const ASTContext &Context,
102     std::map<std::string, tooling::Replacements> &Replacements) {
103   assert(Definition && "Definition is null");
104
105   SmallVector<const FieldDecl *, 10> Fields;
106   for (const auto *Field : Definition->fields())
107     Fields.push_back(Field);
108
109   // Check that the permutation of the fields doesn't change the accesses
110   for (const auto *Field : Definition->fields()) {
111     const auto FieldIndex = Field->getFieldIndex();
112     if (Field->getAccess() != Fields[NewFieldsOrder[FieldIndex]]->getAccess()) {
113       llvm::errs() << "Currently reodering of fields with different accesses "
114                       "is not supported\n";
115       return false;
116     }
117   }
118
119   for (const auto *Field : Definition->fields()) {
120     const auto FieldIndex = Field->getFieldIndex();
121     if (FieldIndex == NewFieldsOrder[FieldIndex])
122       continue;
123     addReplacement(Field->getSourceRange(),
124                    Fields[NewFieldsOrder[FieldIndex]]->getSourceRange(),
125                    Context, Replacements);
126   }
127   return true;
128 }
129
130 /// \brief Reorders initializers in a C++ struct/class constructor.
131 ///
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.
134 static void reorderFieldsInConstructor(
135     const CXXConstructorDecl *CtorDecl, ArrayRef<unsigned> NewFieldsOrder,
136     const ASTContext &Context,
137     std::map<std::string, tooling::Replacements> &Replacements) {
138   assert(CtorDecl && "Constructor declaration is null");
139   assert(CtorDecl->isThisDeclarationADefinition() && "Not a definition");
140   if (CtorDecl->isImplicit() || CtorDecl->getNumCtorInitializers() <= 1)
141     return;
142
143   SmallVector<unsigned, 10> NewFieldsPositions(NewFieldsOrder.size());
144   for (unsigned i = 0, e = NewFieldsOrder.size(); i < e; ++i)
145     NewFieldsPositions[NewFieldsOrder[i]] = i;
146
147   SmallVector<const CXXCtorInitializer *, 10> OldWrittenInitializersOrder;
148   SmallVector<const CXXCtorInitializer *, 10> NewWrittenInitializersOrder;
149   for (const auto *Initializer : CtorDecl->inits()) {
150     if (!Initializer->isWritten())
151       continue;
152     OldWrittenInitializersOrder.push_back(Initializer);
153     NewWrittenInitializersOrder.push_back(Initializer);
154   }
155   auto ByFieldNewPosition = [&](const CXXCtorInitializer *LHS,
156                                 const CXXCtorInitializer *RHS) {
157     assert(LHS && RHS);
158     return NewFieldsPositions[LHS->getMember()->getFieldIndex()] <
159            NewFieldsPositions[RHS->getMember()->getFieldIndex()];
160   };
161   std::sort(std::begin(NewWrittenInitializersOrder),
162             std::end(NewWrittenInitializersOrder), ByFieldNewPosition);
163   assert(OldWrittenInitializersOrder.size() ==
164          NewWrittenInitializersOrder.size());
165   for (unsigned i = 0, e = NewWrittenInitializersOrder.size(); i < e; ++i)
166     if (OldWrittenInitializersOrder[i] != NewWrittenInitializersOrder[i])
167       addReplacement(OldWrittenInitializersOrder[i]->getSourceRange(),
168                      NewWrittenInitializersOrder[i]->getSourceRange(), Context,
169                      Replacements);
170 }
171
172 /// \brief Reorders initializers in the brace initialization of an aggregate.
173 ///
174 /// At the moment partial initialization is not supported.
175 /// \returns true on success
176 static bool reorderFieldsInInitListExpr(
177     const InitListExpr *InitListEx, ArrayRef<unsigned> NewFieldsOrder,
178     const ASTContext &Context,
179     std::map<std::string, tooling::Replacements> &Replacements) {
180   assert(InitListEx && "Init list expression is null");
181   // We care only about InitListExprs which originate from source code. 
182   // Implicit InitListExprs are created by the semantic analyzer.
183   if (!InitListEx->isExplicit())
184     return true;
185   // The method InitListExpr::getSyntacticForm may return nullptr indicating that
186   // the current initializer list also serves as its syntactic form.
187   if (const auto *SyntacticForm = InitListEx->getSyntacticForm())
188     InitListEx = SyntacticForm;
189   // If there are no initializers we do not need to change anything.
190   if (!InitListEx->getNumInits())
191     return true;
192   if (InitListEx->getNumInits() != NewFieldsOrder.size()) {
193     llvm::errs() << "Currently only full initialization is supported\n";
194     return false;
195   }
196   for (unsigned i = 0, e = InitListEx->getNumInits(); i < e; ++i)
197     if (i != NewFieldsOrder[i])
198       addReplacement(
199           InitListEx->getInit(i)->getSourceRange(),
200           InitListEx->getInit(NewFieldsOrder[i])->getSourceRange(), Context,
201           Replacements);
202   return true;
203 }
204
205 namespace {
206 class ReorderingConsumer : public ASTConsumer {
207   StringRef RecordName;
208   ArrayRef<std::string> DesiredFieldsOrder;
209   std::map<std::string, tooling::Replacements> &Replacements;
210
211 public:
212   ReorderingConsumer(StringRef RecordName,
213                      ArrayRef<std::string> DesiredFieldsOrder,
214                      std::map<std::string, tooling::Replacements> &Replacements)
215       : RecordName(RecordName), DesiredFieldsOrder(DesiredFieldsOrder),
216         Replacements(Replacements) {}
217
218   ReorderingConsumer(const ReorderingConsumer &) = delete;
219   ReorderingConsumer &operator=(const ReorderingConsumer &) = delete;
220
221   void HandleTranslationUnit(ASTContext &Context) override {
222     const CXXRecordDecl *RD = findDefinition(RecordName, Context);
223     if (!RD)
224       return;
225     SmallVector<unsigned, 4> NewFieldsOrder =
226         getNewFieldsOrder(RD, DesiredFieldsOrder);
227     if (NewFieldsOrder.empty())
228       return;
229     if (!reorderFieldsInDefinition(RD, NewFieldsOrder, Context, Replacements))
230       return;
231     for (const auto *C : RD->ctors())
232       if (const auto *D = dyn_cast<CXXConstructorDecl>(C->getDefinition()))
233         reorderFieldsInConstructor(cast<const CXXConstructorDecl>(D),
234                                    NewFieldsOrder, Context, Replacements);
235
236     // We only need to reorder init list expressions for aggregate types.
237     // For other types the order of constructor parameters is used,
238     // which we don't change at the moment.
239     // Now (v0) partial initialization is not supported.
240     if (RD->isAggregate())
241       for (auto Result :
242            match(initListExpr(hasType(equalsNode(RD))).bind("initListExpr"),
243                  Context))
244         if (!reorderFieldsInInitListExpr(
245                 Result.getNodeAs<InitListExpr>("initListExpr"), NewFieldsOrder,
246                 Context, Replacements)) {
247           Replacements.clear();
248           return;
249         }
250   }
251 };
252 } // end anonymous namespace
253
254 std::unique_ptr<ASTConsumer> ReorderFieldsAction::newASTConsumer() {
255   return llvm::make_unique<ReorderingConsumer>(RecordName, DesiredFieldsOrder,
256                                                Replacements);
257 }
258
259 } // namespace reorder_fields
260 } // namespace clang