Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/clang/lib/Format/ObjCPropertyAttributeOrderFixer.cpp
35233 views
1
//===--- ObjCPropertyAttributeOrderFixer.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 implements ObjCPropertyAttributeOrderFixer, a TokenAnalyzer that
11
/// adjusts the order of attributes in an ObjC `@property(...)` declaration,
12
/// depending on the style.
13
///
14
//===----------------------------------------------------------------------===//
15
16
#include "ObjCPropertyAttributeOrderFixer.h"
17
18
#include <algorithm>
19
20
namespace clang {
21
namespace format {
22
23
ObjCPropertyAttributeOrderFixer::ObjCPropertyAttributeOrderFixer(
24
const Environment &Env, const FormatStyle &Style)
25
: TokenAnalyzer(Env, Style) {
26
// Create an "order priority" map to use to sort properties.
27
unsigned Index = 0;
28
for (const auto &Property : Style.ObjCPropertyAttributeOrder)
29
SortOrderMap[Property] = Index++;
30
}
31
32
struct ObjCPropertyEntry {
33
StringRef Attribute; // eg, `readwrite`
34
StringRef Value; // eg, the `foo` of the attribute `getter=foo`
35
};
36
37
void ObjCPropertyAttributeOrderFixer::sortPropertyAttributes(
38
const SourceManager &SourceMgr, tooling::Replacements &Fixes,
39
const FormatToken *BeginTok, const FormatToken *EndTok) {
40
assert(BeginTok);
41
assert(EndTok);
42
assert(EndTok->Previous);
43
44
// If there are zero or one tokens, nothing to do.
45
if (BeginTok == EndTok || BeginTok->Next == EndTok)
46
return;
47
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
54
// Collect the attributes.
55
SmallVector<ObjCPropertyEntry> PropertyAttributes;
56
bool HasDuplicates = false;
57
int Index = 0;
58
for (auto Tok = BeginTok; Tok != EndTok; Tok = Tok->Next) {
59
assert(Tok);
60
if (Tok->is(tok::comma)) {
61
// Ignore the comma separators.
62
continue;
63
}
64
65
// Most attributes look like identifiers, but `class` is a keyword.
66
if (!Tok->isOneOf(tok::identifier, tok::kw_class)) {
67
// If we hit any other kind of token, just bail.
68
return;
69
}
70
71
const StringRef Attribute{Tok->TokenText};
72
StringRef Value;
73
74
// Also handle `getter=getFoo` attributes.
75
// (Note: no check needed against `EndTok`, since its type is not
76
// BinaryOperator or Identifier)
77
assert(Tok->Next);
78
if (Tok->Next->is(tok::equal)) {
79
Tok = Tok->Next;
80
assert(Tok->Next);
81
if (Tok->Next->isNot(tok::identifier)) {
82
// If we hit any other kind of token, just bail. It's unusual/illegal.
83
return;
84
}
85
Tok = Tok->Next;
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;
98
}
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});
106
}
107
108
if (!HasDuplicates) {
109
// There's nothing to do unless there's more than one attribute.
110
if (PropertyAttributes.size() < 2)
111
return;
112
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
}
129
130
// Generate the replacement text.
131
std::string NewText;
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]];
140
NewText += PropertyEntry.Attribute;
141
142
if (const auto Value = PropertyEntry.Value; !Value.empty()) {
143
NewText += '=';
144
NewText += Value;
145
}
146
}
147
148
auto Range = CharSourceRange::getCharRange(
149
BeginTok->getStartOfNonWhitespace(), EndTok->Previous->Tok.getEndLoc());
150
auto Replacement = tooling::Replacement(SourceMgr, Range, NewText);
151
auto Err = Fixes.add(Replacement);
152
if (Err) {
153
llvm::errs() << "Error while reodering ObjC property attributes : "
154
<< llvm::toString(std::move(Err)) << "\n";
155
}
156
}
157
158
void ObjCPropertyAttributeOrderFixer::analyzeObjCPropertyDecl(
159
const SourceManager &SourceMgr, const AdditionalKeywords &Keywords,
160
tooling::Replacements &Fixes, const FormatToken *Tok) {
161
assert(Tok);
162
163
// Expect `property` to be the very next token or else just bail early.
164
const FormatToken *const PropertyTok = Tok->Next;
165
if (!PropertyTok || PropertyTok->isNot(Keywords.kw_property))
166
return;
167
168
// Expect the opening paren to be the next token or else just bail early.
169
const FormatToken *const LParenTok = PropertyTok->getNextNonComment();
170
if (!LParenTok || LParenTok->isNot(tok::l_paren))
171
return;
172
173
// Get the matching right-paren, the bounds for property attributes.
174
const FormatToken *const RParenTok = LParenTok->MatchingParen;
175
if (!RParenTok)
176
return;
177
178
sortPropertyAttributes(SourceMgr, Fixes, LParenTok->Next, RParenTok);
179
}
180
181
std::pair<tooling::Replacements, unsigned>
182
ObjCPropertyAttributeOrderFixer::analyze(
183
TokenAnnotator & /*Annotator*/,
184
SmallVectorImpl<AnnotatedLine *> &AnnotatedLines,
185
FormatTokenLexer &Tokens) {
186
tooling::Replacements Fixes;
187
const AdditionalKeywords &Keywords = Tokens.getKeywords();
188
const SourceManager &SourceMgr = Env.getSourceManager();
189
AffectedRangeMgr.computeAffectedLines(AnnotatedLines);
190
191
for (AnnotatedLine *Line : AnnotatedLines) {
192
assert(Line);
193
if (!Line->Affected || Line->Type != LT_ObjCProperty)
194
continue;
195
FormatToken *First = Line->First;
196
assert(First);
197
if (First->Finalized)
198
continue;
199
200
const auto *Last = Line->Last;
201
202
for (const auto *Tok = First; Tok != Last; Tok = Tok->Next) {
203
assert(Tok);
204
205
// Skip until the `@` of a `@property` declaration.
206
if (Tok->isNot(TT_ObjCProperty))
207
continue;
208
209
analyzeObjCPropertyDecl(SourceMgr, Keywords, Fixes, Tok);
210
211
// There are never two `@property` in a line (they are split
212
// by other passes), so this pass can break after just one.
213
break;
214
}
215
}
216
return {Fixes, 0};
217
}
218
219
} // namespace format
220
} // namespace clang
221
222