16#include "clang/AST/AST.h"
17#include "clang/AST/ASTConsumer.h"
18#include "clang/AST/ASTContext.h"
19#include "clang/AST/Decl.h"
20#include "clang/AST/RecursiveASTVisitor.h"
21#include "clang/ASTMatchers/ASTMatchFinder.h"
22#include "clang/Lex/Lexer.h"
23#include "clang/Tooling/Refactoring.h"
24#include "llvm/ADT/STLExtras.h"
25#include "llvm/ADT/SetVector.h"
29namespace reorder_fields {
31using llvm::SmallSetVector;
37 ASTContext &Context) {
39 match(recordDecl(hasName(RecordName), isDefinition()).bind(
"recordDecl"),
42 llvm::errs() <<
"Definition of " << RecordName <<
" not found\n";
46 llvm::errs() <<
"The name " << RecordName
47 <<
" is ambiguous, several definitions found\n";
50 return selectFirst<RecordDecl>(
"recordDecl",
Results);
56static SmallVector<unsigned, 4>
58 ArrayRef<std::string> DesiredFieldsOrder) {
59 assert(Definition &&
"Definition is null");
61 llvm::StringMap<unsigned> NameToIndex;
62 for (
const auto *
Field : Definition->fields())
63 NameToIndex[
Field->getName()] =
Field->getFieldIndex();
65 if (DesiredFieldsOrder.size() != NameToIndex.size()) {
66 llvm::errs() <<
"Number of provided fields (" << DesiredFieldsOrder.size()
67 <<
") doesn't match definition (" << NameToIndex.size()
71 SmallVector<unsigned, 4> NewFieldsOrder;
72 for (
const auto &
Name : DesiredFieldsOrder) {
73 if (!NameToIndex.count(
Name)) {
74 llvm::errs() <<
"Field " <<
Name <<
" not found in definition.\n";
77 NewFieldsOrder.push_back(NameToIndex[
Name]);
79 assert(NewFieldsOrder.size() == NameToIndex.size());
80 return NewFieldsOrder;
87 std::map<std::string, tooling::Replacements> &Replacements) {
89 Lexer::getSourceText(CharSourceRange::getTokenRange(New),
90 Context.getSourceManager(), Context.getLangOpts());
91 tooling::Replacement R(Context.getSourceManager(),
92 CharSourceRange::getTokenRange(Old), NewText,
93 Context.getLangOpts());
94 consumeError(Replacements[std::string(R.getFilePath())].add(R));
101static SmallSetVector<FieldDecl *, 1>
103 ASTContext &Context) {
104 SmallSetVector<FieldDecl *, 1>
Results;
109 auto FoundExprs = match(
112 findAll(memberExpr(hasObjectExpression(cxxThisExpr())).bind(
"ME"))),
113 *Initializer->getInit(), Context);
114 for (BoundNodes &BN : FoundExprs)
115 if (
auto *MemExpr = BN.getNodeAs<MemberExpr>(
"ME"))
116 if (
auto *FD = dyn_cast<FieldDecl>(MemExpr->getMemberDecl()))
123 const SourceManager &SM,
124 const LangOptions &LangOpts) {
125 if (
Loc.isMacroID()) {
128 Loc = Lexer::getLocForEndOfToken(
Loc, 0, SM, LangOpts);
131 std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(
Loc);
134 bool InvalidTemp =
false;
135 StringRef File = SM.getBufferData(LocInfo.first, &InvalidTemp);
139 const char *TokenBegin = File.data() + LocInfo.second;
141 Lexer lexer(SM.getLocForStartOfFile(LocInfo.first), LangOpts, File.begin(),
142 TokenBegin, File.end());
143 lexer.SetCommentRetentionState(
true);
146 lexer.LexFromRawLexer(Tok);
152 const SourceManager &SM,
153 const LangOptions &LangOpts) {
156 const unsigned Column = SM.getPresumedColumnNumber(
Loc);
158 while (Tok && Tok->is(tok::comment) &&
159 SM.getPresumedColumnNumber(Tok->getLocation()) >
Column) {
160 Loc = Tok->getEndLoc();
170 const ASTContext &Context) {
171 const SourceRange
Range =
Field.getSourceRange();
172 SourceLocation Begin =
Range.getBegin();
173 SourceLocation End =
Range.getEnd();
174 const SourceManager &SM = Context.getSourceManager();
175 const LangOptions &LangOpts = Context.getLangOpts();
177 std::optional<Token> CurrentToken = Lexer::findNextToken(End, SM, LangOpts);
180 return SourceRange(Begin, End);
182 if (CurrentToken->is(tok::eof))
185 End = CurrentToken->getLastLoc();
187 if (CurrentToken->is(tok::semi))
191 return SourceRange(Begin, End);
200 const RecordDecl *Definition, ArrayRef<unsigned> NewFieldsOrder,
201 const ASTContext &Context,
202 std::map<std::string, tooling::Replacements> &Replacements) {
203 assert(Definition &&
"Definition is null");
205 SmallVector<const FieldDecl *, 10> Fields;
206 for (
const auto *
Field : Definition->fields())
207 Fields.push_back(
Field);
210 for (
const auto *
Field : Definition->fields()) {
211 const auto FieldIndex =
Field->getFieldIndex();
212 if (
Field->getAccess() != Fields[NewFieldsOrder[FieldIndex]]->getAccess()) {
213 llvm::errs() <<
"Currently reordering of fields with different accesses "
214 "is not supported\n";
219 for (
const auto *
Field : Definition->fields()) {
220 const auto FieldIndex =
Field->getFieldIndex();
221 if (FieldIndex == NewFieldsOrder[FieldIndex])
226 Context, Replacements);
237 const CXXConstructorDecl *CtorDecl, ArrayRef<unsigned> NewFieldsOrder,
239 std::map<std::string, tooling::Replacements> &Replacements) {
240 assert(CtorDecl &&
"Constructor declaration is null");
241 if (CtorDecl->isImplicit() || CtorDecl->getNumCtorInitializers() <= 1)
247 assert(CtorDecl->isThisDeclarationADefinition() &&
"Not a definition");
249 SmallVector<unsigned, 10> NewFieldsPositions(NewFieldsOrder.size());
250 for (
unsigned i = 0, e = NewFieldsOrder.size(); i < e; ++i)
251 NewFieldsPositions[NewFieldsOrder[i]] = i;
253 SmallVector<const CXXCtorInitializer *, 10> OldWrittenInitializersOrder;
254 SmallVector<const CXXCtorInitializer *, 10> NewWrittenInitializersOrder;
255 for (
const auto *Initializer : CtorDecl->inits()) {
256 if (!Initializer->isMemberInitializer() || !Initializer->isWritten())
260 const FieldDecl *ThisM = Initializer->getMember();
262 for (
const FieldDecl *UM : UsedMembers) {
263 if (NewFieldsPositions[UM->getFieldIndex()] >
264 NewFieldsPositions[ThisM->getFieldIndex()]) {
265 DiagnosticsEngine &DiagEngine = Context.getDiagnostics();
266 auto Description = (
"reordering field " + UM->getName() +
" after " +
267 ThisM->getName() +
" makes " + UM->getName() +
268 " uninitialized when used in init expression")
270 unsigned ID = DiagEngine.getDiagnosticIDs()->getCustomDiagID(
271 DiagnosticIDs::Warning, Description);
272 DiagEngine.Report(Initializer->getSourceLocation(),
ID);
276 OldWrittenInitializersOrder.push_back(Initializer);
277 NewWrittenInitializersOrder.push_back(Initializer);
279 auto ByFieldNewPosition = [&](
const CXXCtorInitializer *LHS,
280 const CXXCtorInitializer *RHS) {
282 return NewFieldsPositions[LHS->getMember()->getFieldIndex()] <
283 NewFieldsPositions[RHS->getMember()->getFieldIndex()];
285 llvm::sort(NewWrittenInitializersOrder, ByFieldNewPosition);
286 assert(OldWrittenInitializersOrder.size() ==
287 NewWrittenInitializersOrder.size());
288 for (
unsigned i = 0, e = NewWrittenInitializersOrder.size(); i < e; ++i)
289 if (OldWrittenInitializersOrder[i] != NewWrittenInitializersOrder[i])
291 NewWrittenInitializersOrder[i]->getSourceRange(), Context,
300 const InitListExpr *InitListEx, ArrayRef<unsigned> NewFieldsOrder,
301 const ASTContext &Context,
302 std::map<std::string, tooling::Replacements> &Replacements) {
303 assert(InitListEx &&
"Init list expression is null");
306 if (!InitListEx->isExplicit())
310 if (
const auto *SyntacticForm = InitListEx->getSyntacticForm())
311 InitListEx = SyntacticForm;
313 if (!InitListEx->getNumInits())
315 if (InitListEx->getNumInits() != NewFieldsOrder.size()) {
316 llvm::errs() <<
"Currently only full initialization is supported\n";
319 for (
unsigned i = 0, e = InitListEx->getNumInits(); i < e; ++i)
320 if (i != NewFieldsOrder[i])
322 InitListEx->getInit(NewFieldsOrder[i])->getSourceRange(),
323 Context, Replacements);
329 StringRef RecordName;
330 ArrayRef<std::string> DesiredFieldsOrder;
331 std::map<std::string, tooling::Replacements> &Replacements;
334 ReorderingConsumer(StringRef RecordName,
335 ArrayRef<std::string> DesiredFieldsOrder,
336 std::map<std::string, tooling::Replacements> &Replacements)
337 : RecordName(RecordName), DesiredFieldsOrder(DesiredFieldsOrder),
338 Replacements(Replacements) {}
340 ReorderingConsumer(
const ReorderingConsumer &) =
delete;
341 ReorderingConsumer &operator=(
const ReorderingConsumer &) =
delete;
343 void HandleTranslationUnit(ASTContext &Context)
override {
347 SmallVector<unsigned, 4> NewFieldsOrder =
349 if (NewFieldsOrder.empty())
355 const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD);
357 for (
const auto *
C : CXXRD->ctors())
358 if (
const auto *D = dyn_cast<CXXConstructorDecl>(
C->getDefinition()))
360 NewFieldsOrder, Context, Replacements);
367 if (!CXXRD || CXXRD->isAggregate())
369 match(initListExpr(hasType(equalsNode(RD))).bind(
"initListExpr"),
372 Result.getNodeAs<InitListExpr>(
"initListExpr"), NewFieldsOrder,
373 Context, Replacements)) {
374 Replacements.clear();
382 return std::make_unique<ReorderingConsumer>(RecordName, DesiredFieldsOrder,
llvm::SmallString< 256U > Name
std::vector< CodeCompletionResult > Results
CharSourceRange Range
SourceRange for the file name.
This file contains the declarations of the ReorderFieldsAction class and the FieldPosition struct.
std::unique_ptr< ASTConsumer > newASTConsumer()
static void reorderFieldsInConstructor(const CXXConstructorDecl *CtorDecl, ArrayRef< unsigned > NewFieldsOrder, ASTContext &Context, std::map< std::string, tooling::Replacements > &Replacements)
Reorders initializers in a C++ struct/class constructor.
static const RecordDecl * findDefinition(StringRef RecordName, ASTContext &Context)
Finds the definition of a record by name.
static bool reorderFieldsInInitListExpr(const InitListExpr *InitListEx, ArrayRef< unsigned > NewFieldsOrder, const ASTContext &Context, std::map< std::string, tooling::Replacements > &Replacements)
Reorders initializers in the brace initialization of an aggregate.
static void addReplacement(SourceRange Old, SourceRange New, const ASTContext &Context, std::map< std::string, tooling::Replacements > &Replacements)
Replaces one range of source code by another.
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 bool reorderFieldsInDefinition(const RecordDecl *Definition, ArrayRef< unsigned > NewFieldsOrder, const ASTContext &Context, std::map< std::string, tooling::Replacements > &Replacements)
Reorders fields in the definition of a struct/class.
static std::optional< Token > getTokenAfter(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts)
Returns the next token after Loc (including comment tokens).
static SmallVector< unsigned, 4 > getNewFieldsOrder(const RecordDecl *Definition, ArrayRef< std::string > DesiredFieldsOrder)
Calculates the new order of fields.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//