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"
34using namespace clang::ast_matchers;
35using llvm::SmallSetVector;
41 ASTContext &Context) {
43 match(recordDecl(hasName(
RecordName), isDefinition()).bind(
"recordDecl"),
45 if (Results.empty()) {
46 llvm::errs() <<
"Definition of " <<
RecordName <<
" not found\n";
49 if (Results.size() > 1) {
51 <<
" is ambiguous, several definitions found\n";
54 return selectFirst<RecordDecl>(
"recordDecl", Results);
58 SourceLocation LastTypeLoc;
59 for (
const auto &Field : Decl->fields()) {
60 SourceLocation TypeLoc =
61 Field->getTypeSourceInfo()->getTypeLoc().getBeginLoc();
62 if (LastTypeLoc.isValid() && TypeLoc == LastTypeLoc)
64 LastTypeLoc = TypeLoc;
70 const SourceManager &SrcMgr) {
71 SourceLocation LastMacroLoc;
72 for (
const auto &Field : Decl->fields()) {
73 if (!Field->getLocation().isMacroID())
75 SourceLocation MacroLoc = SrcMgr.getExpansionLoc(Field->getLocation());
76 if (LastMacroLoc.isValid() && MacroLoc == LastMacroLoc)
78 LastMacroLoc = MacroLoc;
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())
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);
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)
114 if (Decl->field_empty())
121 const SourceManager &SrcMgr = Context.getSourceManager();
139static SmallVector<unsigned, 4>
141 ArrayRef<std::string> DesiredFieldsOrder) {
142 assert(Definition &&
"Definition is null");
144 llvm::StringMap<unsigned> NameToIndex;
145 for (
const auto *Field : Definition->fields())
146 NameToIndex[Field->getName()] = Field->getFieldIndex();
148 if (DesiredFieldsOrder.size() != NameToIndex.size()) {
149 llvm::errs() <<
"Number of provided fields (" << DesiredFieldsOrder.size()
150 <<
") doesn't match definition (" << NameToIndex.size()
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";
161 NewFieldsOrder.push_back(It->second);
163 assert(NewFieldsOrder.size() == NameToIndex.size());
164 return NewFieldsOrder;
167static bool isOrderValid(
const RecordDecl *RD, ArrayRef<unsigned> FieldOrder) {
168 if (FieldOrder.empty())
173 if (RD->hasFlexibleArrayMember() &&
174 FieldOrder.back() != FieldOrder.size() - 1) {
176 <<
"Flexible array member must remain the last field in the struct\n";
213 "Incompatible structs");
226 "Incompatible designators");
233 return LhsIdx < RhsIdx;
235 llvm_unreachable(
"Invalid designator tag");
240 return std::lexicographical_compare(Lhs.
begin(), Lhs.
end(), Rhs.
begin(),
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));
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()))
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();
277 Lexer::getSourceText(CharSourceRange::getTokenRange(New),
278 Context.getSourceManager(), Context.getLangOpts());
286static SmallSetVector<FieldDecl *, 1>
288 ASTContext &Context) {
289 SmallSetVector<FieldDecl *, 1> Results;
294 auto FoundExprs = match(
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()))
308 const SourceManager &SM,
309 const LangOptions &LangOpts) {
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,
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) {
324 Tok = Lexer::findPreviousToken(Loc, SM, LangOpts,
true);
331 const SourceManager &SM,
332 const LangOptions &LangOpts) {
335 const unsigned Column = SM.getPresumedColumnNumber(Loc);
336 std::optional<Token> Tok =
337 Lexer::findNextToken(Loc, SM, LangOpts,
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,
true);
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();
357 std::optional<Token> CurrentToken = Lexer::findNextToken(End, SM, LangOpts);
360 return SourceRange(Begin, End);
362 if (CurrentToken->is(tok::eof))
365 End = CurrentToken->getLastLoc();
367 if (CurrentToken->is(tok::semi))
372 return SourceRange(Begin, End);
382 std::map<std::string, tooling::Replacements> &Replacements) {
383 assert(RS.
Definition &&
"Definition is null");
385 SmallVector<const FieldDecl *, 10> Fields;
386 for (
const auto *Field : RS.
Definition->fields())
387 Fields.push_back(Field);
390 for (
const auto *Field : RS.
Definition->fields()) {
391 const auto FieldIndex = Field->getFieldIndex();
392 if (Field->getAccess() !=
394 llvm::errs() <<
"Currently reordering of fields with different accesses "
395 "is not supported\n";
400 for (
const auto *Field : RS.
Definition->fields()) {
401 const auto FieldIndex = Field->getFieldIndex();
407 Context, Replacements);
420 std::map<std::string, tooling::Replacements> &Replacements) {
421 assert(CtorDecl &&
"Constructor declaration is null");
422 if (CtorDecl->isImplicit() || CtorDecl->getNumCtorInitializers() <= 1)
428 assert(CtorDecl->isThisDeclarationADefinition() &&
"Not a definition");
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())
437 const FieldDecl *ThisM = Initializer->getMember();
439 for (
const FieldDecl *UM : UsedMembers) {
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")
447 unsigned ID = DiagEngine.getDiagnosticIDs()->getCustomDiagID(
448 DiagnosticIDs::Warning, Description);
449 DiagEngine.Report(Initializer->getSourceLocation(), ID);
453 OldWrittenInitializersOrder.push_back(Initializer);
454 NewWrittenInitializersOrder.push_back(Initializer);
456 auto ByFieldNewPosition = [&](
const CXXCtorInitializer *LHS,
457 const CXXCtorInitializer *RHS) {
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])
468 NewWrittenInitializersOrder[i]->getSourceRange(), Context,
474static bool isImplicitILE(
const InitListExpr *ILE,
const ASTContext &Context) {
483 for (
const Expr *Init : ILE->inits()) {
484 if (ILE->getLBraceLoc() == Init->getBeginLoc() ||
485 ILE->getRBraceLoc() == Init->getEndLoc())
494 ASTContext &Context) {
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);
505 if (!TopLevelILE->isSemanticForm()) {
506 return TopLevelILE->getSemanticForm();
512 const SourceManager &SM) {
514 llvm::errs() << SM.getFilename(Loc) <<
":" << SM.getPresumedLineNumber(Loc)
515 <<
":" << SM.getPresumedColumnNumber(Loc) <<
": ";
517 llvm::errs() << Message;
527 std::map<std::string, tooling::Replacements> &Replacements) {
528 assert(InitListEx &&
"Init list expression is null");
530 if (!InitListEx->isSemanticForm()) {
535 if (!InitListEx->getNumInits())
549 bool HasImplicitInit =
false;
550 bool HasDesignatedInit =
false;
553 const InitListExpr *SyntacticInitListEx = InitListEx;
554 if (
const InitListExpr *SynILE = InitListEx->getSyntacticForm()) {
557 if (SynILE->isIdiomaticZeroInitializer(Context.getLangOpts()))
560 HasImplicitInit = InitListEx->getNumInits() != SynILE->getNumInits();
561 HasDesignatedInit = llvm::any_of(SynILE->inits(), [](
const Expr *Init) {
562 return isa<DesignatedInitExpr>(Init);
565 SyntacticInitListEx = SynILE;
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));
578 if (HasImplicitInit || HasDesignatedInit) {
580 if (!HasDesignatedInit && Context.getLangOpts().CPlusPlus &&
581 !Context.getLangOpts().CPlusPlus20) {
583 "Only full initialization without implicit values is supported\n",
584 InitListEx->getBeginLoc(), Context.getSourceManager());
591 std::optional<Designators> CurrentDesignators;
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());
606 Rewrites.emplace_back(*CurrentDesignators, DIE->getInit());
610 if (!CurrentDesignators) {
611 CurrentDesignators.emplace(Init, SyntacticInitListEx, &Context);
612 if (!CurrentDesignators->isValid()) {
614 InitListEx->getBeginLoc(), Context.getSourceManager());
617 }
else if (!CurrentDesignators->advanceToNextField(Init)) {
619 InitListEx->getBeginLoc(), Context.getSourceManager());
625 if (!isa<ImplicitValueInitExpr>(Init))
626 Rewrites.emplace_back(*CurrentDesignators, Init);
631 llvm::stable_sort(Rewrites, [&RS](
const auto &Lhs,
const auto &Rhs) {
632 return RS(Lhs.first, Rhs.first);
635 for (
unsigned i = 0, e = Rewrites.size(); i < e; ++i) {
637 Rewrites[i].second->getSourceRange(),
638 Rewrites[i].first.toString(), Context, Replacements);
642 assert(SyntacticInitListEx->getNumInits() >= InitListEx->getNumInits());
651 Context, Replacements);
660 ArrayRef<std::string> DesiredFieldsOrder;
661 std::map<std::string, tooling::Replacements> &Replacements;
665 ArrayRef<std::string> DesiredFieldsOrder,
666 std::map<std::string, tooling::Replacements> &Replacements)
668 Replacements(Replacements) {}
670 ReorderingConsumer(
const ReorderingConsumer &) =
delete;
671 ReorderingConsumer &operator=(
const ReorderingConsumer &) =
delete;
673 void HandleTranslationUnit(ASTContext &Context)
override {
679 SmallVector<unsigned, 4> NewFieldsOrder =
683 ReorderedStruct RS{RD, NewFieldsOrder};
689 const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD);
691 for (
const auto *C : CXXRD->ctors())
692 if (
const auto *D = dyn_cast<CXXConstructorDecl>(
C->getDefinition()))
694 Context, Replacements);
701 if (!CXXRD || CXXRD->isAggregate()) {
703 match(initListExpr(hasType(equalsNode(RD))).bind(
"initListExpr"),
706 Result.getNodeAs<InitListExpr>(
"initListExpr"), RS, Context,
708 Replacements.clear();
717 return std::make_unique<ReorderingConsumer>(RecordName, DesiredFieldsOrder,
static cl::opt< std::string > RecordName("record-name", cl::Required, cl::desc("The name of the struct/class."), cl::cat(ClangReorderFieldsCategory))
This file contains the declarations of the Designator and Designators utility classes.
This file contains the declarations of the ReorderFieldsAction class and the FieldPosition struct.
Represents a part of a designation in a C99/C++20 designated initializer.
const RecordDecl * getStructDecl() const
uint64_t getArrayIndex() const
uint64_t getArrayRangeStart() const
const RecordDecl::field_iterator getStructIter() const
SmallVector< Designator >::const_iterator begin() const
SmallVector< Designator >::const_iterator end() const
std::unique_ptr< ASTConsumer > newASTConsumer()
std::vector< std::string > match(const SymbolIndex &I, const FuzzyFindRequest &Req, bool *Incomplete)
static SourceLocation getStartOfLeadingComment(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts)
Returns the start of the leading comments before Loc.
static bool reorderFieldsInInitListExpr(const InitListExpr *InitListEx, const ReorderedStruct &RS, ASTContext &Context, std::map< std::string, tooling::Replacements > &Replacements)
Reorders initializers in the brace initialization of an aggregate.
static bool containsPreprocessorDirectives(const RecordDecl *Decl, const SourceManager &SrcMgr, const LangOptions &LangOpts)
static bool isOrderValid(const RecordDecl *RD, ArrayRef< unsigned > FieldOrder)
static void addReplacement(SourceRange Old, StringRef New, const ASTContext &Context, std::map< std::string, tooling::Replacements > &Replacements)
Replaces a range of source code by the specified text.
static void reorderFieldsInConstructor(const CXXConstructorDecl *CtorDecl, const ReorderedStruct &RS, ASTContext &Context, std::map< std::string, tooling::Replacements > &Replacements)
Reorders initializers in a C++ struct/class constructor.
static bool declaresMultipleFieldsInMacro(const RecordDecl *Decl, const SourceManager &SrcMgr)
static const RecordDecl * findDefinition(StringRef RecordName, ASTContext &Context)
Finds the definition of a record by name.
static bool reorderFieldsInDefinition(const ReorderedStruct &RS, const ASTContext &Context, std::map< std::string, tooling::Replacements > &Replacements)
Reorders fields in the definition of a struct/class.
static bool isSafeToRewrite(const RecordDecl *Decl, const ASTContext &Context)
static bool declaresMultipleFieldsInStatement(const RecordDecl *Decl)
static void reportError(const Twine &Message, SourceLocation Loc, const SourceManager &SM)
static SourceRange getFullFieldSourceRange(const FieldDecl &Field, const ASTContext &Context)
Returns the full source range for the field declaration up to (including) the trailing semicolumn,...
static SourceLocation getEndOfTrailingComment(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts)
Returns the end of the trailing comments after Loc.
static SmallSetVector< FieldDecl *, 1 > findMembersUsedInInitExpr(const CXXCtorInitializer *Initializer, ASTContext &Context)
Find all member fields used in the given init-list initializer expr that belong to the same record.
static SmallVector< unsigned, 4 > getNewFieldsOrder(const RecordDecl *Definition, ArrayRef< std::string > DesiredFieldsOrder)
Calculates the new order of fields.
static const InitListExpr * getExplicitILE(const InitListExpr *ILE, ASTContext &Context)
Finds the semantic form of the first explicit ancestor of the given initializer list including itself...
static bool isImplicitILE(const InitListExpr *ILE, const ASTContext &Context)
Replacement for broken InitListExpr::isExplicit function.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
ArrayRef< unsigned > NewFieldsOrder
SmallVector< unsigned, 4 > NewFieldsPositions
const RecordDecl * Definition
bool operator()(const Designator &Lhs, const Designator &Rhs) const
Compares compatible designators according to the new struct order.
ReorderedStruct(const RecordDecl *Decl, ArrayRef< unsigned > NewFieldsOrder)