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 | (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 | |
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 | |