/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 | 212 | static Base getBase(const StringRef IntegerLiteral) { |
23 | 212 | assert(IntegerLiteral.size() > 1); |
24 | | |
25 | 212 | if (IntegerLiteral[0] > '0') { |
26 | 108 | assert(IntegerLiteral[0] <= '9'); |
27 | 108 | return Base::Decimal; |
28 | 108 | } |
29 | | |
30 | 104 | assert(IntegerLiteral[0] == '0'); |
31 | | |
32 | 104 | switch (IntegerLiteral[1]) { |
33 | 23 | case 'b': |
34 | 39 | case 'B': |
35 | 39 | return Base::Binary; |
36 | 36 | case 'x': |
37 | 54 | case 'X': |
38 | 54 | return Base::Hex; |
39 | 11 | default: |
40 | 11 | return Base::Other; |
41 | 104 | } |
42 | 104 | } |
43 | | |
44 | | std::pair<tooling::Replacements, unsigned> |
45 | | IntegerLiteralSeparatorFixer::process(const Environment &Env, |
46 | 25.0k | const FormatStyle &Style) { |
47 | 25.0k | switch (Style.Language) { |
48 | 14.7k | case FormatStyle::LK_Cpp: |
49 | 21.0k | case FormatStyle::LK_ObjC: |
50 | 21.0k | Separator = '\''; |
51 | 21.0k | break; |
52 | 563 | case FormatStyle::LK_CSharp: |
53 | 862 | case FormatStyle::LK_Java: |
54 | 2.39k | case FormatStyle::LK_JavaScript: |
55 | 2.39k | Separator = '_'; |
56 | 2.39k | break; |
57 | 1.62k | default: |
58 | 1.62k | return {}; |
59 | 25.0k | } |
60 | | |
61 | 23.4k | const auto &Option = Style.IntegerLiteralSeparator; |
62 | 23.4k | const auto Binary = Option.Binary; |
63 | 23.4k | const auto Decimal = Option.Decimal; |
64 | 23.4k | const auto Hex = Option.Hex; |
65 | 23.4k | const bool SkipBinary = Binary == 0; |
66 | 23.4k | const bool SkipDecimal = Decimal == 0; |
67 | 23.4k | const bool SkipHex = Hex == 0; |
68 | | |
69 | 23.4k | if (SkipBinary && SkipDecimal23.3k && SkipHex23.3k ) |
70 | 23.3k | return {}; |
71 | | |
72 | 142 | const auto BinaryMinDigits = |
73 | 142 | std::max((int)Option.BinaryMinDigits, Binary + 1); |
74 | 142 | const auto DecimalMinDigits = |
75 | 142 | std::max((int)Option.DecimalMinDigits, Decimal + 1); |
76 | 142 | const auto HexMinDigits = std::max((int)Option.HexMinDigits, Hex + 1); |
77 | | |
78 | 142 | const auto &SourceMgr = Env.getSourceManager(); |
79 | 142 | AffectedRangeManager AffectedRangeMgr(SourceMgr, Env.getCharRanges()); |
80 | | |
81 | 142 | const auto ID = Env.getFileID(); |
82 | 142 | const auto LangOpts = getFormattingLangOpts(Style); |
83 | 142 | Lexer Lex(ID, SourceMgr.getBufferOrFake(ID), SourceMgr, LangOpts); |
84 | 142 | Lex.SetCommentRetentionState(true); |
85 | | |
86 | 142 | Token Tok; |
87 | 142 | tooling::Replacements Result; |
88 | | |
89 | 1.04k | for (bool Skip = false; !Lex.LexFromRawLexer(Tok);) { |
90 | 904 | auto Length = Tok.getLength(); |
91 | 904 | if (Length < 2) |
92 | 560 | continue; |
93 | 344 | auto Location = Tok.getLocation(); |
94 | 344 | auto Text = StringRef(SourceMgr.getCharacterData(Location), Length); |
95 | 344 | if (Tok.is(tok::comment)) { |
96 | 30 | if (isClangFormatOff(Text)) |
97 | 15 | Skip = true; |
98 | 15 | else if (isClangFormatOn(Text)) |
99 | 15 | Skip = false; |
100 | 30 | continue; |
101 | 30 | } |
102 | 314 | if (Skip || Tok.isNot(tok::numeric_constant)299 || Text[0] == '.'227 || |
103 | 314 | !AffectedRangeMgr.affectsCharSourceRange( |
104 | 224 | CharSourceRange::getCharRange(Location, Tok.getEndLoc()))) { |
105 | 102 | continue; |
106 | 102 | } |
107 | 212 | const auto B = getBase(Text); |
108 | 212 | const bool IsBase2 = B == Base::Binary; |
109 | 212 | const bool IsBase10 = B == Base::Decimal; |
110 | 212 | const bool IsBase16 = B == Base::Hex; |
111 | 212 | if ((IsBase2 && SkipBinary39 ) || (208 IsBase10208 && SkipDecimal108 ) || |
112 | 212 | (199 IsBase16199 && SkipHex54 ) || B == Base::Other190 ) { |
113 | 33 | continue; |
114 | 33 | } |
115 | 179 | if (Style.isCpp()) { |
116 | | // Hex alpha digits a-f/A-F must be at the end of the string literal. |
117 | 111 | StringRef Suffixes = "_himnsuyd"; |
118 | 111 | if (const auto Pos = |
119 | 111 | Text.find_first_of(IsBase16 ? Suffixes.drop_back()27 : Suffixes84 ); |
120 | 111 | Pos != StringRef::npos) { |
121 | 51 | Text = Text.substr(0, Pos); |
122 | 51 | Length = Pos; |
123 | 51 | } |
124 | 111 | } |
125 | 179 | if ((IsBase10 && Text.find_last_of(".eEfFdDmM") != StringRef::npos99 ) || |
126 | 179 | (153 IsBase16153 && Text.find_last_of(".pP") != StringRef::npos45 )) { |
127 | 31 | continue; |
128 | 31 | } |
129 | 148 | const auto Start = Text[0] == '0' ? 275 : 073 ; |
130 | 148 | auto End = Text.find_first_of("uUlLzZn", Start); |
131 | 148 | if (End == StringRef::npos) |
132 | 132 | End = Length; |
133 | 148 | if (Start > 0 || End < Length73 ) { |
134 | 85 | Length = End - Start; |
135 | 85 | Text = Text.substr(Start, Length); |
136 | 85 | } |
137 | 148 | auto DigitsPerGroup = Decimal; |
138 | 148 | auto MinDigits = DecimalMinDigits; |
139 | 148 | if (IsBase2) { |
140 | 35 | DigitsPerGroup = Binary; |
141 | 35 | MinDigits = BinaryMinDigits; |
142 | 113 | } else if (IsBase16) { |
143 | 40 | DigitsPerGroup = Hex; |
144 | 40 | MinDigits = HexMinDigits; |
145 | 40 | } |
146 | 148 | const auto SeparatorCount = Text.count(Separator); |
147 | 148 | const int DigitCount = Length - SeparatorCount; |
148 | 148 | const bool RemoveSeparator = DigitsPerGroup < 0 || DigitCount < MinDigits121 ; |
149 | 148 | if (RemoveSeparator && SeparatorCount == 039 ) |
150 | 16 | continue; |
151 | 132 | if (!RemoveSeparator && SeparatorCount > 0109 && |
152 | 132 | checkSeparator(Text, DigitsPerGroup)78 ) { |
153 | 43 | continue; |
154 | 43 | } |
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 | 142 | return {Result, 0}; |
165 | 142 | } |
166 | | |
167 | | bool IntegerLiteralSeparatorFixer::checkSeparator( |
168 | 78 | const StringRef IntegerLiteral, int DigitsPerGroup) const { |
169 | 78 | assert(DigitsPerGroup > 0); |
170 | | |
171 | 78 | int I = 0; |
172 | 671 | for (auto C : llvm::reverse(IntegerLiteral)) { |
173 | 671 | if (C == Separator) { |
174 | 168 | if (I < DigitsPerGroup) |
175 | 31 | return false; |
176 | 137 | I = 0; |
177 | 503 | } else { |
178 | 503 | if (I == DigitsPerGroup) |
179 | 4 | return false; |
180 | 499 | ++I; |
181 | 499 | } |
182 | 671 | } |
183 | | |
184 | 43 | return true; |
185 | 78 | } |
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 |