xref: /llvm-project/clang-tools-extra/clang-reorder-fields/ReorderFieldsAction.cpp (revision fbd86d05fe51d45f19df8d63aee41d979c268f8f)
1bf3c84cfSAlexander Shaposhnikov //===-- tools/extra/clang-reorder-fields/ReorderFieldsAction.cpp -*- C++ -*-===//
2bf3c84cfSAlexander Shaposhnikov //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6bf3c84cfSAlexander Shaposhnikov //
7bf3c84cfSAlexander Shaposhnikov //===----------------------------------------------------------------------===//
8bf3c84cfSAlexander Shaposhnikov ///
9bf3c84cfSAlexander Shaposhnikov /// \file
10bf3c84cfSAlexander Shaposhnikov /// This file contains the definition of the
11bf3c84cfSAlexander Shaposhnikov /// ReorderFieldsAction::newASTConsumer method
12bf3c84cfSAlexander Shaposhnikov ///
13bf3c84cfSAlexander Shaposhnikov //===----------------------------------------------------------------------===//
14bf3c84cfSAlexander Shaposhnikov 
15bf3c84cfSAlexander Shaposhnikov #include "ReorderFieldsAction.h"
16bf3c84cfSAlexander Shaposhnikov #include "clang/AST/AST.h"
17bf3c84cfSAlexander Shaposhnikov #include "clang/AST/ASTConsumer.h"
18bf3c84cfSAlexander Shaposhnikov #include "clang/AST/ASTContext.h"
19bf3c84cfSAlexander Shaposhnikov #include "clang/AST/Decl.h"
20bf3c84cfSAlexander Shaposhnikov #include "clang/AST/RecursiveASTVisitor.h"
21bf3c84cfSAlexander Shaposhnikov #include "clang/ASTMatchers/ASTMatchFinder.h"
22bf3c84cfSAlexander Shaposhnikov #include "clang/Lex/Lexer.h"
23bf3c84cfSAlexander Shaposhnikov #include "clang/Tooling/Refactoring.h"
24aba43035SDmitri Gribenko #include "llvm/ADT/STLExtras.h"
25b687fddaSAlexander Shaposhnikov #include "llvm/ADT/SetVector.h"
26bf3c84cfSAlexander Shaposhnikov #include <string>
27bf3c84cfSAlexander Shaposhnikov 
28bf3c84cfSAlexander Shaposhnikov namespace clang {
29bf3c84cfSAlexander Shaposhnikov namespace reorder_fields {
30bf3c84cfSAlexander Shaposhnikov using namespace clang::ast_matchers;
31b687fddaSAlexander Shaposhnikov using llvm::SmallSetVector;
32bf3c84cfSAlexander Shaposhnikov 
33282dc72cSDmitri Gribenko /// Finds the definition of a record by name.
34bf3c84cfSAlexander Shaposhnikov ///
35bf3c84cfSAlexander Shaposhnikov /// \returns nullptr if the name is ambiguous or not found.
36eaf833caSAlexander Shaposhnikov static const RecordDecl *findDefinition(StringRef RecordName,
37bf3c84cfSAlexander Shaposhnikov                                         ASTContext &Context) {
38b687fddaSAlexander Shaposhnikov   auto Results =
394e3f4f03SBenjamin Kramer       match(recordDecl(hasName(RecordName), isDefinition()).bind("recordDecl"),
40bf3c84cfSAlexander Shaposhnikov             Context);
41bf3c84cfSAlexander Shaposhnikov   if (Results.empty()) {
42bf3c84cfSAlexander Shaposhnikov     llvm::errs() << "Definition of " << RecordName << "  not found\n";
43bf3c84cfSAlexander Shaposhnikov     return nullptr;
44bf3c84cfSAlexander Shaposhnikov   }
45bf3c84cfSAlexander Shaposhnikov   if (Results.size() > 1) {
46bf3c84cfSAlexander Shaposhnikov     llvm::errs() << "The name " << RecordName
47bf3c84cfSAlexander Shaposhnikov                  << " is ambiguous, several definitions found\n";
48bf3c84cfSAlexander Shaposhnikov     return nullptr;
49bf3c84cfSAlexander Shaposhnikov   }
50eaf833caSAlexander Shaposhnikov   return selectFirst<RecordDecl>("recordDecl", Results);
51bf3c84cfSAlexander Shaposhnikov }
52bf3c84cfSAlexander Shaposhnikov 
53282dc72cSDmitri Gribenko /// Calculates the new order of fields.
54bf3c84cfSAlexander Shaposhnikov ///
55bf3c84cfSAlexander Shaposhnikov /// \returns empty vector if the list of fields doesn't match the definition.
56bf3c84cfSAlexander Shaposhnikov static SmallVector<unsigned, 4>
57eaf833caSAlexander Shaposhnikov getNewFieldsOrder(const RecordDecl *Definition,
58bf3c84cfSAlexander Shaposhnikov                   ArrayRef<std::string> DesiredFieldsOrder) {
59bf3c84cfSAlexander Shaposhnikov   assert(Definition && "Definition is null");
60bf3c84cfSAlexander Shaposhnikov 
61bf3c84cfSAlexander Shaposhnikov   llvm::StringMap<unsigned> NameToIndex;
62bf3c84cfSAlexander Shaposhnikov   for (const auto *Field : Definition->fields())
63bf3c84cfSAlexander Shaposhnikov     NameToIndex[Field->getName()] = Field->getFieldIndex();
64bf3c84cfSAlexander Shaposhnikov 
65bf3c84cfSAlexander Shaposhnikov   if (DesiredFieldsOrder.size() != NameToIndex.size()) {
666affc183SClement Courbet     llvm::errs() << "Number of provided fields (" << DesiredFieldsOrder.size()
676affc183SClement Courbet                  << ") doesn't match definition (" << NameToIndex.size()
686affc183SClement Courbet                  << ").\n";
69bf3c84cfSAlexander Shaposhnikov     return {};
70bf3c84cfSAlexander Shaposhnikov   }
71bf3c84cfSAlexander Shaposhnikov   SmallVector<unsigned, 4> NewFieldsOrder;
72bf3c84cfSAlexander Shaposhnikov   for (const auto &Name : DesiredFieldsOrder) {
73bf3c84cfSAlexander Shaposhnikov     if (!NameToIndex.count(Name)) {
74bf3c84cfSAlexander Shaposhnikov       llvm::errs() << "Field " << Name << " not found in definition.\n";
75bf3c84cfSAlexander Shaposhnikov       return {};
76bf3c84cfSAlexander Shaposhnikov     }
77bf3c84cfSAlexander Shaposhnikov     NewFieldsOrder.push_back(NameToIndex[Name]);
78bf3c84cfSAlexander Shaposhnikov   }
79bf3c84cfSAlexander Shaposhnikov   assert(NewFieldsOrder.size() == NameToIndex.size());
80bf3c84cfSAlexander Shaposhnikov   return NewFieldsOrder;
81bf3c84cfSAlexander Shaposhnikov }
82bf3c84cfSAlexander Shaposhnikov 
83bf3c84cfSAlexander Shaposhnikov // FIXME: error-handling
84282dc72cSDmitri Gribenko /// Replaces one range of source code by another.
85bf3c84cfSAlexander Shaposhnikov static void
86bf3c84cfSAlexander Shaposhnikov addReplacement(SourceRange Old, SourceRange New, const ASTContext &Context,
87bf3c84cfSAlexander Shaposhnikov                std::map<std::string, tooling::Replacements> &Replacements) {
88bf3c84cfSAlexander Shaposhnikov   StringRef NewText =
89bf3c84cfSAlexander Shaposhnikov       Lexer::getSourceText(CharSourceRange::getTokenRange(New),
90bf3c84cfSAlexander Shaposhnikov                            Context.getSourceManager(), Context.getLangOpts());
91bf3c84cfSAlexander Shaposhnikov   tooling::Replacement R(Context.getSourceManager(),
92bf3c84cfSAlexander Shaposhnikov                          CharSourceRange::getTokenRange(Old), NewText,
93bf3c84cfSAlexander Shaposhnikov                          Context.getLangOpts());
94adcd0268SBenjamin Kramer   consumeError(Replacements[std::string(R.getFilePath())].add(R));
95bf3c84cfSAlexander Shaposhnikov }
96bf3c84cfSAlexander Shaposhnikov 
97282dc72cSDmitri Gribenko /// Find all member fields used in the given init-list initializer expr
98b687fddaSAlexander Shaposhnikov /// that belong to the same record
99b687fddaSAlexander Shaposhnikov ///
100b687fddaSAlexander Shaposhnikov /// \returns a set of field declarations, empty if none were present
101b687fddaSAlexander Shaposhnikov static SmallSetVector<FieldDecl *, 1>
102b687fddaSAlexander Shaposhnikov findMembersUsedInInitExpr(const CXXCtorInitializer *Initializer,
103b687fddaSAlexander Shaposhnikov                           ASTContext &Context) {
104b687fddaSAlexander Shaposhnikov   SmallSetVector<FieldDecl *, 1> Results;
105b687fddaSAlexander Shaposhnikov   // Note that this does not pick up member fields of base classes since
106b687fddaSAlexander Shaposhnikov   // for those accesses Sema::PerformObjectMemberConversion always inserts an
107b687fddaSAlexander Shaposhnikov   // UncheckedDerivedToBase ImplicitCastExpr between the this expr and the
108b687fddaSAlexander Shaposhnikov   // object expression
1098d62eba1SStephen Kelly   auto FoundExprs = match(
1108d62eba1SStephen Kelly       traverse(
1118d62eba1SStephen Kelly           TK_AsIs,
1128d62eba1SStephen Kelly           findAll(memberExpr(hasObjectExpression(cxxThisExpr())).bind("ME"))),
113b687fddaSAlexander Shaposhnikov       *Initializer->getInit(), Context);
114b687fddaSAlexander Shaposhnikov   for (BoundNodes &BN : FoundExprs)
115b687fddaSAlexander Shaposhnikov     if (auto *MemExpr = BN.getNodeAs<MemberExpr>("ME"))
116b687fddaSAlexander Shaposhnikov       if (auto *FD = dyn_cast<FieldDecl>(MemExpr->getMemberDecl()))
117b687fddaSAlexander Shaposhnikov         Results.insert(FD);
118b687fddaSAlexander Shaposhnikov   return Results;
119b687fddaSAlexander Shaposhnikov }
120b687fddaSAlexander Shaposhnikov 
121*fbd86d05SClement Courbet /// Returns the start of the leading comments before `Loc`.
122*fbd86d05SClement Courbet static SourceLocation getStartOfLeadingComment(SourceLocation Loc,
123*fbd86d05SClement Courbet                                                const SourceManager &SM,
124*fbd86d05SClement Courbet                                                const LangOptions &LangOpts) {
125*fbd86d05SClement Courbet   // We consider any leading comment token that is on the same line or
126*fbd86d05SClement Courbet   // indented similarly to the first comment to be part of the leading comment.
127*fbd86d05SClement Courbet   const unsigned Line = SM.getPresumedLineNumber(Loc);
128*fbd86d05SClement Courbet   const unsigned Column = SM.getPresumedColumnNumber(Loc);
129*fbd86d05SClement Courbet   std::optional<Token> Tok =
130*fbd86d05SClement Courbet       Lexer::findPreviousToken(Loc, SM, LangOpts, /*IncludeComments=*/true);
131*fbd86d05SClement Courbet   while (Tok && Tok->is(tok::comment)) {
132*fbd86d05SClement Courbet     const SourceLocation CommentLoc =
133*fbd86d05SClement Courbet         Lexer::GetBeginningOfToken(Tok->getLocation(), SM, LangOpts);
134*fbd86d05SClement Courbet     if (SM.getPresumedLineNumber(CommentLoc) != Line &&
135*fbd86d05SClement Courbet         SM.getPresumedColumnNumber(CommentLoc) != Column) {
136*fbd86d05SClement Courbet       break;
137*fbd86d05SClement Courbet     }
138*fbd86d05SClement Courbet     Loc = CommentLoc;
139*fbd86d05SClement Courbet     Tok = Lexer::findPreviousToken(Loc, SM, LangOpts, /*IncludeComments=*/true);
140*fbd86d05SClement Courbet   }
141*fbd86d05SClement Courbet   return Loc;
142*fbd86d05SClement Courbet }
143*fbd86d05SClement Courbet 
1446affc183SClement Courbet /// Returns the end of the trailing comments after `Loc`.
1456affc183SClement Courbet static SourceLocation getEndOfTrailingComment(SourceLocation Loc,
1466affc183SClement Courbet                                               const SourceManager &SM,
1476affc183SClement Courbet                                               const LangOptions &LangOpts) {
1486affc183SClement Courbet   // We consider any following comment token that is indented more than the
1496affc183SClement Courbet   // first comment to be part of the trailing comment.
1506affc183SClement Courbet   const unsigned Column = SM.getPresumedColumnNumber(Loc);
15118196466SClement Courbet   std::optional<Token> Tok =
15218196466SClement Courbet       Lexer::findNextToken(Loc, SM, LangOpts, /*IncludeComments=*/true);
1536affc183SClement Courbet   while (Tok && Tok->is(tok::comment) &&
1546affc183SClement Courbet          SM.getPresumedColumnNumber(Tok->getLocation()) > Column) {
1556affc183SClement Courbet     Loc = Tok->getEndLoc();
15618196466SClement Courbet     Tok = Lexer::findNextToken(Loc, SM, LangOpts, /*IncludeComments=*/true);
1576affc183SClement Courbet   }
1586affc183SClement Courbet   return Loc;
1596affc183SClement Courbet }
1606affc183SClement Courbet 
1616affc183SClement Courbet /// Returns the full source range for the field declaration up to (including)
1626affc183SClement Courbet /// the trailing semicolumn, including potential macro invocations,
1636affc183SClement Courbet /// e.g. `int a GUARDED_BY(mu);`. If there is a trailing comment, include it.
16491285e26SClement Courbet static SourceRange getFullFieldSourceRange(const FieldDecl &Field,
16591285e26SClement Courbet                                            const ASTContext &Context) {
1666affc183SClement Courbet   const SourceRange Range = Field.getSourceRange();
1676affc183SClement Courbet   SourceLocation Begin = Range.getBegin();
16891285e26SClement Courbet   SourceLocation End = Range.getEnd();
16991285e26SClement Courbet   const SourceManager &SM = Context.getSourceManager();
17091285e26SClement Courbet   const LangOptions &LangOpts = Context.getLangOpts();
17191285e26SClement Courbet   while (true) {
17291285e26SClement Courbet     std::optional<Token> CurrentToken = Lexer::findNextToken(End, SM, LangOpts);
17391285e26SClement Courbet 
1746affc183SClement Courbet     if (!CurrentToken)
1756affc183SClement Courbet       return SourceRange(Begin, End);
17691285e26SClement Courbet 
17791285e26SClement Courbet     if (CurrentToken->is(tok::eof))
17891285e26SClement Courbet       return Range; // Something is wrong, return the original range.
1796affc183SClement Courbet 
18091285e26SClement Courbet     End = CurrentToken->getLastLoc();
1816affc183SClement Courbet 
1826affc183SClement Courbet     if (CurrentToken->is(tok::semi))
1836affc183SClement Courbet       break;
18491285e26SClement Courbet   }
185*fbd86d05SClement Courbet   Begin = getStartOfLeadingComment(Begin, SM, LangOpts);
1866affc183SClement Courbet   End = getEndOfTrailingComment(End, SM, LangOpts);
1876affc183SClement Courbet   return SourceRange(Begin, End);
18891285e26SClement Courbet }
18991285e26SClement Courbet 
190282dc72cSDmitri Gribenko /// Reorders fields in the definition of a struct/class.
191bf3c84cfSAlexander Shaposhnikov ///
192dd5571d5SKazuaki Ishizaki /// At the moment reordering of fields with
193bf3c84cfSAlexander Shaposhnikov /// different accesses (public/protected/private) is not supported.
194bf3c84cfSAlexander Shaposhnikov /// \returns true on success.
195bf3c84cfSAlexander Shaposhnikov static bool reorderFieldsInDefinition(
196eaf833caSAlexander Shaposhnikov     const RecordDecl *Definition, ArrayRef<unsigned> NewFieldsOrder,
197bf3c84cfSAlexander Shaposhnikov     const ASTContext &Context,
198bf3c84cfSAlexander Shaposhnikov     std::map<std::string, tooling::Replacements> &Replacements) {
199bf3c84cfSAlexander Shaposhnikov   assert(Definition && "Definition is null");
200bf3c84cfSAlexander Shaposhnikov 
201bf3c84cfSAlexander Shaposhnikov   SmallVector<const FieldDecl *, 10> Fields;
202bf3c84cfSAlexander Shaposhnikov   for (const auto *Field : Definition->fields())
203bf3c84cfSAlexander Shaposhnikov     Fields.push_back(Field);
204bf3c84cfSAlexander Shaposhnikov 
205bf3c84cfSAlexander Shaposhnikov   // Check that the permutation of the fields doesn't change the accesses
206bf3c84cfSAlexander Shaposhnikov   for (const auto *Field : Definition->fields()) {
207bf3c84cfSAlexander Shaposhnikov     const auto FieldIndex = Field->getFieldIndex();
208bf3c84cfSAlexander Shaposhnikov     if (Field->getAccess() != Fields[NewFieldsOrder[FieldIndex]]->getAccess()) {
209dd5571d5SKazuaki Ishizaki       llvm::errs() << "Currently reordering of fields with different accesses "
210bf3c84cfSAlexander Shaposhnikov                       "is not supported\n";
211bf3c84cfSAlexander Shaposhnikov       return false;
212bf3c84cfSAlexander Shaposhnikov     }
213bf3c84cfSAlexander Shaposhnikov   }
214bf3c84cfSAlexander Shaposhnikov 
215bf3c84cfSAlexander Shaposhnikov   for (const auto *Field : Definition->fields()) {
216bf3c84cfSAlexander Shaposhnikov     const auto FieldIndex = Field->getFieldIndex();
217bf3c84cfSAlexander Shaposhnikov     if (FieldIndex == NewFieldsOrder[FieldIndex])
218bf3c84cfSAlexander Shaposhnikov       continue;
21991285e26SClement Courbet     addReplacement(
22091285e26SClement Courbet         getFullFieldSourceRange(*Field, Context),
22191285e26SClement Courbet         getFullFieldSourceRange(*Fields[NewFieldsOrder[FieldIndex]], Context),
222bf3c84cfSAlexander Shaposhnikov         Context, Replacements);
223bf3c84cfSAlexander Shaposhnikov   }
224bf3c84cfSAlexander Shaposhnikov   return true;
225bf3c84cfSAlexander Shaposhnikov }
226bf3c84cfSAlexander Shaposhnikov 
227282dc72cSDmitri Gribenko /// Reorders initializers in a C++ struct/class constructor.
228bf3c84cfSAlexander Shaposhnikov ///
229b687fddaSAlexander Shaposhnikov /// A constructor can have initializers for an arbitrary subset of the class's
230b687fddaSAlexander Shaposhnikov /// fields. Thus, we need to ensure that we reorder just the initializers that
231b687fddaSAlexander Shaposhnikov /// are present.
232bf3c84cfSAlexander Shaposhnikov static void reorderFieldsInConstructor(
233bf3c84cfSAlexander Shaposhnikov     const CXXConstructorDecl *CtorDecl, ArrayRef<unsigned> NewFieldsOrder,
234b687fddaSAlexander Shaposhnikov     ASTContext &Context,
235bf3c84cfSAlexander Shaposhnikov     std::map<std::string, tooling::Replacements> &Replacements) {
236bf3c84cfSAlexander Shaposhnikov   assert(CtorDecl && "Constructor declaration is null");
237bf3c84cfSAlexander Shaposhnikov   if (CtorDecl->isImplicit() || CtorDecl->getNumCtorInitializers() <= 1)
238bf3c84cfSAlexander Shaposhnikov     return;
239bf3c84cfSAlexander Shaposhnikov 
240bf3c84cfSAlexander Shaposhnikov   // The method FunctionDecl::isThisDeclarationADefinition returns false
241bf3c84cfSAlexander Shaposhnikov   // for a defaulted function unless that function has been implicitly defined.
242bf3c84cfSAlexander Shaposhnikov   // Thus this assert needs to be after the previous checks.
243bf3c84cfSAlexander Shaposhnikov   assert(CtorDecl->isThisDeclarationADefinition() && "Not a definition");
244bf3c84cfSAlexander Shaposhnikov 
245bf3c84cfSAlexander Shaposhnikov   SmallVector<unsigned, 10> NewFieldsPositions(NewFieldsOrder.size());
246bf3c84cfSAlexander Shaposhnikov   for (unsigned i = 0, e = NewFieldsOrder.size(); i < e; ++i)
247bf3c84cfSAlexander Shaposhnikov     NewFieldsPositions[NewFieldsOrder[i]] = i;
248bf3c84cfSAlexander Shaposhnikov 
249bf3c84cfSAlexander Shaposhnikov   SmallVector<const CXXCtorInitializer *, 10> OldWrittenInitializersOrder;
250bf3c84cfSAlexander Shaposhnikov   SmallVector<const CXXCtorInitializer *, 10> NewWrittenInitializersOrder;
251bf3c84cfSAlexander Shaposhnikov   for (const auto *Initializer : CtorDecl->inits()) {
252b687fddaSAlexander Shaposhnikov     if (!Initializer->isMemberInitializer() || !Initializer->isWritten())
253bf3c84cfSAlexander Shaposhnikov       continue;
254b687fddaSAlexander Shaposhnikov 
255b687fddaSAlexander Shaposhnikov     // Warn if this reordering violates initialization expr dependencies.
256b687fddaSAlexander Shaposhnikov     const FieldDecl *ThisM = Initializer->getMember();
257b687fddaSAlexander Shaposhnikov     const auto UsedMembers = findMembersUsedInInitExpr(Initializer, Context);
258b687fddaSAlexander Shaposhnikov     for (const FieldDecl *UM : UsedMembers) {
259b687fddaSAlexander Shaposhnikov       if (NewFieldsPositions[UM->getFieldIndex()] >
260b687fddaSAlexander Shaposhnikov           NewFieldsPositions[ThisM->getFieldIndex()]) {
261b687fddaSAlexander Shaposhnikov         DiagnosticsEngine &DiagEngine = Context.getDiagnostics();
262b687fddaSAlexander Shaposhnikov         auto Description = ("reordering field " + UM->getName() + " after " +
263b687fddaSAlexander Shaposhnikov                             ThisM->getName() + " makes " + UM->getName() +
264b687fddaSAlexander Shaposhnikov                             " uninitialized when used in init expression")
265b687fddaSAlexander Shaposhnikov                                .str();
266b687fddaSAlexander Shaposhnikov         unsigned ID = DiagEngine.getDiagnosticIDs()->getCustomDiagID(
267b687fddaSAlexander Shaposhnikov             DiagnosticIDs::Warning, Description);
268b687fddaSAlexander Shaposhnikov         DiagEngine.Report(Initializer->getSourceLocation(), ID);
269b687fddaSAlexander Shaposhnikov       }
270b687fddaSAlexander Shaposhnikov     }
271b687fddaSAlexander Shaposhnikov 
272bf3c84cfSAlexander Shaposhnikov     OldWrittenInitializersOrder.push_back(Initializer);
273bf3c84cfSAlexander Shaposhnikov     NewWrittenInitializersOrder.push_back(Initializer);
274bf3c84cfSAlexander Shaposhnikov   }
275bf3c84cfSAlexander Shaposhnikov   auto ByFieldNewPosition = [&](const CXXCtorInitializer *LHS,
276bf3c84cfSAlexander Shaposhnikov                                 const CXXCtorInitializer *RHS) {
277bf3c84cfSAlexander Shaposhnikov     assert(LHS && RHS);
278bf3c84cfSAlexander Shaposhnikov     return NewFieldsPositions[LHS->getMember()->getFieldIndex()] <
279bf3c84cfSAlexander Shaposhnikov            NewFieldsPositions[RHS->getMember()->getFieldIndex()];
280bf3c84cfSAlexander Shaposhnikov   };
281aba43035SDmitri Gribenko   llvm::sort(NewWrittenInitializersOrder, ByFieldNewPosition);
282bf3c84cfSAlexander Shaposhnikov   assert(OldWrittenInitializersOrder.size() ==
283bf3c84cfSAlexander Shaposhnikov          NewWrittenInitializersOrder.size());
284bf3c84cfSAlexander Shaposhnikov   for (unsigned i = 0, e = NewWrittenInitializersOrder.size(); i < e; ++i)
285bf3c84cfSAlexander Shaposhnikov     if (OldWrittenInitializersOrder[i] != NewWrittenInitializersOrder[i])
286bf3c84cfSAlexander Shaposhnikov       addReplacement(OldWrittenInitializersOrder[i]->getSourceRange(),
287bf3c84cfSAlexander Shaposhnikov                      NewWrittenInitializersOrder[i]->getSourceRange(), Context,
288bf3c84cfSAlexander Shaposhnikov                      Replacements);
289bf3c84cfSAlexander Shaposhnikov }
290bf3c84cfSAlexander Shaposhnikov 
291282dc72cSDmitri Gribenko /// Reorders initializers in the brace initialization of an aggregate.
292bf3c84cfSAlexander Shaposhnikov ///
293bf3c84cfSAlexander Shaposhnikov /// At the moment partial initialization is not supported.
294bf3c84cfSAlexander Shaposhnikov /// \returns true on success
295bf3c84cfSAlexander Shaposhnikov static bool reorderFieldsInInitListExpr(
296bf3c84cfSAlexander Shaposhnikov     const InitListExpr *InitListEx, ArrayRef<unsigned> NewFieldsOrder,
297bf3c84cfSAlexander Shaposhnikov     const ASTContext &Context,
298bf3c84cfSAlexander Shaposhnikov     std::map<std::string, tooling::Replacements> &Replacements) {
299bf3c84cfSAlexander Shaposhnikov   assert(InitListEx && "Init list expression is null");
300bf3c84cfSAlexander Shaposhnikov   // We care only about InitListExprs which originate from source code.
301bf3c84cfSAlexander Shaposhnikov   // Implicit InitListExprs are created by the semantic analyzer.
302bf3c84cfSAlexander Shaposhnikov   if (!InitListEx->isExplicit())
303bf3c84cfSAlexander Shaposhnikov     return true;
304b687fddaSAlexander Shaposhnikov   // The method InitListExpr::getSyntacticForm may return nullptr indicating
305b687fddaSAlexander Shaposhnikov   // that the current initializer list also serves as its syntactic form.
306bf3c84cfSAlexander Shaposhnikov   if (const auto *SyntacticForm = InitListEx->getSyntacticForm())
307bf3c84cfSAlexander Shaposhnikov     InitListEx = SyntacticForm;
308bf3c84cfSAlexander Shaposhnikov   // If there are no initializers we do not need to change anything.
309bf3c84cfSAlexander Shaposhnikov   if (!InitListEx->getNumInits())
310bf3c84cfSAlexander Shaposhnikov     return true;
311bf3c84cfSAlexander Shaposhnikov   if (InitListEx->getNumInits() != NewFieldsOrder.size()) {
312bf3c84cfSAlexander Shaposhnikov     llvm::errs() << "Currently only full initialization is supported\n";
313bf3c84cfSAlexander Shaposhnikov     return false;
314bf3c84cfSAlexander Shaposhnikov   }
315bf3c84cfSAlexander Shaposhnikov   for (unsigned i = 0, e = InitListEx->getNumInits(); i < e; ++i)
316bf3c84cfSAlexander Shaposhnikov     if (i != NewFieldsOrder[i])
317b687fddaSAlexander Shaposhnikov       addReplacement(InitListEx->getInit(i)->getSourceRange(),
318b687fddaSAlexander Shaposhnikov                      InitListEx->getInit(NewFieldsOrder[i])->getSourceRange(),
319b687fddaSAlexander Shaposhnikov                      Context, Replacements);
320bf3c84cfSAlexander Shaposhnikov   return true;
321bf3c84cfSAlexander Shaposhnikov }
322bf3c84cfSAlexander Shaposhnikov 
323bf3c84cfSAlexander Shaposhnikov namespace {
324bf3c84cfSAlexander Shaposhnikov class ReorderingConsumer : public ASTConsumer {
325bf3c84cfSAlexander Shaposhnikov   StringRef RecordName;
326bf3c84cfSAlexander Shaposhnikov   ArrayRef<std::string> DesiredFieldsOrder;
327bf3c84cfSAlexander Shaposhnikov   std::map<std::string, tooling::Replacements> &Replacements;
328bf3c84cfSAlexander Shaposhnikov 
329bf3c84cfSAlexander Shaposhnikov public:
330bf3c84cfSAlexander Shaposhnikov   ReorderingConsumer(StringRef RecordName,
331bf3c84cfSAlexander Shaposhnikov                      ArrayRef<std::string> DesiredFieldsOrder,
332bf3c84cfSAlexander Shaposhnikov                      std::map<std::string, tooling::Replacements> &Replacements)
333bf3c84cfSAlexander Shaposhnikov       : RecordName(RecordName), DesiredFieldsOrder(DesiredFieldsOrder),
334bf3c84cfSAlexander Shaposhnikov         Replacements(Replacements) {}
335bf3c84cfSAlexander Shaposhnikov 
336bf3c84cfSAlexander Shaposhnikov   ReorderingConsumer(const ReorderingConsumer &) = delete;
337bf3c84cfSAlexander Shaposhnikov   ReorderingConsumer &operator=(const ReorderingConsumer &) = delete;
338bf3c84cfSAlexander Shaposhnikov 
339bf3c84cfSAlexander Shaposhnikov   void HandleTranslationUnit(ASTContext &Context) override {
340eaf833caSAlexander Shaposhnikov     const RecordDecl *RD = findDefinition(RecordName, Context);
341bf3c84cfSAlexander Shaposhnikov     if (!RD)
342bf3c84cfSAlexander Shaposhnikov       return;
343bf3c84cfSAlexander Shaposhnikov     SmallVector<unsigned, 4> NewFieldsOrder =
344bf3c84cfSAlexander Shaposhnikov         getNewFieldsOrder(RD, DesiredFieldsOrder);
345bf3c84cfSAlexander Shaposhnikov     if (NewFieldsOrder.empty())
346bf3c84cfSAlexander Shaposhnikov       return;
347bf3c84cfSAlexander Shaposhnikov     if (!reorderFieldsInDefinition(RD, NewFieldsOrder, Context, Replacements))
348bf3c84cfSAlexander Shaposhnikov       return;
349eaf833caSAlexander Shaposhnikov 
350eaf833caSAlexander Shaposhnikov     // CXXRD will be nullptr if C code (not C++) is being processed.
351eaf833caSAlexander Shaposhnikov     const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD);
352eaf833caSAlexander Shaposhnikov     if (CXXRD)
353eaf833caSAlexander Shaposhnikov       for (const auto *C : CXXRD->ctors())
354bf3c84cfSAlexander Shaposhnikov         if (const auto *D = dyn_cast<CXXConstructorDecl>(C->getDefinition()))
355bf3c84cfSAlexander Shaposhnikov           reorderFieldsInConstructor(cast<const CXXConstructorDecl>(D),
356bf3c84cfSAlexander Shaposhnikov                                      NewFieldsOrder, Context, Replacements);
357bf3c84cfSAlexander Shaposhnikov 
358eaf833caSAlexander Shaposhnikov     // We only need to reorder init list expressions for
359eaf833caSAlexander Shaposhnikov     // plain C structs or C++ aggregate types.
360bf3c84cfSAlexander Shaposhnikov     // For other types the order of constructor parameters is used,
361bf3c84cfSAlexander Shaposhnikov     // which we don't change at the moment.
362bf3c84cfSAlexander Shaposhnikov     // Now (v0) partial initialization is not supported.
363eaf833caSAlexander Shaposhnikov     if (!CXXRD || CXXRD->isAggregate())
364bf3c84cfSAlexander Shaposhnikov       for (auto Result :
365bf3c84cfSAlexander Shaposhnikov            match(initListExpr(hasType(equalsNode(RD))).bind("initListExpr"),
366bf3c84cfSAlexander Shaposhnikov                  Context))
367bf3c84cfSAlexander Shaposhnikov         if (!reorderFieldsInInitListExpr(
368bf3c84cfSAlexander Shaposhnikov                 Result.getNodeAs<InitListExpr>("initListExpr"), NewFieldsOrder,
369bf3c84cfSAlexander Shaposhnikov                 Context, Replacements)) {
370bf3c84cfSAlexander Shaposhnikov           Replacements.clear();
371bf3c84cfSAlexander Shaposhnikov           return;
372bf3c84cfSAlexander Shaposhnikov         }
373bf3c84cfSAlexander Shaposhnikov   }
374bf3c84cfSAlexander Shaposhnikov };
375bf3c84cfSAlexander Shaposhnikov } // end anonymous namespace
376bf3c84cfSAlexander Shaposhnikov 
377bf3c84cfSAlexander Shaposhnikov std::unique_ptr<ASTConsumer> ReorderFieldsAction::newASTConsumer() {
3781c705d9cSJonas Devlieghere   return std::make_unique<ReorderingConsumer>(RecordName, DesiredFieldsOrder,
379bf3c84cfSAlexander Shaposhnikov                                                Replacements);
380bf3c84cfSAlexander Shaposhnikov }
381bf3c84cfSAlexander Shaposhnikov 
382bf3c84cfSAlexander Shaposhnikov } // namespace reorder_fields
383bf3c84cfSAlexander Shaposhnikov } // namespace clang
384