xref: /llvm-project/clang/lib/Format/IntegerLiteralSeparatorFixer.cpp (revision 61c83e9491b2be71a54b255cdb11f65365245953)
189aad1e6SOwen Pan //===--- IntegerLiteralSeparatorFixer.cpp -----------------------*- C++ -*-===//
289aad1e6SOwen Pan //
389aad1e6SOwen Pan // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
489aad1e6SOwen Pan // See https://llvm.org/LICENSE.txt for license information.
589aad1e6SOwen Pan // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
689aad1e6SOwen Pan //
789aad1e6SOwen Pan //===----------------------------------------------------------------------===//
889aad1e6SOwen Pan ///
989aad1e6SOwen Pan /// \file
1089aad1e6SOwen Pan /// This file implements IntegerLiteralSeparatorFixer that fixes C++ integer
1189aad1e6SOwen Pan /// literal separators.
1289aad1e6SOwen Pan ///
1389aad1e6SOwen Pan //===----------------------------------------------------------------------===//
1489aad1e6SOwen Pan 
1589aad1e6SOwen Pan #include "IntegerLiteralSeparatorFixer.h"
1689aad1e6SOwen Pan 
1789aad1e6SOwen Pan namespace clang {
1889aad1e6SOwen Pan namespace format {
1989aad1e6SOwen Pan 
2089aad1e6SOwen Pan enum class Base { Binary, Decimal, Hex, Other };
2189aad1e6SOwen Pan 
getBase(const StringRef IntegerLiteral)2289aad1e6SOwen Pan static Base getBase(const StringRef IntegerLiteral) {
2389aad1e6SOwen Pan   assert(IntegerLiteral.size() > 1);
2489aad1e6SOwen Pan 
2589aad1e6SOwen Pan   if (IntegerLiteral[0] > '0') {
2689aad1e6SOwen Pan     assert(IntegerLiteral[0] <= '9');
2789aad1e6SOwen Pan     return Base::Decimal;
2889aad1e6SOwen Pan   }
2989aad1e6SOwen Pan 
3089aad1e6SOwen Pan   assert(IntegerLiteral[0] == '0');
3189aad1e6SOwen Pan 
3289aad1e6SOwen Pan   switch (IntegerLiteral[1]) {
3389aad1e6SOwen Pan   case 'b':
3489aad1e6SOwen Pan   case 'B':
3589aad1e6SOwen Pan     return Base::Binary;
3689aad1e6SOwen Pan   case 'x':
3789aad1e6SOwen Pan   case 'X':
3889aad1e6SOwen Pan     return Base::Hex;
3989aad1e6SOwen Pan   default:
4089aad1e6SOwen Pan     return Base::Other;
4189aad1e6SOwen Pan   }
4289aad1e6SOwen Pan }
4389aad1e6SOwen Pan 
4489aad1e6SOwen Pan std::pair<tooling::Replacements, unsigned>
process(const Environment & Env,const FormatStyle & Style)4589aad1e6SOwen Pan IntegerLiteralSeparatorFixer::process(const Environment &Env,
4689aad1e6SOwen Pan                                       const FormatStyle &Style) {
4789aad1e6SOwen Pan   switch (Style.Language) {
4889aad1e6SOwen Pan   case FormatStyle::LK_Cpp:
4989aad1e6SOwen Pan   case FormatStyle::LK_ObjC:
5089aad1e6SOwen Pan     Separator = '\'';
5189aad1e6SOwen Pan     break;
5289aad1e6SOwen Pan   case FormatStyle::LK_CSharp:
5389aad1e6SOwen Pan   case FormatStyle::LK_Java:
5489aad1e6SOwen Pan   case FormatStyle::LK_JavaScript:
5589aad1e6SOwen Pan     Separator = '_';
5689aad1e6SOwen Pan     break;
5789aad1e6SOwen Pan   default:
5889aad1e6SOwen Pan     return {};
5989aad1e6SOwen Pan   }
6089aad1e6SOwen Pan 
6189aad1e6SOwen Pan   const auto &Option = Style.IntegerLiteralSeparator;
6289aad1e6SOwen Pan   const auto Binary = Option.Binary;
6389aad1e6SOwen Pan   const auto Decimal = Option.Decimal;
6489aad1e6SOwen Pan   const auto Hex = Option.Hex;
6589aad1e6SOwen Pan   const bool SkipBinary = Binary == 0;
6689aad1e6SOwen Pan   const bool SkipDecimal = Decimal == 0;
6789aad1e6SOwen Pan   const bool SkipHex = Hex == 0;
6889aad1e6SOwen Pan 
6989aad1e6SOwen Pan   if (SkipBinary && SkipDecimal && SkipHex)
7089aad1e6SOwen Pan     return {};
7189aad1e6SOwen Pan 
72253985d5SOwen Pan   const auto BinaryMinDigits =
73253985d5SOwen Pan       std::max((int)Option.BinaryMinDigits, Binary + 1);
74253985d5SOwen Pan   const auto DecimalMinDigits =
75253985d5SOwen Pan       std::max((int)Option.DecimalMinDigits, Decimal + 1);
76253985d5SOwen Pan   const auto HexMinDigits = std::max((int)Option.HexMinDigits, Hex + 1);
77253985d5SOwen Pan 
7889aad1e6SOwen Pan   const auto &SourceMgr = Env.getSourceManager();
7989aad1e6SOwen Pan   AffectedRangeManager AffectedRangeMgr(SourceMgr, Env.getCharRanges());
8089aad1e6SOwen Pan 
8189aad1e6SOwen Pan   const auto ID = Env.getFileID();
82*61c83e94SOwen Pan   const auto LangOpts = getFormattingLangOpts(Style);
8389aad1e6SOwen Pan   Lexer Lex(ID, SourceMgr.getBufferOrFake(ID), SourceMgr, LangOpts);
8489aad1e6SOwen Pan   Lex.SetCommentRetentionState(true);
8589aad1e6SOwen Pan 
8689aad1e6SOwen Pan   Token Tok;
8789aad1e6SOwen Pan   tooling::Replacements Result;
88b027cdc5SOwen Pan 
89b027cdc5SOwen Pan   for (bool Skip = false; !Lex.LexFromRawLexer(Tok);) {
9089aad1e6SOwen Pan     auto Length = Tok.getLength();
9189aad1e6SOwen Pan     if (Length < 2)
9289aad1e6SOwen Pan       continue;
9389aad1e6SOwen Pan     auto Location = Tok.getLocation();
9489aad1e6SOwen Pan     auto Text = StringRef(SourceMgr.getCharacterData(Location), Length);
9589aad1e6SOwen Pan     if (Tok.is(tok::comment)) {
9625e2d0f3SOwen Pan       if (isClangFormatOff(Text))
9789aad1e6SOwen Pan         Skip = true;
9825e2d0f3SOwen Pan       else if (isClangFormatOn(Text))
9989aad1e6SOwen Pan         Skip = false;
10089aad1e6SOwen Pan       continue;
10189aad1e6SOwen Pan     }
10289aad1e6SOwen Pan     if (Skip || Tok.isNot(tok::numeric_constant) || Text[0] == '.' ||
10389aad1e6SOwen Pan         !AffectedRangeMgr.affectsCharSourceRange(
10489aad1e6SOwen Pan             CharSourceRange::getCharRange(Location, Tok.getEndLoc()))) {
10589aad1e6SOwen Pan       continue;
10689aad1e6SOwen Pan     }
10789aad1e6SOwen Pan     const auto B = getBase(Text);
10889aad1e6SOwen Pan     const bool IsBase2 = B == Base::Binary;
10989aad1e6SOwen Pan     const bool IsBase10 = B == Base::Decimal;
11089aad1e6SOwen Pan     const bool IsBase16 = B == Base::Hex;
11189aad1e6SOwen Pan     if ((IsBase2 && SkipBinary) || (IsBase10 && SkipDecimal) ||
11289aad1e6SOwen Pan         (IsBase16 && SkipHex) || B == Base::Other) {
11389aad1e6SOwen Pan       continue;
11489aad1e6SOwen Pan     }
1154d21868bSOwen Pan     if (Style.isCpp()) {
116a72b064aSOwen Pan       // Hex alpha digits a-f/A-F must be at the end of the string literal.
117a72b064aSOwen Pan       StringRef Suffixes = "_himnsuyd";
118a72b064aSOwen Pan       if (const auto Pos =
119a72b064aSOwen Pan               Text.find_first_of(IsBase16 ? Suffixes.drop_back() : Suffixes);
120a72b064aSOwen Pan           Pos != StringRef::npos) {
1214d21868bSOwen Pan         Text = Text.substr(0, Pos);
1224d21868bSOwen Pan         Length = Pos;
1234d21868bSOwen Pan       }
1244d21868bSOwen Pan     }
12589aad1e6SOwen Pan     if ((IsBase10 && Text.find_last_of(".eEfFdDmM") != StringRef::npos) ||
12689aad1e6SOwen Pan         (IsBase16 && Text.find_last_of(".pP") != StringRef::npos)) {
12789aad1e6SOwen Pan       continue;
12889aad1e6SOwen Pan     }
12989aad1e6SOwen Pan     const auto Start = Text[0] == '0' ? 2 : 0;
1304d21868bSOwen Pan     auto End = Text.find_first_of("uUlLzZn", Start);
13189aad1e6SOwen Pan     if (End == StringRef::npos)
13289aad1e6SOwen Pan       End = Length;
13389aad1e6SOwen Pan     if (Start > 0 || End < Length) {
13489aad1e6SOwen Pan       Length = End - Start;
13589aad1e6SOwen Pan       Text = Text.substr(Start, Length);
13689aad1e6SOwen Pan     }
13789aad1e6SOwen Pan     auto DigitsPerGroup = Decimal;
138253985d5SOwen Pan     auto MinDigits = DecimalMinDigits;
139253985d5SOwen Pan     if (IsBase2) {
14089aad1e6SOwen Pan       DigitsPerGroup = Binary;
141253985d5SOwen Pan       MinDigits = BinaryMinDigits;
142253985d5SOwen Pan     } else if (IsBase16) {
14389aad1e6SOwen Pan       DigitsPerGroup = Hex;
144253985d5SOwen Pan       MinDigits = HexMinDigits;
145253985d5SOwen Pan     }
146253985d5SOwen Pan     const auto SeparatorCount = Text.count(Separator);
147253985d5SOwen Pan     const int DigitCount = Length - SeparatorCount;
148253985d5SOwen Pan     const bool RemoveSeparator = DigitsPerGroup < 0 || DigitCount < MinDigits;
149253985d5SOwen Pan     if (RemoveSeparator && SeparatorCount == 0)
15089aad1e6SOwen Pan       continue;
151253985d5SOwen Pan     if (!RemoveSeparator && SeparatorCount > 0 &&
152253985d5SOwen Pan         checkSeparator(Text, DigitsPerGroup)) {
153253985d5SOwen Pan       continue;
154253985d5SOwen Pan     }
155253985d5SOwen Pan     const auto &Formatted =
156253985d5SOwen Pan         format(Text, DigitsPerGroup, DigitCount, RemoveSeparator);
1575b5c49adSOwen Pan     assert(Formatted != Text);
15889aad1e6SOwen Pan     if (Start > 0)
15989aad1e6SOwen Pan       Location = Location.getLocWithOffset(Start);
160b4243bb6SOwen Pan     cantFail(Result.add(
161b4243bb6SOwen Pan         tooling::Replacement(SourceMgr, Location, Length, Formatted)));
162b4243bb6SOwen Pan   }
16389aad1e6SOwen Pan 
16489aad1e6SOwen Pan   return {Result, 0};
16589aad1e6SOwen Pan }
16689aad1e6SOwen Pan 
checkSeparator(const StringRef IntegerLiteral,int DigitsPerGroup) const16789aad1e6SOwen Pan bool IntegerLiteralSeparatorFixer::checkSeparator(
16889aad1e6SOwen Pan     const StringRef IntegerLiteral, int DigitsPerGroup) const {
16989aad1e6SOwen Pan   assert(DigitsPerGroup > 0);
17089aad1e6SOwen Pan 
17189aad1e6SOwen Pan   int I = 0;
17289aad1e6SOwen Pan   for (auto C : llvm::reverse(IntegerLiteral)) {
17389aad1e6SOwen Pan     if (C == Separator) {
17489aad1e6SOwen Pan       if (I < DigitsPerGroup)
17589aad1e6SOwen Pan         return false;
17689aad1e6SOwen Pan       I = 0;
17789aad1e6SOwen Pan     } else {
17889aad1e6SOwen Pan       if (I == DigitsPerGroup)
17989aad1e6SOwen Pan         return false;
1805b5c49adSOwen Pan       ++I;
18189aad1e6SOwen Pan     }
18289aad1e6SOwen Pan   }
18389aad1e6SOwen Pan 
18489aad1e6SOwen Pan   return true;
18589aad1e6SOwen Pan }
18689aad1e6SOwen Pan 
format(const StringRef IntegerLiteral,int DigitsPerGroup,int DigitCount,bool RemoveSeparator) const18789aad1e6SOwen Pan std::string IntegerLiteralSeparatorFixer::format(const StringRef IntegerLiteral,
188253985d5SOwen Pan                                                  int DigitsPerGroup,
189253985d5SOwen Pan                                                  int DigitCount,
190253985d5SOwen Pan                                                  bool RemoveSeparator) const {
19189aad1e6SOwen Pan   assert(DigitsPerGroup != 0);
19289aad1e6SOwen Pan 
19389aad1e6SOwen Pan   std::string Formatted;
19489aad1e6SOwen Pan 
195253985d5SOwen Pan   if (RemoveSeparator) {
19689aad1e6SOwen Pan     for (auto C : IntegerLiteral)
19789aad1e6SOwen Pan       if (C != Separator)
19889aad1e6SOwen Pan         Formatted.push_back(C);
19989aad1e6SOwen Pan     return Formatted;
20089aad1e6SOwen Pan   }
20189aad1e6SOwen Pan 
20289aad1e6SOwen Pan   int Remainder = DigitCount % DigitsPerGroup;
20389aad1e6SOwen Pan 
20489aad1e6SOwen Pan   int I = 0;
20589aad1e6SOwen Pan   for (auto C : IntegerLiteral) {
20689aad1e6SOwen Pan     if (C == Separator)
20789aad1e6SOwen Pan       continue;
20889aad1e6SOwen Pan     if (I == (Remainder > 0 ? Remainder : DigitsPerGroup)) {
20989aad1e6SOwen Pan       Formatted.push_back(Separator);
21089aad1e6SOwen Pan       I = 0;
21189aad1e6SOwen Pan       Remainder = 0;
21289aad1e6SOwen Pan     }
21389aad1e6SOwen Pan     Formatted.push_back(C);
21489aad1e6SOwen Pan     ++I;
21589aad1e6SOwen Pan   }
21689aad1e6SOwen Pan 
21789aad1e6SOwen Pan   return Formatted;
21889aad1e6SOwen Pan }
21989aad1e6SOwen Pan 
22089aad1e6SOwen Pan } // namespace format
22189aad1e6SOwen Pan } // namespace clang
222