/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Format/IntegerLiteralSeparatorFixer.cpp
Line | Count | Source (jump to first uncovered line) |
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-exception |
6 | | // |
7 | | //===----------------------------------------------------------------------===// |
8 | | /// |
9 | | /// \file |
10 | | /// This file implements IntegerLiteralSeparatorFixer that fixes C++ integer |
11 | | /// literal separators. |
12 | | /// |
13 | | //===----------------------------------------------------------------------===// |
14 | | |
15 | | #include "IntegerLiteralSeparatorFixer.h" |
16 | | |
17 | | namespace clang { |
18 | | namespace format { |
19 | | |
20 | | enum class Base { Binary, Decimal, Hex, Other }; |
21 | | |
22 | 222 | static Base getBase(const StringRef IntegerLiteral) { |
23 | 222 | assert(IntegerLiteral.size() > 1); |
24 | | |
25 | 222 | if (IntegerLiteral[0] > '0') { |
26 | 117 | assert(IntegerLiteral[0] <= '9'); |
27 | 117 | return Base::Decimal; |
28 | 117 | } |
29 | | |
30 | 105 | assert(IntegerLiteral[0] == '0'); |
31 | | |
32 | 105 | switch (IntegerLiteral[1]) { |
33 | 23 | case 'b': |
34 | 37 | case 'B': |
35 | 37 | return Base::Binary; |
36 | 37 | case 'x': |
37 | 52 | case 'X': |
38 | 52 | return Base::Hex; |
39 | 16 | default: |
40 | 16 | return Base::Other; |
41 | 105 | } |
42 | 105 | } |
43 | | |
44 | | std::pair<tooling::Replacements, unsigned> |
45 | | IntegerLiteralSeparatorFixer::process(const Environment &Env, |
46 | 30.4k | const FormatStyle &Style) { |
47 | 30.4k | switch (Style.Language) { |
48 | 16.0k | case FormatStyle::LK_Cpp: |
49 | 26.1k | case FormatStyle::LK_ObjC: |
50 | 26.1k | Separator = '\''; |
51 | 26.1k | break; |
52 | 610 | case FormatStyle::LK_CSharp: |
53 | 953 | case FormatStyle::LK_Java: |
54 | 2.48k | case FormatStyle::LK_JavaScript: |
55 | 2.48k | Separator = '_'; |
56 | 2.48k | break; |
57 | 1.76k | default: |
58 | 1.76k | return {}; |
59 | 30.4k | } |
60 | | |
61 | 28.6k | const auto &Option = Style.IntegerLiteralSeparator; |
62 | 28.6k | const auto Binary = Option.Binary; |
63 | 28.6k | const auto Decimal = Option.Decimal; |
64 | 28.6k | const auto Hex = Option.Hex; |
65 | 28.6k | const bool SkipBinary = Binary == 0; |
66 | 28.6k | const bool SkipDecimal = Decimal == 0; |
67 | 28.6k | const bool SkipHex = Hex == 0; |
68 | | |
69 | 28.6k | if (SkipBinary && SkipDecimal28.5k && SkipHex28.5k ) |
70 | 28.5k | return {}; |
71 | | |
72 | 135 | const auto BinaryMinDigits = |
73 | 135 | std::max((int)Option.BinaryMinDigits, Binary + 1); |
74 | 135 | const auto DecimalMinDigits = |
75 | 135 | std::max((int)Option.DecimalMinDigits, Decimal + 1); |
76 | 135 | const auto HexMinDigits = std::max((int)Option.HexMinDigits, Hex + 1); |
77 | | |
78 | 135 | const auto &SourceMgr = Env.getSourceManager(); |
79 | 135 | AffectedRangeManager AffectedRangeMgr(SourceMgr, Env.getCharRanges()); |
80 | | |
81 | 135 | const auto ID = Env.getFileID(); |
82 | 135 | const auto LangOpts = getFormattingLangOpts(Style); |
83 | 135 | Lexer Lex(ID, SourceMgr.getBufferOrFake(ID), SourceMgr, LangOpts); |
84 | 135 | Lex.SetCommentRetentionState(true); |
85 | | |
86 | 135 | Token Tok; |
87 | 135 | tooling::Replacements Result; |
88 | | |
89 | 1.08k | for (bool Skip = false; !Lex.LexFromRawLexer(Tok);) { |
90 | 954 | auto Length = Tok.getLength(); |
91 | 954 | if (Length < 2) |
92 | 593 | continue; |
93 | 361 | auto Location = Tok.getLocation(); |
94 | 361 | auto Text = StringRef(SourceMgr.getCharacterData(Location), Length); |
95 | 361 | if (Tok.is(tok::comment)) { |
96 | 28 | if (isClangFormatOff(Text)) |
97 | 14 | Skip = true; |
98 | 14 | else if (isClangFormatOn(Text)) |
99 | 14 | Skip = false; |
100 | 28 | continue; |
101 | 28 | } |
102 | 333 | if (Skip || Tok.isNot(tok::numeric_constant)319 || Text[0] == '.'237 || |
103 | 333 | !AffectedRangeMgr.affectsCharSourceRange( |
104 | 232 | CharSourceRange::getCharRange(Location, Tok.getEndLoc()))) { |
105 | 111 | continue; |
106 | 111 | } |
107 | 222 | const auto B = getBase(Text); |
108 | 222 | const bool IsBase2 = B == Base::Binary; |
109 | 222 | const bool IsBase10 = B == Base::Decimal; |
110 | 222 | const bool IsBase16 = B == Base::Hex; |
111 | 222 | if ((IsBase2 && SkipBinary37 ) || (220 IsBase10220 && SkipDecimal117 ) || |
112 | 222 | (215 IsBase16215 && SkipHex52 ) || B == Base::Other210 ) { |
113 | 28 | continue; |
114 | 28 | } |
115 | 194 | if (Style.isCpp()) { |
116 | | // Hex alpha digits a-f/A-F must be at the end of the string literal. |
117 | 117 | StringRef Suffixes = "_himnsuyd"; |
118 | 117 | if (const auto Pos = |
119 | 117 | Text.find_first_of(IsBase16 ? Suffixes.drop_back()29 : Suffixes88 ); |
120 | 117 | Pos != StringRef::npos) { |
121 | 51 | Text = Text.substr(0, Pos); |
122 | 51 | Length = Pos; |
123 | 51 | } |
124 | 117 | } |
125 | 194 | if ((IsBase10 && Text.find_last_of(".eEfFdDmM") != StringRef::npos112 ) || |
126 | 194 | (154 IsBase16154 && Text.find_last_of(".pP") != StringRef::npos47 )) { |
127 | 48 | continue; |
128 | 48 | } |
129 | 146 | const auto Start = Text[0] == '0' ? 274 : 072 ; |
130 | 146 | auto End = Text.find_first_of("uUlLzZn", Start); |
131 | 146 | if (End == StringRef::npos) |
132 | 132 | End = Length; |
133 | 146 | if (Start > 0 || End < Length72 ) { |
134 | 83 | Length = End - Start; |
135 | 83 | Text = Text.substr(Start, Length); |
136 | 83 | } |
137 | 146 | auto DigitsPerGroup = Decimal; |
138 | 146 | auto MinDigits = DecimalMinDigits; |
139 | 146 | if (IsBase2) { |
140 | 35 | DigitsPerGroup = Binary; |
141 | 35 | MinDigits = BinaryMinDigits; |
142 | 111 | } else if (IsBase16) { |
143 | 39 | DigitsPerGroup = Hex; |
144 | 39 | MinDigits = HexMinDigits; |
145 | 39 | } |
146 | 146 | const auto SeparatorCount = Text.count(Separator); |
147 | 146 | const int DigitCount = Length - SeparatorCount; |
148 | 146 | const bool RemoveSeparator = DigitsPerGroup < 0 || DigitCount < MinDigits119 ; |
149 | 146 | if (RemoveSeparator && SeparatorCount == 039 ) |
150 | 16 | continue; |
151 | 130 | if (!RemoveSeparator && SeparatorCount > 0107 && |
152 | 130 | checkSeparator(Text, DigitsPerGroup)76 ) { |
153 | 41 | continue; |
154 | 41 | } |
155 | 89 | const auto &Formatted = |
156 | 89 | format(Text, DigitsPerGroup, DigitCount, RemoveSeparator); |
157 | 89 | assert(Formatted != Text); |
158 | 89 | if (Start > 0) |
159 | 44 | Location = Location.getLocWithOffset(Start); |
160 | 89 | cantFail(Result.add( |
161 | 89 | tooling::Replacement(SourceMgr, Location, Length, Formatted))); |
162 | 89 | } |
163 | | |
164 | 135 | return {Result, 0}; |
165 | 135 | } |
166 | | |
167 | | bool IntegerLiteralSeparatorFixer::checkSeparator( |
168 | 76 | const StringRef IntegerLiteral, int DigitsPerGroup) const { |
169 | 76 | assert(DigitsPerGroup > 0); |
170 | | |
171 | 76 | int I = 0; |
172 | 630 | for (auto C : llvm::reverse(IntegerLiteral)) { |
173 | 630 | if (C == Separator) { |
174 | 157 | if (I < DigitsPerGroup) |
175 | 31 | return false; |
176 | 126 | I = 0; |
177 | 473 | } else { |
178 | 473 | if (I == DigitsPerGroup) |
179 | 4 | return false; |
180 | 469 | ++I; |
181 | 469 | } |
182 | 630 | } |
183 | | |
184 | 41 | return true; |
185 | 76 | } |
186 | | |
187 | | std::string IntegerLiteralSeparatorFixer::format(const StringRef IntegerLiteral, |
188 | | int DigitsPerGroup, |
189 | | int DigitCount, |
190 | 89 | bool RemoveSeparator) const { |
191 | 89 | assert(DigitsPerGroup != 0); |
192 | | |
193 | 89 | std::string Formatted; |
194 | | |
195 | 89 | if (RemoveSeparator) { |
196 | 23 | for (auto C : IntegerLiteral) |
197 | 346 | if (C != Separator) |
198 | 276 | Formatted.push_back(C); |
199 | 23 | return Formatted; |
200 | 23 | } |
201 | | |
202 | 66 | int Remainder = DigitCount % DigitsPerGroup; |
203 | | |
204 | 66 | int I = 0; |
205 | 632 | for (auto C : IntegerLiteral) { |
206 | 632 | if (C == Separator) |
207 | 90 | continue; |
208 | 542 | if (I == (Remainder > 0 ? Remainder110 : DigitsPerGroup432 )) { |
209 | 164 | Formatted.push_back(Separator); |
210 | 164 | I = 0; |
211 | 164 | Remainder = 0; |
212 | 164 | } |
213 | 542 | Formatted.push_back(C); |
214 | 542 | ++I; |
215 | 542 | } |
216 | | |
217 | 66 | return Formatted; |
218 | 89 | } |
219 | | |
220 | | } // namespace format |
221 | | } // namespace clang |