File: | clang/lib/Format/QualifierAlignmentFixer.cpp |
Warning: | line 265, column 9 Value stored to 'Next' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | //===--- LeftRightQualifierAlignmentFixer.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 LeftRightQualifierAlignmentFixer, a TokenAnalyzer that |
11 | /// enforces either left or right const depending on the style. |
12 | /// |
13 | //===----------------------------------------------------------------------===// |
14 | |
15 | #include "QualifierAlignmentFixer.h" |
16 | #include "FormatToken.h" |
17 | #include "llvm/Support/Debug.h" |
18 | #include "llvm/Support/Regex.h" |
19 | |
20 | #include <algorithm> |
21 | |
22 | #define DEBUG_TYPE"format-qualifier-alignment-fixer" "format-qualifier-alignment-fixer" |
23 | |
24 | namespace clang { |
25 | namespace format { |
26 | |
27 | QualifierAlignmentFixer::QualifierAlignmentFixer( |
28 | const Environment &Env, const FormatStyle &Style, StringRef &Code, |
29 | ArrayRef<tooling::Range> Ranges, unsigned FirstStartColumn, |
30 | unsigned NextStartColumn, unsigned LastStartColumn, StringRef FileName) |
31 | : TokenAnalyzer(Env, Style), Code(Code), Ranges(Ranges), |
32 | FirstStartColumn(FirstStartColumn), NextStartColumn(NextStartColumn), |
33 | LastStartColumn(LastStartColumn), FileName(FileName) { |
34 | std::vector<std::string> LeftOrder; |
35 | std::vector<std::string> RightOrder; |
36 | std::vector<tok::TokenKind> ConfiguredQualifierTokens; |
37 | PrepareLeftRightOrdering(Style.QualifierOrder, LeftOrder, RightOrder, |
38 | ConfiguredQualifierTokens); |
39 | |
40 | // Handle the left and right Alignment Seperately |
41 | for (const auto &Qualifier : LeftOrder) { |
42 | Passes.emplace_back( |
43 | [&, Qualifier, ConfiguredQualifierTokens](const Environment &Env) { |
44 | return LeftRightQualifierAlignmentFixer(Env, Style, Qualifier, |
45 | ConfiguredQualifierTokens, |
46 | /*RightAlign=*/false) |
47 | .process(); |
48 | }); |
49 | } |
50 | for (const auto &Qualifier : RightOrder) { |
51 | Passes.emplace_back( |
52 | [&, Qualifier, ConfiguredQualifierTokens](const Environment &Env) { |
53 | return LeftRightQualifierAlignmentFixer(Env, Style, Qualifier, |
54 | ConfiguredQualifierTokens, |
55 | /*RightAlign=*/true) |
56 | .process(); |
57 | }); |
58 | } |
59 | } |
60 | |
61 | std::pair<tooling::Replacements, unsigned> QualifierAlignmentFixer::analyze( |
62 | TokenAnnotator &Annotator, SmallVectorImpl<AnnotatedLine *> &AnnotatedLines, |
63 | FormatTokenLexer &Tokens) { |
64 | auto Env = Environment::make(Code, FileName, Ranges, FirstStartColumn, |
65 | NextStartColumn, LastStartColumn); |
66 | if (!Env) |
67 | return {}; |
68 | llvm::Optional<std::string> CurrentCode = None; |
69 | tooling::Replacements Fixes; |
70 | for (size_t I = 0, E = Passes.size(); I < E; ++I) { |
71 | std::pair<tooling::Replacements, unsigned> PassFixes = Passes[I](*Env); |
72 | auto NewCode = applyAllReplacements( |
73 | CurrentCode ? StringRef(*CurrentCode) : Code, PassFixes.first); |
74 | if (NewCode) { |
75 | Fixes = Fixes.merge(PassFixes.first); |
76 | if (I + 1 < E) { |
77 | CurrentCode = std::move(*NewCode); |
78 | Env = Environment::make( |
79 | *CurrentCode, FileName, |
80 | tooling::calculateRangesAfterReplacements(Fixes, Ranges), |
81 | FirstStartColumn, NextStartColumn, LastStartColumn); |
82 | if (!Env) |
83 | return {}; |
84 | } |
85 | } |
86 | } |
87 | |
88 | // Don't make replacements that replace nothing. |
89 | tooling::Replacements NonNoOpFixes; |
90 | |
91 | for (const tooling::Replacement &Fix : Fixes) { |
92 | StringRef OriginalCode = Code.substr(Fix.getOffset(), Fix.getLength()); |
93 | |
94 | if (!OriginalCode.equals(Fix.getReplacementText())) { |
95 | auto Err = NonNoOpFixes.add(Fix); |
96 | if (Err) |
97 | llvm::errs() << "Error adding replacements : " |
98 | << llvm::toString(std::move(Err)) << "\n"; |
99 | } |
100 | } |
101 | return {NonNoOpFixes, 0}; |
102 | } |
103 | |
104 | static void replaceToken(const SourceManager &SourceMgr, |
105 | tooling::Replacements &Fixes, |
106 | const CharSourceRange &Range, std::string NewText) { |
107 | auto Replacement = tooling::Replacement(SourceMgr, Range, NewText); |
108 | auto Err = Fixes.add(Replacement); |
109 | |
110 | if (Err) |
111 | llvm::errs() << "Error while rearranging Qualifier : " |
112 | << llvm::toString(std::move(Err)) << "\n"; |
113 | } |
114 | |
115 | static void removeToken(const SourceManager &SourceMgr, |
116 | tooling::Replacements &Fixes, |
117 | const FormatToken *First) { |
118 | auto Range = CharSourceRange::getCharRange(First->getStartOfNonWhitespace(), |
119 | First->Tok.getEndLoc()); |
120 | replaceToken(SourceMgr, Fixes, Range, ""); |
121 | } |
122 | |
123 | static void insertQualifierAfter(const SourceManager &SourceMgr, |
124 | tooling::Replacements &Fixes, |
125 | const FormatToken *First, |
126 | const std::string &Qualifier) { |
127 | FormatToken *Next = First->Next; |
128 | if (!Next) |
129 | return; |
130 | auto Range = CharSourceRange::getCharRange(Next->getStartOfNonWhitespace(), |
131 | Next->Tok.getEndLoc()); |
132 | |
133 | std::string NewText = " " + Qualifier + " "; |
134 | NewText += Next->TokenText; |
135 | replaceToken(SourceMgr, Fixes, Range, NewText); |
136 | } |
137 | |
138 | static void insertQualifierBefore(const SourceManager &SourceMgr, |
139 | tooling::Replacements &Fixes, |
140 | const FormatToken *First, |
141 | const std::string &Qualifier) { |
142 | auto Range = CharSourceRange::getCharRange(First->getStartOfNonWhitespace(), |
143 | First->Tok.getEndLoc()); |
144 | |
145 | std::string NewText = " " + Qualifier + " "; |
146 | NewText += First->TokenText; |
147 | |
148 | replaceToken(SourceMgr, Fixes, Range, NewText); |
149 | } |
150 | |
151 | static bool endsWithSpace(const std::string &s) { |
152 | if (s.empty()) { |
153 | return false; |
154 | } |
155 | return isspace(s.back()); |
156 | } |
157 | |
158 | static bool startsWithSpace(const std::string &s) { |
159 | if (s.empty()) { |
160 | return false; |
161 | } |
162 | return isspace(s.front()); |
163 | } |
164 | |
165 | static void rotateTokens(const SourceManager &SourceMgr, |
166 | tooling::Replacements &Fixes, const FormatToken *First, |
167 | const FormatToken *Last, bool Left) { |
168 | auto *End = Last; |
169 | auto *Begin = First; |
170 | if (!Left) { |
171 | End = Last->Next; |
172 | Begin = First->Next; |
173 | } |
174 | |
175 | std::string NewText; |
176 | // If we are rotating to the left we move the Last token to the front. |
177 | if (Left) { |
178 | NewText += Last->TokenText; |
179 | NewText += " "; |
180 | } |
181 | |
182 | // Then move through the other tokens. |
183 | auto *Tok = Begin; |
184 | while (Tok != End) { |
185 | if (!NewText.empty() && !endsWithSpace(NewText)) { |
186 | NewText += " "; |
187 | } |
188 | |
189 | NewText += Tok->TokenText; |
190 | Tok = Tok->Next; |
191 | } |
192 | |
193 | // If we are rotating to the right we move the first token to the back. |
194 | if (!Left) { |
195 | if (!NewText.empty() && !startsWithSpace(NewText)) { |
196 | NewText += " "; |
197 | } |
198 | NewText += First->TokenText; |
199 | } |
200 | |
201 | auto Range = CharSourceRange::getCharRange(First->getStartOfNonWhitespace(), |
202 | Last->Tok.getEndLoc()); |
203 | |
204 | replaceToken(SourceMgr, Fixes, Range, NewText); |
205 | } |
206 | |
207 | const FormatToken *LeftRightQualifierAlignmentFixer::analyzeRight( |
208 | const SourceManager &SourceMgr, const AdditionalKeywords &Keywords, |
209 | tooling::Replacements &Fixes, const FormatToken *Tok, |
210 | const std::string &Qualifier, tok::TokenKind QualifierType) { |
211 | // We only need to think about streams that begin with a qualifier. |
212 | if (!Tok->is(QualifierType)) |
213 | return Tok; |
214 | // Don't concern yourself if nothing follows the qualifier. |
215 | if (!Tok->Next) |
216 | return Tok; |
217 | if (LeftRightQualifierAlignmentFixer::isPossibleMacro(Tok->Next)) |
218 | return Tok; |
219 | |
220 | FormatToken *Qual = Tok->Next; |
221 | FormatToken *LastQual = Qual; |
222 | while (Qual && isQualifierOrType(Qual, ConfiguredQualifierTokens)) { |
223 | LastQual = Qual; |
224 | Qual = Qual->Next; |
225 | } |
226 | if (LastQual && Qual != LastQual) { |
227 | rotateTokens(SourceMgr, Fixes, Tok, LastQual, /*Left=*/false); |
228 | Tok = LastQual; |
229 | } else if (Tok->startsSequence(QualifierType, tok::identifier, |
230 | TT_TemplateOpener)) { |
231 | // Read from the TemplateOpener to |
232 | // TemplateCloser as in const ArrayRef<int> a; const ArrayRef<int> &a; |
233 | FormatToken *EndTemplate = Tok->Next->Next->MatchingParen; |
234 | if (EndTemplate) { |
235 | // Move to the end of any template class members e.g. |
236 | // `Foo<int>::iterator`. |
237 | if (EndTemplate->startsSequence(TT_TemplateCloser, tok::coloncolon, |
238 | tok::identifier)) |
239 | EndTemplate = EndTemplate->Next->Next; |
240 | } |
241 | if (EndTemplate && EndTemplate->Next && |
242 | !EndTemplate->Next->isOneOf(tok::equal, tok::l_paren)) { |
243 | insertQualifierAfter(SourceMgr, Fixes, EndTemplate, Qualifier); |
244 | // Remove the qualifier. |
245 | removeToken(SourceMgr, Fixes, Tok); |
246 | return Tok; |
247 | } |
248 | } else if (Tok->startsSequence(QualifierType, tok::identifier)) { |
249 | FormatToken *Next = Tok->Next; |
250 | // The case `const Foo` -> `Foo const` |
251 | // The case `const Foo *` -> `Foo const *` |
252 | // The case `const Foo &` -> `Foo const &` |
253 | // The case `const Foo &&` -> `Foo const &&` |
254 | // The case `const std::Foo &&` -> `std::Foo const &&` |
255 | // The case `const std::Foo<T> &&` -> `std::Foo<T> const &&` |
256 | while (Next && Next->isOneOf(tok::identifier, tok::coloncolon)) { |
257 | Next = Next->Next; |
258 | } |
259 | if (Next && Next->is(TT_TemplateOpener)) { |
260 | Next = Next->MatchingParen; |
261 | // Move to the end of any template class members e.g. |
262 | // `Foo<int>::iterator`. |
263 | if (Next && Next->startsSequence(TT_TemplateCloser, tok::coloncolon, |
264 | tok::identifier)) { |
265 | Next = Next->Next->Next; |
Value stored to 'Next' is never read | |
266 | return Tok; |
267 | } |
268 | assert(Next && "Missing template opener")(static_cast <bool> (Next && "Missing template opener" ) ? void (0) : __assert_fail ("Next && \"Missing template opener\"" , "clang/lib/Format/QualifierAlignmentFixer.cpp", 268, __extension__ __PRETTY_FUNCTION__)); |
269 | Next = Next->Next; |
270 | } |
271 | if (Next && Next->isOneOf(tok::star, tok::amp, tok::ampamp) && |
272 | !Tok->Next->isOneOf(Keywords.kw_override, Keywords.kw_final)) { |
273 | if (Next->Previous && !Next->Previous->is(QualifierType)) { |
274 | insertQualifierAfter(SourceMgr, Fixes, Next->Previous, Qualifier); |
275 | removeToken(SourceMgr, Fixes, Tok); |
276 | } |
277 | return Next; |
278 | } |
279 | } |
280 | |
281 | return Tok; |
282 | } |
283 | |
284 | const FormatToken *LeftRightQualifierAlignmentFixer::analyzeLeft( |
285 | const SourceManager &SourceMgr, const AdditionalKeywords &Keywords, |
286 | tooling::Replacements &Fixes, const FormatToken *Tok, |
287 | const std::string &Qualifier, tok::TokenKind QualifierType) { |
288 | // if Tok is an identifier and possibly a macro then don't convert. |
289 | if (LeftRightQualifierAlignmentFixer::isPossibleMacro(Tok)) |
290 | return Tok; |
291 | |
292 | const FormatToken *Qual = Tok; |
293 | const FormatToken *LastQual = Qual; |
294 | while (Qual && isQualifierOrType(Qual, ConfiguredQualifierTokens)) { |
295 | LastQual = Qual; |
296 | Qual = Qual->Next; |
297 | if (Qual && Qual->is(QualifierType)) |
298 | break; |
299 | } |
300 | |
301 | if (!Qual) { |
302 | return Tok; |
303 | } |
304 | |
305 | if (LastQual && Qual != LastQual && Qual->is(QualifierType)) { |
306 | rotateTokens(SourceMgr, Fixes, Tok, Qual, /*Left=*/true); |
307 | Tok = Qual->Next; |
308 | } else if (Tok->startsSequence(tok::identifier, QualifierType)) { |
309 | if (Tok->Next->Next && Tok->Next->Next->isOneOf(tok::identifier, tok::star, |
310 | tok::amp, tok::ampamp)) { |
311 | // Don't swap `::iterator const` to `::const iterator`. |
312 | if (!Tok->Previous || |
313 | (Tok->Previous && !Tok->Previous->is(tok::coloncolon))) { |
314 | rotateTokens(SourceMgr, Fixes, Tok, Tok->Next, /*Left=*/true); |
315 | Tok = Tok->Next; |
316 | } |
317 | } |
318 | } |
319 | if (Tok->is(TT_TemplateOpener) && Tok->Next && |
320 | (Tok->Next->is(tok::identifier) || Tok->Next->isSimpleTypeSpecifier()) && |
321 | Tok->Next->Next && Tok->Next->Next->is(QualifierType)) { |
322 | rotateTokens(SourceMgr, Fixes, Tok->Next, Tok->Next->Next, /*Left=*/true); |
323 | } |
324 | if (Tok->startsSequence(tok::identifier) && Tok->Next) { |
325 | if (Tok->Previous && |
326 | Tok->Previous->isOneOf(tok::star, tok::ampamp, tok::amp)) { |
327 | return Tok; |
328 | } |
329 | const FormatToken *Next = Tok->Next; |
330 | // The case `std::Foo<T> const` -> `const std::Foo<T> &&` |
331 | while (Next && Next->isOneOf(tok::identifier, tok::coloncolon)) |
332 | Next = Next->Next; |
333 | if (Next && Next->Previous && |
334 | Next->Previous->startsSequence(tok::identifier, TT_TemplateOpener)) { |
335 | // Read from to the end of the TemplateOpener to |
336 | // TemplateCloser const ArrayRef<int> a; const ArrayRef<int> &a; |
337 | if (Next->is(tok::comment) && Next->getNextNonComment()) |
338 | Next = Next->getNextNonComment(); |
339 | assert(Next->MatchingParen && "Missing template closer")(static_cast <bool> (Next->MatchingParen && "Missing template closer" ) ? void (0) : __assert_fail ("Next->MatchingParen && \"Missing template closer\"" , "clang/lib/Format/QualifierAlignmentFixer.cpp", 339, __extension__ __PRETTY_FUNCTION__)); |
340 | Next = Next->MatchingParen->Next; |
341 | |
342 | // Move to the end of any template class members e.g. |
343 | // `Foo<int>::iterator`. |
344 | if (Next && Next->startsSequence(tok::coloncolon, tok::identifier)) |
345 | Next = Next->Next->Next; |
346 | if (Next && Next->is(QualifierType)) { |
347 | // Remove the const. |
348 | insertQualifierBefore(SourceMgr, Fixes, Tok, Qualifier); |
349 | removeToken(SourceMgr, Fixes, Next); |
350 | return Next; |
351 | } |
352 | } |
353 | if (Next && Next->Next && |
354 | Next->Next->isOneOf(tok::amp, tok::ampamp, tok::star)) { |
355 | if (Next->is(QualifierType)) { |
356 | // Remove the qualifier. |
357 | insertQualifierBefore(SourceMgr, Fixes, Tok, Qualifier); |
358 | removeToken(SourceMgr, Fixes, Next); |
359 | return Next; |
360 | } |
361 | } |
362 | } |
363 | return Tok; |
364 | } |
365 | |
366 | tok::TokenKind LeftRightQualifierAlignmentFixer::getTokenFromQualifier( |
367 | const std::string &Qualifier) { |
368 | // Don't let 'type' be an identifier, but steal typeof token. |
369 | return llvm::StringSwitch<tok::TokenKind>(Qualifier) |
370 | .Case("type", tok::kw_typeof) |
371 | .Case("const", tok::kw_const) |
372 | .Case("volatile", tok::kw_volatile) |
373 | .Case("static", tok::kw_static) |
374 | .Case("inline", tok::kw_inline) |
375 | .Case("constexpr", tok::kw_constexpr) |
376 | .Case("restrict", tok::kw_restrict) |
377 | .Default(tok::identifier); |
378 | } |
379 | |
380 | LeftRightQualifierAlignmentFixer::LeftRightQualifierAlignmentFixer( |
381 | const Environment &Env, const FormatStyle &Style, |
382 | const std::string &Qualifier, |
383 | const std::vector<tok::TokenKind> &QualifierTokens, bool RightAlign) |
384 | : TokenAnalyzer(Env, Style), Qualifier(Qualifier), RightAlign(RightAlign), |
385 | ConfiguredQualifierTokens(QualifierTokens) {} |
386 | |
387 | std::pair<tooling::Replacements, unsigned> |
388 | LeftRightQualifierAlignmentFixer::analyze( |
389 | TokenAnnotator &Annotator, SmallVectorImpl<AnnotatedLine *> &AnnotatedLines, |
390 | FormatTokenLexer &Tokens) { |
391 | tooling::Replacements Fixes; |
392 | const AdditionalKeywords &Keywords = Tokens.getKeywords(); |
393 | const SourceManager &SourceMgr = Env.getSourceManager(); |
394 | AffectedRangeMgr.computeAffectedLines(AnnotatedLines); |
395 | |
396 | tok::TokenKind QualifierToken = getTokenFromQualifier(Qualifier); |
397 | assert(QualifierToken != tok::identifier && "Unrecognised Qualifier")(static_cast <bool> (QualifierToken != tok::identifier && "Unrecognised Qualifier") ? void (0) : __assert_fail ("QualifierToken != tok::identifier && \"Unrecognised Qualifier\"" , "clang/lib/Format/QualifierAlignmentFixer.cpp", 397, __extension__ __PRETTY_FUNCTION__)); |
398 | |
399 | for (AnnotatedLine *Line : AnnotatedLines) { |
400 | FormatToken *First = Line->First; |
401 | const auto *Last = Line->Last; |
402 | |
403 | for (const auto *Tok = First; Tok && Tok != Last && Tok->Next; |
404 | Tok = Tok->Next) { |
405 | if (Tok->is(tok::comment)) |
406 | continue; |
407 | if (RightAlign) |
408 | Tok = analyzeRight(SourceMgr, Keywords, Fixes, Tok, Qualifier, |
409 | QualifierToken); |
410 | else |
411 | Tok = analyzeLeft(SourceMgr, Keywords, Fixes, Tok, Qualifier, |
412 | QualifierToken); |
413 | } |
414 | } |
415 | return {Fixes, 0}; |
416 | } |
417 | |
418 | void QualifierAlignmentFixer::PrepareLeftRightOrdering( |
419 | const std::vector<std::string> &Order, std::vector<std::string> &LeftOrder, |
420 | std::vector<std::string> &RightOrder, |
421 | std::vector<tok::TokenKind> &Qualifiers) { |
422 | |
423 | // Depending on the position of type in the order you need |
424 | // To iterate forward or backward through the order list as qualifier |
425 | // can push through each other. |
426 | // The Order list must define the position of "type" to signify |
427 | assert(llvm::is_contained(Order, "type") &&(static_cast <bool> (llvm::is_contained(Order, "type") && "QualifierOrder must contain type") ? void (0) : __assert_fail ("llvm::is_contained(Order, \"type\") && \"QualifierOrder must contain type\"" , "clang/lib/Format/QualifierAlignmentFixer.cpp", 428, __extension__ __PRETTY_FUNCTION__)) |
428 | "QualifierOrder must contain type")(static_cast <bool> (llvm::is_contained(Order, "type") && "QualifierOrder must contain type") ? void (0) : __assert_fail ("llvm::is_contained(Order, \"type\") && \"QualifierOrder must contain type\"" , "clang/lib/Format/QualifierAlignmentFixer.cpp", 428, __extension__ __PRETTY_FUNCTION__)); |
429 | // Split the Order list by type and reverse the left side. |
430 | |
431 | bool left = true; |
432 | for (const auto &s : Order) { |
433 | if (s == "type") { |
434 | left = false; |
435 | continue; |
436 | } |
437 | |
438 | tok::TokenKind QualifierToken = |
439 | LeftRightQualifierAlignmentFixer::getTokenFromQualifier(s); |
440 | if (QualifierToken != tok::kw_typeof && QualifierToken != tok::identifier) { |
441 | Qualifiers.push_back(QualifierToken); |
442 | } |
443 | |
444 | if (left) |
445 | // Reverse the order for left aligned items. |
446 | LeftOrder.insert(LeftOrder.begin(), s); |
447 | else |
448 | RightOrder.push_back(s); |
449 | } |
450 | } |
451 | |
452 | bool LeftRightQualifierAlignmentFixer::isQualifierOrType( |
453 | const FormatToken *Tok, const std::vector<tok::TokenKind> &specifiedTypes) { |
454 | return Tok && (Tok->isSimpleTypeSpecifier() || Tok->is(tok::kw_auto) || |
455 | llvm::is_contained(specifiedTypes, Tok->Tok.getKind())); |
456 | } |
457 | |
458 | // If a token is an identifier and it's upper case, it could |
459 | // be a macro and hence we need to be able to ignore it. |
460 | bool LeftRightQualifierAlignmentFixer::isPossibleMacro(const FormatToken *Tok) { |
461 | if (!Tok) |
462 | return false; |
463 | if (!Tok->is(tok::identifier)) |
464 | return false; |
465 | if (Tok->TokenText.upper() == Tok->TokenText.str()) |
466 | return true; |
467 | return false; |
468 | } |
469 | |
470 | } // namespace format |
471 | } // namespace clang |