178 lines · cpp
1//===--- NumericLiteralCaseFixer.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 NumericLiteralCaseFixer that standardizes character11/// case within numeric literals.12///13//===----------------------------------------------------------------------===//14 15#include "NumericLiteralCaseFixer.h"16#include "NumericLiteralInfo.h"17 18#include "llvm/ADT/StringExtras.h"19 20#include <algorithm>21 22namespace clang {23namespace format {24 25static bool isNumericLiteralCaseFixerNeeded(const FormatStyle &Style) {26 // Check if language is supported.27 switch (Style.Language) {28 case FormatStyle::LK_C:29 case FormatStyle::LK_Cpp:30 case FormatStyle::LK_ObjC:31 case FormatStyle::LK_CSharp:32 case FormatStyle::LK_Java:33 case FormatStyle::LK_JavaScript:34 break;35 default:36 return false;37 }38 39 // Check if style options are set.40 const auto &Option = Style.NumericLiteralCase;41 const auto Leave = FormatStyle::NLCS_Leave;42 return Option.Prefix != Leave || Option.HexDigit != Leave ||43 Option.ExponentLetter != Leave || Option.Suffix != Leave;44}45 46static std::string47transformComponent(StringRef Component,48 FormatStyle::NumericLiteralComponentStyle ConfigValue) {49 switch (ConfigValue) {50 case FormatStyle::NLCS_Upper:51 return Component.upper();52 case FormatStyle::NLCS_Lower:53 return Component.lower();54 default:55 // Covers FormatStyle::NLCS_Leave.56 return Component.str();57 }58}59 60/// Test if Suffix matches a C++ literal reserved by the library.61/// Matches against all suffixes reserved in the C++23 standard.62static bool matchesReservedSuffix(StringRef Suffix) {63 static constexpr std::array<StringRef, 11> SortedReservedSuffixes = {64 "d", "h", "i", "if", "il", "min", "ms", "ns", "s", "us", "y",65 };66 67 // This can be static_assert when we have access to constexpr is_sorted in68 // C++ 20.69 assert(llvm::is_sorted(SortedReservedSuffixes) &&70 "Must be sorted as precondition for lower_bound().");71 72 auto entry = llvm::lower_bound(SortedReservedSuffixes, Suffix);73 if (entry == SortedReservedSuffixes.cend())74 return false;75 return *entry == Suffix;76}77 78static std::string format(StringRef NumericLiteral, const FormatStyle &Style) {79 const char Separator = Style.isCpp() ? '\'' : '_';80 const NumericLiteralInfo Info(NumericLiteral, Separator);81 const bool HasBaseLetter = Info.BaseLetterPos != StringRef::npos;82 const bool HasExponent = Info.ExponentLetterPos != StringRef::npos;83 const bool HasSuffix = Info.SuffixPos != StringRef::npos;84 85 std::string Formatted;86 87 if (HasBaseLetter) {88 Formatted +=89 transformComponent(NumericLiteral.take_front(1 + Info.BaseLetterPos),90 Style.NumericLiteralCase.Prefix);91 }92 // Reformat this slice as HexDigit whether or not the digit has hexadecimal93 // characters because binary/decimal/octal digits are unchanged.94 Formatted += transformComponent(95 NumericLiteral.slice(HasBaseLetter ? 1 + Info.BaseLetterPos : 0,96 HasExponent ? Info.ExponentLetterPos97 : HasSuffix ? Info.SuffixPos98 : NumericLiteral.size()),99 Style.NumericLiteralCase.HexDigit);100 101 if (HasExponent) {102 Formatted += transformComponent(103 NumericLiteral.slice(Info.ExponentLetterPos,104 HasSuffix ? Info.SuffixPos105 : NumericLiteral.size()),106 Style.NumericLiteralCase.ExponentLetter);107 }108 109 if (HasSuffix) {110 StringRef Suffix = NumericLiteral.drop_front(Info.SuffixPos);111 if (matchesReservedSuffix(Suffix) || Suffix.front() == '_') {112 // In C++, it is idiomatic, but NOT standardized to define user-defined113 // literals with a leading '_'. Omit user defined literals and standard114 // reserved suffixes from transformation.115 Formatted += Suffix.str();116 } else {117 Formatted += transformComponent(Suffix, Style.NumericLiteralCase.Suffix);118 }119 }120 121 return Formatted;122}123 124std::pair<tooling::Replacements, unsigned>125NumericLiteralCaseFixer::process(const Environment &Env,126 const FormatStyle &Style) {127 if (!isNumericLiteralCaseFixerNeeded(Style))128 return {};129 130 const auto &SourceMgr = Env.getSourceManager();131 AffectedRangeManager AffectedRangeMgr(SourceMgr, Env.getCharRanges());132 133 const auto ID = Env.getFileID();134 const auto LangOpts = getFormattingLangOpts(Style);135 Lexer Lex(ID, SourceMgr.getBufferOrFake(ID), SourceMgr, LangOpts);136 Lex.SetCommentRetentionState(true);137 138 Token Tok;139 tooling::Replacements Result;140 141 for (bool Skip = false; !Lex.LexFromRawLexer(Tok);) {142 // Skip tokens that are too small to contain a formattable literal.143 // Size=2 is the smallest possible literal that could contain formattable144 // components, for example "1u".145 auto Length = Tok.getLength();146 if (Length < 2)147 continue;148 149 // Service clang-format off/on comments.150 auto Location = Tok.getLocation();151 auto Text = StringRef(SourceMgr.getCharacterData(Location), Length);152 if (Tok.is(tok::comment)) {153 if (isClangFormatOff(Text))154 Skip = true;155 else if (isClangFormatOn(Text))156 Skip = false;157 continue;158 }159 160 if (Skip || Tok.isNot(tok::numeric_constant) ||161 !AffectedRangeMgr.affectsCharSourceRange(162 CharSourceRange::getCharRange(Location, Tok.getEndLoc()))) {163 continue;164 }165 166 const auto Formatted = format(Text, Style);167 if (Formatted != Text) {168 cantFail(Result.add(169 tooling::Replacement(SourceMgr, Location, Length, Formatted)));170 }171 }172 173 return {Result, 0};174}175 176} // namespace format177} // namespace clang178