Skip to content

Commit 144f878

Browse files
committed
[clang-reorder-fields] Support designated initializers
Initializer lists with designators, missing elements or omitted braces can now be rewritten. Any missing designators are added and they get sorted according to the new order. ``` struct Foo { int a; int b; int c; }; struct Foo foo = { .a = 1, 2, 3 } ``` when reordering elements to "b,a,c" becomes: ``` struct Foo { int b; int a; int c; }; struct Foo foo = { .b = 2, .a = 1, .c = 3 } ```
1 parent 69381c7 commit 144f878

File tree

5 files changed

+597
-20
lines changed

5 files changed

+597
-20
lines changed

clang-tools-extra/clang-reorder-fields/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ set(LLVM_LINK_COMPONENTS
44
)
55

66
add_clang_library(clangReorderFields STATIC
7+
Designator.cpp
78
ReorderFieldsAction.cpp
89

910
DEPENDS
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
//===-- tools/extra/clang-reorder-fields/utils/Designator.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 DesignatorIter and Designators
11+
/// utility classes.
12+
///
13+
//===----------------------------------------------------------------------===//
14+
15+
#include "Designator.h"
16+
#include "clang/AST/ASTContext.h"
17+
#include "clang/AST/Expr.h"
18+
19+
namespace clang {
20+
namespace reorder_fields {
21+
22+
DesignatorIter &DesignatorIter::operator++() {
23+
assert(!isFinished() && "Iterator is already finished");
24+
switch (Tag) {
25+
case STRUCT:
26+
if (StructIt.Record->isUnion()) {
27+
// Union always finishes on first increment.
28+
StructIt.Field = StructIt.Record->field_end();
29+
Type = QualType();
30+
break;
31+
}
32+
++StructIt.Field;
33+
if (StructIt.Field != StructIt.Record->field_end()) {
34+
Type = StructIt.Field->getType();
35+
} else {
36+
Type = QualType();
37+
}
38+
break;
39+
case ARRAY:
40+
++ArrayIt.Index;
41+
break;
42+
case ARRAY_RANGE:
43+
ArrayIt.Index = ArrayRangeIt.End + 1;
44+
ArrayIt.Size = ArrayRangeIt.Size;
45+
Tag = ARRAY;
46+
break;
47+
}
48+
return *this;
49+
}
50+
51+
bool DesignatorIter::isFinished() {
52+
switch (Tag) {
53+
case STRUCT:
54+
return StructIt.Field == StructIt.Record->field_end();
55+
case ARRAY:
56+
return ArrayIt.Index == ArrayIt.Size;
57+
case ARRAY_RANGE:
58+
return ArrayRangeIt.End == ArrayRangeIt.Size;
59+
}
60+
return false;
61+
}
62+
63+
Designators::Designators(const DesignatedInitExpr *DIE, const InitListExpr *ILE,
64+
const ASTContext &Context) {
65+
for (const auto &D : DIE->designators()) {
66+
if (D.isFieldDesignator()) {
67+
RecordDecl *DesignatorRecord = D.getFieldDecl()->getParent();
68+
for (auto FieldIt = DesignatorRecord->field_begin();
69+
FieldIt != DesignatorRecord->field_end(); ++FieldIt) {
70+
if (*FieldIt == D.getFieldDecl()) {
71+
DesignatorList.push_back(
72+
{FieldIt->getType(), FieldIt, DesignatorRecord});
73+
break;
74+
}
75+
}
76+
} else {
77+
const QualType CurrentType = DesignatorList.empty()
78+
? ILE->getType()
79+
: DesignatorList.back().getType();
80+
const ConstantArrayType *CAT =
81+
Context.getAsConstantArrayType(CurrentType);
82+
if (!CAT) {
83+
// Non-constant-sized arrays are not supported.
84+
DesignatorList.clear();
85+
return;
86+
}
87+
if (D.isArrayDesignator()) {
88+
DesignatorList.push_back({CAT->getElementType(),
89+
DIE->getArrayIndex(D)
90+
->EvaluateKnownConstInt(Context)
91+
.getZExtValue(),
92+
CAT->getSize().getZExtValue()});
93+
} else if (D.isArrayRangeDesignator()) {
94+
DesignatorList.push_back({CAT->getElementType(),
95+
DIE->getArrayRangeStart(D)
96+
->EvaluateKnownConstInt(Context)
97+
.getZExtValue(),
98+
DIE->getArrayRangeEnd(D)
99+
->EvaluateKnownConstInt(Context)
100+
.getZExtValue(),
101+
CAT->getSize().getZExtValue()});
102+
} else {
103+
llvm_unreachable("Unexpected designator kind");
104+
}
105+
}
106+
}
107+
}
108+
109+
bool Designators::increment(const InitListExpr *ILE, const Expr *Init,
110+
const ASTContext &Context) {
111+
if (DesignatorList.empty()) {
112+
// First field is not designated. Initialize to the first field or
113+
// array index.
114+
if (ILE->getType()->isArrayType()) {
115+
const ConstantArrayType *CAT =
116+
Context.getAsConstantArrayType(ILE->getType());
117+
// Only constant size arrays are supported.
118+
if (!CAT) {
119+
DesignatorList.clear();
120+
return false;
121+
}
122+
DesignatorList.push_back(
123+
{CAT->getElementType(), 0, CAT->getSize().getZExtValue()});
124+
} else {
125+
const RecordDecl *DesignatorRD = ILE->getType()->getAsRecordDecl();
126+
DesignatorList.push_back({DesignatorRD->field_begin()->getType(),
127+
DesignatorRD->field_begin(), DesignatorRD});
128+
}
129+
} else {
130+
while (!DesignatorList.empty()) {
131+
auto &CurrentDesignator = DesignatorList.back();
132+
++CurrentDesignator;
133+
if (CurrentDesignator.isFinished()) {
134+
DesignatorList.pop_back();
135+
continue;
136+
}
137+
break;
138+
}
139+
}
140+
141+
// If the designator list is empty at this point, then there must be excess
142+
// elements in the initializer list. They are not currently supported.
143+
if (DesignatorList.empty())
144+
return false;
145+
146+
// Check for missing braces. If the types don't match then there are
147+
// missing braces.
148+
while (true) {
149+
const QualType T = DesignatorList.back().getType();
150+
// If the types match, there are no missing braces.
151+
if (Init->getType() == T)
152+
break;
153+
154+
// If the current type is a struct, then get its first field.
155+
if (T->isRecordType()) {
156+
DesignatorList.push_back({T->getAsRecordDecl()->field_begin()->getType(),
157+
T->getAsRecordDecl()->field_begin(),
158+
T->getAsRecordDecl()});
159+
continue;
160+
}
161+
// If the current type is an array, then get its first element.
162+
if (T->isArrayType()) {
163+
DesignatorList.push_back(
164+
{Context.getAsArrayType(T)->getElementType(), 0,
165+
Context.getAsConstantArrayType(T)->getSize().getZExtValue()});
166+
continue;
167+
}
168+
169+
// The initializer doesn't match the expected type. The initializer list is
170+
// invalid.
171+
return false;
172+
}
173+
174+
return true;
175+
}
176+
177+
std::string Designators::toString() const {
178+
if (DesignatorList.empty())
179+
return "";
180+
std::string Designator = "";
181+
for (auto &I : DesignatorList) {
182+
switch (I.getTag()) {
183+
case DesignatorIter::STRUCT:
184+
Designator += "." + I.getStructIter()->getName().str();
185+
break;
186+
case DesignatorIter::ARRAY:
187+
Designator += "[" + std::to_string(I.getArrayIndex()) + "]";
188+
break;
189+
case DesignatorIter::ARRAY_RANGE:
190+
Designator += "[" + std::to_string(I.getArrayRangeStart()) + "..." +
191+
std::to_string(I.getArrayRangeEnd()) + "]";
192+
}
193+
}
194+
Designator += " = ";
195+
return Designator;
196+
}
197+
198+
} // namespace reorder_fields
199+
} // namespace clang
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
//===-- tools/extra/clang-reorder-fields/utils/Designator.h -----*- 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 declarations of the DesignatorIter and Designators
11+
/// utility class.
12+
///
13+
//===----------------------------------------------------------------------===//
14+
15+
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_REORDER_FIELDS_UTILS_DESIGNATOR_H
16+
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_REORDER_FIELDS_UTILS_DESIGNATOR_H
17+
18+
#include "clang/AST/Decl.h"
19+
#include "clang/AST/Expr.h"
20+
#include "clang/AST/Type.h"
21+
22+
namespace clang {
23+
namespace reorder_fields {
24+
25+
class DesignatorIter {
26+
public:
27+
enum Kind { STRUCT, ARRAY, ARRAY_RANGE };
28+
29+
DesignatorIter(const QualType Type, RecordDecl::field_iterator Field,
30+
const RecordDecl *RD)
31+
: Tag(STRUCT), Type(Type), StructIt({Field, RD}) {}
32+
33+
DesignatorIter(const QualType Type, uint64_t Idx, uint64_t Size)
34+
: Tag(ARRAY), Type(Type), ArrayIt({Idx, Size}) {}
35+
36+
DesignatorIter(const QualType Type, uint64_t Start, uint64_t End,
37+
uint64_t Size)
38+
: Tag(ARRAY_RANGE), Type(Type), ArrayRangeIt({Start, End, Size}) {}
39+
40+
/// Moves the iterator to the next element.
41+
DesignatorIter &operator++();
42+
43+
/// Checks if the iterator has iterated through all elements.
44+
bool isFinished();
45+
46+
Kind getTag() const { return Tag; }
47+
QualType getType() const { return Type; }
48+
49+
const RecordDecl::field_iterator getStructIter() const {
50+
assert(Tag == STRUCT && "Must be a field designator");
51+
return StructIt.Field;
52+
}
53+
54+
const RecordDecl *getStructDecl() const {
55+
assert(Tag == STRUCT && "Must be a field designator");
56+
return StructIt.Record;
57+
}
58+
59+
uint64_t getArrayIndex() const {
60+
assert(Tag == ARRAY && "Must be an array designator");
61+
return ArrayIt.Index;
62+
}
63+
64+
uint64_t getArrayRangeStart() const {
65+
assert(Tag == ARRAY_RANGE && "Must be an array range designator");
66+
return ArrayRangeIt.Start;
67+
}
68+
69+
uint64_t getArrayRangeEnd() const {
70+
assert(Tag == ARRAY_RANGE && "Must be an array range designator");
71+
return ArrayRangeIt.End;
72+
}
73+
74+
uint64_t getArraySize() const {
75+
assert((Tag == ARRAY || Tag == ARRAY_RANGE) &&
76+
"Must be an array or range designator");
77+
if (Tag == ARRAY)
78+
return ArrayIt.Size;
79+
return ArrayRangeIt.Size;
80+
}
81+
82+
private:
83+
/// Type of the designator.
84+
Kind Tag;
85+
86+
/// Type of the designated entry. For arrays this is the type of the element.
87+
QualType Type;
88+
89+
/// Field designator has the iterator to the field and the record the field
90+
/// is declared in.
91+
struct StructIter {
92+
RecordDecl::field_iterator Field;
93+
const RecordDecl *Record;
94+
};
95+
96+
/// Array designator has an index and size of the array.
97+
struct ArrayIter {
98+
uint64_t Index;
99+
uint64_t Size;
100+
};
101+
102+
/// Array range designator has a start and end index and size of the array.
103+
struct ArrayRangeIter {
104+
uint64_t Start;
105+
uint64_t End;
106+
uint64_t Size;
107+
};
108+
109+
union {
110+
StructIter StructIt;
111+
ArrayIter ArrayIt;
112+
ArrayRangeIter ArrayRangeIt;
113+
};
114+
};
115+
116+
class Designators {
117+
public:
118+
Designators() = default;
119+
Designators(const DesignatedInitExpr *DIE, const InitListExpr *ILE,
120+
const ASTContext &Context);
121+
122+
/// Moves the designators to the next initializer in the struct/array. If the
123+
/// type of next initializer doesn't match the expected type then there are
124+
/// omitted braces and we add new designators to reflect that.
125+
bool increment(const InitListExpr *ILE, const Expr *Init,
126+
const ASTContext &Context);
127+
128+
/// Gets a string representation from a list of designators. This string will
129+
/// be inserted before an initializer expression to make it designated.
130+
std::string toString() const;
131+
132+
size_t size() const { return DesignatorList.size(); }
133+
134+
const DesignatorIter &operator[](unsigned Idx) const {
135+
return DesignatorList[Idx];
136+
}
137+
138+
private:
139+
SmallVector<DesignatorIter, 1> DesignatorList;
140+
};
141+
142+
} // namespace reorder_fields
143+
} // namespace clang
144+
145+
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_REORDER_FIELDS_UTILS_DESIGNATOR_H

0 commit comments

Comments
 (0)