QualifierAlignmentFixer.cpp 19 KB

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