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