clang 20.0.0git
ObjCPropertyAttributeOrderFixer.cpp
Go to the documentation of this file.
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
17
18#include <algorithm>
19
20namespace clang {
21namespace format {
22
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
33 StringRef Attribute; // eg, `readwrite`
34 StringRef Value; // eg, the `foo` of the attribute `getter=foo`
35};
36
37void 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 // Sort the indices based on the priority stored in `SortOrderMap`.
90 const auto Ordinal =
91 SortOrderMap.try_emplace(Attribute, SortOrderMap.size()).first->second;
92 if (!Ordinals.insert(Ordinal).second) {
93 HasDuplicates = true;
94 continue;
95 }
96
97 if (Ordinal >= Indices.size())
98 Indices.resize(Ordinal + 1);
99 Indices[Ordinal] = Index++;
100
101 // Memoize the attribute.
102 PropertyAttributes.push_back({Attribute, Value});
103 }
104
105 if (!HasDuplicates) {
106 // There's nothing to do unless there's more than one attribute.
107 if (PropertyAttributes.size() < 2)
108 return;
109
110 int PrevIndex = -1;
111 bool IsSorted = true;
112 for (const auto Ordinal : Ordinals) {
113 const auto Index = Indices[Ordinal];
114 if (Index < PrevIndex) {
115 IsSorted = false;
116 break;
117 }
118 assert(Index > PrevIndex);
119 PrevIndex = Index;
120 }
121
122 // If the property order is already correct, then no fix-up is needed.
123 if (IsSorted)
124 return;
125 }
126
127 // Generate the replacement text.
128 std::string NewText;
129 bool IsFirst = true;
130 for (const auto Ordinal : Ordinals) {
131 if (IsFirst)
132 IsFirst = false;
133 else
134 NewText += ", ";
135
136 const auto &PropertyEntry = PropertyAttributes[Indices[Ordinal]];
137 NewText += PropertyEntry.Attribute;
138
139 if (const auto Value = PropertyEntry.Value; !Value.empty()) {
140 NewText += '=';
141 NewText += Value;
142 }
143 }
144
146 BeginTok->getStartOfNonWhitespace(), EndTok->Previous->Tok.getEndLoc());
147 auto Replacement = tooling::Replacement(SourceMgr, Range, NewText);
148 auto Err = Fixes.add(Replacement);
149 if (Err) {
150 llvm::errs() << "Error while reodering ObjC property attributes : "
151 << llvm::toString(std::move(Err)) << "\n";
152 }
153}
154
155void ObjCPropertyAttributeOrderFixer::analyzeObjCPropertyDecl(
156 const SourceManager &SourceMgr, const AdditionalKeywords &Keywords,
157 tooling::Replacements &Fixes, const FormatToken *Tok) {
158 assert(Tok);
159
160 // Expect `property` to be the very next token or else just bail early.
161 const FormatToken *const PropertyTok = Tok->Next;
162 if (!PropertyTok || PropertyTok->isNot(Keywords.kw_property))
163 return;
164
165 // Expect the opening paren to be the next token or else just bail early.
166 const FormatToken *const LParenTok = PropertyTok->getNextNonComment();
167 if (!LParenTok || LParenTok->isNot(tok::l_paren))
168 return;
169
170 // Get the matching right-paren, the bounds for property attributes.
171 const FormatToken *const RParenTok = LParenTok->MatchingParen;
172 if (!RParenTok)
173 return;
174
175 sortPropertyAttributes(SourceMgr, Fixes, LParenTok->Next, RParenTok);
176}
177
178std::pair<tooling::Replacements, unsigned>
179ObjCPropertyAttributeOrderFixer::analyze(
180 TokenAnnotator & /*Annotator*/,
181 SmallVectorImpl<AnnotatedLine *> &AnnotatedLines,
182 FormatTokenLexer &Tokens) {
183 tooling::Replacements Fixes;
184 const AdditionalKeywords &Keywords = Tokens.getKeywords();
185 const SourceManager &SourceMgr = Env.getSourceManager();
187
188 for (AnnotatedLine *Line : AnnotatedLines) {
189 assert(Line);
190 if (!Line->Affected || Line->Type != LT_ObjCProperty)
191 continue;
192 FormatToken *First = Line->First;
193 assert(First);
194 if (First->Finalized)
195 continue;
196
197 const auto *Last = Line->Last;
198
199 for (const auto *Tok = First; Tok != Last; Tok = Tok->Next) {
200 assert(Tok);
201
202 // Skip until the `@` of a `@property` declaration.
203 if (Tok->isNot(TT_ObjCProperty))
204 continue;
205
206 analyzeObjCPropertyDecl(SourceMgr, Keywords, Fixes, Tok);
207
208 // There are never two `@property` in a line (they are split
209 // by other passes), so this pass can break after just one.
210 break;
211 }
212 }
213 return {Fixes, 0};
214}
215
216} // namespace format
217} // namespace clang
const Environment & Env
Definition: HTMLLogger.cpp:147
This file declares ObjCPropertyAttributeOrderFixer, a TokenAnalyzer that adjusts the order of attribu...
SourceRange Range
Definition: SemaObjC.cpp:758
static CharSourceRange getCharRange(SourceRange R)
This class handles loading and caching of source files into memory.
SourceLocation getEndLoc() const
Definition: Token.h:159
bool computeAffectedLines(SmallVectorImpl< AnnotatedLine * > &Lines)
SourceManager & getSourceManager() const
Definition: TokenAnalyzer.h:38
ObjCPropertyAttributeOrderFixer(const Environment &Env, const FormatStyle &Style)
AffectedRangeManager AffectedRangeMgr
Definition: TokenAnalyzer.h:99
const Environment & Env
Definition: TokenAnalyzer.h:97
Maintains a set of replacements that are conflict-free.
Definition: Replacement.h:212
llvm::Error add(const Replacement &R)
Adds a new replacement R to the current set of replacements.
The JSON file list parser is used to communicate input to InstallAPI.
@ Property
The type of a property.
The FormatStyle is used to configure the formatting to follow specific guidelines.
Definition: Format.h:55
std::vector< std::string > ObjCPropertyAttributeOrder
The order in which ObjC property attributes should appear.
Definition: Format.h:3541
A wrapper around a Token storing information about the whitespace characters preceding it.
Definition: FormatToken.h:294
SourceLocation getStartOfNonWhitespace() const
Returns actual token start location without leading escaped newlines and whitespace.
Definition: FormatToken.h:819
FormatToken * Next
The next token in the unwrapped line.
Definition: FormatToken.h:566
FormatToken * Previous
The previous token in the unwrapped line.
Definition: FormatToken.h:563