Skip to content

Commit 924f6ca

Browse files
authored
[clang-format] Remove duplicates in @Property using std::set (#74235)
Re-implement ObjCPropertyAttributeOrder using std::set for sorting and removing duplicates. (We can't use llvm::SmallSet because it's unordered.)
1 parent 4b1254e commit 924f6ca

File tree

3 files changed

+80
-62
lines changed

3 files changed

+80
-62
lines changed

clang/lib/Format/ObjCPropertyAttributeOrderFixer.cpp

Lines changed: 69 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@
1515

1616
#include "ObjCPropertyAttributeOrderFixer.h"
1717

18-
#include "llvm/ADT/Sequence.h"
19-
2018
#include <algorithm>
2119

2220
namespace clang {
@@ -25,26 +23,20 @@ namespace format {
2523
ObjCPropertyAttributeOrderFixer::ObjCPropertyAttributeOrderFixer(
2624
const Environment &Env, const FormatStyle &Style)
2725
: TokenAnalyzer(Env, Style) {
28-
2926
// Create an "order priority" map to use to sort properties.
30-
unsigned index = 0;
27+
unsigned Index = 0;
3128
for (const auto &Property : Style.ObjCPropertyAttributeOrder)
32-
SortOrderMap[Property] = index++;
29+
SortOrderMap[Property] = Index++;
3330
}
3431

3532
struct ObjCPropertyEntry {
36-
StringRef Attribute; // eg, "readwrite"
37-
StringRef Value; // eg, the "foo" of the attribute "getter=foo"
33+
StringRef Attribute; // eg, `readwrite`
34+
StringRef Value; // eg, the `foo` of the attribute `getter=foo`
3835
};
3936

40-
static bool isObjCPropertyAttribute(const FormatToken *Tok) {
41-
// Most attributes look like identifiers, but `class` is a keyword.
42-
return Tok->isOneOf(tok::identifier, tok::kw_class);
43-
}
44-
4537
void ObjCPropertyAttributeOrderFixer::sortPropertyAttributes(
4638
const SourceManager &SourceMgr, tooling::Replacements &Fixes,
47-
const FormatToken *BeginTok, const FormatToken *EndTok) const {
39+
const FormatToken *BeginTok, const FormatToken *EndTok) {
4840
assert(BeginTok);
4941
assert(EndTok);
5042
assert(EndTok->Previous);
@@ -53,22 +45,31 @@ void ObjCPropertyAttributeOrderFixer::sortPropertyAttributes(
5345
if (BeginTok == EndTok || BeginTok->Next == EndTok)
5446
return;
5547

48+
// Use a set to sort attributes and remove duplicates.
49+
std::set<unsigned> Ordinals;
50+
51+
// Create a "remapping index" on how to reorder the attributes.
52+
SmallVector<int> Indices;
53+
5654
// Collect the attributes.
57-
SmallVector<ObjCPropertyEntry, 8> PropertyAttributes;
55+
SmallVector<ObjCPropertyEntry> PropertyAttributes;
56+
bool HasDuplicates = false;
57+
int Index = 0;
5858
for (auto Tok = BeginTok; Tok != EndTok; Tok = Tok->Next) {
5959
assert(Tok);
6060
if (Tok->is(tok::comma)) {
6161
// Ignore the comma separators.
6262
continue;
6363
}
6464

65-
if (!isObjCPropertyAttribute(Tok)) {
65+
// Most attributes look like identifiers, but `class` is a keyword.
66+
if (!Tok->isOneOf(tok::identifier, tok::kw_class)) {
6667
// If we hit any other kind of token, just bail.
6768
return;
6869
}
6970

70-
// Memoize the attribute. (Note that 'class' is a legal attribute!)
71-
PropertyAttributes.push_back({Tok->TokenText, StringRef{}});
71+
const StringRef Attribute{Tok->TokenText};
72+
StringRef Value;
7273

7374
// Also handle `getter=getFoo` attributes.
7475
// (Note: no check needed against `EndTok`, since its type is not
@@ -82,49 +83,66 @@ void ObjCPropertyAttributeOrderFixer::sortPropertyAttributes(
8283
return;
8384
}
8485
Tok = Tok->Next;
85-
PropertyAttributes.back().Value = Tok->TokenText;
86+
Value = Tok->TokenText;
87+
}
88+
89+
auto It = SortOrderMap.find(Attribute);
90+
if (It == SortOrderMap.end())
91+
It = SortOrderMap.insert({Attribute, SortOrderMap.size()}).first;
92+
93+
// Sort the indices based on the priority stored in `SortOrderMap`.
94+
const auto Ordinal = It->second;
95+
if (!Ordinals.insert(Ordinal).second) {
96+
HasDuplicates = true;
97+
continue;
8698
}
99+
100+
if (Ordinal >= Indices.size())
101+
Indices.resize(Ordinal + 1);
102+
Indices[Ordinal] = Index++;
103+
104+
// Memoize the attribute.
105+
PropertyAttributes.push_back({Attribute, Value});
87106
}
88107

89-
// There's nothing to do unless there's more than one attribute.
90-
if (PropertyAttributes.size() < 2)
91-
return;
108+
if (!HasDuplicates) {
109+
// There's nothing to do unless there's more than one attribute.
110+
if (PropertyAttributes.size() < 2)
111+
return;
92112

93-
// Create a "remapping index" on how to reorder the attributes.
94-
SmallVector<unsigned, 8> Indices =
95-
llvm::to_vector<8>(llvm::seq<unsigned>(0, PropertyAttributes.size()));
96-
97-
// Sort the indices based on the priority stored in 'SortOrderMap'; use Max
98-
// for missing values.
99-
const auto SortOrderMax = Style.ObjCPropertyAttributeOrder.size();
100-
auto SortIndex = [&](const StringRef &Needle) -> unsigned {
101-
auto I = SortOrderMap.find(Needle);
102-
return (I == SortOrderMap.end()) ? SortOrderMax : I->getValue();
103-
};
104-
llvm::stable_sort(Indices, [&](unsigned LHSI, unsigned RHSI) {
105-
return SortIndex(PropertyAttributes[LHSI].Attribute) <
106-
SortIndex(PropertyAttributes[RHSI].Attribute);
107-
});
108-
109-
// If the property order is already correct, then no fix-up is needed.
110-
if (llvm::is_sorted(Indices))
111-
return;
113+
int PrevIndex = -1;
114+
bool IsSorted = true;
115+
for (const auto Ordinal : Ordinals) {
116+
const auto Index = Indices[Ordinal];
117+
if (Index < PrevIndex) {
118+
IsSorted = false;
119+
break;
120+
}
121+
assert(Index > PrevIndex);
122+
PrevIndex = Index;
123+
}
124+
125+
// If the property order is already correct, then no fix-up is needed.
126+
if (IsSorted)
127+
return;
128+
}
112129

113130
// Generate the replacement text.
114131
std::string NewText;
115-
const auto AppendAttribute = [&](const ObjCPropertyEntry &PropertyEntry) {
132+
bool IsFirst = true;
133+
for (const auto Ordinal : Ordinals) {
134+
if (IsFirst)
135+
IsFirst = false;
136+
else
137+
NewText += ", ";
138+
139+
const auto &PropertyEntry = PropertyAttributes[Indices[Ordinal]];
116140
NewText += PropertyEntry.Attribute;
117141

118-
if (!PropertyEntry.Value.empty()) {
119-
NewText += "=";
120-
NewText += PropertyEntry.Value;
142+
if (const auto Value = PropertyEntry.Value; !Value.empty()) {
143+
NewText += '=';
144+
NewText += Value;
121145
}
122-
};
123-
124-
AppendAttribute(PropertyAttributes[Indices[0]]);
125-
for (unsigned Index : llvm::drop_begin(Indices)) {
126-
NewText += ", ";
127-
AppendAttribute(PropertyAttributes[Index]);
128146
}
129147

130148
auto Range = CharSourceRange::getCharRange(
@@ -139,7 +157,7 @@ void ObjCPropertyAttributeOrderFixer::sortPropertyAttributes(
139157

140158
void ObjCPropertyAttributeOrderFixer::analyzeObjCPropertyDecl(
141159
const SourceManager &SourceMgr, const AdditionalKeywords &Keywords,
142-
tooling::Replacements &Fixes, const FormatToken *Tok) const {
160+
tooling::Replacements &Fixes, const FormatToken *Tok) {
143161
assert(Tok);
144162

145163
// Expect `property` to be the very next token or else just bail early.

clang/lib/Format/ObjCPropertyAttributeOrderFixer.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@ class ObjCPropertyAttributeOrderFixer : public TokenAnalyzer {
2828
void analyzeObjCPropertyDecl(const SourceManager &SourceMgr,
2929
const AdditionalKeywords &Keywords,
3030
tooling::Replacements &Fixes,
31-
const FormatToken *Tok) const;
31+
const FormatToken *Tok);
3232

3333
void sortPropertyAttributes(const SourceManager &SourceMgr,
3434
tooling::Replacements &Fixes,
3535
const FormatToken *BeginTok,
36-
const FormatToken *EndTok) const;
36+
const FormatToken *EndTok);
3737

3838
std::pair<tooling::Replacements, unsigned>
3939
analyze(TokenAnnotator &Annotator,

clang/unittests/Format/ObjCPropertyAttributeOrderFixerTest.cpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -171,18 +171,18 @@ TEST_F(ObjCPropertyAttributeOrderFixerTest, HandlesDuplicatedAttributes) {
171171
Style.ObjCPropertyAttributeOrder = {"a", "b", "c"};
172172

173173
// Just a dup and nothing else.
174-
verifyFormat("@property(a, a) int p;", Style);
174+
verifyFormat("@property(a) int p;", "@property(a, a) int p;", Style);
175175

176176
// A dup and something else.
177-
verifyFormat("@property(a, a, b) int p;", "@property(a, b, a) int p;", Style);
177+
verifyFormat("@property(a, b) int p;", "@property(a, b, a) int p;", Style);
178178

179-
// Duplicates using `=`: stable-sort irrespective of their value.
180-
verifyFormat("@property(a=A, a=A, b=X, b=Y) int p;",
179+
// Duplicates using `=`.
180+
verifyFormat("@property(a=A, b=X) int p;",
181181
"@property(a=A, b=X, a=A, b=Y) int p;", Style);
182-
verifyFormat("@property(a=A, a=A, b=Y, b=X) int p;",
182+
verifyFormat("@property(a=A, b=Y) int p;",
183183
"@property(a=A, b=Y, a=A, b=X) int p;", Style);
184-
verifyFormat("@property(a, a=A, b=B, b) int p;",
185-
"@property(a, b=B, a=A, b) int p;", Style);
184+
verifyFormat("@property(a, b=B) int p;", "@property(a, b=B, a=A, b) int p;",
185+
Style);
186186
}
187187

188188
TEST_F(ObjCPropertyAttributeOrderFixerTest, SortsInPPDirective) {
@@ -200,7 +200,7 @@ TEST_F(ObjCPropertyAttributeOrderFixerTest, SortsInPPDirective) {
200200
}
201201

202202
TEST_F(ObjCPropertyAttributeOrderFixerTest, HandlesAllAttributes) {
203-
// 'class' is the only attribute that is a keyword, so make sure it works too.
203+
// `class` is the only attribute that is a keyword, so make sure it works too.
204204
FormatStyle Style = getLLVMStyle();
205205
Style.Language = FormatStyle::LK_ObjC;
206206
Style.ObjCPropertyAttributeOrder = {"FIRST",
@@ -282,7 +282,7 @@ TEST_F(ObjCPropertyAttributeOrderFixerTest, HandlesAllAttributes) {
282282
verifyFormat("@property(FIRST, null_resettable, LAST) int p;", Style);
283283
verifyFormat("@property(FIRST, null_unspecified, LAST) int p;", Style);
284284

285-
// Reorder: put 'FIRST' and/or 'LAST' in the wrong spot.
285+
// Reorder: put `FIRST` and/or `LAST` in the wrong spot.
286286
verifyFormat("@property(class, LAST) int p;", "@property(LAST, class) int p;",
287287
Style);
288288
verifyFormat("@property(direct, LAST) int p;",

0 commit comments

Comments
 (0)