Coverage Report

Created: 2022-01-18 06:27

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Format/QualifierAlignmentFixer.cpp
Line
Count
Source (jump to first uncovered line)
1
//===--- LeftRightQualifierAlignmentFixer.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 LeftRightQualifierAlignmentFixer, a TokenAnalyzer that
11
/// enforces either left or right const depending on the style.
12
///
13
//===----------------------------------------------------------------------===//
14
15
#include "QualifierAlignmentFixer.h"
16
#include "FormatToken.h"
17
#include "llvm/Support/Debug.h"
18
#include "llvm/Support/Regex.h"
19
20
#include <algorithm>
21
22
#define DEBUG_TYPE "format-qualifier-alignment-fixer"
23
24
namespace clang {
25
namespace format {
26
27
QualifierAlignmentFixer::QualifierAlignmentFixer(
28
    const Environment &Env, const FormatStyle &Style, StringRef &Code,
29
    ArrayRef<tooling::Range> Ranges, unsigned FirstStartColumn,
30
    unsigned NextStartColumn, unsigned LastStartColumn, StringRef FileName)
31
    : TokenAnalyzer(Env, Style), Code(Code), Ranges(Ranges),
32
      FirstStartColumn(FirstStartColumn), NextStartColumn(NextStartColumn),
33
765
      LastStartColumn(LastStartColumn), FileName(FileName) {
34
765
  std::vector<std::string> LeftOrder;
35
765
  std::vector<std::string> RightOrder;
36
765
  std::vector<tok::TokenKind> ConfiguredQualifierTokens;
37
765
  PrepareLeftRightOrdering(Style.QualifierOrder, LeftOrder, RightOrder,
38
765
                           ConfiguredQualifierTokens);
39
40
  // Handle the left and right Alignment Seperately
41
1.45k
  for (const auto &Qualifier : LeftOrder) {
42
1.45k
    Passes.emplace_back(
43
1.48k
        [&, Qualifier, ConfiguredQualifierTokens](const Environment &Env) {
44
1.48k
          return LeftRightQualifierAlignmentFixer(Env, Style, Qualifier,
45
1.48k
                                                  ConfiguredQualifierTokens,
46
1.48k
                                                  /*RightAlign=*/false)
47
1.48k
              .process();
48
1.48k
        });
49
1.45k
  }
50
765
  for (const auto &Qualifier : RightOrder) {
51
708
    Passes.emplace_back(
52
738
        [&, Qualifier, ConfiguredQualifierTokens](const Environment &Env) {
53
738
          return LeftRightQualifierAlignmentFixer(Env, Style, Qualifier,
54
738
                                                  ConfiguredQualifierTokens,
55
738
                                                  /*RightAlign=*/true)
56
738
              .process();
57
738
        });
58
708
  }
59
765
}
60
61
std::pair<tooling::Replacements, unsigned> QualifierAlignmentFixer::analyze(
62
    TokenAnnotator &Annotator, SmallVectorImpl<AnnotatedLine *> &AnnotatedLines,
63
789
    FormatTokenLexer &Tokens) {
64
789
  auto Env = Environment::make(Code, FileName, Ranges, FirstStartColumn,
65
789
                               NextStartColumn, LastStartColumn);
66
789
  if (!Env)
67
0
    return {};
68
789
  llvm::Optional<std::string> CurrentCode = None;
69
789
  tooling::Replacements Fixes;
70
3.01k
  for (size_t I = 0, E = Passes.size(); I < E; 
++I2.22k
) {
71
2.22k
    std::pair<tooling::Replacements, unsigned> PassFixes = Passes[I](*Env);
72
2.22k
    auto NewCode = applyAllReplacements(
73
2.22k
        CurrentCode ? 
StringRef(*CurrentCode)1.43k
:
Code789
, PassFixes.first);
74
2.22k
    if (NewCode) {
75
2.22k
      Fixes = Fixes.merge(PassFixes.first);
76
2.22k
      if (I + 1 < E) {
77
1.43k
        CurrentCode = std::move(*NewCode);
78
1.43k
        Env = Environment::make(
79
1.43k
            *CurrentCode, FileName,
80
1.43k
            tooling::calculateRangesAfterReplacements(Fixes, Ranges),
81
1.43k
            FirstStartColumn, NextStartColumn, LastStartColumn);
82
1.43k
        if (!Env)
83
0
          return {};
84
1.43k
      }
85
2.22k
    }
86
2.22k
  }
87
88
  // Don't make replacements that replace nothing.
89
789
  tooling::Replacements NonNoOpFixes;
90
91
789
  for (const tooling::Replacement &Fix : Fixes) {
92
499
    StringRef OriginalCode = Code.substr(Fix.getOffset(), Fix.getLength());
93
94
499
    if (!OriginalCode.equals(Fix.getReplacementText())) {
95
402
      auto Err = NonNoOpFixes.add(Fix);
96
402
      if (Err)
97
0
        llvm::errs() << "Error adding replacements : "
98
0
                     << llvm::toString(std::move(Err)) << "\n";
99
402
    }
100
499
  }
101
789
  return {NonNoOpFixes, 0};
102
789
}
103
104
static void replaceToken(const SourceManager &SourceMgr,
105
                         tooling::Replacements &Fixes,
106
790
                         const CharSourceRange &Range, std::string NewText) {
107
790
  auto Replacement = tooling::Replacement(SourceMgr, Range, NewText);
108
790
  auto Err = Fixes.add(Replacement);
109
110
790
  if (Err)
111
0
    llvm::errs() << "Error while rearranging Qualifier : "
112
0
                 << llvm::toString(std::move(Err)) << "\n";
113
790
}
114
115
static void removeToken(const SourceManager &SourceMgr,
116
                        tooling::Replacements &Fixes,
117
106
                        const FormatToken *First) {
118
106
  auto Range = CharSourceRange::getCharRange(First->getStartOfNonWhitespace(),
119
106
                                             First->Tok.getEndLoc());
120
106
  replaceToken(SourceMgr, Fixes, Range, "");
121
106
}
122
123
static void insertQualifierAfter(const SourceManager &SourceMgr,
124
                                 tooling::Replacements &Fixes,
125
                                 const FormatToken *First,
126
72
                                 const std::string &Qualifier) {
127
72
  FormatToken *Next = First->Next;
128
72
  if (!Next)
129
0
    return;
130
72
  auto Range = CharSourceRange::getCharRange(Next->getStartOfNonWhitespace(),
131
72
                                             Next->Tok.getEndLoc());
132
133
72
  std::string NewText = " " + Qualifier + " ";
134
72
  NewText += Next->TokenText;
135
72
  replaceToken(SourceMgr, Fixes, Range, NewText);
136
72
}
137
138
static void insertQualifierBefore(const SourceManager &SourceMgr,
139
                                  tooling::Replacements &Fixes,
140
                                  const FormatToken *First,
141
34
                                  const std::string &Qualifier) {
142
34
  auto Range = CharSourceRange::getCharRange(First->getStartOfNonWhitespace(),
143
34
                                             First->Tok.getEndLoc());
144
145
34
  std::string NewText = " " + Qualifier + " ";
146
34
  NewText += First->TokenText;
147
148
34
  replaceToken(SourceMgr, Fixes, Range, NewText);
149
34
}
150
151
643
static bool endsWithSpace(const std::string &s) {
152
643
  if (s.empty()) {
153
0
    return false;
154
0
  }
155
643
  return isspace(s.back());
156
643
}
157
158
216
static bool startsWithSpace(const std::string &s) {
159
216
  if (s.empty()) {
160
0
    return false;
161
0
  }
162
216
  return isspace(s.front());
163
216
}
164
165
static void rotateTokens(const SourceManager &SourceMgr,
166
                         tooling::Replacements &Fixes, const FormatToken *First,
167
578
                         const FormatToken *Last, bool Left) {
168
578
  auto *End = Last;
169
578
  auto *Begin = First;
170
578
  if (!Left) {
171
216
    End = Last->Next;
172
216
    Begin = First->Next;
173
216
  }
174
175
578
  std::string NewText;
176
  // If we are rotating to the left we move the Last token to the front.
177
578
  if (Left) {
178
362
    NewText += Last->TokenText;
179
362
    NewText += " ";
180
362
  }
181
182
  // Then move through the other tokens.
183
578
  auto *Tok = Begin;
184
1.43k
  while (Tok != End) {
185
859
    if (!NewText.empty() && 
!endsWithSpace(NewText)643
) {
186
281
      NewText += " ";
187
281
    }
188
189
859
    NewText += Tok->TokenText;
190
859
    Tok = Tok->Next;
191
859
  }
192
193
  // If we are rotating to the right we move the first token to the back.
194
578
  if (!Left) {
195
216
    if (!NewText.empty() && !startsWithSpace(NewText)) {
196
216
      NewText += " ";
197
216
    }
198
216
    NewText += First->TokenText;
199
216
  }
200
201
578
  auto Range = CharSourceRange::getCharRange(First->getStartOfNonWhitespace(),
202
578
                                             Last->Tok.getEndLoc());
203
204
578
  replaceToken(SourceMgr, Fixes, Range, NewText);
205
578
}
206
207
const FormatToken *LeftRightQualifierAlignmentFixer::analyzeRight(
208
    const SourceManager &SourceMgr, const AdditionalKeywords &Keywords,
209
    tooling::Replacements &Fixes, const FormatToken *Tok,
210
6.07k
    const std::string &Qualifier, tok::TokenKind QualifierType) {
211
  // We only need to think about streams that begin with a qualifier.
212
6.07k
  if (!Tok->is(QualifierType))
213
5.56k
    return Tok;
214
  // Don't concern yourself if nothing follows the qualifier.
215
513
  if (!Tok->Next)
216
0
    return Tok;
217
513
  if (LeftRightQualifierAlignmentFixer::isPossibleMacro(Tok->Next))
218
10
    return Tok;
219
220
503
  FormatToken *Qual = Tok->Next;
221
503
  FormatToken *LastQual = Qual;
222
779
  while (Qual && isQualifierOrType(Qual, ConfiguredQualifierTokens)) {
223
276
    LastQual = Qual;
224
276
    Qual = Qual->Next;
225
276
  }
226
503
  if (LastQual && Qual != LastQual) {
227
216
    rotateTokens(SourceMgr, Fixes, Tok, LastQual, /*Left=*/false);
228
216
    Tok = LastQual;
229
287
  } else if (Tok->startsSequence(QualifierType, tok::identifier,
230
287
                                 TT_TemplateOpener)) {
231
    // Read from the TemplateOpener to
232
    // TemplateCloser as in const ArrayRef<int> a; const ArrayRef<int> &a;
233
53
    FormatToken *EndTemplate = Tok->Next->Next->MatchingParen;
234
53
    if (EndTemplate) {
235
      // Move to the end of any template class members e.g.
236
      // `Foo<int>::iterator`.
237
53
      if (EndTemplate->startsSequence(TT_TemplateCloser, tok::coloncolon,
238
53
                                      tok::identifier))
239
9
        EndTemplate = EndTemplate->Next->Next;
240
53
    }
241
53
    if (EndTemplate && EndTemplate->Next &&
242
53
        !EndTemplate->Next->isOneOf(tok::equal, tok::l_paren)) {
243
46
      insertQualifierAfter(SourceMgr, Fixes, EndTemplate, Qualifier);
244
      // Remove the qualifier.
245
46
      removeToken(SourceMgr, Fixes, Tok);
246
46
      return Tok;
247
46
    }
248
234
  } else if (Tok->startsSequence(QualifierType, tok::identifier)) {
249
92
    FormatToken *Next = Tok->Next;
250
    // The case  `const Foo` -> `Foo const`
251
    // The case  `const Foo *` -> `Foo const *`
252
    // The case  `const Foo &` -> `Foo const &`
253
    // The case  `const Foo &&` -> `Foo const &&`
254
    // The case  `const std::Foo &&` -> `std::Foo const &&`
255
    // The case  `const std::Foo<T> &&` -> `std::Foo<T> const &&`
256
237
    while (Next && 
Next->isOneOf(tok::identifier, tok::coloncolon)234
) {
257
145
      Next = Next->Next;
258
145
    }
259
92
    if (Next && 
Next->is(TT_TemplateOpener)89
) {
260
8
      Next = Next->MatchingParen;
261
      // Move to the end of any template class members e.g.
262
      // `Foo<int>::iterator`.
263
8
      if (Next && Next->startsSequence(TT_TemplateCloser, tok::coloncolon,
264
8
                                       tok::identifier)) {
265
0
        Next = Next->Next->Next;
266
0
        return Tok;
267
0
      }
268
8
      assert(Next && "Missing template opener");
269
0
      Next = Next->Next;
270
8
    }
271
92
    if (Next && 
Next->isOneOf(tok::star, tok::amp, tok::ampamp)89
&&
272
92
        
!Tok->Next->isOneOf(Keywords.kw_override, Keywords.kw_final)26
) {
273
26
      if (Next->Previous && !Next->Previous->is(QualifierType)) {
274
26
        insertQualifierAfter(SourceMgr, Fixes, Next->Previous, Qualifier);
275
26
        removeToken(SourceMgr, Fixes, Tok);
276
26
      }
277
26
      return Next;
278
26
    }
279
92
  }
280
281
431
  return Tok;
282
503
}
283
284
const FormatToken *LeftRightQualifierAlignmentFixer::analyzeLeft(
285
    const SourceManager &SourceMgr, const AdditionalKeywords &Keywords,
286
    tooling::Replacements &Fixes, const FormatToken *Tok,
287
9.72k
    const std::string &Qualifier, tok::TokenKind QualifierType) {
288
  // if Tok is an identifier and possibly a macro then don't convert.
289
9.72k
  if (LeftRightQualifierAlignmentFixer::isPossibleMacro(Tok))
290
540
    return Tok;
291
292
9.18k
  const FormatToken *Qual = Tok;
293
9.18k
  const FormatToken *LastQual = Qual;
294
13.2k
  while (Qual && isQualifierOrType(Qual, ConfiguredQualifierTokens)) {
295
4.46k
    LastQual = Qual;
296
4.46k
    Qual = Qual->Next;
297
4.46k
    if (Qual && Qual->is(QualifierType))
298
352
      break;
299
4.46k
  }
300
301
9.18k
  if (!Qual) {
302
0
    return Tok;
303
0
  }
304
305
9.18k
  if (LastQual && Qual != LastQual && 
Qual->is(QualifierType)2.89k
) {
306
352
    rotateTokens(SourceMgr, Fixes, Tok, Qual, /*Left=*/true);
307
352
    Tok = Qual->Next;
308
8.83k
  } else if (Tok->startsSequence(tok::identifier, QualifierType)) {
309
17
    if (Tok->Next->Next && Tok->Next->Next->isOneOf(tok::identifier, tok::star,
310
17
                                                    tok::amp, tok::ampamp)) {
311
      // Don't swap `::iterator const` to `::const iterator`.
312
2
      if (!Tok->Previous ||
313
2
          (Tok->Previous && !Tok->Previous->is(tok::coloncolon))) {
314
2
        rotateTokens(SourceMgr, Fixes, Tok, Tok->Next, /*Left=*/true);
315
2
        Tok = Tok->Next;
316
2
      }
317
2
    }
318
17
  }
319
9.18k
  if (Tok->is(TT_TemplateOpener) && 
Tok->Next656
&&
320
9.18k
      
(656
Tok->Next->is(tok::identifier)656
||
Tok->Next->isSimpleTypeSpecifier()354
) &&
321
9.18k
      
Tok->Next->Next482
&&
Tok->Next->Next->is(QualifierType)482
) {
322
8
    rotateTokens(SourceMgr, Fixes, Tok->Next, Tok->Next->Next, /*Left=*/true);
323
8
  }
324
9.18k
  if (Tok->startsSequence(tok::identifier) && 
Tok->Next2.40k
) {
325
2.40k
    if (Tok->Previous &&
326
2.40k
        
Tok->Previous->isOneOf(tok::star, tok::ampamp, tok::amp)2.24k
) {
327
498
      return Tok;
328
498
    }
329
1.90k
    const FormatToken *Next = Tok->Next;
330
    // The case  `std::Foo<T> const` -> `const std::Foo<T> &&`
331
2.38k
    while (Next && 
Next->isOneOf(tok::identifier, tok::coloncolon)2.36k
)
332
478
      Next = Next->Next;
333
1.90k
    if (Next && 
Next->Previous1.89k
&&
334
1.90k
        
Next->Previous->startsSequence(tok::identifier, TT_TemplateOpener)1.89k
) {
335
      // Read from to the end of the TemplateOpener to
336
      // TemplateCloser const ArrayRef<int> a; const ArrayRef<int> &a;
337
626
      if (Next->is(tok::comment) && 
Next->getNextNonComment()0
)
338
0
        Next = Next->getNextNonComment();
339
626
      assert(Next->MatchingParen && "Missing template closer");
340
0
      Next = Next->MatchingParen->Next;
341
342
      // Move to the end of any template class members e.g.
343
      // `Foo<int>::iterator`.
344
626
      if (Next && 
Next->startsSequence(tok::coloncolon, tok::identifier)578
)
345
72
        Next = Next->Next->Next;
346
626
      if (Next && 
Next->is(QualifierType)578
) {
347
        // Remove the const.
348
30
        insertQualifierBefore(SourceMgr, Fixes, Tok, Qualifier);
349
30
        removeToken(SourceMgr, Fixes, Next);
350
30
        return Next;
351
30
      }
352
626
    }
353
1.87k
    if (Next && 
Next->Next1.81k
&&
354
1.87k
        
Next->Next->isOneOf(tok::amp, tok::ampamp, tok::star)1.16k
) {
355
122
      if (Next->is(QualifierType)) {
356
        // Remove the qualifier.
357
4
        insertQualifierBefore(SourceMgr, Fixes, Tok, Qualifier);
358
4
        removeToken(SourceMgr, Fixes, Next);
359
4
        return Next;
360
4
      }
361
122
    }
362
1.87k
  }
363
8.65k
  return Tok;
364
9.18k
}
365
366
tok::TokenKind LeftRightQualifierAlignmentFixer::getTokenFromQualifier(
367
4.61k
    const std::string &Qualifier) {
368
  // Don't let 'type' be an identifier, but steal typeof token.
369
4.61k
  return llvm::StringSwitch<tok::TokenKind>(Qualifier)
370
4.61k
      .Case("type", tok::kw_typeof)
371
4.61k
      .Case("const", tok::kw_const)
372
4.61k
      .Case("volatile", tok::kw_volatile)
373
4.61k
      .Case("static", tok::kw_static)
374
4.61k
      .Case("inline", tok::kw_inline)
375
4.61k
      .Case("constexpr", tok::kw_constexpr)
376
4.61k
      .Case("restrict", tok::kw_restrict)
377
4.61k
      .Default(tok::identifier);
378
4.61k
}
379
380
LeftRightQualifierAlignmentFixer::LeftRightQualifierAlignmentFixer(
381
    const Environment &Env, const FormatStyle &Style,
382
    const std::string &Qualifier,
383
    const std::vector<tok::TokenKind> &QualifierTokens, bool RightAlign)
384
    : TokenAnalyzer(Env, Style), Qualifier(Qualifier), RightAlign(RightAlign),
385
2.22k
      ConfiguredQualifierTokens(QualifierTokens) {}
386
387
std::pair<tooling::Replacements, unsigned>
388
LeftRightQualifierAlignmentFixer::analyze(
389
    TokenAnnotator &Annotator, SmallVectorImpl<AnnotatedLine *> &AnnotatedLines,
390
2.35k
    FormatTokenLexer &Tokens) {
391
2.35k
  tooling::Replacements Fixes;
392
2.35k
  const AdditionalKeywords &Keywords = Tokens.getKeywords();
393
2.35k
  const SourceManager &SourceMgr = Env.getSourceManager();
394
2.35k
  AffectedRangeMgr.computeAffectedLines(AnnotatedLines);
395
396
2.35k
  tok::TokenKind QualifierToken = getTokenFromQualifier(Qualifier);
397
2.35k
  assert(QualifierToken != tok::identifier && "Unrecognised Qualifier");
398
399
5.50k
  for (AnnotatedLine *Line : AnnotatedLines) {
400
5.50k
    FormatToken *First = Line->First;
401
5.50k
    const auto *Last = Line->Last;
402
403
21.4k
    for (const auto *Tok = First; Tok && 
Tok != Last21.4k
&&
Tok->Next15.9k
;
404
15.9k
         Tok = Tok->Next) {
405
15.9k
      if (Tok->is(tok::comment))
406
114
        continue;
407
15.8k
      if (RightAlign)
408
6.07k
        Tok = analyzeRight(SourceMgr, Keywords, Fixes, Tok, Qualifier,
409
6.07k
                           QualifierToken);
410
9.72k
      else
411
9.72k
        Tok = analyzeLeft(SourceMgr, Keywords, Fixes, Tok, Qualifier,
412
9.72k
                          QualifierToken);
413
15.8k
    }
414
5.50k
  }
415
2.35k
  return {Fixes, 0};
416
2.35k
}
417
418
void QualifierAlignmentFixer::PrepareLeftRightOrdering(
419
    const std::vector<std::string> &Order, std::vector<std::string> &LeftOrder,
420
    std::vector<std::string> &RightOrder,
421
766
    std::vector<tok::TokenKind> &Qualifiers) {
422
423
  // Depending on the position of type in the order you need
424
  // To iterate forward or backward through the order list as qualifier
425
  // can push through each other.
426
  // The Order list must define the position of "type" to signify
427
766
  assert(llvm::is_contained(Order, "type") &&
428
766
         "QualifierOrder must contain type");
429
  // Split the Order list by type and reverse the left side.
430
431
0
  bool left = true;
432
2.93k
  for (const auto &s : Order) {
433
2.93k
    if (s == "type") {
434
766
      left = false;
435
766
      continue;
436
766
    }
437
438
2.16k
    tok::TokenKind QualifierToken =
439
2.16k
        LeftRightQualifierAlignmentFixer::getTokenFromQualifier(s);
440
2.16k
    if (QualifierToken != tok::kw_typeof && QualifierToken != tok::identifier) {
441
2.16k
      Qualifiers.push_back(QualifierToken);
442
2.16k
    }
443
444
2.16k
    if (left)
445
      // Reverse the order for left aligned items.
446
1.45k
      LeftOrder.insert(LeftOrder.begin(), s);
447
710
    else
448
710
      RightOrder.push_back(s);
449
2.16k
  }
450
766
}
451
452
bool LeftRightQualifierAlignmentFixer::isQualifierOrType(
453
14.0k
    const FormatToken *Tok, const std::vector<tok::TokenKind> &specifiedTypes) {
454
14.0k
  return Tok && (Tok->isSimpleTypeSpecifier() || 
Tok->is(tok::kw_auto)11.7k
||
455
14.0k
                 
llvm::is_contained(specifiedTypes, Tok->Tok.getKind())11.6k
);
456
14.0k
}
457
458
// If a token is an identifier and it's upper case, it could
459
// be a macro and hence we need to be able to ignore it.
460
10.2k
bool LeftRightQualifierAlignmentFixer::isPossibleMacro(const FormatToken *Tok) {
461
10.2k
  if (!Tok)
462
0
    return false;
463
10.2k
  if (!Tok->is(tok::identifier))
464
7.23k
    return false;
465
3.01k
  if (Tok->TokenText.upper() == Tok->TokenText.str())
466
552
    return true;
467
2.46k
  return false;
468
3.01k
}
469
470
} // namespace format
471
} // namespace clang