Clang Project

clang_source_code/lib/Format/UsingDeclarationsSorter.cpp
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
23namespace clang {
24namespace format {
25
26namespace {
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.
35int compareLabels(StringRef A, StringRef B) {
36  SmallVector<StringRef, 2> NamesA;
37  A.split(NamesA, "::", /*MaxSplit=*/-1, /*KeepEmpty=*/false);
38  SmallVector<StringRef, 2> NamesB;
39  B.split(NamesB, "::", /*MaxSplit=*/-1, /*KeepEmpty=*/false);
40  size_t SizeA = NamesA.size();
41  size_t SizeB = NamesB.size();
42  for (size_t I = 0, E = std::min(SizeA, SizeB); I < E; ++I) {
43    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      if (SizeB > SizeA)
48        return -1;
49
50      // Two names within a group compare case-insensitively.
51      return NamesA[I].compare_lower(NamesB[I]);
52    }
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    if (I + 1 == SizeB)
57      return 1;
58
59    // Two namespaces names within a group compare case-insensitively.
60    int C = NamesA[I].compare_lower(NamesB[I]);
61    if (C != 0)
62      return C;
63  }
64  return 0;
65}
66
67struct UsingDeclaration {
68  const AnnotatedLine *Line;
69  std::string Label;
70
71  UsingDeclaration(const AnnotatedLine *Line, const std::string &Label)
72      : Line(Line), Label(Label) {}
73
74  bool operator<(const UsingDeclaration &Other) const {
75    return compareLabels(Label, Other.Label) < 0;
76  }
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.
87std::string computeUsingDeclarationLabel(const FormatToken *UsingTok) {
88   (0) . __assert_fail ("UsingTok && UsingTok->is(tok..kw_using) && \"Expecting a using token\"", "/home/seafit/code_projects/clang_source/clang/lib/Format/UsingDeclarationsSorter.cpp", 88, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(UsingTok && UsingTok->is(tok::kw_using) && "Expecting a using token");
89  std::string Label;
90  const FormatToken *Tok = UsingTok->Next;
91  if (Tok && Tok->is(tok::kw_typename)) {
92    Label.append("typename ");
93    Tok = Tok->Next;
94  }
95  if (Tok && Tok->is(tok::coloncolon)) {
96    Label.append("::");
97    Tok = Tok->Next;
98  }
99  bool HasIdentifier = false;
100  while (Tok && Tok->is(tok::identifier)) {
101    HasIdentifier = true;
102    Label.append(Tok->TokenText.str());
103    Tok = Tok->Next;
104    if (!Tok || Tok->isNot(tok::coloncolon))
105      break;
106    Label.append("::");
107    Tok = Tok->Next;
108  }
109  if (HasIdentifier && Tok && Tok->isOneOf(tok::semi, tok::comma))
110    return Label;
111  return "";
112}
113
114void endUsingDeclarationBlock(
115    SmallVectorImpl<UsingDeclaration> *UsingDeclarations,
116    const SourceManager &SourceMgr, tooling::Replacements *Fixes) {
117  bool BlockAffected = false;
118  for (const UsingDeclaration &Declaration : *UsingDeclarations) {
119    if (Declaration.Line->Affected) {
120      BlockAffected = true;
121      break;
122    }
123  }
124  if (!BlockAffected) {
125    UsingDeclarations->clear();
126    return;
127  }
128  SmallVector<UsingDeclaration, 4> SortedUsingDeclarations(
129      UsingDeclarations->begin(), UsingDeclarations->end());
130  std::stable_sort(SortedUsingDeclarations.begin(),
131                   SortedUsingDeclarations.end());
132  SortedUsingDeclarations.erase(
133      std::unique(SortedUsingDeclarations.begin(),
134                  SortedUsingDeclarations.end(),
135                  [](const UsingDeclaration &a, const UsingDeclaration &b) {
136                    return a.Label == b.Label;
137                  }),
138      SortedUsingDeclarations.end());
139  for (size_t I = 0, E = UsingDeclarations->size(); I < E; ++I) {
140    if (I >= SortedUsingDeclarations.size()) {
141      // This using declaration has been deduplicated, delete it.
142      auto Begin =
143          (*UsingDeclarations)[I].Line->First->WhitespaceRange.getBegin();
144      auto End = (*UsingDeclarations)[I].Line->Last->Tok.getEndLoc();
145      auto Range = CharSourceRange::getCharRange(Begin, End);
146      auto Err = Fixes->add(tooling::Replacement(SourceMgr, Range, ""));
147      if (Err) {
148        llvm::errs() << "Error while sorting using declarations: "
149                     << llvm::toString(std::move(Err)) << "\n";
150      }
151      continue;
152    }
153    if ((*UsingDeclarations)[I].Line == SortedUsingDeclarations[I].Line)
154      continue;
155    auto Begin = (*UsingDeclarations)[I].Line->First->Tok.getLocation();
156    auto End = (*UsingDeclarations)[I].Line->Last->Tok.getEndLoc();
157    auto SortedBegin =
158        SortedUsingDeclarations[I].Line->First->Tok.getLocation();
159    auto SortedEnd = SortedUsingDeclarations[I].Line->Last->Tok.getEndLoc();
160    StringRef Text(SourceMgr.getCharacterData(SortedBegin),
161                   SourceMgr.getCharacterData(SortedEnd) -
162                       SourceMgr.getCharacterData(SortedBegin));
163    LLVM_DEBUG({
164      StringRef OldText(SourceMgr.getCharacterData(Begin),
165                        SourceMgr.getCharacterData(End) -
166                            SourceMgr.getCharacterData(Begin));
167      llvm::dbgs() << "Replacing '" << OldText << "' with '" << Text << "'\n";
168    });
169    auto Range = CharSourceRange::getCharRange(Begin, End);
170    auto Err = Fixes->add(tooling::Replacement(SourceMgr, Range, Text));
171    if (Err) {
172      llvm::errs() << "Error while sorting using declarations: "
173                   << llvm::toString(std::move(Err)) << "\n";
174    }
175  }
176  UsingDeclarations->clear();
177}
178
179} // namespace
180
181UsingDeclarationsSorter::UsingDeclarationsSorter(const Environment &Env,
182                                                 const FormatStyle &Style)
183    : TokenAnalyzer(Env, Style) {}
184
185std::pair<tooling::Replacements, unsigned> UsingDeclarationsSorter::analyze(
186    TokenAnnotator &Annotator, SmallVectorImpl<AnnotatedLine *> &AnnotatedLines,
187    FormatTokenLexer &Tokens) {
188  const SourceManager &SourceMgr = Env.getSourceManager();
189  AffectedRangeMgr.computeAffectedLines(AnnotatedLines);
190  tooling::Replacements Fixes;
191  SmallVector<UsingDeclaration, 4> UsingDeclarations;
192  for (size_t I = 0, E = AnnotatedLines.size(); I != E; ++I) {
193    const auto *FirstTok = AnnotatedLines[I]->First;
194    if (AnnotatedLines[I]->InPPDirective ||
195        !AnnotatedLines[I]->startsWith(tok::kw_using) || FirstTok->Finalized) {
196      endUsingDeclarationBlock(&UsingDeclarations, SourceMgr, &Fixes);
197      continue;
198    }
199    if (FirstTok->NewlinesBefore > 1)
200      endUsingDeclarationBlock(&UsingDeclarations, SourceMgr, &Fixes);
201    const auto *UsingTok =
202        FirstTok->is(tok::comment) ? FirstTok->getNextNonComment() : FirstTok;
203    std::string Label = computeUsingDeclarationLabel(UsingTok);
204    if (Label.empty()) {
205      endUsingDeclarationBlock(&UsingDeclarations, SourceMgr, &Fixes);
206      continue;
207    }
208    UsingDeclarations.push_back(UsingDeclaration(AnnotatedLines[I], Label));
209  }
210  endUsingDeclarationBlock(&UsingDeclarations, SourceMgr, &Fixes);
211  return {Fixes, 0};
212}
213
214} // namespace format
215} // namespace clang
216