IntegerLiteralSeparatorFixer.cpp 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. //===--- IntegerLiteralSeparatorFixer.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 IntegerLiteralSeparatorFixer that fixes C++ integer
  11. /// literal separators.
  12. ///
  13. //===----------------------------------------------------------------------===//
  14. #include "IntegerLiteralSeparatorFixer.h"
  15. namespace clang {
  16. namespace format {
  17. enum class Base { Binary, Decimal, Hex, Other };
  18. static Base getBase(const StringRef IntegerLiteral) {
  19. assert(IntegerLiteral.size() > 1);
  20. if (IntegerLiteral[0] > '0') {
  21. assert(IntegerLiteral[0] <= '9');
  22. return Base::Decimal;
  23. }
  24. assert(IntegerLiteral[0] == '0');
  25. switch (IntegerLiteral[1]) {
  26. case 'b':
  27. case 'B':
  28. return Base::Binary;
  29. case 'x':
  30. case 'X':
  31. return Base::Hex;
  32. default:
  33. return Base::Other;
  34. }
  35. }
  36. std::pair<tooling::Replacements, unsigned>
  37. IntegerLiteralSeparatorFixer::process(const Environment &Env,
  38. const FormatStyle &Style) {
  39. switch (Style.Language) {
  40. case FormatStyle::LK_Cpp:
  41. case FormatStyle::LK_ObjC:
  42. Separator = '\'';
  43. break;
  44. case FormatStyle::LK_CSharp:
  45. case FormatStyle::LK_Java:
  46. case FormatStyle::LK_JavaScript:
  47. Separator = '_';
  48. break;
  49. default:
  50. return {};
  51. }
  52. const auto &Option = Style.IntegerLiteralSeparator;
  53. const auto Binary = Option.Binary;
  54. const auto Decimal = Option.Decimal;
  55. const auto Hex = Option.Hex;
  56. const bool SkipBinary = Binary == 0;
  57. const bool SkipDecimal = Decimal == 0;
  58. const bool SkipHex = Hex == 0;
  59. if (SkipBinary && SkipDecimal && SkipHex)
  60. return {};
  61. const auto &SourceMgr = Env.getSourceManager();
  62. AffectedRangeManager AffectedRangeMgr(SourceMgr, Env.getCharRanges());
  63. const auto ID = Env.getFileID();
  64. const auto LangOpts = getFormattingLangOpts(Style);
  65. Lexer Lex(ID, SourceMgr.getBufferOrFake(ID), SourceMgr, LangOpts);
  66. Lex.SetCommentRetentionState(true);
  67. Token Tok;
  68. tooling::Replacements Result;
  69. for (bool Skip = false; !Lex.LexFromRawLexer(Tok);) {
  70. auto Length = Tok.getLength();
  71. if (Length < 2)
  72. continue;
  73. auto Location = Tok.getLocation();
  74. auto Text = StringRef(SourceMgr.getCharacterData(Location), Length);
  75. if (Tok.is(tok::comment)) {
  76. if (Text == "// clang-format off" || Text == "/* clang-format off */")
  77. Skip = true;
  78. else if (Text == "// clang-format on" || Text == "/* clang-format on */")
  79. Skip = false;
  80. continue;
  81. }
  82. if (Skip || Tok.isNot(tok::numeric_constant) || Text[0] == '.' ||
  83. !AffectedRangeMgr.affectsCharSourceRange(
  84. CharSourceRange::getCharRange(Location, Tok.getEndLoc()))) {
  85. continue;
  86. }
  87. const auto B = getBase(Text);
  88. const bool IsBase2 = B == Base::Binary;
  89. const bool IsBase10 = B == Base::Decimal;
  90. const bool IsBase16 = B == Base::Hex;
  91. if ((IsBase2 && SkipBinary) || (IsBase10 && SkipDecimal) ||
  92. (IsBase16 && SkipHex) || B == Base::Other) {
  93. continue;
  94. }
  95. if ((IsBase10 && Text.find_last_of(".eEfFdDmM") != StringRef::npos) ||
  96. (IsBase16 && Text.find_last_of(".pP") != StringRef::npos)) {
  97. continue;
  98. }
  99. if (((IsBase2 && Binary < 0) || (IsBase10 && Decimal < 0) ||
  100. (IsBase16 && Hex < 0)) &&
  101. Text.find(Separator) == StringRef::npos) {
  102. continue;
  103. }
  104. const auto Start = Text[0] == '0' ? 2 : 0;
  105. auto End = Text.find_first_of("uUlLzZn");
  106. if (End == StringRef::npos)
  107. End = Length;
  108. if (Start > 0 || End < Length) {
  109. Length = End - Start;
  110. Text = Text.substr(Start, Length);
  111. }
  112. auto DigitsPerGroup = Decimal;
  113. if (IsBase2)
  114. DigitsPerGroup = Binary;
  115. else if (IsBase16)
  116. DigitsPerGroup = Hex;
  117. if (DigitsPerGroup > 0 && checkSeparator(Text, DigitsPerGroup))
  118. continue;
  119. if (Start > 0)
  120. Location = Location.getLocWithOffset(Start);
  121. if (const auto &Formatted = format(Text, DigitsPerGroup);
  122. Formatted != Text) {
  123. cantFail(Result.add(
  124. tooling::Replacement(SourceMgr, Location, Length, Formatted)));
  125. }
  126. }
  127. return {Result, 0};
  128. }
  129. bool IntegerLiteralSeparatorFixer::checkSeparator(
  130. const StringRef IntegerLiteral, int DigitsPerGroup) const {
  131. assert(DigitsPerGroup > 0);
  132. int I = 0;
  133. for (auto C : llvm::reverse(IntegerLiteral)) {
  134. if (C == Separator) {
  135. if (I < DigitsPerGroup)
  136. return false;
  137. I = 0;
  138. } else {
  139. ++I;
  140. if (I == DigitsPerGroup)
  141. return false;
  142. }
  143. }
  144. return true;
  145. }
  146. std::string IntegerLiteralSeparatorFixer::format(const StringRef IntegerLiteral,
  147. int DigitsPerGroup) const {
  148. assert(DigitsPerGroup != 0);
  149. std::string Formatted;
  150. if (DigitsPerGroup < 0) {
  151. for (auto C : IntegerLiteral)
  152. if (C != Separator)
  153. Formatted.push_back(C);
  154. return Formatted;
  155. }
  156. int DigitCount = 0;
  157. for (auto C : IntegerLiteral)
  158. if (C != Separator)
  159. ++DigitCount;
  160. int Remainder = DigitCount % DigitsPerGroup;
  161. int I = 0;
  162. for (auto C : IntegerLiteral) {
  163. if (C == Separator)
  164. continue;
  165. if (I == (Remainder > 0 ? Remainder : DigitsPerGroup)) {
  166. Formatted.push_back(Separator);
  167. I = 0;
  168. Remainder = 0;
  169. }
  170. Formatted.push_back(C);
  171. ++I;
  172. }
  173. return Formatted;
  174. }
  175. } // namespace format
  176. } // namespace clang