xref: /openbsd-src/gnu/llvm/clang/lib/Format/UsingDeclarationsSorter.cpp (revision 12c855180aad702bbcca06e0398d774beeafb155)
1e5dd7070Spatrick //===--- UsingDeclarationsSorter.cpp ----------------------------*- C++ -*-===//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick ///
9e5dd7070Spatrick /// \file
10e5dd7070Spatrick /// This file implements UsingDeclarationsSorter, a TokenAnalyzer that
11e5dd7070Spatrick /// sorts consecutive using declarations.
12e5dd7070Spatrick ///
13e5dd7070Spatrick //===----------------------------------------------------------------------===//
14e5dd7070Spatrick 
15e5dd7070Spatrick #include "UsingDeclarationsSorter.h"
16*12c85518Srobert #include "clang/Format/Format.h"
17e5dd7070Spatrick #include "llvm/Support/Debug.h"
18e5dd7070Spatrick #include "llvm/Support/Regex.h"
19e5dd7070Spatrick 
20e5dd7070Spatrick #include <algorithm>
21e5dd7070Spatrick 
22e5dd7070Spatrick #define DEBUG_TYPE "using-declarations-sorter"
23e5dd7070Spatrick 
24e5dd7070Spatrick namespace clang {
25e5dd7070Spatrick namespace format {
26e5dd7070Spatrick 
27e5dd7070Spatrick namespace {
28e5dd7070Spatrick 
29e5dd7070Spatrick // The order of using declaration is defined as follows:
30e5dd7070Spatrick // Split the strings by "::" and discard any initial empty strings. The last
31e5dd7070Spatrick // element of each list is a non-namespace name; all others are namespace
32e5dd7070Spatrick // names. Sort the lists of names lexicographically, where the sort order of
33e5dd7070Spatrick // individual names is that all non-namespace names come before all namespace
34e5dd7070Spatrick // names, and within those groups, names are in case-insensitive lexicographic
35e5dd7070Spatrick // order.
compareLabelsLexicographicNumeric(StringRef A,StringRef B)36*12c85518Srobert int compareLabelsLexicographicNumeric(StringRef A, StringRef B) {
37e5dd7070Spatrick   SmallVector<StringRef, 2> NamesA;
38e5dd7070Spatrick   A.split(NamesA, "::", /*MaxSplit=*/-1, /*KeepEmpty=*/false);
39e5dd7070Spatrick   SmallVector<StringRef, 2> NamesB;
40e5dd7070Spatrick   B.split(NamesB, "::", /*MaxSplit=*/-1, /*KeepEmpty=*/false);
41e5dd7070Spatrick   size_t SizeA = NamesA.size();
42e5dd7070Spatrick   size_t SizeB = NamesB.size();
43e5dd7070Spatrick   for (size_t I = 0, E = std::min(SizeA, SizeB); I < E; ++I) {
44e5dd7070Spatrick     if (I + 1 == SizeA) {
45e5dd7070Spatrick       // I is the last index of NamesA and NamesA[I] is a non-namespace name.
46e5dd7070Spatrick 
47e5dd7070Spatrick       // Non-namespace names come before all namespace names.
48e5dd7070Spatrick       if (SizeB > SizeA)
49e5dd7070Spatrick         return -1;
50e5dd7070Spatrick 
51e5dd7070Spatrick       // Two names within a group compare case-insensitively.
52a9ac8606Spatrick       return NamesA[I].compare_insensitive(NamesB[I]);
53e5dd7070Spatrick     }
54e5dd7070Spatrick 
55e5dd7070Spatrick     // I is the last index of NamesB and NamesB[I] is a non-namespace name.
56e5dd7070Spatrick     // Non-namespace names come before all namespace names.
57e5dd7070Spatrick     if (I + 1 == SizeB)
58e5dd7070Spatrick       return 1;
59e5dd7070Spatrick 
60e5dd7070Spatrick     // Two namespaces names within a group compare case-insensitively.
61a9ac8606Spatrick     int C = NamesA[I].compare_insensitive(NamesB[I]);
62e5dd7070Spatrick     if (C != 0)
63e5dd7070Spatrick       return C;
64e5dd7070Spatrick   }
65e5dd7070Spatrick   return 0;
66e5dd7070Spatrick }
67e5dd7070Spatrick 
compareLabelsLexicographic(StringRef A,StringRef B)68*12c85518Srobert int compareLabelsLexicographic(StringRef A, StringRef B) {
69*12c85518Srobert   SmallVector<StringRef, 2> NamesA;
70*12c85518Srobert   A.split(NamesA, "::", /*MaxSplit=*/-1, /*KeepEmpty=*/false);
71*12c85518Srobert   SmallVector<StringRef, 2> NamesB;
72*12c85518Srobert   B.split(NamesB, "::", /*MaxSplit=*/-1, /*KeepEmpty=*/false);
73*12c85518Srobert   size_t SizeA = NamesA.size();
74*12c85518Srobert   size_t SizeB = NamesB.size();
75*12c85518Srobert   for (size_t I = 0, E = std::min(SizeA, SizeB); I < E; ++I) {
76*12c85518Srobert     // Two namespaces names within a group compare case-insensitively.
77*12c85518Srobert     int C = NamesA[I].compare_insensitive(NamesB[I]);
78*12c85518Srobert     if (C != 0)
79*12c85518Srobert       return C;
80*12c85518Srobert   }
81*12c85518Srobert   if (SizeA < SizeB)
82*12c85518Srobert     return -1;
83*12c85518Srobert   return SizeA == SizeB ? 0 : 1;
84*12c85518Srobert }
85*12c85518Srobert 
compareLabels(StringRef A,StringRef B,FormatStyle::SortUsingDeclarationsOptions SortUsingDeclarations)86*12c85518Srobert int compareLabels(
87*12c85518Srobert     StringRef A, StringRef B,
88*12c85518Srobert     FormatStyle::SortUsingDeclarationsOptions SortUsingDeclarations) {
89*12c85518Srobert   if (SortUsingDeclarations == FormatStyle::SUD_LexicographicNumeric)
90*12c85518Srobert     return compareLabelsLexicographicNumeric(A, B);
91*12c85518Srobert   return compareLabelsLexicographic(A, B);
92*12c85518Srobert }
93*12c85518Srobert 
94e5dd7070Spatrick struct UsingDeclaration {
95e5dd7070Spatrick   const AnnotatedLine *Line;
96e5dd7070Spatrick   std::string Label;
97e5dd7070Spatrick 
UsingDeclarationclang::format::__anone26328270111::UsingDeclaration98e5dd7070Spatrick   UsingDeclaration(const AnnotatedLine *Line, const std::string &Label)
99e5dd7070Spatrick       : Line(Line), Label(Label) {}
100e5dd7070Spatrick };
101e5dd7070Spatrick 
102e5dd7070Spatrick /// Computes the label of a using declaration starting at tthe using token
103e5dd7070Spatrick /// \p UsingTok.
104e5dd7070Spatrick /// If \p UsingTok doesn't begin a using declaration, returns the empty string.
105e5dd7070Spatrick /// Note that this detects specifically using declarations, as in:
106e5dd7070Spatrick /// using A::B::C;
107e5dd7070Spatrick /// and not type aliases, as in:
108e5dd7070Spatrick /// using A = B::C;
109e5dd7070Spatrick /// Type aliases are in general not safe to permute.
computeUsingDeclarationLabel(const FormatToken * UsingTok)110e5dd7070Spatrick std::string computeUsingDeclarationLabel(const FormatToken *UsingTok) {
111e5dd7070Spatrick   assert(UsingTok && UsingTok->is(tok::kw_using) && "Expecting a using token");
112e5dd7070Spatrick   std::string Label;
113e5dd7070Spatrick   const FormatToken *Tok = UsingTok->Next;
114e5dd7070Spatrick   if (Tok && Tok->is(tok::kw_typename)) {
115e5dd7070Spatrick     Label.append("typename ");
116e5dd7070Spatrick     Tok = Tok->Next;
117e5dd7070Spatrick   }
118e5dd7070Spatrick   if (Tok && Tok->is(tok::coloncolon)) {
119e5dd7070Spatrick     Label.append("::");
120e5dd7070Spatrick     Tok = Tok->Next;
121e5dd7070Spatrick   }
122e5dd7070Spatrick   bool HasIdentifier = false;
123e5dd7070Spatrick   while (Tok && Tok->is(tok::identifier)) {
124e5dd7070Spatrick     HasIdentifier = true;
125e5dd7070Spatrick     Label.append(Tok->TokenText.str());
126e5dd7070Spatrick     Tok = Tok->Next;
127e5dd7070Spatrick     if (!Tok || Tok->isNot(tok::coloncolon))
128e5dd7070Spatrick       break;
129e5dd7070Spatrick     Label.append("::");
130e5dd7070Spatrick     Tok = Tok->Next;
131e5dd7070Spatrick   }
132e5dd7070Spatrick   if (HasIdentifier && Tok && Tok->isOneOf(tok::semi, tok::comma))
133e5dd7070Spatrick     return Label;
134e5dd7070Spatrick   return "";
135e5dd7070Spatrick }
136e5dd7070Spatrick 
endUsingDeclarationBlock(SmallVectorImpl<UsingDeclaration> * UsingDeclarations,const SourceManager & SourceMgr,tooling::Replacements * Fixes,FormatStyle::SortUsingDeclarationsOptions SortUsingDeclarations)137e5dd7070Spatrick void endUsingDeclarationBlock(
138e5dd7070Spatrick     SmallVectorImpl<UsingDeclaration> *UsingDeclarations,
139*12c85518Srobert     const SourceManager &SourceMgr, tooling::Replacements *Fixes,
140*12c85518Srobert     FormatStyle::SortUsingDeclarationsOptions SortUsingDeclarations) {
141e5dd7070Spatrick   bool BlockAffected = false;
142e5dd7070Spatrick   for (const UsingDeclaration &Declaration : *UsingDeclarations) {
143e5dd7070Spatrick     if (Declaration.Line->Affected) {
144e5dd7070Spatrick       BlockAffected = true;
145e5dd7070Spatrick       break;
146e5dd7070Spatrick     }
147e5dd7070Spatrick   }
148e5dd7070Spatrick   if (!BlockAffected) {
149e5dd7070Spatrick     UsingDeclarations->clear();
150e5dd7070Spatrick     return;
151e5dd7070Spatrick   }
152e5dd7070Spatrick   SmallVector<UsingDeclaration, 4> SortedUsingDeclarations(
153e5dd7070Spatrick       UsingDeclarations->begin(), UsingDeclarations->end());
154*12c85518Srobert   auto Comp = [SortUsingDeclarations](const UsingDeclaration &Lhs,
155*12c85518Srobert                                       const UsingDeclaration &Rhs) -> bool {
156*12c85518Srobert     return compareLabels(Lhs.Label, Rhs.Label, SortUsingDeclarations) < 0;
157*12c85518Srobert   };
158*12c85518Srobert   llvm::stable_sort(SortedUsingDeclarations, Comp);
159e5dd7070Spatrick   SortedUsingDeclarations.erase(
160e5dd7070Spatrick       std::unique(SortedUsingDeclarations.begin(),
161e5dd7070Spatrick                   SortedUsingDeclarations.end(),
162e5dd7070Spatrick                   [](const UsingDeclaration &a, const UsingDeclaration &b) {
163e5dd7070Spatrick                     return a.Label == b.Label;
164e5dd7070Spatrick                   }),
165e5dd7070Spatrick       SortedUsingDeclarations.end());
166e5dd7070Spatrick   for (size_t I = 0, E = UsingDeclarations->size(); I < E; ++I) {
167e5dd7070Spatrick     if (I >= SortedUsingDeclarations.size()) {
168e5dd7070Spatrick       // This using declaration has been deduplicated, delete it.
169e5dd7070Spatrick       auto Begin =
170e5dd7070Spatrick           (*UsingDeclarations)[I].Line->First->WhitespaceRange.getBegin();
171e5dd7070Spatrick       auto End = (*UsingDeclarations)[I].Line->Last->Tok.getEndLoc();
172e5dd7070Spatrick       auto Range = CharSourceRange::getCharRange(Begin, End);
173e5dd7070Spatrick       auto Err = Fixes->add(tooling::Replacement(SourceMgr, Range, ""));
174e5dd7070Spatrick       if (Err) {
175e5dd7070Spatrick         llvm::errs() << "Error while sorting using declarations: "
176e5dd7070Spatrick                      << llvm::toString(std::move(Err)) << "\n";
177e5dd7070Spatrick       }
178e5dd7070Spatrick       continue;
179e5dd7070Spatrick     }
180e5dd7070Spatrick     if ((*UsingDeclarations)[I].Line == SortedUsingDeclarations[I].Line)
181e5dd7070Spatrick       continue;
182e5dd7070Spatrick     auto Begin = (*UsingDeclarations)[I].Line->First->Tok.getLocation();
183e5dd7070Spatrick     auto End = (*UsingDeclarations)[I].Line->Last->Tok.getEndLoc();
184e5dd7070Spatrick     auto SortedBegin =
185e5dd7070Spatrick         SortedUsingDeclarations[I].Line->First->Tok.getLocation();
186e5dd7070Spatrick     auto SortedEnd = SortedUsingDeclarations[I].Line->Last->Tok.getEndLoc();
187e5dd7070Spatrick     StringRef Text(SourceMgr.getCharacterData(SortedBegin),
188e5dd7070Spatrick                    SourceMgr.getCharacterData(SortedEnd) -
189e5dd7070Spatrick                        SourceMgr.getCharacterData(SortedBegin));
190e5dd7070Spatrick     LLVM_DEBUG({
191e5dd7070Spatrick       StringRef OldText(SourceMgr.getCharacterData(Begin),
192e5dd7070Spatrick                         SourceMgr.getCharacterData(End) -
193e5dd7070Spatrick                             SourceMgr.getCharacterData(Begin));
194e5dd7070Spatrick       llvm::dbgs() << "Replacing '" << OldText << "' with '" << Text << "'\n";
195e5dd7070Spatrick     });
196e5dd7070Spatrick     auto Range = CharSourceRange::getCharRange(Begin, End);
197e5dd7070Spatrick     auto Err = Fixes->add(tooling::Replacement(SourceMgr, Range, Text));
198e5dd7070Spatrick     if (Err) {
199e5dd7070Spatrick       llvm::errs() << "Error while sorting using declarations: "
200e5dd7070Spatrick                    << llvm::toString(std::move(Err)) << "\n";
201e5dd7070Spatrick     }
202e5dd7070Spatrick   }
203e5dd7070Spatrick   UsingDeclarations->clear();
204e5dd7070Spatrick }
205e5dd7070Spatrick 
206e5dd7070Spatrick } // namespace
207e5dd7070Spatrick 
UsingDeclarationsSorter(const Environment & Env,const FormatStyle & Style)208e5dd7070Spatrick UsingDeclarationsSorter::UsingDeclarationsSorter(const Environment &Env,
209e5dd7070Spatrick                                                  const FormatStyle &Style)
210e5dd7070Spatrick     : TokenAnalyzer(Env, Style) {}
211e5dd7070Spatrick 
analyze(TokenAnnotator & Annotator,SmallVectorImpl<AnnotatedLine * > & AnnotatedLines,FormatTokenLexer & Tokens)212e5dd7070Spatrick std::pair<tooling::Replacements, unsigned> UsingDeclarationsSorter::analyze(
213e5dd7070Spatrick     TokenAnnotator &Annotator, SmallVectorImpl<AnnotatedLine *> &AnnotatedLines,
214e5dd7070Spatrick     FormatTokenLexer &Tokens) {
215e5dd7070Spatrick   const SourceManager &SourceMgr = Env.getSourceManager();
216e5dd7070Spatrick   AffectedRangeMgr.computeAffectedLines(AnnotatedLines);
217e5dd7070Spatrick   tooling::Replacements Fixes;
218e5dd7070Spatrick   SmallVector<UsingDeclaration, 4> UsingDeclarations;
219*12c85518Srobert   for (const AnnotatedLine *Line : AnnotatedLines) {
220*12c85518Srobert     const auto *FirstTok = Line->First;
221*12c85518Srobert     if (Line->InPPDirective || !Line->startsWith(tok::kw_using) ||
222*12c85518Srobert         FirstTok->Finalized) {
223*12c85518Srobert       endUsingDeclarationBlock(&UsingDeclarations, SourceMgr, &Fixes,
224*12c85518Srobert                                Style.SortUsingDeclarations);
225e5dd7070Spatrick       continue;
226e5dd7070Spatrick     }
227*12c85518Srobert     if (FirstTok->NewlinesBefore > 1) {
228*12c85518Srobert       endUsingDeclarationBlock(&UsingDeclarations, SourceMgr, &Fixes,
229*12c85518Srobert                                Style.SortUsingDeclarations);
230*12c85518Srobert     }
231e5dd7070Spatrick     const auto *UsingTok =
232e5dd7070Spatrick         FirstTok->is(tok::comment) ? FirstTok->getNextNonComment() : FirstTok;
233e5dd7070Spatrick     std::string Label = computeUsingDeclarationLabel(UsingTok);
234e5dd7070Spatrick     if (Label.empty()) {
235*12c85518Srobert       endUsingDeclarationBlock(&UsingDeclarations, SourceMgr, &Fixes,
236*12c85518Srobert                                Style.SortUsingDeclarations);
237e5dd7070Spatrick       continue;
238e5dd7070Spatrick     }
239*12c85518Srobert     UsingDeclarations.push_back(UsingDeclaration(Line, Label));
240e5dd7070Spatrick   }
241*12c85518Srobert   endUsingDeclarationBlock(&UsingDeclarations, SourceMgr, &Fixes,
242*12c85518Srobert                            Style.SortUsingDeclarations);
243e5dd7070Spatrick   return {Fixes, 0};
244e5dd7070Spatrick }
245e5dd7070Spatrick 
246e5dd7070Spatrick } // namespace format
247e5dd7070Spatrick } // namespace clang
248