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