1a44ab170Smydeveloperday //===--- LeftRightQualifierAlignmentFixer.cpp -------------------*- C++--*-===//
2a44ab170Smydeveloperday //
3a44ab170Smydeveloperday // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4a44ab170Smydeveloperday // See https://llvm.org/LICENSE.txt for license information.
5a44ab170Smydeveloperday // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6a44ab170Smydeveloperday //
7a44ab170Smydeveloperday //===----------------------------------------------------------------------===//
8a44ab170Smydeveloperday ///
9a44ab170Smydeveloperday /// \file
10a44ab170Smydeveloperday /// This file implements LeftRightQualifierAlignmentFixer, a TokenAnalyzer that
11a44ab170Smydeveloperday /// enforces either left or right const depending on the style.
12a44ab170Smydeveloperday ///
13a44ab170Smydeveloperday //===----------------------------------------------------------------------===//
14a44ab170Smydeveloperday 
15a44ab170Smydeveloperday #include "QualifierAlignmentFixer.h"
16a44ab170Smydeveloperday #include "FormatToken.h"
17a44ab170Smydeveloperday #include "llvm/Support/Debug.h"
18a44ab170Smydeveloperday #include "llvm/Support/Regex.h"
19a44ab170Smydeveloperday 
20a44ab170Smydeveloperday #include <algorithm>
21a44ab170Smydeveloperday 
22a44ab170Smydeveloperday #define DEBUG_TYPE "format-qualifier-alignment-fixer"
23a44ab170Smydeveloperday 
24a44ab170Smydeveloperday namespace clang {
25a44ab170Smydeveloperday namespace format {
26a44ab170Smydeveloperday 
QualifierAlignmentFixer(const Environment & Env,const FormatStyle & Style,StringRef & Code,ArrayRef<tooling::Range> Ranges,unsigned FirstStartColumn,unsigned NextStartColumn,unsigned LastStartColumn,StringRef FileName)27a44ab170Smydeveloperday QualifierAlignmentFixer::QualifierAlignmentFixer(
28a44ab170Smydeveloperday     const Environment &Env, const FormatStyle &Style, StringRef &Code,
29a44ab170Smydeveloperday     ArrayRef<tooling::Range> Ranges, unsigned FirstStartColumn,
30a44ab170Smydeveloperday     unsigned NextStartColumn, unsigned LastStartColumn, StringRef FileName)
31a44ab170Smydeveloperday     : TokenAnalyzer(Env, Style), Code(Code), Ranges(Ranges),
32a44ab170Smydeveloperday       FirstStartColumn(FirstStartColumn), NextStartColumn(NextStartColumn),
33a44ab170Smydeveloperday       LastStartColumn(LastStartColumn), FileName(FileName) {
34a44ab170Smydeveloperday   std::vector<std::string> LeftOrder;
35a44ab170Smydeveloperday   std::vector<std::string> RightOrder;
36a44ab170Smydeveloperday   std::vector<tok::TokenKind> ConfiguredQualifierTokens;
37a44ab170Smydeveloperday   PrepareLeftRightOrdering(Style.QualifierOrder, LeftOrder, RightOrder,
38a44ab170Smydeveloperday                            ConfiguredQualifierTokens);
39a44ab170Smydeveloperday 
407d639734SMarek Kurdej   // Handle the left and right alignment separately.
41a44ab170Smydeveloperday   for (const auto &Qualifier : LeftOrder) {
42a44ab170Smydeveloperday     Passes.emplace_back(
43a44ab170Smydeveloperday         [&, Qualifier, ConfiguredQualifierTokens](const Environment &Env) {
44a44ab170Smydeveloperday           return LeftRightQualifierAlignmentFixer(Env, Style, Qualifier,
45a44ab170Smydeveloperday                                                   ConfiguredQualifierTokens,
46a44ab170Smydeveloperday                                                   /*RightAlign=*/false)
47a44ab170Smydeveloperday               .process();
48a44ab170Smydeveloperday         });
49a44ab170Smydeveloperday   }
50a44ab170Smydeveloperday   for (const auto &Qualifier : RightOrder) {
51a44ab170Smydeveloperday     Passes.emplace_back(
52a44ab170Smydeveloperday         [&, Qualifier, ConfiguredQualifierTokens](const Environment &Env) {
53a44ab170Smydeveloperday           return LeftRightQualifierAlignmentFixer(Env, Style, Qualifier,
54a44ab170Smydeveloperday                                                   ConfiguredQualifierTokens,
55a44ab170Smydeveloperday                                                   /*RightAlign=*/true)
56a44ab170Smydeveloperday               .process();
57a44ab170Smydeveloperday         });
58a44ab170Smydeveloperday   }
59a44ab170Smydeveloperday }
60a44ab170Smydeveloperday 
analyze(TokenAnnotator &,SmallVectorImpl<AnnotatedLine * > &,FormatTokenLexer &)61a44ab170Smydeveloperday std::pair<tooling::Replacements, unsigned> QualifierAlignmentFixer::analyze(
6206e42590SMarek Kurdej     TokenAnnotator & /*Annotator*/,
6306e42590SMarek Kurdej     SmallVectorImpl<AnnotatedLine *> & /*AnnotatedLines*/,
6406e42590SMarek Kurdej     FormatTokenLexer & /*Tokens*/) {
65c2271926SManuel Klimek   auto Env = Environment::make(Code, FileName, Ranges, FirstStartColumn,
66a44ab170Smydeveloperday                                NextStartColumn, LastStartColumn);
67c2271926SManuel Klimek   if (!Env)
68c2271926SManuel Klimek     return {};
69a44ab170Smydeveloperday   llvm::Optional<std::string> CurrentCode = None;
70a44ab170Smydeveloperday   tooling::Replacements Fixes;
71a44ab170Smydeveloperday   for (size_t I = 0, E = Passes.size(); I < E; ++I) {
72a44ab170Smydeveloperday     std::pair<tooling::Replacements, unsigned> PassFixes = Passes[I](*Env);
73a44ab170Smydeveloperday     auto NewCode = applyAllReplacements(
74a44ab170Smydeveloperday         CurrentCode ? StringRef(*CurrentCode) : Code, PassFixes.first);
75a44ab170Smydeveloperday     if (NewCode) {
76a44ab170Smydeveloperday       Fixes = Fixes.merge(PassFixes.first);
77a44ab170Smydeveloperday       if (I + 1 < E) {
78a44ab170Smydeveloperday         CurrentCode = std::move(*NewCode);
79c2271926SManuel Klimek         Env = Environment::make(
80a44ab170Smydeveloperday             *CurrentCode, FileName,
81a44ab170Smydeveloperday             tooling::calculateRangesAfterReplacements(Fixes, Ranges),
82a44ab170Smydeveloperday             FirstStartColumn, NextStartColumn, LastStartColumn);
83c2271926SManuel Klimek         if (!Env)
84c2271926SManuel Klimek           return {};
85a44ab170Smydeveloperday       }
86a44ab170Smydeveloperday     }
87a44ab170Smydeveloperday   }
88c2ec5dd2Smydeveloperday 
89c2ec5dd2Smydeveloperday   // Don't make replacements that replace nothing.
90c2ec5dd2Smydeveloperday   tooling::Replacements NonNoOpFixes;
91c2ec5dd2Smydeveloperday 
924681ae93SMarek Kurdej   for (const tooling::Replacement &Fix : Fixes) {
934681ae93SMarek Kurdej     StringRef OriginalCode = Code.substr(Fix.getOffset(), Fix.getLength());
94c2ec5dd2Smydeveloperday 
954681ae93SMarek Kurdej     if (!OriginalCode.equals(Fix.getReplacementText())) {
964681ae93SMarek Kurdej       auto Err = NonNoOpFixes.add(Fix);
97bebf7bdfSowenca       if (Err) {
98c2ec5dd2Smydeveloperday         llvm::errs() << "Error adding replacements : "
99c2ec5dd2Smydeveloperday                      << llvm::toString(std::move(Err)) << "\n";
100c2ec5dd2Smydeveloperday       }
101c2ec5dd2Smydeveloperday     }
102bebf7bdfSowenca   }
103c2ec5dd2Smydeveloperday   return {NonNoOpFixes, 0};
104a44ab170Smydeveloperday }
105a44ab170Smydeveloperday 
replaceToken(const SourceManager & SourceMgr,tooling::Replacements & Fixes,const CharSourceRange & Range,std::string NewText)106a44ab170Smydeveloperday static void replaceToken(const SourceManager &SourceMgr,
107a44ab170Smydeveloperday                          tooling::Replacements &Fixes,
108a44ab170Smydeveloperday                          const CharSourceRange &Range, std::string NewText) {
109a44ab170Smydeveloperday   auto Replacement = tooling::Replacement(SourceMgr, Range, NewText);
110a44ab170Smydeveloperday   auto Err = Fixes.add(Replacement);
111a44ab170Smydeveloperday 
112bebf7bdfSowenca   if (Err) {
113a44ab170Smydeveloperday     llvm::errs() << "Error while rearranging Qualifier : "
114a44ab170Smydeveloperday                  << llvm::toString(std::move(Err)) << "\n";
115a44ab170Smydeveloperday   }
116bebf7bdfSowenca }
117a44ab170Smydeveloperday 
removeToken(const SourceManager & SourceMgr,tooling::Replacements & Fixes,const FormatToken * First)118a44ab170Smydeveloperday static void removeToken(const SourceManager &SourceMgr,
119a44ab170Smydeveloperday                         tooling::Replacements &Fixes,
120a44ab170Smydeveloperday                         const FormatToken *First) {
121a44ab170Smydeveloperday   auto Range = CharSourceRange::getCharRange(First->getStartOfNonWhitespace(),
122a44ab170Smydeveloperday                                              First->Tok.getEndLoc());
123a44ab170Smydeveloperday   replaceToken(SourceMgr, Fixes, Range, "");
124a44ab170Smydeveloperday }
125a44ab170Smydeveloperday 
insertQualifierAfter(const SourceManager & SourceMgr,tooling::Replacements & Fixes,const FormatToken * First,const std::string & Qualifier)126a44ab170Smydeveloperday static void insertQualifierAfter(const SourceManager &SourceMgr,
127a44ab170Smydeveloperday                                  tooling::Replacements &Fixes,
128a44ab170Smydeveloperday                                  const FormatToken *First,
129a44ab170Smydeveloperday                                  const std::string &Qualifier) {
130a44ab170Smydeveloperday   FormatToken *Next = First->Next;
131a44ab170Smydeveloperday   if (!Next)
132a44ab170Smydeveloperday     return;
133a44ab170Smydeveloperday   auto Range = CharSourceRange::getCharRange(Next->getStartOfNonWhitespace(),
134a44ab170Smydeveloperday                                              Next->Tok.getEndLoc());
135a44ab170Smydeveloperday 
136a44ab170Smydeveloperday   std::string NewText = " " + Qualifier + " ";
137a44ab170Smydeveloperday   NewText += Next->TokenText;
138a44ab170Smydeveloperday   replaceToken(SourceMgr, Fixes, Range, NewText);
139a44ab170Smydeveloperday }
140a44ab170Smydeveloperday 
insertQualifierBefore(const SourceManager & SourceMgr,tooling::Replacements & Fixes,const FormatToken * First,const std::string & Qualifier)141a44ab170Smydeveloperday static void insertQualifierBefore(const SourceManager &SourceMgr,
142a44ab170Smydeveloperday                                   tooling::Replacements &Fixes,
143a44ab170Smydeveloperday                                   const FormatToken *First,
144a44ab170Smydeveloperday                                   const std::string &Qualifier) {
145a44ab170Smydeveloperday   auto Range = CharSourceRange::getCharRange(First->getStartOfNonWhitespace(),
146a44ab170Smydeveloperday                                              First->Tok.getEndLoc());
147a44ab170Smydeveloperday 
148a44ab170Smydeveloperday   std::string NewText = " " + Qualifier + " ";
149a44ab170Smydeveloperday   NewText += First->TokenText;
150a44ab170Smydeveloperday 
151a44ab170Smydeveloperday   replaceToken(SourceMgr, Fixes, Range, NewText);
152a44ab170Smydeveloperday }
153a44ab170Smydeveloperday 
endsWithSpace(const std::string & s)154a44ab170Smydeveloperday static bool endsWithSpace(const std::string &s) {
155d079995dSMarek Kurdej   if (s.empty())
156a44ab170Smydeveloperday     return false;
157a44ab170Smydeveloperday   return isspace(s.back());
158a44ab170Smydeveloperday }
159a44ab170Smydeveloperday 
startsWithSpace(const std::string & s)160a44ab170Smydeveloperday static bool startsWithSpace(const std::string &s) {
161d079995dSMarek Kurdej   if (s.empty())
162a44ab170Smydeveloperday     return false;
163a44ab170Smydeveloperday   return isspace(s.front());
164a44ab170Smydeveloperday }
165a44ab170Smydeveloperday 
rotateTokens(const SourceManager & SourceMgr,tooling::Replacements & Fixes,const FormatToken * First,const FormatToken * Last,bool Left)166a44ab170Smydeveloperday static void rotateTokens(const SourceManager &SourceMgr,
167a44ab170Smydeveloperday                          tooling::Replacements &Fixes, const FormatToken *First,
168a44ab170Smydeveloperday                          const FormatToken *Last, bool Left) {
169a44ab170Smydeveloperday   auto *End = Last;
170a44ab170Smydeveloperday   auto *Begin = First;
171a44ab170Smydeveloperday   if (!Left) {
172a44ab170Smydeveloperday     End = Last->Next;
173a44ab170Smydeveloperday     Begin = First->Next;
174a44ab170Smydeveloperday   }
175a44ab170Smydeveloperday 
176a44ab170Smydeveloperday   std::string NewText;
177a44ab170Smydeveloperday   // If we are rotating to the left we move the Last token to the front.
178a44ab170Smydeveloperday   if (Left) {
179a44ab170Smydeveloperday     NewText += Last->TokenText;
180a44ab170Smydeveloperday     NewText += " ";
181a44ab170Smydeveloperday   }
182a44ab170Smydeveloperday 
183a44ab170Smydeveloperday   // Then move through the other tokens.
184a44ab170Smydeveloperday   auto *Tok = Begin;
185a44ab170Smydeveloperday   while (Tok != End) {
186d079995dSMarek Kurdej     if (!NewText.empty() && !endsWithSpace(NewText))
187a44ab170Smydeveloperday       NewText += " ";
188a44ab170Smydeveloperday 
189a44ab170Smydeveloperday     NewText += Tok->TokenText;
190a44ab170Smydeveloperday     Tok = Tok->Next;
191a44ab170Smydeveloperday   }
192a44ab170Smydeveloperday 
193a44ab170Smydeveloperday   // If we are rotating to the right we move the first token to the back.
194a44ab170Smydeveloperday   if (!Left) {
195d079995dSMarek Kurdej     if (!NewText.empty() && !startsWithSpace(NewText))
196a44ab170Smydeveloperday       NewText += " ";
197a44ab170Smydeveloperday     NewText += First->TokenText;
198a44ab170Smydeveloperday   }
199a44ab170Smydeveloperday 
200a44ab170Smydeveloperday   auto Range = CharSourceRange::getCharRange(First->getStartOfNonWhitespace(),
201a44ab170Smydeveloperday                                              Last->Tok.getEndLoc());
202a44ab170Smydeveloperday 
203a44ab170Smydeveloperday   replaceToken(SourceMgr, Fixes, Range, NewText);
204a44ab170Smydeveloperday }
205a44ab170Smydeveloperday 
analyzeRight(const SourceManager & SourceMgr,const AdditionalKeywords & Keywords,tooling::Replacements & Fixes,const FormatToken * Tok,const std::string & Qualifier,tok::TokenKind QualifierType)206031d3eceSmydeveloperday const FormatToken *LeftRightQualifierAlignmentFixer::analyzeRight(
207a44ab170Smydeveloperday     const SourceManager &SourceMgr, const AdditionalKeywords &Keywords,
208031d3eceSmydeveloperday     tooling::Replacements &Fixes, const FormatToken *Tok,
209a44ab170Smydeveloperday     const std::string &Qualifier, tok::TokenKind QualifierType) {
210a44ab170Smydeveloperday   // We only need to think about streams that begin with a qualifier.
211a44ab170Smydeveloperday   if (!Tok->is(QualifierType))
212a44ab170Smydeveloperday     return Tok;
213a44ab170Smydeveloperday   // Don't concern yourself if nothing follows the qualifier.
214a44ab170Smydeveloperday   if (!Tok->Next)
215a44ab170Smydeveloperday     return Tok;
216a44ab170Smydeveloperday   if (LeftRightQualifierAlignmentFixer::isPossibleMacro(Tok->Next))
217a44ab170Smydeveloperday     return Tok;
218a44ab170Smydeveloperday 
219*d4d28f2aSMarek Kurdej   auto AnalyzeTemplate =
220*d4d28f2aSMarek Kurdej       [&](const FormatToken *Tok,
221*d4d28f2aSMarek Kurdej           const FormatToken *StartTemplate) -> const FormatToken * {
222*d4d28f2aSMarek Kurdej     // Read from the TemplateOpener to TemplateCloser.
223*d4d28f2aSMarek Kurdej     FormatToken *EndTemplate = StartTemplate->MatchingParen;
224*d4d28f2aSMarek Kurdej     if (EndTemplate) {
225*d4d28f2aSMarek Kurdej       // Move to the end of any template class members e.g.
226*d4d28f2aSMarek Kurdej       // `Foo<int>::iterator`.
227*d4d28f2aSMarek Kurdej       if (EndTemplate->startsSequence(TT_TemplateCloser, tok::coloncolon,
228*d4d28f2aSMarek Kurdej                                       tok::identifier)) {
229*d4d28f2aSMarek Kurdej         EndTemplate = EndTemplate->Next->Next;
230*d4d28f2aSMarek Kurdej       }
231*d4d28f2aSMarek Kurdej     }
232*d4d28f2aSMarek Kurdej     if (EndTemplate && EndTemplate->Next &&
233*d4d28f2aSMarek Kurdej         !EndTemplate->Next->isOneOf(tok::equal, tok::l_paren)) {
234*d4d28f2aSMarek Kurdej       insertQualifierAfter(SourceMgr, Fixes, EndTemplate, Qualifier);
235*d4d28f2aSMarek Kurdej       // Remove the qualifier.
236*d4d28f2aSMarek Kurdej       removeToken(SourceMgr, Fixes, Tok);
237*d4d28f2aSMarek Kurdej       return Tok;
238*d4d28f2aSMarek Kurdej     }
239*d4d28f2aSMarek Kurdej     return nullptr;
240*d4d28f2aSMarek Kurdej   };
241*d4d28f2aSMarek Kurdej 
242a44ab170Smydeveloperday   FormatToken *Qual = Tok->Next;
243a44ab170Smydeveloperday   FormatToken *LastQual = Qual;
244a44ab170Smydeveloperday   while (Qual && isQualifierOrType(Qual, ConfiguredQualifierTokens)) {
245a44ab170Smydeveloperday     LastQual = Qual;
246a44ab170Smydeveloperday     Qual = Qual->Next;
247a44ab170Smydeveloperday   }
248a44ab170Smydeveloperday   if (LastQual && Qual != LastQual) {
249a44ab170Smydeveloperday     rotateTokens(SourceMgr, Fixes, Tok, LastQual, /*Left=*/false);
250a44ab170Smydeveloperday     Tok = LastQual;
251a44ab170Smydeveloperday   } else if (Tok->startsSequence(QualifierType, tok::identifier,
25228bb040dSmydeveloperday                                  TT_TemplateCloser)) {
25328bb040dSmydeveloperday     FormatToken *Closer = Tok->Next->Next;
25428bb040dSmydeveloperday     rotateTokens(SourceMgr, Fixes, Tok, Tok->Next, /*Left=*/false);
25528bb040dSmydeveloperday     Tok = Closer;
25628bb040dSmydeveloperday     return Tok;
25728bb040dSmydeveloperday   } else if (Tok->startsSequence(QualifierType, tok::identifier,
258a44ab170Smydeveloperday                                  TT_TemplateOpener)) {
259*d4d28f2aSMarek Kurdej     // `const ArrayRef<int> a;`
260*d4d28f2aSMarek Kurdej     // `const ArrayRef<int> &a;`
261*d4d28f2aSMarek Kurdej     const FormatToken *NewTok = AnalyzeTemplate(Tok, Tok->Next->Next);
262*d4d28f2aSMarek Kurdej     if (NewTok)
263*d4d28f2aSMarek Kurdej       return NewTok;
264*d4d28f2aSMarek Kurdej   } else if (Tok->startsSequence(QualifierType, tok::coloncolon,
265*d4d28f2aSMarek Kurdej                                  tok::identifier, TT_TemplateOpener)) {
266*d4d28f2aSMarek Kurdej     // `const ::ArrayRef<int> a;`
267*d4d28f2aSMarek Kurdej     // `const ::ArrayRef<int> &a;`
268*d4d28f2aSMarek Kurdej     const FormatToken *NewTok = AnalyzeTemplate(Tok, Tok->Next->Next->Next);
269*d4d28f2aSMarek Kurdej     if (NewTok)
270*d4d28f2aSMarek Kurdej       return NewTok;
271*d4d28f2aSMarek Kurdej   } else if (Tok->startsSequence(QualifierType, tok::identifier) ||
272*d4d28f2aSMarek Kurdej              Tok->startsSequence(QualifierType, tok::coloncolon,
273bebf7bdfSowenca                                  tok::identifier)) {
274a44ab170Smydeveloperday     FormatToken *Next = Tok->Next;
275a44ab170Smydeveloperday     // The case  `const Foo` -> `Foo const`
276*d4d28f2aSMarek Kurdej     // The case  `const ::Foo` -> `::Foo const`
277a44ab170Smydeveloperday     // The case  `const Foo *` -> `Foo const *`
278a44ab170Smydeveloperday     // The case  `const Foo &` -> `Foo const &`
279a44ab170Smydeveloperday     // The case  `const Foo &&` -> `Foo const &&`
280a44ab170Smydeveloperday     // The case  `const std::Foo &&` -> `std::Foo const &&`
281a44ab170Smydeveloperday     // The case  `const std::Foo<T> &&` -> `std::Foo<T> const &&`
282d079995dSMarek Kurdej     while (Next && Next->isOneOf(tok::identifier, tok::coloncolon))
283a44ab170Smydeveloperday       Next = Next->Next;
284a44ab170Smydeveloperday     if (Next && Next->is(TT_TemplateOpener)) {
285a44ab170Smydeveloperday       Next = Next->MatchingParen;
286a44ab170Smydeveloperday       // Move to the end of any template class members e.g.
287a44ab170Smydeveloperday       // `Foo<int>::iterator`.
288a44ab170Smydeveloperday       if (Next && Next->startsSequence(TT_TemplateCloser, tok::coloncolon,
289bebf7bdfSowenca                                        tok::identifier)) {
290a44ab170Smydeveloperday         return Tok;
291bebf7bdfSowenca       }
292a44ab170Smydeveloperday       assert(Next && "Missing template opener");
293a44ab170Smydeveloperday       Next = Next->Next;
294a44ab170Smydeveloperday     }
295a44ab170Smydeveloperday     if (Next && Next->isOneOf(tok::star, tok::amp, tok::ampamp) &&
296a44ab170Smydeveloperday         !Tok->Next->isOneOf(Keywords.kw_override, Keywords.kw_final)) {
297a44ab170Smydeveloperday       if (Next->Previous && !Next->Previous->is(QualifierType)) {
298a44ab170Smydeveloperday         insertQualifierAfter(SourceMgr, Fixes, Next->Previous, Qualifier);
299a44ab170Smydeveloperday         removeToken(SourceMgr, Fixes, Tok);
300a44ab170Smydeveloperday       }
301a44ab170Smydeveloperday       return Next;
302a44ab170Smydeveloperday     }
303a44ab170Smydeveloperday   }
304a44ab170Smydeveloperday 
305a44ab170Smydeveloperday   return Tok;
306a44ab170Smydeveloperday }
307a44ab170Smydeveloperday 
analyzeLeft(const SourceManager & SourceMgr,const AdditionalKeywords & Keywords,tooling::Replacements & Fixes,const FormatToken * Tok,const std::string & Qualifier,tok::TokenKind QualifierType)308031d3eceSmydeveloperday const FormatToken *LeftRightQualifierAlignmentFixer::analyzeLeft(
309a44ab170Smydeveloperday     const SourceManager &SourceMgr, const AdditionalKeywords &Keywords,
310031d3eceSmydeveloperday     tooling::Replacements &Fixes, const FormatToken *Tok,
311a44ab170Smydeveloperday     const std::string &Qualifier, tok::TokenKind QualifierType) {
312a44ab170Smydeveloperday   // if Tok is an identifier and possibly a macro then don't convert.
313a44ab170Smydeveloperday   if (LeftRightQualifierAlignmentFixer::isPossibleMacro(Tok))
314a44ab170Smydeveloperday     return Tok;
315a44ab170Smydeveloperday 
316031d3eceSmydeveloperday   const FormatToken *Qual = Tok;
317031d3eceSmydeveloperday   const FormatToken *LastQual = Qual;
318a44ab170Smydeveloperday   while (Qual && isQualifierOrType(Qual, ConfiguredQualifierTokens)) {
319a44ab170Smydeveloperday     LastQual = Qual;
320a44ab170Smydeveloperday     Qual = Qual->Next;
321a44ab170Smydeveloperday     if (Qual && Qual->is(QualifierType))
322a44ab170Smydeveloperday       break;
323a44ab170Smydeveloperday   }
324a44ab170Smydeveloperday 
325d079995dSMarek Kurdej   if (!Qual)
326a44ab170Smydeveloperday     return Tok;
327a44ab170Smydeveloperday 
328a44ab170Smydeveloperday   if (LastQual && Qual != LastQual && Qual->is(QualifierType)) {
329a44ab170Smydeveloperday     rotateTokens(SourceMgr, Fixes, Tok, Qual, /*Left=*/true);
330492cb7bfSOwen Pan     if (!Qual->Next)
331492cb7bfSOwen Pan       return Tok;
332a44ab170Smydeveloperday     Tok = Qual->Next;
333a44ab170Smydeveloperday   } else if (Tok->startsSequence(tok::identifier, QualifierType)) {
334a44ab170Smydeveloperday     if (Tok->Next->Next && Tok->Next->Next->isOneOf(tok::identifier, tok::star,
335a44ab170Smydeveloperday                                                     tok::amp, tok::ampamp)) {
336a44ab170Smydeveloperday       // Don't swap `::iterator const` to `::const iterator`.
337a44ab170Smydeveloperday       if (!Tok->Previous ||
338a44ab170Smydeveloperday           (Tok->Previous && !Tok->Previous->is(tok::coloncolon))) {
339a44ab170Smydeveloperday         rotateTokens(SourceMgr, Fixes, Tok, Tok->Next, /*Left=*/true);
340a44ab170Smydeveloperday         Tok = Tok->Next;
341a44ab170Smydeveloperday       }
34228bb040dSmydeveloperday     } else if (Tok->startsSequence(tok::identifier, QualifierType,
34328bb040dSmydeveloperday                                    TT_TemplateCloser)) {
34428bb040dSmydeveloperday       FormatToken *Closer = Tok->Next->Next;
34528bb040dSmydeveloperday       rotateTokens(SourceMgr, Fixes, Tok, Tok->Next, /*Left=*/true);
34628bb040dSmydeveloperday       Tok = Closer;
347a44ab170Smydeveloperday     }
348a44ab170Smydeveloperday   }
349a44ab170Smydeveloperday   if (Tok->is(TT_TemplateOpener) && Tok->Next &&
350a44ab170Smydeveloperday       (Tok->Next->is(tok::identifier) || Tok->Next->isSimpleTypeSpecifier()) &&
351bebf7bdfSowenca       Tok->Next->Next && Tok->Next->Next->is(QualifierType)) {
352a44ab170Smydeveloperday     rotateTokens(SourceMgr, Fixes, Tok->Next, Tok->Next->Next, /*Left=*/true);
353bebf7bdfSowenca   }
354*d4d28f2aSMarek Kurdej   if ((Tok->startsSequence(tok::coloncolon, tok::identifier) ||
355*d4d28f2aSMarek Kurdej        Tok->is(tok::identifier)) &&
356*d4d28f2aSMarek Kurdej       Tok->Next) {
357a44ab170Smydeveloperday     if (Tok->Previous &&
358bebf7bdfSowenca         Tok->Previous->isOneOf(tok::star, tok::ampamp, tok::amp)) {
359a44ab170Smydeveloperday       return Tok;
360bebf7bdfSowenca     }
361031d3eceSmydeveloperday     const FormatToken *Next = Tok->Next;
362a44ab170Smydeveloperday     // The case  `std::Foo<T> const` -> `const std::Foo<T> &&`
363a44ab170Smydeveloperday     while (Next && Next->isOneOf(tok::identifier, tok::coloncolon))
364a44ab170Smydeveloperday       Next = Next->Next;
365a44ab170Smydeveloperday     if (Next && Next->Previous &&
366a44ab170Smydeveloperday         Next->Previous->startsSequence(tok::identifier, TT_TemplateOpener)) {
367a44ab170Smydeveloperday       // Read from to the end of the TemplateOpener to
368a44ab170Smydeveloperday       // TemplateCloser const ArrayRef<int> a; const ArrayRef<int> &a;
369031d3eceSmydeveloperday       if (Next->is(tok::comment) && Next->getNextNonComment())
370031d3eceSmydeveloperday         Next = Next->getNextNonComment();
371a44ab170Smydeveloperday       assert(Next->MatchingParen && "Missing template closer");
37246f6c834SMarek Kurdej       Next = Next->MatchingParen;
37328bb040dSmydeveloperday 
37428bb040dSmydeveloperday       // If the template closer is closing the requires clause,
37528bb040dSmydeveloperday       // then stop and go back to the TemplateOpener and do whatever is
37628bb040dSmydeveloperday       // inside the <>.
37746f6c834SMarek Kurdej       if (Next->ClosesRequiresClause)
37828bb040dSmydeveloperday         return Next->MatchingParen;
37946f6c834SMarek Kurdej       Next = Next->Next;
380a44ab170Smydeveloperday 
381a44ab170Smydeveloperday       // Move to the end of any template class members e.g.
382a44ab170Smydeveloperday       // `Foo<int>::iterator`.
383a44ab170Smydeveloperday       if (Next && Next->startsSequence(tok::coloncolon, tok::identifier))
384a44ab170Smydeveloperday         Next = Next->Next->Next;
385a44ab170Smydeveloperday       if (Next && Next->is(QualifierType)) {
38646f6c834SMarek Kurdej         // Move the qualifier.
387a44ab170Smydeveloperday         insertQualifierBefore(SourceMgr, Fixes, Tok, Qualifier);
388a44ab170Smydeveloperday         removeToken(SourceMgr, Fixes, Next);
389a44ab170Smydeveloperday         return Next;
390a44ab170Smydeveloperday       }
391a44ab170Smydeveloperday     }
392a44ab170Smydeveloperday     if (Next && Next->Next &&
393a44ab170Smydeveloperday         Next->Next->isOneOf(tok::amp, tok::ampamp, tok::star)) {
394a44ab170Smydeveloperday       if (Next->is(QualifierType)) {
39546f6c834SMarek Kurdej         // Move the qualifier.
396a44ab170Smydeveloperday         insertQualifierBefore(SourceMgr, Fixes, Tok, Qualifier);
397a44ab170Smydeveloperday         removeToken(SourceMgr, Fixes, Next);
398a44ab170Smydeveloperday         return Next;
399a44ab170Smydeveloperday       }
400a44ab170Smydeveloperday     }
401a44ab170Smydeveloperday   }
402a44ab170Smydeveloperday   return Tok;
403a44ab170Smydeveloperday }
404a44ab170Smydeveloperday 
getTokenFromQualifier(const std::string & Qualifier)405a44ab170Smydeveloperday tok::TokenKind LeftRightQualifierAlignmentFixer::getTokenFromQualifier(
406a44ab170Smydeveloperday     const std::string &Qualifier) {
4072c60cfc0SMarek Kurdej   // Don't let 'type' be an identifier, but steal typeof token.
408a44ab170Smydeveloperday   return llvm::StringSwitch<tok::TokenKind>(Qualifier)
409a44ab170Smydeveloperday       .Case("type", tok::kw_typeof)
410a44ab170Smydeveloperday       .Case("const", tok::kw_const)
411a44ab170Smydeveloperday       .Case("volatile", tok::kw_volatile)
412a44ab170Smydeveloperday       .Case("static", tok::kw_static)
413a44ab170Smydeveloperday       .Case("inline", tok::kw_inline)
414a44ab170Smydeveloperday       .Case("constexpr", tok::kw_constexpr)
415a44ab170Smydeveloperday       .Case("restrict", tok::kw_restrict)
416a44ab170Smydeveloperday       .Default(tok::identifier);
417a44ab170Smydeveloperday }
418a44ab170Smydeveloperday 
LeftRightQualifierAlignmentFixer(const Environment & Env,const FormatStyle & Style,const std::string & Qualifier,const std::vector<tok::TokenKind> & QualifierTokens,bool RightAlign)419a44ab170Smydeveloperday LeftRightQualifierAlignmentFixer::LeftRightQualifierAlignmentFixer(
420a44ab170Smydeveloperday     const Environment &Env, const FormatStyle &Style,
421a44ab170Smydeveloperday     const std::string &Qualifier,
422a44ab170Smydeveloperday     const std::vector<tok::TokenKind> &QualifierTokens, bool RightAlign)
423a44ab170Smydeveloperday     : TokenAnalyzer(Env, Style), Qualifier(Qualifier), RightAlign(RightAlign),
424a44ab170Smydeveloperday       ConfiguredQualifierTokens(QualifierTokens) {}
425a44ab170Smydeveloperday 
426a44ab170Smydeveloperday std::pair<tooling::Replacements, unsigned>
analyze(TokenAnnotator &,SmallVectorImpl<AnnotatedLine * > & AnnotatedLines,FormatTokenLexer & Tokens)427a44ab170Smydeveloperday LeftRightQualifierAlignmentFixer::analyze(
42806e42590SMarek Kurdej     TokenAnnotator & /*Annotator*/,
42906e42590SMarek Kurdej     SmallVectorImpl<AnnotatedLine *> &AnnotatedLines,
430a44ab170Smydeveloperday     FormatTokenLexer &Tokens) {
431a44ab170Smydeveloperday   tooling::Replacements Fixes;
432a44ab170Smydeveloperday   const AdditionalKeywords &Keywords = Tokens.getKeywords();
433a44ab170Smydeveloperday   const SourceManager &SourceMgr = Env.getSourceManager();
434a44ab170Smydeveloperday   AffectedRangeMgr.computeAffectedLines(AnnotatedLines);
435a44ab170Smydeveloperday 
436a44ab170Smydeveloperday   tok::TokenKind QualifierToken = getTokenFromQualifier(Qualifier);
437a44ab170Smydeveloperday   assert(QualifierToken != tok::identifier && "Unrecognised Qualifier");
438a44ab170Smydeveloperday 
4394681ae93SMarek Kurdej   for (AnnotatedLine *Line : AnnotatedLines) {
440eee536ddSowenca     if (Line->InPPDirective)
441eee536ddSowenca       continue;
4424681ae93SMarek Kurdej     FormatToken *First = Line->First;
443e329b586SMarek Kurdej     assert(First);
444e329b586SMarek Kurdej     if (First->Finalized)
445e329b586SMarek Kurdej       continue;
446e329b586SMarek Kurdej 
4474681ae93SMarek Kurdej     const auto *Last = Line->Last;
448a44ab170Smydeveloperday 
449031d3eceSmydeveloperday     for (const auto *Tok = First; Tok && Tok != Last && Tok->Next;
450031d3eceSmydeveloperday          Tok = Tok->Next) {
451a44ab170Smydeveloperday       if (Tok->is(tok::comment))
452a44ab170Smydeveloperday         continue;
453bebf7bdfSowenca       if (RightAlign) {
454a44ab170Smydeveloperday         Tok = analyzeRight(SourceMgr, Keywords, Fixes, Tok, Qualifier,
455a44ab170Smydeveloperday                            QualifierToken);
456bebf7bdfSowenca       } else {
457a44ab170Smydeveloperday         Tok = analyzeLeft(SourceMgr, Keywords, Fixes, Tok, Qualifier,
458a44ab170Smydeveloperday                           QualifierToken);
459a44ab170Smydeveloperday       }
460a44ab170Smydeveloperday     }
461bebf7bdfSowenca   }
462a44ab170Smydeveloperday   return {Fixes, 0};
463a44ab170Smydeveloperday }
464a44ab170Smydeveloperday 
PrepareLeftRightOrdering(const std::vector<std::string> & Order,std::vector<std::string> & LeftOrder,std::vector<std::string> & RightOrder,std::vector<tok::TokenKind> & Qualifiers)465a44ab170Smydeveloperday void QualifierAlignmentFixer::PrepareLeftRightOrdering(
466a44ab170Smydeveloperday     const std::vector<std::string> &Order, std::vector<std::string> &LeftOrder,
467a44ab170Smydeveloperday     std::vector<std::string> &RightOrder,
468a44ab170Smydeveloperday     std::vector<tok::TokenKind> &Qualifiers) {
469a44ab170Smydeveloperday 
470a44ab170Smydeveloperday   // Depending on the position of type in the order you need
471a44ab170Smydeveloperday   // To iterate forward or backward through the order list as qualifier
472a44ab170Smydeveloperday   // can push through each other.
473a44ab170Smydeveloperday   // The Order list must define the position of "type" to signify
474e567f37dSKazu Hirata   assert(llvm::is_contained(Order, "type") &&
475efb284c0SDmitri Gribenko          "QualifierOrder must contain type");
476a44ab170Smydeveloperday   // Split the Order list by type and reverse the left side.
477a44ab170Smydeveloperday 
478a44ab170Smydeveloperday   bool left = true;
479a44ab170Smydeveloperday   for (const auto &s : Order) {
480a44ab170Smydeveloperday     if (s == "type") {
481a44ab170Smydeveloperday       left = false;
482a44ab170Smydeveloperday       continue;
483a44ab170Smydeveloperday     }
484a44ab170Smydeveloperday 
485a44ab170Smydeveloperday     tok::TokenKind QualifierToken =
486a44ab170Smydeveloperday         LeftRightQualifierAlignmentFixer::getTokenFromQualifier(s);
487d079995dSMarek Kurdej     if (QualifierToken != tok::kw_typeof && QualifierToken != tok::identifier)
488a44ab170Smydeveloperday       Qualifiers.push_back(QualifierToken);
489a44ab170Smydeveloperday 
490bebf7bdfSowenca     if (left) {
491a44ab170Smydeveloperday       // Reverse the order for left aligned items.
492a44ab170Smydeveloperday       LeftOrder.insert(LeftOrder.begin(), s);
493bebf7bdfSowenca     } else {
494a44ab170Smydeveloperday       RightOrder.push_back(s);
495a44ab170Smydeveloperday     }
496a44ab170Smydeveloperday   }
497bebf7bdfSowenca }
498a44ab170Smydeveloperday 
isQualifierOrType(const FormatToken * Tok,const std::vector<tok::TokenKind> & specifiedTypes)499a44ab170Smydeveloperday bool LeftRightQualifierAlignmentFixer::isQualifierOrType(
500a44ab170Smydeveloperday     const FormatToken *Tok, const std::vector<tok::TokenKind> &specifiedTypes) {
501a44ab170Smydeveloperday   return Tok && (Tok->isSimpleTypeSpecifier() || Tok->is(tok::kw_auto) ||
502e567f37dSKazu Hirata                  llvm::is_contained(specifiedTypes, Tok->Tok.getKind()));
503a44ab170Smydeveloperday }
504a44ab170Smydeveloperday 
505a44ab170Smydeveloperday // If a token is an identifier and it's upper case, it could
506a44ab170Smydeveloperday // be a macro and hence we need to be able to ignore it.
isPossibleMacro(const FormatToken * Tok)507a44ab170Smydeveloperday bool LeftRightQualifierAlignmentFixer::isPossibleMacro(const FormatToken *Tok) {
508a44ab170Smydeveloperday   if (!Tok)
509a44ab170Smydeveloperday     return false;
510a44ab170Smydeveloperday   if (!Tok->is(tok::identifier))
511a44ab170Smydeveloperday     return false;
512bebf7bdfSowenca   if (Tok->TokenText.upper() == Tok->TokenText.str()) {
51328bb040dSmydeveloperday     // T,K,U,V likely could be template arguments
51428bb040dSmydeveloperday     return (Tok->TokenText.size() != 1);
515bebf7bdfSowenca   }
516a44ab170Smydeveloperday   return false;
517a44ab170Smydeveloperday }
518a44ab170Smydeveloperday 
519a44ab170Smydeveloperday } // namespace format
520a44ab170Smydeveloperday } // namespace clang
521