723 lines · 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-exception6//7//===----------------------------------------------------------------------===//8///9/// \file10/// This file contains the definition of the11/// ReorderFieldsAction::newASTConsumer method12///13//===----------------------------------------------------------------------===//14 15#include "ReorderFieldsAction.h"16#include "Designator.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/Basic/LangOptions.h"24#include "clang/Basic/SourceLocation.h"25#include "clang/Lex/Lexer.h"26#include "clang/Tooling/Refactoring.h"27#include "llvm/ADT/STLExtras.h"28#include "llvm/ADT/SetVector.h"29#include "llvm/Support/ErrorHandling.h"30#include <string>31 32namespace clang {33namespace reorder_fields {34using namespace clang::ast_matchers;35using llvm::SmallSetVector;36 37/// Finds the definition of a record by name.38///39/// \returns nullptr if the name is ambiguous or not found.40static const RecordDecl *findDefinition(StringRef RecordName,41 ASTContext &Context) {42 auto Results =43 match(recordDecl(hasName(RecordName), isDefinition()).bind("recordDecl"),44 Context);45 if (Results.empty()) {46 llvm::errs() << "Definition of " << RecordName << " not found\n";47 return nullptr;48 }49 if (Results.size() > 1) {50 llvm::errs() << "The name " << RecordName51 << " is ambiguous, several definitions found\n";52 return nullptr;53 }54 return selectFirst<RecordDecl>("recordDecl", Results);55}56 57static bool declaresMultipleFieldsInStatement(const RecordDecl *Decl) {58 SourceLocation LastTypeLoc;59 for (const auto &Field : Decl->fields()) {60 SourceLocation TypeLoc =61 Field->getTypeSourceInfo()->getTypeLoc().getBeginLoc();62 if (LastTypeLoc.isValid() && TypeLoc == LastTypeLoc)63 return true;64 LastTypeLoc = TypeLoc;65 }66 return false;67}68 69static bool declaresMultipleFieldsInMacro(const RecordDecl *Decl,70 const SourceManager &SrcMgr) {71 SourceLocation LastMacroLoc;72 for (const auto &Field : Decl->fields()) {73 if (!Field->getLocation().isMacroID())74 continue;75 SourceLocation MacroLoc = SrcMgr.getExpansionLoc(Field->getLocation());76 if (LastMacroLoc.isValid() && MacroLoc == LastMacroLoc)77 return true;78 LastMacroLoc = MacroLoc;79 }80 return false;81}82 83static bool containsPreprocessorDirectives(const RecordDecl *Decl,84 const SourceManager &SrcMgr,85 const LangOptions &LangOpts) {86 std::pair<FileID, unsigned> FileAndOffset =87 SrcMgr.getDecomposedLoc(Decl->field_begin()->getBeginLoc());88 assert(!Decl->field_empty());89 auto LastField = Decl->field_begin();90 while (std::next(LastField) != Decl->field_end())91 ++LastField;92 unsigned EndOffset = SrcMgr.getFileOffset(LastField->getEndLoc());93 StringRef SrcBuffer = SrcMgr.getBufferData(FileAndOffset.first);94 Lexer L(SrcMgr.getLocForStartOfFile(FileAndOffset.first), LangOpts,95 SrcBuffer.data(), SrcBuffer.data() + FileAndOffset.second,96 SrcBuffer.data() + SrcBuffer.size());97 IdentifierTable Identifiers(LangOpts);98 clang::Token T;99 while (!L.LexFromRawLexer(T) && L.getCurrentBufferOffset() < EndOffset) {100 if (T.getKind() == tok::hash) {101 L.LexFromRawLexer(T);102 if (T.getKind() == tok::raw_identifier) {103 clang::IdentifierInfo &II = Identifiers.get(T.getRawIdentifier());104 if (II.getPPKeywordID() != clang::tok::pp_not_keyword)105 return true;106 }107 }108 }109 return false;110}111 112static bool isSafeToRewrite(const RecordDecl *Decl, const ASTContext &Context) {113 // All following checks expect at least one field declaration.114 if (Decl->field_empty())115 return true;116 117 // Don't attempt to rewrite if there is a declaration like 'int a, b;'.118 if (declaresMultipleFieldsInStatement(Decl))119 return false;120 121 const SourceManager &SrcMgr = Context.getSourceManager();122 123 // Don't attempt to rewrite if a single macro expansion creates multiple124 // fields.125 if (declaresMultipleFieldsInMacro(Decl, SrcMgr))126 return false;127 128 // Prevent rewriting if there are preprocessor directives present between the129 // start of the first field and the end of last field.130 if (containsPreprocessorDirectives(Decl, SrcMgr, Context.getLangOpts()))131 return false;132 133 return true;134}135 136/// Calculates the new order of fields.137///138/// \returns empty vector if the list of fields doesn't match the definition.139static SmallVector<unsigned, 4>140getNewFieldsOrder(const RecordDecl *Definition,141 ArrayRef<std::string> DesiredFieldsOrder) {142 assert(Definition && "Definition is null");143 144 llvm::StringMap<unsigned> NameToIndex;145 for (const auto *Field : Definition->fields())146 NameToIndex[Field->getName()] = Field->getFieldIndex();147 148 if (DesiredFieldsOrder.size() != NameToIndex.size()) {149 llvm::errs() << "Number of provided fields (" << DesiredFieldsOrder.size()150 << ") doesn't match definition (" << NameToIndex.size()151 << ").\n";152 return {};153 }154 SmallVector<unsigned, 4> NewFieldsOrder;155 for (const auto &Name : DesiredFieldsOrder) {156 auto It = NameToIndex.find(Name);157 if (It == NameToIndex.end()) {158 llvm::errs() << "Field " << Name << " not found in definition.\n";159 return {};160 }161 NewFieldsOrder.push_back(It->second);162 }163 assert(NewFieldsOrder.size() == NameToIndex.size());164 return NewFieldsOrder;165}166 167static bool isOrderValid(const RecordDecl *RD, ArrayRef<unsigned> FieldOrder) {168 if (FieldOrder.empty())169 return false;170 171 // If there is a flexible array member in the struct, it must remain the last172 // field.173 if (RD->hasFlexibleArrayMember() &&174 FieldOrder.back() != FieldOrder.size() - 1) {175 llvm::errs()176 << "Flexible array member must remain the last field in the struct\n";177 return false;178 }179 180 return true;181}182 183struct ReorderedStruct {184public:185 ReorderedStruct(const RecordDecl *Decl, ArrayRef<unsigned> NewFieldsOrder)186 : Definition(Decl), NewFieldsOrder(NewFieldsOrder),187 NewFieldsPositions(NewFieldsOrder.size()) {188 for (unsigned I = 0; I < NewFieldsPositions.size(); ++I)189 NewFieldsPositions[NewFieldsOrder[I]] = I;190 }191 192 /// Compares compatible designators according to the new struct order.193 /// Returns a negative value if Lhs < Rhs, positive value if Lhs > Rhs and 0194 /// if they are equal.195 bool operator()(const Designator &Lhs, const Designator &Rhs) const;196 197 /// Compares compatible designator lists according to the new struct order.198 /// Returns a negative value if Lhs < Rhs, positive value if Lhs > Rhs and 0199 /// if they are equal.200 bool operator()(const Designators &Lhs, const Designators &Rhs) const;201 202 const RecordDecl *Definition;203 ArrayRef<unsigned> NewFieldsOrder;204 SmallVector<unsigned, 4> NewFieldsPositions;205};206 207bool ReorderedStruct::operator()(const Designator &Lhs,208 const Designator &Rhs) const {209 switch (Lhs.getTag()) {210 case Designator::STRUCT:211 assert(Rhs.getTag() == Designator::STRUCT && "Incompatible designators");212 assert(Lhs.getStructDecl() == Rhs.getStructDecl() &&213 "Incompatible structs");214 // Use the new layout for reordered struct.215 if (Definition == Lhs.getStructDecl()) {216 return NewFieldsPositions[Lhs.getStructIter()->getFieldIndex()] <217 NewFieldsPositions[Rhs.getStructIter()->getFieldIndex()];218 }219 return Lhs.getStructIter()->getFieldIndex() <220 Rhs.getStructIter()->getFieldIndex();221 case Designator::ARRAY:222 case Designator::ARRAY_RANGE:223 // Array designators can be compared to array range designators.224 assert((Rhs.getTag() == Designator::ARRAY ||225 Rhs.getTag() == Designator::ARRAY_RANGE) &&226 "Incompatible designators");227 size_t LhsIdx = Lhs.getTag() == Designator::ARRAY228 ? Lhs.getArrayIndex()229 : Lhs.getArrayRangeStart();230 size_t RhsIdx = Rhs.getTag() == Designator::ARRAY231 ? Rhs.getArrayIndex()232 : Rhs.getArrayRangeStart();233 return LhsIdx < RhsIdx;234 }235 llvm_unreachable("Invalid designator tag");236}237 238bool ReorderedStruct::operator()(const Designators &Lhs,239 const Designators &Rhs) const {240 return std::lexicographical_compare(Lhs.begin(), Lhs.end(), Rhs.begin(),241 Rhs.end(), *this);242}243 244// FIXME: error-handling245/// Replaces a range of source code by the specified text.246static void247addReplacement(SourceRange Old, StringRef New, const ASTContext &Context,248 std::map<std::string, tooling::Replacements> &Replacements) {249 tooling::Replacement R(Context.getSourceManager(),250 CharSourceRange::getTokenRange(Old), New,251 Context.getLangOpts());252 consumeError(Replacements[std::string(R.getFilePath())].add(R));253}254 255/// Replaces one range of source code by another and adds a prefix.256static void257addReplacement(SourceRange Old, SourceRange New, StringRef Prefix,258 const ASTContext &Context,259 std::map<std::string, tooling::Replacements> &Replacements) {260 std::string NewText =261 (Prefix + Lexer::getSourceText(CharSourceRange::getTokenRange(New),262 Context.getSourceManager(),263 Context.getLangOpts()))264 .str();265 addReplacement(Old, NewText, Context, Replacements);266}267 268/// Replaces one range of source code by another.269static void270addReplacement(SourceRange Old, SourceRange New, const ASTContext &Context,271 std::map<std::string, tooling::Replacements> &Replacements) {272 if (Old.getBegin().isMacroID())273 Old = Context.getSourceManager().getExpansionRange(Old).getAsRange();274 if (New.getBegin().isMacroID())275 New = Context.getSourceManager().getExpansionRange(New).getAsRange();276 StringRef NewText =277 Lexer::getSourceText(CharSourceRange::getTokenRange(New),278 Context.getSourceManager(), Context.getLangOpts());279 addReplacement(Old, NewText.str(), Context, Replacements);280}281 282/// Find all member fields used in the given init-list initializer expr283/// that belong to the same record284///285/// \returns a set of field declarations, empty if none were present286static SmallSetVector<FieldDecl *, 1>287findMembersUsedInInitExpr(const CXXCtorInitializer *Initializer,288 ASTContext &Context) {289 SmallSetVector<FieldDecl *, 1> Results;290 // Note that this does not pick up member fields of base classes since291 // for those accesses Sema::PerformObjectMemberConversion always inserts an292 // UncheckedDerivedToBase ImplicitCastExpr between the this expr and the293 // object expression294 auto FoundExprs = match(295 traverse(296 TK_AsIs,297 findAll(memberExpr(hasObjectExpression(cxxThisExpr())).bind("ME"))),298 *Initializer->getInit(), Context);299 for (BoundNodes &BN : FoundExprs)300 if (auto *MemExpr = BN.getNodeAs<MemberExpr>("ME"))301 if (auto *FD = dyn_cast<FieldDecl>(MemExpr->getMemberDecl()))302 Results.insert(FD);303 return Results;304}305 306/// Returns the start of the leading comments before `Loc`.307static SourceLocation getStartOfLeadingComment(SourceLocation Loc,308 const SourceManager &SM,309 const LangOptions &LangOpts) {310 // We consider any leading comment token that is on the same line or311 // indented similarly to the first comment to be part of the leading comment.312 const unsigned Line = SM.getPresumedLineNumber(Loc);313 const unsigned Column = SM.getPresumedColumnNumber(Loc);314 std::optional<Token> Tok =315 Lexer::findPreviousToken(Loc, SM, LangOpts, /*IncludeComments=*/true);316 while (Tok && Tok->is(tok::comment)) {317 const SourceLocation CommentLoc =318 Lexer::GetBeginningOfToken(Tok->getLocation(), SM, LangOpts);319 if (SM.getPresumedLineNumber(CommentLoc) != Line &&320 SM.getPresumedColumnNumber(CommentLoc) != Column) {321 break;322 }323 Loc = CommentLoc;324 Tok = Lexer::findPreviousToken(Loc, SM, LangOpts, /*IncludeComments=*/true);325 }326 return Loc;327}328 329/// Returns the end of the trailing comments after `Loc`.330static SourceLocation getEndOfTrailingComment(SourceLocation Loc,331 const SourceManager &SM,332 const LangOptions &LangOpts) {333 // We consider any following comment token that is indented more than the334 // first comment to be part of the trailing comment.335 const unsigned Column = SM.getPresumedColumnNumber(Loc);336 std::optional<Token> Tok =337 Lexer::findNextToken(Loc, SM, LangOpts, /*IncludeComments=*/true);338 while (Tok && Tok->is(tok::comment) &&339 SM.getPresumedColumnNumber(Tok->getLocation()) > Column) {340 Loc = Tok->getEndLoc();341 Tok = Lexer::findNextToken(Loc, SM, LangOpts, /*IncludeComments=*/true);342 }343 return Loc;344}345 346/// Returns the full source range for the field declaration up to (including)347/// the trailing semicolumn, including potential macro invocations,348/// e.g. `int a GUARDED_BY(mu);`. If there is a trailing comment, include it.349static SourceRange getFullFieldSourceRange(const FieldDecl &Field,350 const ASTContext &Context) {351 const SourceRange Range = Field.getSourceRange();352 SourceLocation Begin = Range.getBegin();353 SourceLocation End = Range.getEnd();354 const SourceManager &SM = Context.getSourceManager();355 const LangOptions &LangOpts = Context.getLangOpts();356 while (true) {357 std::optional<Token> CurrentToken = Lexer::findNextToken(End, SM, LangOpts);358 359 if (!CurrentToken)360 return SourceRange(Begin, End);361 362 if (CurrentToken->is(tok::eof))363 return Range; // Something is wrong, return the original range.364 365 End = CurrentToken->getLastLoc();366 367 if (CurrentToken->is(tok::semi))368 break;369 }370 Begin = getStartOfLeadingComment(Begin, SM, LangOpts);371 End = getEndOfTrailingComment(End, SM, LangOpts);372 return SourceRange(Begin, End);373}374 375/// Reorders fields in the definition of a struct/class.376///377/// At the moment reordering of fields with378/// different accesses (public/protected/private) is not supported.379/// \returns true on success.380static bool reorderFieldsInDefinition(381 const ReorderedStruct &RS, const ASTContext &Context,382 std::map<std::string, tooling::Replacements> &Replacements) {383 assert(RS.Definition && "Definition is null");384 385 SmallVector<const FieldDecl *, 10> Fields;386 for (const auto *Field : RS.Definition->fields())387 Fields.push_back(Field);388 389 // Check that the permutation of the fields doesn't change the accesses390 for (const auto *Field : RS.Definition->fields()) {391 const auto FieldIndex = Field->getFieldIndex();392 if (Field->getAccess() !=393 Fields[RS.NewFieldsOrder[FieldIndex]]->getAccess()) {394 llvm::errs() << "Currently reordering of fields with different accesses "395 "is not supported\n";396 return false;397 }398 }399 400 for (const auto *Field : RS.Definition->fields()) {401 const auto FieldIndex = Field->getFieldIndex();402 if (FieldIndex == RS.NewFieldsOrder[FieldIndex])403 continue;404 addReplacement(getFullFieldSourceRange(*Field, Context),405 getFullFieldSourceRange(406 *Fields[RS.NewFieldsOrder[FieldIndex]], Context),407 Context, Replacements);408 }409 return true;410}411 412/// Reorders initializers in a C++ struct/class constructor.413///414/// A constructor can have initializers for an arbitrary subset of the class's415/// fields. Thus, we need to ensure that we reorder just the initializers that416/// are present.417static void reorderFieldsInConstructor(418 const CXXConstructorDecl *CtorDecl, const ReorderedStruct &RS,419 ASTContext &Context,420 std::map<std::string, tooling::Replacements> &Replacements) {421 assert(CtorDecl && "Constructor declaration is null");422 if (CtorDecl->isImplicit() || CtorDecl->getNumCtorInitializers() <= 1)423 return;424 425 // The method FunctionDecl::isThisDeclarationADefinition returns false426 // for a defaulted function unless that function has been implicitly defined.427 // Thus this assert needs to be after the previous checks.428 assert(CtorDecl->isThisDeclarationADefinition() && "Not a definition");429 430 SmallVector<const CXXCtorInitializer *, 10> OldWrittenInitializersOrder;431 SmallVector<const CXXCtorInitializer *, 10> NewWrittenInitializersOrder;432 for (const auto *Initializer : CtorDecl->inits()) {433 if (!Initializer->isMemberInitializer() || !Initializer->isWritten())434 continue;435 436 // Warn if this reordering violates initialization expr dependencies.437 const FieldDecl *ThisM = Initializer->getMember();438 const auto UsedMembers = findMembersUsedInInitExpr(Initializer, Context);439 for (const FieldDecl *UM : UsedMembers) {440 if (RS.NewFieldsPositions[UM->getFieldIndex()] >441 RS.NewFieldsPositions[ThisM->getFieldIndex()]) {442 DiagnosticsEngine &DiagEngine = Context.getDiagnostics();443 auto Description = ("reordering field " + UM->getName() + " after " +444 ThisM->getName() + " makes " + UM->getName() +445 " uninitialized when used in init expression")446 .str();447 unsigned ID = DiagEngine.getDiagnosticIDs()->getCustomDiagID(448 DiagnosticIDs::Warning, Description);449 DiagEngine.Report(Initializer->getSourceLocation(), ID);450 }451 }452 453 OldWrittenInitializersOrder.push_back(Initializer);454 NewWrittenInitializersOrder.push_back(Initializer);455 }456 auto ByFieldNewPosition = [&](const CXXCtorInitializer *LHS,457 const CXXCtorInitializer *RHS) {458 assert(LHS && RHS);459 return RS.NewFieldsPositions[LHS->getMember()->getFieldIndex()] <460 RS.NewFieldsPositions[RHS->getMember()->getFieldIndex()];461 };462 llvm::sort(NewWrittenInitializersOrder, ByFieldNewPosition);463 assert(OldWrittenInitializersOrder.size() ==464 NewWrittenInitializersOrder.size());465 for (unsigned i = 0, e = NewWrittenInitializersOrder.size(); i < e; ++i)466 if (OldWrittenInitializersOrder[i] != NewWrittenInitializersOrder[i])467 addReplacement(OldWrittenInitializersOrder[i]->getSourceRange(),468 NewWrittenInitializersOrder[i]->getSourceRange(), Context,469 Replacements);470}471 472/// Replacement for broken InitListExpr::isExplicit function.473/// FIXME: Remove when InitListExpr::isExplicit is fixed.474static bool isImplicitILE(const InitListExpr *ILE, const ASTContext &Context) {475 // The ILE is implicit if either:476 // - The left brace loc of the ILE matches the start of first init expression477 // (for non designated decls)478 // - The right brace loc of the ILE matches the end of first init expression479 // (for designated decls)480 // The first init expression should be taken from the syntactic form, but481 // since the ILE could be implicit, there might not be a syntactic form.482 // For that reason we have to check against all init expressions.483 for (const Expr *Init : ILE->inits()) {484 if (ILE->getLBraceLoc() == Init->getBeginLoc() ||485 ILE->getRBraceLoc() == Init->getEndLoc())486 return true;487 }488 return false;489}490 491/// Finds the semantic form of the first explicit ancestor of the given492/// initializer list including itself.493static const InitListExpr *getExplicitILE(const InitListExpr *ILE,494 ASTContext &Context) {495 if (!isImplicitILE(ILE, Context))496 return ILE;497 const InitListExpr *TopLevelILE = ILE;498 DynTypedNodeList Parents = Context.getParents(*TopLevelILE);499 while (!Parents.empty() && Parents.begin()->get<InitListExpr>()) {500 TopLevelILE = Parents.begin()->get<InitListExpr>();501 Parents = Context.getParents(*TopLevelILE);502 if (!isImplicitILE(TopLevelILE, Context))503 break;504 }505 if (!TopLevelILE->isSemanticForm()) {506 return TopLevelILE->getSemanticForm();507 }508 return TopLevelILE;509}510 511static void reportError(const Twine &Message, SourceLocation Loc,512 const SourceManager &SM) {513 if (Loc.isValid()) {514 llvm::errs() << SM.getFilename(Loc) << ":" << SM.getPresumedLineNumber(Loc)515 << ":" << SM.getPresumedColumnNumber(Loc) << ": ";516 }517 llvm::errs() << Message;518}519 520/// Reorders initializers in the brace initialization of an aggregate.521///522/// At the moment partial initialization is not supported.523/// \returns true on success524static bool reorderFieldsInInitListExpr(525 const InitListExpr *InitListEx, const ReorderedStruct &RS,526 ASTContext &Context,527 std::map<std::string, tooling::Replacements> &Replacements) {528 assert(InitListEx && "Init list expression is null");529 // Only process semantic forms of initializer lists.530 if (!InitListEx->isSemanticForm()) {531 return true;532 }533 534 // If there are no initializers we do not need to change anything.535 if (!InitListEx->getNumInits())536 return true;537 538 // We care only about InitListExprs which originate from source code.539 // Implicit InitListExprs are created by the semantic analyzer.540 // We find the first parent InitListExpr that exists in source code and541 // process it. This is necessary because of designated initializer lists and542 // possible omitted braces.543 InitListEx = getExplicitILE(InitListEx, Context);544 545 // Find if there are any designated initializations or implicit values. If all546 // initializers are present and none have designators then just reorder them547 // normally. Otherwise, designators are added to all initializers and they are548 // sorted in the new order.549 bool HasImplicitInit = false;550 bool HasDesignatedInit = false;551 // The method InitListExpr::getSyntacticForm may return nullptr indicating552 // that the current initializer list also serves as its syntactic form.553 const InitListExpr *SyntacticInitListEx = InitListEx;554 if (const InitListExpr *SynILE = InitListEx->getSyntacticForm()) {555 // Do not rewrite zero initializers. This check is only valid for syntactic556 // forms.557 if (SynILE->isIdiomaticZeroInitializer(Context.getLangOpts()))558 return true;559 560 HasImplicitInit = InitListEx->getNumInits() != SynILE->getNumInits();561 HasDesignatedInit = llvm::any_of(SynILE->inits(), [](const Expr *Init) {562 return isa<DesignatedInitExpr>(Init);563 });564 565 SyntacticInitListEx = SynILE;566 } else {567 // If there is no syntactic form, there can be no designators. Instead,568 // there might be implicit values.569 HasImplicitInit =570 (RS.NewFieldsOrder.size() != InitListEx->getNumInits()) ||571 llvm::any_of(InitListEx->inits(), [&Context](const Expr *Init) {572 return isa<ImplicitValueInitExpr>(Init) ||573 (isa<InitListExpr>(Init) &&574 isImplicitILE(dyn_cast<InitListExpr>(Init), Context));575 });576 }577 578 if (HasImplicitInit || HasDesignatedInit) {579 // Designators are only supported from C++20.580 if (!HasDesignatedInit && Context.getLangOpts().CPlusPlus &&581 !Context.getLangOpts().CPlusPlus20) {582 reportError(583 "Only full initialization without implicit values is supported\n",584 InitListEx->getBeginLoc(), Context.getSourceManager());585 return false;586 }587 588 // Handle case when some fields are designated. Some fields can be589 // missing. Insert any missing designators and reorder the expressions590 // according to the new order.591 std::optional<Designators> CurrentDesignators;592 // Remember each initializer expression along with its designators. They are593 // sorted later to determine the correct order.594 std::vector<std::pair<Designators, const Expr *>> Rewrites;595 for (const Expr *Init : SyntacticInitListEx->inits()) {596 if (const auto *DIE = dyn_cast_or_null<DesignatedInitExpr>(Init)) {597 CurrentDesignators.emplace(DIE, SyntacticInitListEx, &Context);598 if (!CurrentDesignators->isValid()) {599 reportError("Unsupported initializer list\n", DIE->getBeginLoc(),600 Context.getSourceManager());601 return false;602 }603 604 // Use the child of the DesignatedInitExpr. This way designators are605 // always replaced.606 Rewrites.emplace_back(*CurrentDesignators, DIE->getInit());607 } else {608 // If designators are not initialized then initialize to the first609 // field, otherwise move the next field.610 if (!CurrentDesignators) {611 CurrentDesignators.emplace(Init, SyntacticInitListEx, &Context);612 if (!CurrentDesignators->isValid()) {613 reportError("Unsupported initializer list\n",614 InitListEx->getBeginLoc(), Context.getSourceManager());615 return false;616 }617 } else if (!CurrentDesignators->advanceToNextField(Init)) {618 reportError("Unsupported initializer list\n",619 InitListEx->getBeginLoc(), Context.getSourceManager());620 return false;621 }622 623 // Do not rewrite implicit values. They just had to be processed to624 // find the correct designator.625 if (!isa<ImplicitValueInitExpr>(Init))626 Rewrites.emplace_back(*CurrentDesignators, Init);627 }628 }629 630 // Sort the designators according to the new order.631 llvm::stable_sort(Rewrites, [&RS](const auto &Lhs, const auto &Rhs) {632 return RS(Lhs.first, Rhs.first);633 });634 635 for (unsigned i = 0, e = Rewrites.size(); i < e; ++i) {636 addReplacement(SyntacticInitListEx->getInit(i)->getSourceRange(),637 Rewrites[i].second->getSourceRange(),638 Rewrites[i].first.toString(), Context, Replacements);639 }640 } else {641 // Handle excess initializers by leaving them unchanged.642 assert(SyntacticInitListEx->getNumInits() >= InitListEx->getNumInits());643 644 // All field initializers are present and none have designators. They can be645 // reordered normally.646 for (unsigned i = 0, e = RS.NewFieldsOrder.size(); i < e; ++i) {647 if (i != RS.NewFieldsOrder[i])648 addReplacement(SyntacticInitListEx->getInit(i)->getSourceRange(),649 SyntacticInitListEx->getInit(RS.NewFieldsOrder[i])650 ->getSourceRange(),651 Context, Replacements);652 }653 }654 return true;655}656 657namespace {658class ReorderingConsumer : public ASTConsumer {659 StringRef RecordName;660 ArrayRef<std::string> DesiredFieldsOrder;661 std::map<std::string, tooling::Replacements> &Replacements;662 663public:664 ReorderingConsumer(StringRef RecordName,665 ArrayRef<std::string> DesiredFieldsOrder,666 std::map<std::string, tooling::Replacements> &Replacements)667 : RecordName(RecordName), DesiredFieldsOrder(DesiredFieldsOrder),668 Replacements(Replacements) {}669 670 ReorderingConsumer(const ReorderingConsumer &) = delete;671 ReorderingConsumer &operator=(const ReorderingConsumer &) = delete;672 673 void HandleTranslationUnit(ASTContext &Context) override {674 const RecordDecl *RD = findDefinition(RecordName, Context);675 if (!RD)676 return;677 if (!isSafeToRewrite(RD, Context))678 return;679 SmallVector<unsigned, 4> NewFieldsOrder =680 getNewFieldsOrder(RD, DesiredFieldsOrder);681 if (!isOrderValid(RD, NewFieldsOrder))682 return;683 ReorderedStruct RS{RD, NewFieldsOrder};684 685 if (!reorderFieldsInDefinition(RS, Context, Replacements))686 return;687 688 // CXXRD will be nullptr if C code (not C++) is being processed.689 const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD);690 if (CXXRD)691 for (const auto *C : CXXRD->ctors())692 if (const auto *D = dyn_cast<CXXConstructorDecl>(C->getDefinition()))693 reorderFieldsInConstructor(cast<const CXXConstructorDecl>(D), RS,694 Context, Replacements);695 696 // We only need to reorder init list expressions for697 // plain C structs or C++ aggregate types.698 // For other types the order of constructor parameters is used,699 // which we don't change at the moment.700 // Now (v0) partial initialization is not supported.701 if (!CXXRD || CXXRD->isAggregate()) {702 for (auto Result :703 match(initListExpr(hasType(equalsNode(RD))).bind("initListExpr"),704 Context))705 if (!reorderFieldsInInitListExpr(706 Result.getNodeAs<InitListExpr>("initListExpr"), RS, Context,707 Replacements)) {708 Replacements.clear();709 return;710 }711 }712 }713};714} // end anonymous namespace715 716std::unique_ptr<ASTConsumer> ReorderFieldsAction::newASTConsumer() {717 return std::make_unique<ReorderingConsumer>(RecordName, DesiredFieldsOrder,718 Replacements);719}720 721} // namespace reorder_fields722} // namespace clang723