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/Lex/Lexer.h" 24 #include "clang/Tooling/Refactoring.h" 25 #include "llvm/ADT/SetVector.h" 30 namespace reorder_fields {
32 using llvm::SmallSetVector;
38 ASTContext &Context) {
40 match(recordDecl(hasName(RecordName), isDefinition()).bind(
"recordDecl"),
43 llvm::errs() <<
"Definition of " << RecordName <<
" not found\n";
47 llvm::errs() <<
"The name " << RecordName
48 <<
" is ambiguous, several definitions found\n";
51 return selectFirst<RecordDecl>(
"recordDecl",
Results);
57 static SmallVector<unsigned, 4>
59 ArrayRef<std::string> DesiredFieldsOrder) {
60 assert(Definition &&
"Definition is null");
62 llvm::StringMap<unsigned> NameToIndex;
63 for (
const auto *Field : Definition->fields())
64 NameToIndex[Field->getName()] = Field->getFieldIndex();
66 if (DesiredFieldsOrder.size() != NameToIndex.size()) {
67 llvm::errs() <<
"Number of provided fields doesn't match definition.\n";
70 SmallVector<unsigned, 4> NewFieldsOrder;
71 for (
const auto &
Name : DesiredFieldsOrder) {
72 if (!NameToIndex.count(
Name)) {
73 llvm::errs() <<
"Field " <<
Name <<
" not found in definition.\n";
76 NewFieldsOrder.push_back(NameToIndex[
Name]);
78 assert(NewFieldsOrder.size() == NameToIndex.size());
79 return NewFieldsOrder;
86 std::map<std::string, tooling::Replacements> &Replacements) {
88 Lexer::getSourceText(CharSourceRange::getTokenRange(New),
89 Context.getSourceManager(), Context.getLangOpts());
90 tooling::Replacement R(Context.getSourceManager(),
91 CharSourceRange::getTokenRange(Old), NewText,
92 Context.getLangOpts());
93 consumeError(Replacements[R.getFilePath()].add(R));
100 static SmallSetVector<FieldDecl *, 1>
102 ASTContext &Context) {
103 SmallSetVector<FieldDecl *, 1>
Results;
109 match(findAll(memberExpr(hasObjectExpression(cxxThisExpr())).bind(
"ME")),
110 *Initializer->getInit(), Context);
111 for (BoundNodes &BN : FoundExprs)
112 if (
auto *MemExpr = BN.getNodeAs<MemberExpr>(
"ME"))
113 if (
auto *FD = dyn_cast<FieldDecl>(MemExpr->getMemberDecl()))
124 const RecordDecl *Definition, ArrayRef<unsigned> NewFieldsOrder,
125 const ASTContext &Context,
126 std::map<std::string, tooling::Replacements> &Replacements) {
127 assert(Definition &&
"Definition is null");
129 SmallVector<const FieldDecl *, 10> Fields;
130 for (
const auto *Field : Definition->fields())
131 Fields.push_back(Field);
134 for (
const auto *Field : Definition->fields()) {
135 const auto FieldIndex = Field->getFieldIndex();
136 if (Field->getAccess() != Fields[NewFieldsOrder[FieldIndex]]->getAccess()) {
137 llvm::errs() <<
"Currently reodering of fields with different accesses " 138 "is not supported\n";
143 for (
const auto *Field : Definition->fields()) {
144 const auto FieldIndex = Field->getFieldIndex();
145 if (FieldIndex == NewFieldsOrder[FieldIndex])
148 Fields[NewFieldsOrder[FieldIndex]]->getSourceRange(),
149 Context, Replacements);
160 const CXXConstructorDecl *CtorDecl, ArrayRef<unsigned> NewFieldsOrder,
162 std::map<std::string, tooling::Replacements> &Replacements) {
163 assert(CtorDecl &&
"Constructor declaration is null");
164 if (CtorDecl->isImplicit() || CtorDecl->getNumCtorInitializers() <= 1)
170 assert(CtorDecl->isThisDeclarationADefinition() &&
"Not a definition");
172 SmallVector<unsigned, 10> NewFieldsPositions(NewFieldsOrder.size());
173 for (
unsigned i = 0, e = NewFieldsOrder.size(); i < e; ++i)
174 NewFieldsPositions[NewFieldsOrder[i]] = i;
176 SmallVector<const CXXCtorInitializer *, 10> OldWrittenInitializersOrder;
177 SmallVector<const CXXCtorInitializer *, 10> NewWrittenInitializersOrder;
178 for (
const auto *Initializer : CtorDecl->inits()) {
179 if (!Initializer->isMemberInitializer() || !Initializer->isWritten())
183 const FieldDecl *ThisM = Initializer->getMember();
185 for (
const FieldDecl *UM : UsedMembers) {
186 if (NewFieldsPositions[UM->getFieldIndex()] >
187 NewFieldsPositions[ThisM->getFieldIndex()]) {
188 DiagnosticsEngine &DiagEngine = Context.getDiagnostics();
189 auto Description = (
"reordering field " + UM->getName() +
" after " +
190 ThisM->getName() +
" makes " + UM->getName() +
191 " uninitialized when used in init expression")
193 unsigned ID = DiagEngine.getDiagnosticIDs()->getCustomDiagID(
194 DiagnosticIDs::Warning, Description);
195 DiagEngine.Report(Initializer->getSourceLocation(), ID);
199 OldWrittenInitializersOrder.push_back(Initializer);
200 NewWrittenInitializersOrder.push_back(Initializer);
202 auto ByFieldNewPosition = [&](
const CXXCtorInitializer *LHS,
203 const CXXCtorInitializer *RHS) {
205 return NewFieldsPositions[LHS->getMember()->getFieldIndex()] <
206 NewFieldsPositions[RHS->getMember()->getFieldIndex()];
208 std::sort(std::begin(NewWrittenInitializersOrder),
209 std::end(NewWrittenInitializersOrder), ByFieldNewPosition);
210 assert(OldWrittenInitializersOrder.size() ==
211 NewWrittenInitializersOrder.size());
212 for (
unsigned i = 0, e = NewWrittenInitializersOrder.size(); i < e; ++i)
213 if (OldWrittenInitializersOrder[i] != NewWrittenInitializersOrder[i])
215 NewWrittenInitializersOrder[i]->getSourceRange(), Context,
224 const InitListExpr *InitListEx, ArrayRef<unsigned> NewFieldsOrder,
225 const ASTContext &Context,
226 std::map<std::string, tooling::Replacements> &Replacements) {
227 assert(InitListEx &&
"Init list expression is null");
230 if (!InitListEx->isExplicit())
234 if (
const auto *SyntacticForm = InitListEx->getSyntacticForm())
235 InitListEx = SyntacticForm;
237 if (!InitListEx->getNumInits())
239 if (InitListEx->getNumInits() != NewFieldsOrder.size()) {
240 llvm::errs() <<
"Currently only full initialization is supported\n";
243 for (
unsigned i = 0, e = InitListEx->getNumInits(); i < e; ++i)
244 if (i != NewFieldsOrder[i])
246 InitListEx->getInit(NewFieldsOrder[i])->getSourceRange(),
247 Context, Replacements);
254 ArrayRef<std::string> DesiredFieldsOrder;
255 std::map<std::string, tooling::Replacements> &Replacements;
258 ReorderingConsumer(StringRef RecordName,
259 ArrayRef<std::string> DesiredFieldsOrder,
260 std::map<std::string, tooling::Replacements> &Replacements)
261 :
RecordName(RecordName), DesiredFieldsOrder(DesiredFieldsOrder),
262 Replacements(Replacements) {}
264 ReorderingConsumer(
const ReorderingConsumer &) =
delete;
265 ReorderingConsumer &operator=(
const ReorderingConsumer &) =
delete;
267 void HandleTranslationUnit(ASTContext &Context)
override {
271 SmallVector<unsigned, 4> NewFieldsOrder =
273 if (NewFieldsOrder.empty())
279 const CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(RD);
281 for (
const auto *C : CXXRD->ctors())
282 if (
const auto *D = dyn_cast<CXXConstructorDecl>(C->getDefinition()))
284 NewFieldsOrder, Context, Replacements);
291 if (!CXXRD || CXXRD->isAggregate())
293 match(initListExpr(hasType(equalsNode(RD))).bind(
"initListExpr"),
296 Result.getNodeAs<InitListExpr>(
"initListExpr"), NewFieldsOrder,
297 Context, Replacements)) {
298 Replacements.clear();
306 return llvm::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))
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 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 const RecordDecl * findDefinition(StringRef RecordName, ASTContext &Context)
Finds the definition of a record by name.
std::unique_ptr< ASTConsumer > newASTConsumer()
std::vector< CodeCompletionResult > Results
This file contains the declarations of the ReorderFieldsAction class and the FieldPosition struct...
static SmallVector< unsigned, 4 > getNewFieldsOrder(const RecordDecl *Definition, ArrayRef< std::string > DesiredFieldsOrder)
Calculates the new order of fields.
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 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.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
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.