brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.3 KiB · a283884 Raw
242 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8///9/// \file10/// This file implements IntegerLiteralSeparatorFixer that fixes C++ integer11/// literal separators.12///13//===----------------------------------------------------------------------===//14 15#include "IntegerLiteralSeparatorFixer.h"16 17namespace clang {18namespace format {19 20enum class Base { Binary, Decimal, Hex, Other };21 22static Base getBase(StringRef IntegerLiteral) {23  assert(IntegerLiteral.size() > 1);24 25  if (IntegerLiteral[0] > '0') {26    assert(IntegerLiteral[0] <= '9');27    return Base::Decimal;28  }29 30  assert(IntegerLiteral[0] == '0');31 32  switch (IntegerLiteral[1]) {33  case 'b':34  case 'B':35    return Base::Binary;36  case 'x':37  case 'X':38    return Base::Hex;39  default:40    return Base::Other;41  }42}43 44std::pair<tooling::Replacements, unsigned>45IntegerLiteralSeparatorFixer::process(const Environment &Env,46                                      const FormatStyle &Style) {47  switch (Style.Language) {48  case FormatStyle::LK_CSharp:49  case FormatStyle::LK_Java:50  case FormatStyle::LK_JavaScript:51    Separator = '_';52    break;53  case FormatStyle::LK_Cpp:54  case FormatStyle::LK_ObjC:55    if (Style.Standard >= FormatStyle::LS_Cpp14) {56      Separator = '\'';57      break;58    }59    [[fallthrough]];60  default:61    return {};62  }63 64  const auto &Option = Style.IntegerLiteralSeparator;65  const auto Binary = Option.Binary;66  const auto Decimal = Option.Decimal;67  const auto Hex = Option.Hex;68  const bool SkipBinary = Binary == 0;69  const bool SkipDecimal = Decimal == 0;70  const bool SkipHex = Hex == 0;71 72  if (SkipBinary && SkipDecimal && SkipHex)73    return {};74 75  auto CalcMinAndMax = [](int DigitsPerGroup, int MinDigitsInsert,76                          int MaxDigitsRemove) {77    MinDigitsInsert = std::max(MinDigitsInsert, DigitsPerGroup + 1);78    if (MinDigitsInsert < 1)79      MaxDigitsRemove = 0;80    else if (MaxDigitsRemove < 1 || MaxDigitsRemove >= MinDigitsInsert)81      MaxDigitsRemove = MinDigitsInsert - 1;82    return std::pair(MinDigitsInsert, MaxDigitsRemove);83  };84 85  const auto [BinaryMinDigitsInsert, BinaryMaxDigitsRemove] = CalcMinAndMax(86      Binary, Option.BinaryMinDigitsInsert, Option.BinaryMaxDigitsRemove);87  const auto [DecimalMinDigitsInsert, DecimalMaxDigitsRemove] = CalcMinAndMax(88      Decimal, Option.DecimalMinDigitsInsert, Option.DecimalMaxDigitsRemove);89  const auto [HexMinDigitsInsert, HexMaxDigitsRemove] =90      CalcMinAndMax(Hex, Option.HexMinDigitsInsert, Option.HexMaxDigitsRemove);91 92  const auto &SourceMgr = Env.getSourceManager();93  AffectedRangeManager AffectedRangeMgr(SourceMgr, Env.getCharRanges());94 95  const auto ID = Env.getFileID();96  const auto LangOpts = getFormattingLangOpts(Style);97  Lexer Lex(ID, SourceMgr.getBufferOrFake(ID), SourceMgr, LangOpts);98  Lex.SetCommentRetentionState(true);99 100  Token Tok;101  tooling::Replacements Result;102 103  for (bool Skip = false; !Lex.LexFromRawLexer(Tok);) {104    auto Length = Tok.getLength();105    if (Length < 2)106      continue;107    auto Location = Tok.getLocation();108    auto Text = StringRef(SourceMgr.getCharacterData(Location), Length);109    if (Tok.is(tok::comment)) {110      if (isClangFormatOff(Text))111        Skip = true;112      else if (isClangFormatOn(Text))113        Skip = false;114      continue;115    }116    if (Skip || Tok.isNot(tok::numeric_constant) || Text[0] == '.' ||117        !AffectedRangeMgr.affectsCharSourceRange(118            CharSourceRange::getCharRange(Location, Tok.getEndLoc()))) {119      continue;120    }121    const auto B = getBase(Text);122    const bool IsBase2 = B == Base::Binary;123    const bool IsBase10 = B == Base::Decimal;124    const bool IsBase16 = B == Base::Hex;125    if ((IsBase2 && SkipBinary) || (IsBase10 && SkipDecimal) ||126        (IsBase16 && SkipHex) || B == Base::Other) {127      continue;128    }129    if (Style.isCpp()) {130      // Hex alpha digits a-f/A-F must be at the end of the string literal.131      static constexpr StringRef Suffixes("_himnsuyd");132      if (const auto Pos =133              Text.find_first_of(IsBase16 ? Suffixes.drop_back() : Suffixes);134          Pos != StringRef::npos) {135        Text = Text.substr(0, Pos);136        Length = Pos;137      }138    }139    if ((IsBase10 && Text.find_last_of(".eEfFdDmM") != StringRef::npos) ||140        (IsBase16 && Text.find_last_of(".pP") != StringRef::npos)) {141      continue;142    }143    const auto Start = Text[0] == '0' ? 2 : 0;144    auto End = Text.find_first_of("uUlLzZn", Start);145    if (End == StringRef::npos)146      End = Length;147    if (Start > 0 || End < Length) {148      Length = End - Start;149      Text = Text.substr(Start, Length);150    }151    auto DigitsPerGroup = Decimal;152    auto MinDigitsInsert = DecimalMinDigitsInsert;153    auto MaxDigitsRemove = DecimalMaxDigitsRemove;154    if (IsBase2) {155      DigitsPerGroup = Binary;156      MinDigitsInsert = BinaryMinDigitsInsert;157      MaxDigitsRemove = BinaryMaxDigitsRemove;158    } else if (IsBase16) {159      DigitsPerGroup = Hex;160      MinDigitsInsert = HexMinDigitsInsert;161      MaxDigitsRemove = HexMaxDigitsRemove;162    }163    const auto SeparatorCount = Text.count(Separator);164    const int DigitCount = Length - SeparatorCount;165    if (DigitCount > MaxDigitsRemove && DigitCount < MinDigitsInsert)166      continue;167    const bool RemoveSeparator =168        DigitsPerGroup < 0 || DigitCount <= MaxDigitsRemove;169    if (RemoveSeparator && SeparatorCount == 0)170      continue;171    if (!RemoveSeparator && SeparatorCount > 0 &&172        checkSeparator(Text, DigitsPerGroup)) {173      continue;174    }175    const auto &Formatted =176        format(Text, DigitsPerGroup, DigitCount, RemoveSeparator);177    assert(Formatted != Text);178    if (Start > 0)179      Location = Location.getLocWithOffset(Start);180    cantFail(Result.add(181        tooling::Replacement(SourceMgr, Location, Length, Formatted)));182  }183 184  return {Result, 0};185}186 187bool IntegerLiteralSeparatorFixer::checkSeparator(StringRef IntegerLiteral,188                                                  int DigitsPerGroup) const {189  assert(DigitsPerGroup > 0);190 191  int I = 0;192  for (auto C : llvm::reverse(IntegerLiteral)) {193    if (C == Separator) {194      if (I < DigitsPerGroup)195        return false;196      I = 0;197    } else {198      if (I == DigitsPerGroup)199        return false;200      ++I;201    }202  }203 204  return true;205}206 207std::string IntegerLiteralSeparatorFixer::format(StringRef IntegerLiteral,208                                                 int DigitsPerGroup,209                                                 int DigitCount,210                                                 bool RemoveSeparator) const {211  assert(DigitsPerGroup != 0);212 213  std::string Formatted;214 215  if (RemoveSeparator) {216    for (auto C : IntegerLiteral)217      if (C != Separator)218        Formatted.push_back(C);219    return Formatted;220  }221 222  int Remainder = DigitCount % DigitsPerGroup;223 224  int I = 0;225  for (auto C : IntegerLiteral) {226    if (C == Separator)227      continue;228    if (I == (Remainder > 0 ? Remainder : DigitsPerGroup)) {229      Formatted.push_back(Separator);230      I = 0;231      Remainder = 0;232    }233    Formatted.push_back(C);234    ++I;235  }236 237  return Formatted;238}239 240} // namespace format241} // namespace clang242