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 int 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 
67 struct 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.
87 std::string computeUsingDeclarationLabel(const FormatToken *UsingTok) {
88   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 
114 void 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 
181 UsingDeclarationsSorter::UsingDeclarationsSorter(const Environment &Env,
182                                                  const FormatStyle &Style)
183     : TokenAnalyzer(Env, Style) {}
184 
185 std::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