Coverage Report

Created: 2020-09-22 08:39

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Format/UsingDeclarationsSorter.cpp
Line
Count
Source (jump to first uncovered line)
1
//===--- UsingDeclarationsSorter.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 UsingDeclarationsSorter, a TokenAnalyzer that
11
/// sorts consecutive using declarations.
12
///
13
//===----------------------------------------------------------------------===//
14
15
#include "UsingDeclarationsSorter.h"
16
#include "llvm/Support/Debug.h"
17
#include "llvm/Support/Regex.h"
18
19
#include <algorithm>
20
21
#define DEBUG_TYPE "using-declarations-sorter"
22
23
namespace clang {
24
namespace format {
25
26
namespace {
27
28
// The order of using declaration is defined as follows:
29
// Split the strings by "::" and discard any initial empty strings. The last
30
// element of each list is a non-namespace name; all others are namespace
31
// names. Sort the lists of names lexicographically, where the sort order of
32
// individual names is that all non-namespace names come before all namespace
33
// names, and within those groups, names are in case-insensitive lexicographic
34
// order.
35
107
int compareLabels(StringRef A, StringRef B) {
36
107
  SmallVector<StringRef, 2> NamesA;
37
107
  A.split(NamesA, "::", /*MaxSplit=*/-1, /*KeepEmpty=*/false);
38
107
  SmallVector<StringRef, 2> NamesB;
39
107
  B.split(NamesB, "::", /*MaxSplit=*/-1, /*KeepEmpty=*/false);
40
107
  size_t SizeA = NamesA.size();
41
107
  size_t SizeB = NamesB.size();
42
125
  for (size_t I = 0, E = std::min(SizeA, SizeB); I < E; 
++I18
) {
43
125
    if (I + 1 == SizeA) {
44
      // I is the last index of NamesA and NamesA[I] is a non-namespace name.
45
46
      // Non-namespace names come before all namespace names.
47
101
      if (SizeB > SizeA)
48
1
        return -1;
49
50
      // Two names within a group compare case-insensitively.
51
100
      return NamesA[I].compare_lower(NamesB[I]);
52
100
    }
53
54
    // I is the last index of NamesB and NamesB[I] is a non-namespace name.
55
    // Non-namespace names come before all namespace names.
56
24
    if (I + 1 == SizeB)
57
4
      return 1;
58
59
    // Two namespaces names within a group compare case-insensitively.
60
20
    int C = NamesA[I].compare_lower(NamesB[I]);
61
20
    if (C != 0)
62
2
      return C;
63
20
  }
64
0
  return 0;
65
107
}
66
67
struct UsingDeclaration {
68
  const AnnotatedLine *Line;
69
  std::string Label;
70
71
  UsingDeclaration(const AnnotatedLine *Line, const std::string &Label)
72
161
      : Line(Line), Label(Label) {}
73
74
107
  bool operator<(const UsingDeclaration &Other) const {
75
107
    return compareLabels(Label, Other.Label) < 0;
76
107
  }
77
};
78
79
/// Computes the label of a using declaration starting at tthe using token
80
/// \p UsingTok.
81
/// If \p UsingTok doesn't begin a using declaration, returns the empty string.
82
/// Note that this detects specifically using declarations, as in:
83
/// using A::B::C;
84
/// and not type aliases, as in:
85
/// using A = B::C;
86
/// Type aliases are in general not safe to permute.
87
343
std::string computeUsingDeclarationLabel(const FormatToken *UsingTok) {
88
343
  assert(UsingTok && UsingTok->is(tok::kw_using) && "Expecting a using token");
89
343
  std::string Label;
90
343
  const FormatToken *Tok = UsingTok->Next;
91
343
  if (Tok && Tok->is(tok::kw_typename)) {
92
2
    Label.append("typename ");
93
2
    Tok = Tok->Next;
94
2
  }
95
343
  if (Tok && Tok->is(tok::coloncolon)) {
96
16
    Label.append("::");
97
16
    Tok = Tok->Next;
98
16
  }
99
343
  bool HasIdentifier = false;
100
432
  while (Tok && Tok->is(tok::identifier)) {
101
424
    HasIdentifier = true;
102
424
    Label.append(Tok->TokenText.str());
103
424
    Tok = Tok->Next;
104
424
    if (!Tok || 
Tok->isNot(tok::coloncolon)423
)
105
335
      break;
106
89
    Label.append("::");
107
89
    Tok = Tok->Next;
108
89
  }
109
343
  if (HasIdentifier && 
Tok340
&&
Tok->isOneOf(tok::semi, tok::comma)339
)
110
161
    return Label;
111
182
  return "";
112
182
}
113
114
void endUsingDeclarationBlock(
115
    SmallVectorImpl<UsingDeclaration> *UsingDeclarations,
116
49.1k
    const SourceManager &SourceMgr, tooling::Replacements *Fixes) {
117
49.1k
  bool BlockAffected = false;
118
99
  for (const UsingDeclaration &Declaration : *UsingDeclarations) {
119
99
    if (Declaration.Line->Affected) {
120
92
      BlockAffected = true;
121
92
      break;
122
92
    }
123
99
  }
124
49.1k
  if (!BlockAffected) {
125
49.0k
    UsingDeclarations->clear();
126
49.0k
    return;
127
49.0k
  }
128
92
  SmallVector<UsingDeclaration, 4> SortedUsingDeclarations(
129
92
      UsingDeclarations->begin(), UsingDeclarations->end());
130
92
  llvm::stable_sort(SortedUsingDeclarations);
131
92
  SortedUsingDeclarations.erase(
132
92
      std::unique(SortedUsingDeclarations.begin(),
133
92
                  SortedUsingDeclarations.end(),
134
64
                  [](const UsingDeclaration &a, const UsingDeclaration &b) {
135
64
                    return a.Label == b.Label;
136
64
                  }),
137
92
      SortedUsingDeclarations.end());
138
248
  for (size_t I = 0, E = UsingDeclarations->size(); I < E; 
++I156
) {
139
156
    if (I >= SortedUsingDeclarations.size()) {
140
      // This using declaration has been deduplicated, delete it.
141
7
      auto Begin =
142
7
          (*UsingDeclarations)[I].Line->First->WhitespaceRange.getBegin();
143
7
      auto End = (*UsingDeclarations)[I].Line->Last->Tok.getEndLoc();
144
7
      auto Range = CharSourceRange::getCharRange(Begin, End);
145
7
      auto Err = Fixes->add(tooling::Replacement(SourceMgr, Range, ""));
146
7
      if (Err) {
147
0
        llvm::errs() << "Error while sorting using declarations: "
148
0
                     << llvm::toString(std::move(Err)) << "\n";
149
0
      }
150
7
      continue;
151
7
    }
152
149
    if ((*UsingDeclarations)[I].Line == SortedUsingDeclarations[I].Line)
153
76
      continue;
154
73
    auto Begin = (*UsingDeclarations)[I].Line->First->Tok.getLocation();
155
73
    auto End = (*UsingDeclarations)[I].Line->Last->Tok.getEndLoc();
156
73
    auto SortedBegin =
157
73
        SortedUsingDeclarations[I].Line->First->Tok.getLocation();
158
73
    auto SortedEnd = SortedUsingDeclarations[I].Line->Last->Tok.getEndLoc();
159
73
    StringRef Text(SourceMgr.getCharacterData(SortedBegin),
160
73
                   SourceMgr.getCharacterData(SortedEnd) -
161
73
                       SourceMgr.getCharacterData(SortedBegin));
162
73
    LLVM_DEBUG({
163
73
      StringRef OldText(SourceMgr.getCharacterData(Begin),
164
73
                        SourceMgr.getCharacterData(End) -
165
73
                            SourceMgr.getCharacterData(Begin));
166
73
      llvm::dbgs() << "Replacing '" << OldText << "' with '" << Text << "'\n";
167
73
    });
168
73
    auto Range = CharSourceRange::getCharRange(Begin, End);
169
73
    auto Err = Fixes->add(tooling::Replacement(SourceMgr, Range, Text));
170
73
    if (Err) {
171
0
      llvm::errs() << "Error while sorting using declarations: "
172
0
                   << llvm::toString(std::move(Err)) << "\n";
173
0
    }
174
73
  }
175
92
  UsingDeclarations->clear();
176
92
}
177
178
} // namespace
179
180
UsingDeclarationsSorter::UsingDeclarationsSorter(const Environment &Env,
181
                                                 const FormatStyle &Style)
182
9.64k
    : TokenAnalyzer(Env, Style) {}
183
184
std::pair<tooling::Replacements, unsigned> UsingDeclarationsSorter::analyze(
185
    TokenAnnotator &Annotator, SmallVectorImpl<AnnotatedLine *> &AnnotatedLines,
186
9.72k
    FormatTokenLexer &Tokens) {
187
9.72k
  const SourceManager &SourceMgr = Env.getSourceManager();
188
9.72k
  AffectedRangeMgr.computeAffectedLines(AnnotatedLines);
189
9.72k
  tooling::Replacements Fixes;
190
9.72k
  SmallVector<UsingDeclaration, 4> UsingDeclarations;
191
49.3k
  for (size_t I = 0, E = AnnotatedLines.size(); I != E; 
++I39.5k
) {
192
39.5k
    const auto *FirstTok = AnnotatedLines[I]->First;
193
39.5k
    if (AnnotatedLines[I]->InPPDirective ||
194
39.2k
        
!AnnotatedLines[I]->startsWith(tok::kw_using)36.1k
||
FirstTok->Finalized345
) {
195
39.2k
      endUsingDeclarationBlock(&UsingDeclarations, SourceMgr, &Fixes);
196
39.2k
      continue;
197
39.2k
    }
198
343
    if (FirstTok->NewlinesBefore > 1)
199
7
      endUsingDeclarationBlock(&UsingDeclarations, SourceMgr, &Fixes);
200
343
    const auto *UsingTok =
201
341
        FirstTok->is(tok::comment) ? 
FirstTok->getNextNonComment()2
: FirstTok;
202
343
    std::string Label = computeUsingDeclarationLabel(UsingTok);
203
343
    if (Label.empty()) {
204
182
      endUsingDeclarationBlock(&UsingDeclarations, SourceMgr, &Fixes);
205
182
      continue;
206
182
    }
207
161
    UsingDeclarations.push_back(UsingDeclaration(AnnotatedLines[I], Label));
208
161
  }
209
9.72k
  endUsingDeclarationBlock(&UsingDeclarations, SourceMgr, &Fixes);
210
9.72k
  return {Fixes, 0};
211
9.72k
}
212
213
} // namespace format
214
} // namespace clang