Path: blob/main/contrib/llvm-project/clang/lib/Format/IntegerLiteralSeparatorFixer.cpp
35234 views
//===--- IntegerLiteralSeparatorFixer.cpp -----------------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7///8/// \file9/// This file implements IntegerLiteralSeparatorFixer that fixes C++ integer10/// literal separators.11///12//===----------------------------------------------------------------------===//1314#include "IntegerLiteralSeparatorFixer.h"1516namespace clang {17namespace format {1819enum class Base { Binary, Decimal, Hex, Other };2021static Base getBase(const StringRef IntegerLiteral) {22assert(IntegerLiteral.size() > 1);2324if (IntegerLiteral[0] > '0') {25assert(IntegerLiteral[0] <= '9');26return Base::Decimal;27}2829assert(IntegerLiteral[0] == '0');3031switch (IntegerLiteral[1]) {32case 'b':33case 'B':34return Base::Binary;35case 'x':36case 'X':37return Base::Hex;38default:39return Base::Other;40}41}4243std::pair<tooling::Replacements, unsigned>44IntegerLiteralSeparatorFixer::process(const Environment &Env,45const FormatStyle &Style) {46switch (Style.Language) {47case FormatStyle::LK_Cpp:48case FormatStyle::LK_ObjC:49Separator = '\'';50break;51case FormatStyle::LK_CSharp:52case FormatStyle::LK_Java:53case FormatStyle::LK_JavaScript:54Separator = '_';55break;56default:57return {};58}5960const auto &Option = Style.IntegerLiteralSeparator;61const auto Binary = Option.Binary;62const auto Decimal = Option.Decimal;63const auto Hex = Option.Hex;64const bool SkipBinary = Binary == 0;65const bool SkipDecimal = Decimal == 0;66const bool SkipHex = Hex == 0;6768if (SkipBinary && SkipDecimal && SkipHex)69return {};7071const auto BinaryMinDigits =72std::max((int)Option.BinaryMinDigits, Binary + 1);73const auto DecimalMinDigits =74std::max((int)Option.DecimalMinDigits, Decimal + 1);75const auto HexMinDigits = std::max((int)Option.HexMinDigits, Hex + 1);7677const auto &SourceMgr = Env.getSourceManager();78AffectedRangeManager AffectedRangeMgr(SourceMgr, Env.getCharRanges());7980const auto ID = Env.getFileID();81const auto LangOpts = getFormattingLangOpts(Style);82Lexer Lex(ID, SourceMgr.getBufferOrFake(ID), SourceMgr, LangOpts);83Lex.SetCommentRetentionState(true);8485Token Tok;86tooling::Replacements Result;8788for (bool Skip = false; !Lex.LexFromRawLexer(Tok);) {89auto Length = Tok.getLength();90if (Length < 2)91continue;92auto Location = Tok.getLocation();93auto Text = StringRef(SourceMgr.getCharacterData(Location), Length);94if (Tok.is(tok::comment)) {95if (isClangFormatOff(Text))96Skip = true;97else if (isClangFormatOn(Text))98Skip = false;99continue;100}101if (Skip || Tok.isNot(tok::numeric_constant) || Text[0] == '.' ||102!AffectedRangeMgr.affectsCharSourceRange(103CharSourceRange::getCharRange(Location, Tok.getEndLoc()))) {104continue;105}106const auto B = getBase(Text);107const bool IsBase2 = B == Base::Binary;108const bool IsBase10 = B == Base::Decimal;109const bool IsBase16 = B == Base::Hex;110if ((IsBase2 && SkipBinary) || (IsBase10 && SkipDecimal) ||111(IsBase16 && SkipHex) || B == Base::Other) {112continue;113}114if (Style.isCpp()) {115// Hex alpha digits a-f/A-F must be at the end of the string literal.116StringRef Suffixes = "_himnsuyd";117if (const auto Pos =118Text.find_first_of(IsBase16 ? Suffixes.drop_back() : Suffixes);119Pos != StringRef::npos) {120Text = Text.substr(0, Pos);121Length = Pos;122}123}124if ((IsBase10 && Text.find_last_of(".eEfFdDmM") != StringRef::npos) ||125(IsBase16 && Text.find_last_of(".pP") != StringRef::npos)) {126continue;127}128const auto Start = Text[0] == '0' ? 2 : 0;129auto End = Text.find_first_of("uUlLzZn", Start);130if (End == StringRef::npos)131End = Length;132if (Start > 0 || End < Length) {133Length = End - Start;134Text = Text.substr(Start, Length);135}136auto DigitsPerGroup = Decimal;137auto MinDigits = DecimalMinDigits;138if (IsBase2) {139DigitsPerGroup = Binary;140MinDigits = BinaryMinDigits;141} else if (IsBase16) {142DigitsPerGroup = Hex;143MinDigits = HexMinDigits;144}145const auto SeparatorCount = Text.count(Separator);146const int DigitCount = Length - SeparatorCount;147const bool RemoveSeparator = DigitsPerGroup < 0 || DigitCount < MinDigits;148if (RemoveSeparator && SeparatorCount == 0)149continue;150if (!RemoveSeparator && SeparatorCount > 0 &&151checkSeparator(Text, DigitsPerGroup)) {152continue;153}154const auto &Formatted =155format(Text, DigitsPerGroup, DigitCount, RemoveSeparator);156assert(Formatted != Text);157if (Start > 0)158Location = Location.getLocWithOffset(Start);159cantFail(Result.add(160tooling::Replacement(SourceMgr, Location, Length, Formatted)));161}162163return {Result, 0};164}165166bool IntegerLiteralSeparatorFixer::checkSeparator(167const StringRef IntegerLiteral, int DigitsPerGroup) const {168assert(DigitsPerGroup > 0);169170int I = 0;171for (auto C : llvm::reverse(IntegerLiteral)) {172if (C == Separator) {173if (I < DigitsPerGroup)174return false;175I = 0;176} else {177if (I == DigitsPerGroup)178return false;179++I;180}181}182183return true;184}185186std::string IntegerLiteralSeparatorFixer::format(const StringRef IntegerLiteral,187int DigitsPerGroup,188int DigitCount,189bool RemoveSeparator) const {190assert(DigitsPerGroup != 0);191192std::string Formatted;193194if (RemoveSeparator) {195for (auto C : IntegerLiteral)196if (C != Separator)197Formatted.push_back(C);198return Formatted;199}200201int Remainder = DigitCount % DigitsPerGroup;202203int I = 0;204for (auto C : IntegerLiteral) {205if (C == Separator)206continue;207if (I == (Remainder > 0 ? Remainder : DigitsPerGroup)) {208Formatted.push_back(Separator);209I = 0;210Remainder = 0;211}212Formatted.push_back(C);213++I;214}215216return Formatted;217}218219} // namespace format220} // namespace clang221222223