blob: affa276a0c5503e531709011a90b443f97ea4ab2 [file] [log] [blame] [edit]
//===-- tools/extra/clang-reorder-fields/ReorderFieldsAction.cpp -*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file contains the definition of the
/// ReorderFieldsAction::newASTConsumer method
///
//===----------------------------------------------------------------------===//
#include "ReorderFieldsAction.h"
#include "Designator.h"
#include "clang/AST/AST.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Lex/Lexer.h"
#include "clang/Tooling/Refactoring.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/Support/ErrorHandling.h"
#include <string>
namespace clang {
namespace reorder_fields {
using namespace clang::ast_matchers;
using llvm::SmallSetVector;
/// Finds the definition of a record by name.
///
/// \returns nullptr if the name is ambiguous or not found.
static const RecordDecl *findDefinition(StringRef RecordName,
ASTContext &Context) {
auto Results =
match(recordDecl(hasName(RecordName), isDefinition()).bind("recordDecl"),
Context);
if (Results.empty()) {
llvm::errs() << "Definition of " << RecordName << " not found\n";
return nullptr;
}
if (Results.size() > 1) {
llvm::errs() << "The name " << RecordName
<< " is ambiguous, several definitions found\n";
return nullptr;
}
return selectFirst<RecordDecl>("recordDecl", Results);
}
static bool declaresMultipleFieldsInStatement(const RecordDecl *Decl) {
SourceLocation LastTypeLoc;
for (const auto &Field : Decl->fields()) {
SourceLocation TypeLoc =
Field->getTypeSourceInfo()->getTypeLoc().getBeginLoc();
if (LastTypeLoc.isValid() && TypeLoc == LastTypeLoc)
return true;
LastTypeLoc = TypeLoc;
}
return false;
}
static bool declaresMultipleFieldsInMacro(const RecordDecl *Decl,
const SourceManager &SrcMgr) {
SourceLocation LastMacroLoc;
for (const auto &Field : Decl->fields()) {
if (!Field->getLocation().isMacroID())
continue;
SourceLocation MacroLoc = SrcMgr.getExpansionLoc(Field->getLocation());
if (LastMacroLoc.isValid() && MacroLoc == LastMacroLoc)
return true;
LastMacroLoc = MacroLoc;
}
return false;
}
static bool containsPreprocessorDirectives(const RecordDecl *Decl,
const SourceManager &SrcMgr,
const LangOptions &LangOpts) {
std::pair<FileID, unsigned> FileAndOffset =
SrcMgr.getDecomposedLoc(Decl->field_begin()->getBeginLoc());
assert(!Decl->field_empty());
auto LastField = Decl->field_begin();
while (std::next(LastField) != Decl->field_end())
++LastField;
unsigned EndOffset = SrcMgr.getFileOffset(LastField->getEndLoc());
StringRef SrcBuffer = SrcMgr.getBufferData(FileAndOffset.first);
Lexer L(SrcMgr.getLocForStartOfFile(FileAndOffset.first), LangOpts,
SrcBuffer.data(), SrcBuffer.data() + FileAndOffset.second,
SrcBuffer.data() + SrcBuffer.size());
IdentifierTable Identifiers(LangOpts);
clang::Token T;
while (!L.LexFromRawLexer(T) && L.getCurrentBufferOffset() < EndOffset) {
if (T.getKind() == tok::hash) {
L.LexFromRawLexer(T);
if (T.getKind() == tok::raw_identifier) {
clang::IdentifierInfo &II = Identifiers.get(T.getRawIdentifier());
if (II.getPPKeywordID() != clang::tok::pp_not_keyword)
return true;
}
}
}
return false;
}
static bool isSafeToRewrite(const RecordDecl *Decl, const ASTContext &Context) {
// All following checks expect at least one field declaration.
if (Decl->field_empty())
return true;
// Don't attempt to rewrite if there is a declaration like 'int a, b;'.
if (declaresMultipleFieldsInStatement(Decl))
return false;
const SourceManager &SrcMgr = Context.getSourceManager();
// Don't attempt to rewrite if a single macro expansion creates multiple
// fields.
if (declaresMultipleFieldsInMacro(Decl, SrcMgr))
return false;
// Prevent rewriting if there are preprocessor directives present between the
// start of the first field and the end of last field.
if (containsPreprocessorDirectives(Decl, SrcMgr, Context.getLangOpts()))
return false;
return true;
}
/// Calculates the new order of fields.
///
/// \returns empty vector if the list of fields doesn't match the definition.
static SmallVector<unsigned, 4>
getNewFieldsOrder(const RecordDecl *Definition,
ArrayRef<std::string> DesiredFieldsOrder) {
assert(Definition && "Definition is null");
llvm::StringMap<unsigned> NameToIndex;
for (const auto *Field : Definition->fields())
NameToIndex[Field->getName()] = Field->getFieldIndex();
if (DesiredFieldsOrder.size() != NameToIndex.size()) {
llvm::errs() << "Number of provided fields (" << DesiredFieldsOrder.size()
<< ") doesn't match definition (" << NameToIndex.size()
<< ").\n";
return {};
}
SmallVector<unsigned, 4> NewFieldsOrder;
for (const auto &Name : DesiredFieldsOrder) {
auto It = NameToIndex.find(Name);
if (It == NameToIndex.end()) {
llvm::errs() << "Field " << Name << " not found in definition.\n";
return {};
}
NewFieldsOrder.push_back(It->second);
}
assert(NewFieldsOrder.size() == NameToIndex.size());
return NewFieldsOrder;
}
struct ReorderedStruct {
public:
ReorderedStruct(const RecordDecl *Decl, ArrayRef<unsigned> NewFieldsOrder)
: Definition(Decl), NewFieldsOrder(NewFieldsOrder),
NewFieldsPositions(NewFieldsOrder.size()) {
for (unsigned I = 0; I < NewFieldsPositions.size(); ++I)
NewFieldsPositions[NewFieldsOrder[I]] = I;
}
/// Compares compatible designators according to the new struct order.
/// Returns a negative value if Lhs < Rhs, positive value if Lhs > Rhs and 0
/// if they are equal.
bool operator()(const Designator &Lhs, const Designator &Rhs) const;
/// Compares compatible designator lists according to the new struct order.
/// Returns a negative value if Lhs < Rhs, positive value if Lhs > Rhs and 0
/// if they are equal.
bool operator()(const Designators &Lhs, const Designators &Rhs) const;
const RecordDecl *Definition;
ArrayRef<unsigned> NewFieldsOrder;
SmallVector<unsigned, 4> NewFieldsPositions;
};
bool ReorderedStruct::operator()(const Designator &Lhs,
const Designator &Rhs) const {
switch (Lhs.getTag()) {
case Designator::STRUCT:
assert(Rhs.getTag() == Designator::STRUCT && "Incompatible designators");
assert(Lhs.getStructDecl() == Rhs.getStructDecl() &&
"Incompatible structs");
// Use the new layout for reordered struct.
if (Definition == Lhs.getStructDecl()) {
return NewFieldsPositions[Lhs.getStructIter()->getFieldIndex()] <
NewFieldsPositions[Rhs.getStructIter()->getFieldIndex()];
}
return Lhs.getStructIter()->getFieldIndex() <
Rhs.getStructIter()->getFieldIndex();
case Designator::ARRAY:
case Designator::ARRAY_RANGE:
// Array designators can be compared to array range designators.
assert((Rhs.getTag() == Designator::ARRAY ||
Rhs.getTag() == Designator::ARRAY_RANGE) &&
"Incompatible designators");
size_t LhsIdx = Lhs.getTag() == Designator::ARRAY
? Lhs.getArrayIndex()
: Lhs.getArrayRangeStart();
size_t RhsIdx = Rhs.getTag() == Designator::ARRAY
? Rhs.getArrayIndex()
: Rhs.getArrayRangeStart();
return LhsIdx < RhsIdx;
}
llvm_unreachable("Invalid designator tag");
}
bool ReorderedStruct::operator()(const Designators &Lhs,
const Designators &Rhs) const {
return std::lexicographical_compare(Lhs.begin(), Lhs.end(), Rhs.begin(),
Rhs.end(), *this);
}
// FIXME: error-handling
/// Replaces a range of source code by the specified text.
static void
addReplacement(SourceRange Old, StringRef New, const ASTContext &Context,
std::map<std::string, tooling::Replacements> &Replacements) {
tooling::Replacement R(Context.getSourceManager(),
CharSourceRange::getTokenRange(Old), New,
Context.getLangOpts());
consumeError(Replacements[std::string(R.getFilePath())].add(R));
}
/// Replaces one range of source code by another and adds a prefix.
static void
addReplacement(SourceRange Old, SourceRange New, StringRef Prefix,
const ASTContext &Context,
std::map<std::string, tooling::Replacements> &Replacements) {
std::string NewText =
(Prefix + Lexer::getSourceText(CharSourceRange::getTokenRange(New),
Context.getSourceManager(),
Context.getLangOpts()))
.str();
addReplacement(Old, NewText, Context, Replacements);
}
/// Replaces one range of source code by another.
static void
addReplacement(SourceRange Old, SourceRange New, const ASTContext &Context,
std::map<std::string, tooling::Replacements> &Replacements) {
if (Old.getBegin().isMacroID())
Old = Context.getSourceManager().getExpansionRange(Old).getAsRange();
if (New.getBegin().isMacroID())
New = Context.getSourceManager().getExpansionRange(New).getAsRange();
StringRef NewText =
Lexer::getSourceText(CharSourceRange::getTokenRange(New),
Context.getSourceManager(), Context.getLangOpts());
addReplacement(Old, NewText.str(), Context, Replacements);
}
/// Find all member fields used in the given init-list initializer expr
/// that belong to the same record
///
/// \returns a set of field declarations, empty if none were present
static SmallSetVector<FieldDecl *, 1>
findMembersUsedInInitExpr(const CXXCtorInitializer *Initializer,
ASTContext &Context) {
SmallSetVector<FieldDecl *, 1> Results;
// Note that this does not pick up member fields of base classes since
// for those accesses Sema::PerformObjectMemberConversion always inserts an
// UncheckedDerivedToBase ImplicitCastExpr between the this expr and the
// object expression
auto FoundExprs = match(
traverse(
TK_AsIs,
findAll(memberExpr(hasObjectExpression(cxxThisExpr())).bind("ME"))),
*Initializer->getInit(), Context);
for (BoundNodes &BN : FoundExprs)
if (auto *MemExpr = BN.getNodeAs<MemberExpr>("ME"))
if (auto *FD = dyn_cast<FieldDecl>(MemExpr->getMemberDecl()))
Results.insert(FD);
return Results;
}
/// Returns the start of the leading comments before `Loc`.
static SourceLocation getStartOfLeadingComment(SourceLocation Loc,
const SourceManager &SM,
const LangOptions &LangOpts) {
// We consider any leading comment token that is on the same line or
// indented similarly to the first comment to be part of the leading comment.
const unsigned Line = SM.getPresumedLineNumber(Loc);
const unsigned Column = SM.getPresumedColumnNumber(Loc);
std::optional<Token> Tok =
Lexer::findPreviousToken(Loc, SM, LangOpts, /*IncludeComments=*/true);
while (Tok && Tok->is(tok::comment)) {
const SourceLocation CommentLoc =
Lexer::GetBeginningOfToken(Tok->getLocation(), SM, LangOpts);
if (SM.getPresumedLineNumber(CommentLoc) != Line &&
SM.getPresumedColumnNumber(CommentLoc) != Column) {
break;
}
Loc = CommentLoc;
Tok = Lexer::findPreviousToken(Loc, SM, LangOpts, /*IncludeComments=*/true);
}
return Loc;
}
/// Returns the end of the trailing comments after `Loc`.
static SourceLocation getEndOfTrailingComment(SourceLocation Loc,
const SourceManager &SM,
const LangOptions &LangOpts) {
// We consider any following comment token that is indented more than the
// first comment to be part of the trailing comment.
const unsigned Column = SM.getPresumedColumnNumber(Loc);
std::optional<Token> Tok =
Lexer::findNextToken(Loc, SM, LangOpts, /*IncludeComments=*/true);
while (Tok && Tok->is(tok::comment) &&
SM.getPresumedColumnNumber(Tok->getLocation()) > Column) {
Loc = Tok->getEndLoc();
Tok = Lexer::findNextToken(Loc, SM, LangOpts, /*IncludeComments=*/true);
}
return Loc;
}
/// Returns the full source range for the field declaration up to (including)
/// the trailing semicolumn, including potential macro invocations,
/// e.g. `int a GUARDED_BY(mu);`. If there is a trailing comment, include it.
static SourceRange getFullFieldSourceRange(const FieldDecl &Field,
const ASTContext &Context) {
const SourceRange Range = Field.getSourceRange();
SourceLocation Begin = Range.getBegin();
SourceLocation End = Range.getEnd();
const SourceManager &SM = Context.getSourceManager();
const LangOptions &LangOpts = Context.getLangOpts();
while (true) {
std::optional<Token> CurrentToken = Lexer::findNextToken(End, SM, LangOpts);
if (!CurrentToken)
return SourceRange(Begin, End);
if (CurrentToken->is(tok::eof))
return Range; // Something is wrong, return the original range.
End = CurrentToken->getLastLoc();
if (CurrentToken->is(tok::semi))
break;
}
Begin = getStartOfLeadingComment(Begin, SM, LangOpts);
End = getEndOfTrailingComment(End, SM, LangOpts);
return SourceRange(Begin, End);
}
/// Reorders fields in the definition of a struct/class.
///
/// At the moment reordering of fields with
/// different accesses (public/protected/private) is not supported.
/// \returns true on success.
static bool reorderFieldsInDefinition(
const ReorderedStruct &RS, const ASTContext &Context,
std::map<std::string, tooling::Replacements> &Replacements) {
assert(RS.Definition && "Definition is null");
SmallVector<const FieldDecl *, 10> Fields;
for (const auto *Field : RS.Definition->fields())
Fields.push_back(Field);
// Check that the permutation of the fields doesn't change the accesses
for (const auto *Field : RS.Definition->fields()) {
const auto FieldIndex = Field->getFieldIndex();
if (Field->getAccess() !=
Fields[RS.NewFieldsOrder[FieldIndex]]->getAccess()) {
llvm::errs() << "Currently reordering of fields with different accesses "
"is not supported\n";
return false;
}
}
for (const auto *Field : RS.Definition->fields()) {
const auto FieldIndex = Field->getFieldIndex();
if (FieldIndex == RS.NewFieldsOrder[FieldIndex])
continue;
addReplacement(getFullFieldSourceRange(*Field, Context),
getFullFieldSourceRange(
*Fields[RS.NewFieldsOrder[FieldIndex]], Context),
Context, Replacements);
}
return true;
}
/// Reorders initializers in a C++ struct/class constructor.
///
/// A constructor can have initializers for an arbitrary subset of the class's
/// fields. Thus, we need to ensure that we reorder just the initializers that
/// are present.
static void reorderFieldsInConstructor(
const CXXConstructorDecl *CtorDecl, const ReorderedStruct &RS,
ASTContext &Context,
std::map<std::string, tooling::Replacements> &Replacements) {
assert(CtorDecl && "Constructor declaration is null");
if (CtorDecl->isImplicit() || CtorDecl->getNumCtorInitializers() <= 1)
return;
// The method FunctionDecl::isThisDeclarationADefinition returns false
// for a defaulted function unless that function has been implicitly defined.
// Thus this assert needs to be after the previous checks.
assert(CtorDecl->isThisDeclarationADefinition() && "Not a definition");
SmallVector<const CXXCtorInitializer *, 10> OldWrittenInitializersOrder;
SmallVector<const CXXCtorInitializer *, 10> NewWrittenInitializersOrder;
for (const auto *Initializer : CtorDecl->inits()) {
if (!Initializer->isMemberInitializer() || !Initializer->isWritten())
continue;
// Warn if this reordering violates initialization expr dependencies.
const FieldDecl *ThisM = Initializer->getMember();
const auto UsedMembers = findMembersUsedInInitExpr(Initializer, Context);
for (const FieldDecl *UM : UsedMembers) {
if (RS.NewFieldsPositions[UM->getFieldIndex()] >
RS.NewFieldsPositions[ThisM->getFieldIndex()]) {
DiagnosticsEngine &DiagEngine = Context.getDiagnostics();
auto Description = ("reordering field " + UM->getName() + " after " +
ThisM->getName() + " makes " + UM->getName() +
" uninitialized when used in init expression")
.str();
unsigned ID = DiagEngine.getDiagnosticIDs()->getCustomDiagID(
DiagnosticIDs::Warning, Description);
DiagEngine.Report(Initializer->getSourceLocation(), ID);
}
}
OldWrittenInitializersOrder.push_back(Initializer);
NewWrittenInitializersOrder.push_back(Initializer);
}
auto ByFieldNewPosition = [&](const CXXCtorInitializer *LHS,
const CXXCtorInitializer *RHS) {
assert(LHS && RHS);
return RS.NewFieldsPositions[LHS->getMember()->getFieldIndex()] <
RS.NewFieldsPositions[RHS->getMember()->getFieldIndex()];
};
llvm::sort(NewWrittenInitializersOrder, ByFieldNewPosition);
assert(OldWrittenInitializersOrder.size() ==
NewWrittenInitializersOrder.size());
for (unsigned i = 0, e = NewWrittenInitializersOrder.size(); i < e; ++i)
if (OldWrittenInitializersOrder[i] != NewWrittenInitializersOrder[i])
addReplacement(OldWrittenInitializersOrder[i]->getSourceRange(),
NewWrittenInitializersOrder[i]->getSourceRange(), Context,
Replacements);
}
/// Replacement for broken InitListExpr::isExplicit function.
/// FIXME: Remove when InitListExpr::isExplicit is fixed.
static bool isImplicitILE(const InitListExpr *ILE, const ASTContext &Context) {
// The ILE is implicit if either:
// - The left brace loc of the ILE matches the start of first init expression
// (for non designated decls)
// - The right brace loc of the ILE matches the end of first init expression
// (for designated decls)
// The first init expression should be taken from the syntactic form, but
// since the ILE could be implicit, there might not be a syntactic form.
// For that reason we have to check against all init expressions.
for (const Expr *Init : ILE->inits()) {
if (ILE->getLBraceLoc() == Init->getBeginLoc() ||
ILE->getRBraceLoc() == Init->getEndLoc())
return true;
}
return false;
}
/// Finds the semantic form of the first explicit ancestor of the given
/// initializer list including itself.
static const InitListExpr *getExplicitILE(const InitListExpr *ILE,
ASTContext &Context) {
if (!isImplicitILE(ILE, Context))
return ILE;
const InitListExpr *TopLevelILE = ILE;
DynTypedNodeList Parents = Context.getParents(*TopLevelILE);
while (!Parents.empty() && Parents.begin()->get<InitListExpr>()) {
TopLevelILE = Parents.begin()->get<InitListExpr>();
Parents = Context.getParents(*TopLevelILE);
if (!isImplicitILE(TopLevelILE, Context))
break;
}
if (!TopLevelILE->isSemanticForm()) {
return TopLevelILE->getSemanticForm();
}
return TopLevelILE;
}
static void reportError(const Twine &Message, SourceLocation Loc,
const SourceManager &SM) {
if (Loc.isValid()) {
llvm::errs() << SM.getFilename(Loc) << ":" << SM.getPresumedLineNumber(Loc)
<< ":" << SM.getPresumedColumnNumber(Loc) << ": ";
}
llvm::errs() << Message;
}
/// Reorders initializers in the brace initialization of an aggregate.
///
/// At the moment partial initialization is not supported.
/// \returns true on success
static bool reorderFieldsInInitListExpr(
const InitListExpr *InitListEx, const ReorderedStruct &RS,
ASTContext &Context,
std::map<std::string, tooling::Replacements> &Replacements) {
assert(InitListEx && "Init list expression is null");
// Only process semantic forms of initializer lists.
if (!InitListEx->isSemanticForm()) {
return true;
}
// If there are no initializers we do not need to change anything.
if (!InitListEx->getNumInits())
return true;
// We care only about InitListExprs which originate from source code.
// Implicit InitListExprs are created by the semantic analyzer.
// We find the first parent InitListExpr that exists in source code and
// process it. This is necessary because of designated initializer lists and
// possible omitted braces.
InitListEx = getExplicitILE(InitListEx, Context);
// Find if there are any designated initializations or implicit values. If all
// initializers are present and none have designators then just reorder them
// normally. Otherwise, designators are added to all initializers and they are
// sorted in the new order.
bool HasImplicitInit = false;
bool HasDesignatedInit = false;
// The method InitListExpr::getSyntacticForm may return nullptr indicating
// that the current initializer list also serves as its syntactic form.
const InitListExpr *SyntacticInitListEx = InitListEx;
if (const InitListExpr *SynILE = InitListEx->getSyntacticForm()) {
// Do not rewrite zero initializers. This check is only valid for syntactic
// forms.
if (SynILE->isIdiomaticZeroInitializer(Context.getLangOpts()))
return true;
HasImplicitInit = InitListEx->getNumInits() != SynILE->getNumInits();
HasDesignatedInit = llvm::any_of(SynILE->inits(), [](const Expr *Init) {
return isa<DesignatedInitExpr>(Init);
});
SyntacticInitListEx = SynILE;
} else {
// If there is no syntactic form, there can be no designators. Instead,
// there might be implicit values.
HasImplicitInit =
(RS.NewFieldsOrder.size() != InitListEx->getNumInits()) ||
llvm::any_of(InitListEx->inits(), [&Context](const Expr *Init) {
return isa<ImplicitValueInitExpr>(Init) ||
(isa<InitListExpr>(Init) &&
isImplicitILE(dyn_cast<InitListExpr>(Init), Context));
});
}
if (HasImplicitInit || HasDesignatedInit) {
// Designators are only supported from C++20.
if (!HasDesignatedInit && Context.getLangOpts().CPlusPlus &&
!Context.getLangOpts().CPlusPlus20) {
reportError(
"Only full initialization without implicit values is supported\n",
InitListEx->getBeginLoc(), Context.getSourceManager());
return false;
}
// Handle case when some fields are designated. Some fields can be
// missing. Insert any missing designators and reorder the expressions
// according to the new order.
std::optional<Designators> CurrentDesignators;
// Remember each initializer expression along with its designators. They are
// sorted later to determine the correct order.
std::vector<std::pair<Designators, const Expr *>> Rewrites;
for (const Expr *Init : SyntacticInitListEx->inits()) {
if (const auto *DIE = dyn_cast_or_null<DesignatedInitExpr>(Init)) {
CurrentDesignators.emplace(DIE, SyntacticInitListEx, &Context);
if (!CurrentDesignators->isValid()) {
reportError("Unsupported initializer list\n", DIE->getBeginLoc(),
Context.getSourceManager());
return false;
}
// Use the child of the DesignatedInitExpr. This way designators are
// always replaced.
Rewrites.emplace_back(*CurrentDesignators, DIE->getInit());
} else {
// If designators are not initialized then initialize to the first
// field, otherwise move the next field.
if (!CurrentDesignators) {
CurrentDesignators.emplace(Init, SyntacticInitListEx, &Context);
if (!CurrentDesignators->isValid()) {
reportError("Unsupported initializer list\n",
InitListEx->getBeginLoc(), Context.getSourceManager());
return false;
}
} else if (!CurrentDesignators->advanceToNextField(Init)) {
reportError("Unsupported initializer list\n",
InitListEx->getBeginLoc(), Context.getSourceManager());
return false;
}
// Do not rewrite implicit values. They just had to be processed to
// find the correct designator.
if (!isa<ImplicitValueInitExpr>(Init))
Rewrites.emplace_back(*CurrentDesignators, Init);
}
}
// Sort the designators according to the new order.
llvm::stable_sort(Rewrites, [&RS](const auto &Lhs, const auto &Rhs) {
return RS(Lhs.first, Rhs.first);
});
for (unsigned i = 0, e = Rewrites.size(); i < e; ++i) {
addReplacement(SyntacticInitListEx->getInit(i)->getSourceRange(),
Rewrites[i].second->getSourceRange(),
Rewrites[i].first.toString(), Context, Replacements);
}
} else {
// Handle excess initializers by leaving them unchanged.
assert(SyntacticInitListEx->getNumInits() >= InitListEx->getNumInits());
// All field initializers are present and none have designators. They can be
// reordered normally.
for (unsigned i = 0, e = RS.NewFieldsOrder.size(); i < e; ++i) {
if (i != RS.NewFieldsOrder[i])
addReplacement(SyntacticInitListEx->getInit(i)->getSourceRange(),
SyntacticInitListEx->getInit(RS.NewFieldsOrder[i])
->getSourceRange(),
Context, Replacements);
}
}
return true;
}
namespace {
class ReorderingConsumer : public ASTConsumer {
StringRef RecordName;
ArrayRef<std::string> DesiredFieldsOrder;
std::map<std::string, tooling::Replacements> &Replacements;
public:
ReorderingConsumer(StringRef RecordName,
ArrayRef<std::string> DesiredFieldsOrder,
std::map<std::string, tooling::Replacements> &Replacements)
: RecordName(RecordName), DesiredFieldsOrder(DesiredFieldsOrder),
Replacements(Replacements) {}
ReorderingConsumer(const ReorderingConsumer &) = delete;
ReorderingConsumer &operator=(const ReorderingConsumer &) = delete;
void HandleTranslationUnit(ASTContext &Context) override {
const RecordDecl *RD = findDefinition(RecordName, Context);
if (!RD)
return;
if (!isSafeToRewrite(RD, Context))
return;
SmallVector<unsigned, 4> NewFieldsOrder =
getNewFieldsOrder(RD, DesiredFieldsOrder);
if (NewFieldsOrder.empty())
return;
ReorderedStruct RS{RD, NewFieldsOrder};
if (!reorderFieldsInDefinition(RS, Context, Replacements))
return;
// CXXRD will be nullptr if C code (not C++) is being processed.
const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD);
if (CXXRD)
for (const auto *C : CXXRD->ctors())
if (const auto *D = dyn_cast<CXXConstructorDecl>(C->getDefinition()))
reorderFieldsInConstructor(cast<const CXXConstructorDecl>(D), RS,
Context, Replacements);
// We only need to reorder init list expressions for
// plain C structs or C++ aggregate types.
// For other types the order of constructor parameters is used,
// which we don't change at the moment.
// Now (v0) partial initialization is not supported.
if (!CXXRD || CXXRD->isAggregate()) {
for (auto Result :
match(initListExpr(hasType(equalsNode(RD))).bind("initListExpr"),
Context))
if (!reorderFieldsInInitListExpr(
Result.getNodeAs<InitListExpr>("initListExpr"), RS, Context,
Replacements)) {
Replacements.clear();
return;
}
}
}
};
} // end anonymous namespace
std::unique_ptr<ASTConsumer> ReorderFieldsAction::newASTConsumer() {
return std::make_unique<ReorderingConsumer>(RecordName, DesiredFieldsOrder,
Replacements);
}
} // namespace reorder_fields
} // namespace clang