clang-tools 22.0.0git
ReorderFieldsAction.cpp
Go to the documentation of this file.
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-exception
6//
7//===----------------------------------------------------------------------===//
8///
9/// \file
10/// This file contains the definition of the
11/// ReorderFieldsAction::newASTConsumer method
12///
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 " << RecordName
51 << " 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;'.
119 return false;
120
121 const SourceManager &SrcMgr = Context.getSourceManager();
122
123 // Don't attempt to rewrite if a single macro expansion creates multiple
124 // fields.
125 if (declaresMultipleFieldsInMacro(Decl, SrcMgr))
126 return false;
127
128 // Prevent rewriting if there are preprocessor directives present between the
129 // 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 last
172 // 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
184public:
185 ReorderedStruct(const RecordDecl *Decl, ArrayRef<unsigned> NewFieldsOrder)
188 for (unsigned I = 0; I < NewFieldsPositions.size(); ++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 0
194 /// 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 0
199 /// 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
208 const Designator &Rhs) const {
209 switch (Lhs.getTag()) {
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();
223 // Array designators can be compared to array range designators.
224 assert((Rhs.getTag() == Designator::ARRAY ||
226 "Incompatible designators");
227 size_t LhsIdx = Lhs.getTag() == Designator::ARRAY
228 ? Lhs.getArrayIndex()
229 : Lhs.getArrayRangeStart();
230 size_t RhsIdx = Rhs.getTag() == Designator::ARRAY
231 ? Rhs.getArrayIndex()
232 : Rhs.getArrayRangeStart();
233 return LhsIdx < RhsIdx;
234 }
235 llvm_unreachable("Invalid designator tag");
236}
237
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-handling
245/// Replaces a range of source code by the specified text.
246static void
247addReplacement(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 void
257addReplacement(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 void
270addReplacement(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 expr
283/// that belong to the same record
284///
285/// \returns a set of field declarations, empty if none were present
286static 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 since
291 // for those accesses Sema::PerformObjectMemberConversion always inserts an
292 // UncheckedDerivedToBase ImplicitCastExpr between the this expr and the
293 // object expression
294 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 or
311 // 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 the
334 // 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 with
378/// different accesses (public/protected/private) is not supported.
379/// \returns true on success.
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 accesses
390 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;
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's
415/// fields. Thus, we need to ensure that we reorder just the initializers that
416/// are present.
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 false
426 // 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 expression
477 // (for non designated decls)
478 // - The right brace loc of the ILE matches the end of first init expression
479 // (for designated decls)
480 // The first init expression should be taken from the syntactic form, but
481 // 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 given
492/// 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 success
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 and
541 // process it. This is necessary because of designated initializer lists and
542 // possible omitted braces.
543 InitListEx = getExplicitILE(InitListEx, Context);
544
545 // Find if there are any designated initializations or implicit values. If all
546 // initializers are present and none have designators then just reorder them
547 // normally. Otherwise, designators are added to all initializers and they are
548 // sorted in the new order.
549 bool HasImplicitInit = false;
550 bool HasDesignatedInit = false;
551 // The method InitListExpr::getSyntacticForm may return nullptr indicating
552 // 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 syntactic
556 // 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) {
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 be
589 // missing. Insert any missing designators and reorder the expressions
590 // according to the new order.
591 std::optional<Designators> CurrentDesignators;
592 // Remember each initializer expression along with its designators. They are
593 // 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 are
605 // always replaced.
606 Rewrites.emplace_back(*CurrentDesignators, DIE->getInit());
607 } else {
608 // If designators are not initialized then initialize to the first
609 // 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 to
624 // 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 be
645 // 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 for
697 // 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))
706 Result.getNodeAs<InitListExpr>("initListExpr"), RS, Context,
707 Replacements)) {
708 Replacements.clear();
709 return;
710 }
711 }
712 }
713};
714} // end anonymous namespace
715
716std::unique_ptr<ASTConsumer> ReorderFieldsAction::newASTConsumer() {
717 return std::make_unique<ReorderingConsumer>(RecordName, DesiredFieldsOrder,
718 Replacements);
719}
720
721} // namespace reorder_fields
722} // namespace clang
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.
Definition Designator.h:29
const RecordDecl * getStructDecl() const
Definition Designator.h:57
uint64_t getArrayRangeStart() const
Definition Designator.h:67
const RecordDecl::field_iterator getStructIter() const
Definition Designator.h:52
SmallVector< Designator >::const_iterator begin() const
Definition Designator.h:145
SmallVector< Designator >::const_iterator end() const
Definition Designator.h:148
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++ -*-===//
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)