Merge branch 'addmainunit4-altlink-sharedstmt-dieref-dwz3-inlinebug-testcategory...
[lldb.git] / clang-tools-extra / clang-reorder-fields / ReorderFieldsAction.cpp
1 //===-- tools/extra/clang-reorder-fields/ReorderFieldsAction.cpp -*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// \file
10 /// This file contains the definition of the
11 /// ReorderFieldsAction::newASTConsumer method
12 ///
13 //===----------------------------------------------------------------------===//
14
15 #include "ReorderFieldsAction.h"
16 #include "clang/AST/AST.h"
17 #include "clang/AST/ASTConsumer.h"
18 #include "clang/AST/ASTContext.h"
19 #include "clang/AST/Decl.h"
20 #include "clang/AST/RecursiveASTVisitor.h"
21 #include "clang/ASTMatchers/ASTMatchFinder.h"
22 #include "clang/Lex/Lexer.h"
23 #include "clang/Tooling/Refactoring.h"
24 #include "llvm/ADT/SetVector.h"
25 #include <algorithm>
26 #include <string>
27
28 namespace clang {
29 namespace reorder_fields {
30 using namespace clang::ast_matchers;
31 using llvm::SmallSetVector;
32
33 /// Finds the definition of a record by name.
34 ///
35 /// \returns nullptr if the name is ambiguous or not found.
36 static const RecordDecl *findDefinition(StringRef RecordName,
37                                         ASTContext &Context) {
38   auto Results =
39       match(recordDecl(hasName(RecordName), isDefinition()).bind("recordDecl"),
40             Context);
41   if (Results.empty()) {
42     llvm::errs() << "Definition of " << RecordName << "  not found\n";
43     return nullptr;
44   }
45   if (Results.size() > 1) {
46     llvm::errs() << "The name " << RecordName
47                  << " is ambiguous, several definitions found\n";
48     return nullptr;
49   }
50   return selectFirst<RecordDecl>("recordDecl", Results);
51 }
52
53 /// Calculates the new order of fields.
54 ///
55 /// \returns empty vector if the list of fields doesn't match the definition.
56 static SmallVector<unsigned, 4>
57 getNewFieldsOrder(const RecordDecl *Definition,
58                   ArrayRef<std::string> DesiredFieldsOrder) {
59   assert(Definition && "Definition is null");
60
61   llvm::StringMap<unsigned> NameToIndex;
62   for (const auto *Field : Definition->fields())
63     NameToIndex[Field->getName()] = Field->getFieldIndex();
64
65   if (DesiredFieldsOrder.size() != NameToIndex.size()) {
66     llvm::errs() << "Number of provided fields doesn't match definition.\n";
67     return {};
68   }
69   SmallVector<unsigned, 4> NewFieldsOrder;
70   for (const auto &Name : DesiredFieldsOrder) {
71     if (!NameToIndex.count(Name)) {
72       llvm::errs() << "Field " << Name << " not found in definition.\n";
73       return {};
74     }
75     NewFieldsOrder.push_back(NameToIndex[Name]);
76   }
77   assert(NewFieldsOrder.size() == NameToIndex.size());
78   return NewFieldsOrder;
79 }
80
81 // FIXME: error-handling
82 /// Replaces one range of source code by another.
83 static void
84 addReplacement(SourceRange Old, SourceRange New, const ASTContext &Context,
85                std::map<std::string, tooling::Replacements> &Replacements) {
86   StringRef NewText =
87       Lexer::getSourceText(CharSourceRange::getTokenRange(New),
88                            Context.getSourceManager(), Context.getLangOpts());
89   tooling::Replacement R(Context.getSourceManager(),
90                          CharSourceRange::getTokenRange(Old), NewText,
91                          Context.getLangOpts());
92   consumeError(Replacements[std::string(R.getFilePath())].add(R));
93 }
94
95 /// Find all member fields used in the given init-list initializer expr
96 /// that belong to the same record
97 ///
98 /// \returns a set of field declarations, empty if none were present
99 static SmallSetVector<FieldDecl *, 1>
100 findMembersUsedInInitExpr(const CXXCtorInitializer *Initializer,
101                           ASTContext &Context) {
102   SmallSetVector<FieldDecl *, 1> Results;
103   // Note that this does not pick up member fields of base classes since
104   // for those accesses Sema::PerformObjectMemberConversion always inserts an
105   // UncheckedDerivedToBase ImplicitCastExpr between the this expr and the
106   // object expression
107   auto FoundExprs = match(
108       traverse(
109           TK_AsIs,
110           findAll(memberExpr(hasObjectExpression(cxxThisExpr())).bind("ME"))),
111       *Initializer->getInit(), Context);
112   for (BoundNodes &BN : FoundExprs)
113     if (auto *MemExpr = BN.getNodeAs<MemberExpr>("ME"))
114       if (auto *FD = dyn_cast<FieldDecl>(MemExpr->getMemberDecl()))
115         Results.insert(FD);
116   return Results;
117 }
118
119 /// Reorders fields in the definition of a struct/class.
120 ///
121 /// At the moment reordering of fields with
122 /// different accesses (public/protected/private) is not supported.
123 /// \returns true on success.
124 static bool reorderFieldsInDefinition(
125     const RecordDecl *Definition, ArrayRef<unsigned> NewFieldsOrder,
126     const ASTContext &Context,
127     std::map<std::string, tooling::Replacements> &Replacements) {
128   assert(Definition && "Definition is null");
129
130   SmallVector<const FieldDecl *, 10> Fields;
131   for (const auto *Field : Definition->fields())
132     Fields.push_back(Field);
133
134   // Check that the permutation of the fields doesn't change the accesses
135   for (const auto *Field : Definition->fields()) {
136     const auto FieldIndex = Field->getFieldIndex();
137     if (Field->getAccess() != Fields[NewFieldsOrder[FieldIndex]]->getAccess()) {
138       llvm::errs() << "Currently reordering of fields with different accesses "
139                       "is not supported\n";
140       return false;
141     }
142   }
143
144   for (const auto *Field : Definition->fields()) {
145     const auto FieldIndex = Field->getFieldIndex();
146     if (FieldIndex == NewFieldsOrder[FieldIndex])
147       continue;
148     addReplacement(Field->getSourceRange(),
149                    Fields[NewFieldsOrder[FieldIndex]]->getSourceRange(),
150                    Context, Replacements);
151   }
152   return true;
153 }
154
155 /// Reorders initializers in a C++ struct/class constructor.
156 ///
157 /// A constructor can have initializers for an arbitrary subset of the class's
158 /// fields. Thus, we need to ensure that we reorder just the initializers that
159 /// are present.
160 static void reorderFieldsInConstructor(
161     const CXXConstructorDecl *CtorDecl, ArrayRef<unsigned> NewFieldsOrder,
162     ASTContext &Context,
163     std::map<std::string, tooling::Replacements> &Replacements) {
164   assert(CtorDecl && "Constructor declaration is null");
165   if (CtorDecl->isImplicit() || CtorDecl->getNumCtorInitializers() <= 1)
166     return;
167
168   // The method FunctionDecl::isThisDeclarationADefinition returns false
169   // for a defaulted function unless that function has been implicitly defined.
170   // Thus this assert needs to be after the previous checks.
171   assert(CtorDecl->isThisDeclarationADefinition() && "Not a definition");
172
173   SmallVector<unsigned, 10> NewFieldsPositions(NewFieldsOrder.size());
174   for (unsigned i = 0, e = NewFieldsOrder.size(); i < e; ++i)
175     NewFieldsPositions[NewFieldsOrder[i]] = i;
176
177   SmallVector<const CXXCtorInitializer *, 10> OldWrittenInitializersOrder;
178   SmallVector<const CXXCtorInitializer *, 10> NewWrittenInitializersOrder;
179   for (const auto *Initializer : CtorDecl->inits()) {
180     if (!Initializer->isMemberInitializer() || !Initializer->isWritten())
181       continue;
182
183     // Warn if this reordering violates initialization expr dependencies.
184     const FieldDecl *ThisM = Initializer->getMember();
185     const auto UsedMembers = findMembersUsedInInitExpr(Initializer, Context);
186     for (const FieldDecl *UM : UsedMembers) {
187       if (NewFieldsPositions[UM->getFieldIndex()] >
188           NewFieldsPositions[ThisM->getFieldIndex()]) {
189         DiagnosticsEngine &DiagEngine = Context.getDiagnostics();
190         auto Description = ("reordering field " + UM->getName() + " after " +
191                             ThisM->getName() + " makes " + UM->getName() +
192                             " uninitialized when used in init expression")
193                                .str();
194         unsigned ID = DiagEngine.getDiagnosticIDs()->getCustomDiagID(
195             DiagnosticIDs::Warning, Description);
196         DiagEngine.Report(Initializer->getSourceLocation(), ID);
197       }
198     }
199
200     OldWrittenInitializersOrder.push_back(Initializer);
201     NewWrittenInitializersOrder.push_back(Initializer);
202   }
203   auto ByFieldNewPosition = [&](const CXXCtorInitializer *LHS,
204                                 const CXXCtorInitializer *RHS) {
205     assert(LHS && RHS);
206     return NewFieldsPositions[LHS->getMember()->getFieldIndex()] <
207            NewFieldsPositions[RHS->getMember()->getFieldIndex()];
208   };
209   std::sort(std::begin(NewWrittenInitializersOrder),
210             std::end(NewWrittenInitializersOrder), ByFieldNewPosition);
211   assert(OldWrittenInitializersOrder.size() ==
212          NewWrittenInitializersOrder.size());
213   for (unsigned i = 0, e = NewWrittenInitializersOrder.size(); i < e; ++i)
214     if (OldWrittenInitializersOrder[i] != NewWrittenInitializersOrder[i])
215       addReplacement(OldWrittenInitializersOrder[i]->getSourceRange(),
216                      NewWrittenInitializersOrder[i]->getSourceRange(), Context,
217                      Replacements);
218 }
219
220 /// Reorders initializers in the brace initialization of an aggregate.
221 ///
222 /// At the moment partial initialization is not supported.
223 /// \returns true on success
224 static bool reorderFieldsInInitListExpr(
225     const InitListExpr *InitListEx, ArrayRef<unsigned> NewFieldsOrder,
226     const ASTContext &Context,
227     std::map<std::string, tooling::Replacements> &Replacements) {
228   assert(InitListEx && "Init list expression is null");
229   // We care only about InitListExprs which originate from source code.
230   // Implicit InitListExprs are created by the semantic analyzer.
231   if (!InitListEx->isExplicit())
232     return true;
233   // The method InitListExpr::getSyntacticForm may return nullptr indicating
234   // that the current initializer list also serves as its syntactic form.
235   if (const auto *SyntacticForm = InitListEx->getSyntacticForm())
236     InitListEx = SyntacticForm;
237   // If there are no initializers we do not need to change anything.
238   if (!InitListEx->getNumInits())
239     return true;
240   if (InitListEx->getNumInits() != NewFieldsOrder.size()) {
241     llvm::errs() << "Currently only full initialization is supported\n";
242     return false;
243   }
244   for (unsigned i = 0, e = InitListEx->getNumInits(); i < e; ++i)
245     if (i != NewFieldsOrder[i])
246       addReplacement(InitListEx->getInit(i)->getSourceRange(),
247                      InitListEx->getInit(NewFieldsOrder[i])->getSourceRange(),
248                      Context, Replacements);
249   return true;
250 }
251
252 namespace {
253 class ReorderingConsumer : public ASTConsumer {
254   StringRef RecordName;
255   ArrayRef<std::string> DesiredFieldsOrder;
256   std::map<std::string, tooling::Replacements> &Replacements;
257
258 public:
259   ReorderingConsumer(StringRef RecordName,
260                      ArrayRef<std::string> DesiredFieldsOrder,
261                      std::map<std::string, tooling::Replacements> &Replacements)
262       : RecordName(RecordName), DesiredFieldsOrder(DesiredFieldsOrder),
263         Replacements(Replacements) {}
264
265   ReorderingConsumer(const ReorderingConsumer &) = delete;
266   ReorderingConsumer &operator=(const ReorderingConsumer &) = delete;
267
268   void HandleTranslationUnit(ASTContext &Context) override {
269     const RecordDecl *RD = findDefinition(RecordName, Context);
270     if (!RD)
271       return;
272     SmallVector<unsigned, 4> NewFieldsOrder =
273         getNewFieldsOrder(RD, DesiredFieldsOrder);
274     if (NewFieldsOrder.empty())
275       return;
276     if (!reorderFieldsInDefinition(RD, NewFieldsOrder, Context, Replacements))
277       return;
278
279     // CXXRD will be nullptr if C code (not C++) is being processed.
280     const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD);
281     if (CXXRD)
282       for (const auto *C : CXXRD->ctors())
283         if (const auto *D = dyn_cast<CXXConstructorDecl>(C->getDefinition()))
284           reorderFieldsInConstructor(cast<const CXXConstructorDecl>(D),
285                                      NewFieldsOrder, Context, Replacements);
286
287     // We only need to reorder init list expressions for
288     // plain C structs or C++ aggregate types.
289     // For other types the order of constructor parameters is used,
290     // which we don't change at the moment.
291     // Now (v0) partial initialization is not supported.
292     if (!CXXRD || CXXRD->isAggregate())
293       for (auto Result :
294            match(initListExpr(hasType(equalsNode(RD))).bind("initListExpr"),
295                  Context))
296         if (!reorderFieldsInInitListExpr(
297                 Result.getNodeAs<InitListExpr>("initListExpr"), NewFieldsOrder,
298                 Context, Replacements)) {
299           Replacements.clear();
300           return;
301         }
302   }
303 };
304 } // end anonymous namespace
305
306 std::unique_ptr<ASTConsumer> ReorderFieldsAction::newASTConsumer() {
307   return std::make_unique<ReorderingConsumer>(RecordName, DesiredFieldsOrder,
308                                                Replacements);
309 }
310
311 } // namespace reorder_fields
312 } // namespace clang