-
Notifications
You must be signed in to change notification settings - Fork 15.4k
[clang-reorder-fields] Support designated initializers #142150
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
alexander-shaposhnikov
merged 5 commits into
llvm:main
from
vvuksanovic:reorder-fields-designators
Sep 1, 2025
Merged
Changes from 2 commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
69381c7
[clang-reorder-fields] Refactor the new order information
vvuksanovic 144f878
[clang-reorder-fields] Support designated initializers
vvuksanovic 47344f0
fixup! [clang-reorder-fields] Support designated initializers
vvuksanovic 5eddc1d
fixup! [clang-reorder-fields] Support designated initializers
vvuksanovic 9a4e038
fixup! [clang-reorder-fields] Support designated initializers
vvuksanovic File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,199 @@ | ||
| //===-- tools/extra/clang-reorder-fields/utils/Designator.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 DesignatorIter and Designators | ||
| /// utility classes. | ||
| /// | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "Designator.h" | ||
| #include "clang/AST/ASTContext.h" | ||
| #include "clang/AST/Expr.h" | ||
|
|
||
| namespace clang { | ||
| namespace reorder_fields { | ||
|
|
||
| DesignatorIter &DesignatorIter::operator++() { | ||
| assert(!isFinished() && "Iterator is already finished"); | ||
| switch (Tag) { | ||
| case STRUCT: | ||
| if (StructIt.Record->isUnion()) { | ||
| // Union always finishes on first increment. | ||
| StructIt.Field = StructIt.Record->field_end(); | ||
| Type = QualType(); | ||
| break; | ||
| } | ||
| ++StructIt.Field; | ||
| if (StructIt.Field != StructIt.Record->field_end()) { | ||
| Type = StructIt.Field->getType(); | ||
| } else { | ||
| Type = QualType(); | ||
| } | ||
| break; | ||
| case ARRAY: | ||
| ++ArrayIt.Index; | ||
| break; | ||
| case ARRAY_RANGE: | ||
| ArrayIt.Index = ArrayRangeIt.End + 1; | ||
| ArrayIt.Size = ArrayRangeIt.Size; | ||
| Tag = ARRAY; | ||
| break; | ||
| } | ||
| return *this; | ||
| } | ||
|
|
||
| bool DesignatorIter::isFinished() { | ||
| switch (Tag) { | ||
| case STRUCT: | ||
| return StructIt.Field == StructIt.Record->field_end(); | ||
| case ARRAY: | ||
| return ArrayIt.Index == ArrayIt.Size; | ||
| case ARRAY_RANGE: | ||
| return ArrayRangeIt.End == ArrayRangeIt.Size; | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| Designators::Designators(const DesignatedInitExpr *DIE, const InitListExpr *ILE, | ||
| const ASTContext &Context) { | ||
| for (const auto &D : DIE->designators()) { | ||
| if (D.isFieldDesignator()) { | ||
| RecordDecl *DesignatorRecord = D.getFieldDecl()->getParent(); | ||
| for (auto FieldIt = DesignatorRecord->field_begin(); | ||
| FieldIt != DesignatorRecord->field_end(); ++FieldIt) { | ||
| if (*FieldIt == D.getFieldDecl()) { | ||
| DesignatorList.push_back( | ||
| {FieldIt->getType(), FieldIt, DesignatorRecord}); | ||
| break; | ||
| } | ||
| } | ||
| } else { | ||
| const QualType CurrentType = DesignatorList.empty() | ||
| ? ILE->getType() | ||
| : DesignatorList.back().getType(); | ||
| const ConstantArrayType *CAT = | ||
| Context.getAsConstantArrayType(CurrentType); | ||
| if (!CAT) { | ||
| // Non-constant-sized arrays are not supported. | ||
| DesignatorList.clear(); | ||
| return; | ||
| } | ||
| if (D.isArrayDesignator()) { | ||
| DesignatorList.push_back({CAT->getElementType(), | ||
| DIE->getArrayIndex(D) | ||
| ->EvaluateKnownConstInt(Context) | ||
| .getZExtValue(), | ||
| CAT->getSize().getZExtValue()}); | ||
| } else if (D.isArrayRangeDesignator()) { | ||
| DesignatorList.push_back({CAT->getElementType(), | ||
| DIE->getArrayRangeStart(D) | ||
| ->EvaluateKnownConstInt(Context) | ||
| .getZExtValue(), | ||
| DIE->getArrayRangeEnd(D) | ||
| ->EvaluateKnownConstInt(Context) | ||
| .getZExtValue(), | ||
| CAT->getSize().getZExtValue()}); | ||
| } else { | ||
| llvm_unreachable("Unexpected designator kind"); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| bool Designators::increment(const InitListExpr *ILE, const Expr *Init, | ||
| const ASTContext &Context) { | ||
| if (DesignatorList.empty()) { | ||
| // First field is not designated. Initialize to the first field or | ||
| // array index. | ||
| if (ILE->getType()->isArrayType()) { | ||
| const ConstantArrayType *CAT = | ||
| Context.getAsConstantArrayType(ILE->getType()); | ||
| // Only constant size arrays are supported. | ||
| if (!CAT) { | ||
| DesignatorList.clear(); | ||
| return false; | ||
| } | ||
| DesignatorList.push_back( | ||
| {CAT->getElementType(), 0, CAT->getSize().getZExtValue()}); | ||
| } else { | ||
| const RecordDecl *DesignatorRD = ILE->getType()->getAsRecordDecl(); | ||
| DesignatorList.push_back({DesignatorRD->field_begin()->getType(), | ||
| DesignatorRD->field_begin(), DesignatorRD}); | ||
| } | ||
| } else { | ||
| while (!DesignatorList.empty()) { | ||
| auto &CurrentDesignator = DesignatorList.back(); | ||
| ++CurrentDesignator; | ||
| if (CurrentDesignator.isFinished()) { | ||
| DesignatorList.pop_back(); | ||
| continue; | ||
| } | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| // If the designator list is empty at this point, then there must be excess | ||
| // elements in the initializer list. They are not currently supported. | ||
| if (DesignatorList.empty()) | ||
| return false; | ||
|
|
||
| // Check for missing braces. If the types don't match then there are | ||
| // missing braces. | ||
| while (true) { | ||
| const QualType T = DesignatorList.back().getType(); | ||
| // If the types match, there are no missing braces. | ||
| if (Init->getType() == T) | ||
| break; | ||
|
|
||
| // If the current type is a struct, then get its first field. | ||
| if (T->isRecordType()) { | ||
| DesignatorList.push_back({T->getAsRecordDecl()->field_begin()->getType(), | ||
| T->getAsRecordDecl()->field_begin(), | ||
| T->getAsRecordDecl()}); | ||
| continue; | ||
| } | ||
| // If the current type is an array, then get its first element. | ||
| if (T->isArrayType()) { | ||
| DesignatorList.push_back( | ||
| {Context.getAsArrayType(T)->getElementType(), 0, | ||
| Context.getAsConstantArrayType(T)->getSize().getZExtValue()}); | ||
| continue; | ||
| } | ||
|
|
||
| // The initializer doesn't match the expected type. The initializer list is | ||
| // invalid. | ||
| return false; | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| std::string Designators::toString() const { | ||
| if (DesignatorList.empty()) | ||
| return ""; | ||
| std::string Designator = ""; | ||
| for (auto &I : DesignatorList) { | ||
| switch (I.getTag()) { | ||
| case DesignatorIter::STRUCT: | ||
| Designator += "." + I.getStructIter()->getName().str(); | ||
| break; | ||
| case DesignatorIter::ARRAY: | ||
| Designator += "[" + std::to_string(I.getArrayIndex()) + "]"; | ||
| break; | ||
| case DesignatorIter::ARRAY_RANGE: | ||
| Designator += "[" + std::to_string(I.getArrayRangeStart()) + "..." + | ||
| std::to_string(I.getArrayRangeEnd()) + "]"; | ||
| } | ||
| } | ||
| Designator += " = "; | ||
| return Designator; | ||
| } | ||
|
|
||
| } // namespace reorder_fields | ||
| } // namespace clang | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,145 @@ | ||
| //===-- tools/extra/clang-reorder-fields/utils/Designator.h -----*- 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 declarations of the DesignatorIter and Designators | ||
| /// utility class. | ||
| /// | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_REORDER_FIELDS_UTILS_DESIGNATOR_H | ||
| #define LLVM_CLANG_TOOLS_EXTRA_CLANG_REORDER_FIELDS_UTILS_DESIGNATOR_H | ||
|
|
||
| #include "clang/AST/Decl.h" | ||
| #include "clang/AST/Expr.h" | ||
| #include "clang/AST/Type.h" | ||
|
|
||
| namespace clang { | ||
| namespace reorder_fields { | ||
|
|
||
| class DesignatorIter { | ||
vvuksanovic marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| public: | ||
| enum Kind { STRUCT, ARRAY, ARRAY_RANGE }; | ||
|
|
||
| DesignatorIter(const QualType Type, RecordDecl::field_iterator Field, | ||
| const RecordDecl *RD) | ||
| : Tag(STRUCT), Type(Type), StructIt({Field, RD}) {} | ||
|
|
||
| DesignatorIter(const QualType Type, uint64_t Idx, uint64_t Size) | ||
| : Tag(ARRAY), Type(Type), ArrayIt({Idx, Size}) {} | ||
|
|
||
| DesignatorIter(const QualType Type, uint64_t Start, uint64_t End, | ||
| uint64_t Size) | ||
| : Tag(ARRAY_RANGE), Type(Type), ArrayRangeIt({Start, End, Size}) {} | ||
|
|
||
| /// Moves the iterator to the next element. | ||
| DesignatorIter &operator++(); | ||
|
|
||
| /// Checks if the iterator has iterated through all elements. | ||
| bool isFinished(); | ||
|
|
||
| Kind getTag() const { return Tag; } | ||
| QualType getType() const { return Type; } | ||
|
|
||
| const RecordDecl::field_iterator getStructIter() const { | ||
| assert(Tag == STRUCT && "Must be a field designator"); | ||
| return StructIt.Field; | ||
| } | ||
|
|
||
| const RecordDecl *getStructDecl() const { | ||
| assert(Tag == STRUCT && "Must be a field designator"); | ||
| return StructIt.Record; | ||
| } | ||
|
|
||
| uint64_t getArrayIndex() const { | ||
| assert(Tag == ARRAY && "Must be an array designator"); | ||
| return ArrayIt.Index; | ||
| } | ||
|
|
||
| uint64_t getArrayRangeStart() const { | ||
| assert(Tag == ARRAY_RANGE && "Must be an array range designator"); | ||
| return ArrayRangeIt.Start; | ||
| } | ||
|
|
||
| uint64_t getArrayRangeEnd() const { | ||
| assert(Tag == ARRAY_RANGE && "Must be an array range designator"); | ||
| return ArrayRangeIt.End; | ||
| } | ||
|
|
||
| uint64_t getArraySize() const { | ||
| assert((Tag == ARRAY || Tag == ARRAY_RANGE) && | ||
| "Must be an array or range designator"); | ||
| if (Tag == ARRAY) | ||
| return ArrayIt.Size; | ||
| return ArrayRangeIt.Size; | ||
| } | ||
|
|
||
| private: | ||
| /// Type of the designator. | ||
| Kind Tag; | ||
|
|
||
| /// Type of the designated entry. For arrays this is the type of the element. | ||
| QualType Type; | ||
|
|
||
| /// Field designator has the iterator to the field and the record the field | ||
| /// is declared in. | ||
| struct StructIter { | ||
| RecordDecl::field_iterator Field; | ||
| const RecordDecl *Record; | ||
| }; | ||
|
|
||
| /// Array designator has an index and size of the array. | ||
| struct ArrayIter { | ||
| uint64_t Index; | ||
| uint64_t Size; | ||
| }; | ||
|
|
||
| /// Array range designator has a start and end index and size of the array. | ||
| struct ArrayRangeIter { | ||
| uint64_t Start; | ||
| uint64_t End; | ||
| uint64_t Size; | ||
| }; | ||
|
|
||
| union { | ||
| StructIter StructIt; | ||
| ArrayIter ArrayIt; | ||
| ArrayRangeIter ArrayRangeIt; | ||
| }; | ||
| }; | ||
|
|
||
| class Designators { | ||
| public: | ||
| Designators() = default; | ||
| Designators(const DesignatedInitExpr *DIE, const InitListExpr *ILE, | ||
| const ASTContext &Context); | ||
|
|
||
| /// Moves the designators to the next initializer in the struct/array. If the | ||
| /// type of next initializer doesn't match the expected type then there are | ||
| /// omitted braces and we add new designators to reflect that. | ||
| bool increment(const InitListExpr *ILE, const Expr *Init, | ||
| const ASTContext &Context); | ||
|
|
||
| /// Gets a string representation from a list of designators. This string will | ||
| /// be inserted before an initializer expression to make it designated. | ||
| std::string toString() const; | ||
|
|
||
| size_t size() const { return DesignatorList.size(); } | ||
|
|
||
| const DesignatorIter &operator[](unsigned Idx) const { | ||
| return DesignatorList[Idx]; | ||
| } | ||
|
|
||
| private: | ||
| SmallVector<DesignatorIter, 1> DesignatorList; | ||
| }; | ||
|
|
||
| } // namespace reorder_fields | ||
| } // namespace clang | ||
|
|
||
| #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_REORDER_FIELDS_UTILS_DESIGNATOR_H | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.