Coverage Report

Created: 2023-11-11 10:31

/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