clang  19.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 
20 namespace clang {
21 namespace 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 
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 
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
const Environment & Env
Definition: HTMLLogger.cpp:148
This file declares ObjCPropertyAttributeOrderFixer, a TokenAnalyzer that adjusts the order of attribu...
SourceRange Range
Definition: SemaObjC.cpp:754
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.
std::string toString(const til::SExpr *E)
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:3437
A wrapper around a Token storing information about the whitespace characters preceding it.
Definition: FormatToken.h:290
SourceLocation getStartOfNonWhitespace() const
Returns actual token start location without leading escaped newlines and whitespace.
Definition: FormatToken.h:785
FormatToken * Next
The next token in the unwrapped line.
Definition: FormatToken.h:562
FormatToken * Previous
The previous token in the unwrapped line.
Definition: FormatToken.h:559