1 //===--- UsingDeclarationsSorter.cpp ----------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 ///
10 /// \file
11 /// \brief This file implements UsingDeclarationsSorter, a TokenAnalyzer that
12 /// sorts consecutive using declarations.
13 ///
14 //===----------------------------------------------------------------------===//
15 
16 #include "UsingDeclarationsSorter.h"
17 #include "llvm/Support/Debug.h"
18 #include "llvm/Support/Regex.h"
19 
20 #include <algorithm>
21 
22 #define DEBUG_TYPE "using-declarations-sorter"
23 
24 namespace clang {
25 namespace format {
26 
27 namespace {
28 
29 struct UsingDeclaration {
30   const AnnotatedLine *Line;
31   std::string Label;
32 
33   UsingDeclaration(const AnnotatedLine *Line, const std::string &Label)
34       : Line(Line), Label(Label) {}
35 
36   bool operator<(const UsingDeclaration &Other) const {
37     return StringRef(Label).compare_lower(Other.Label) < 0;
38   }
39 };
40 
41 /// Computes the label of a using declaration starting at tthe using token
42 /// \p UsingTok.
43 /// If \p UsingTok doesn't begin a using declaration, returns the empty string.
44 /// Note that this detects specifically using declarations, as in:
45 /// using A::B::C;
46 /// and not type aliases, as in:
47 /// using A = B::C;
48 /// Type aliases are in general not safe to permute.
49 std::string computeUsingDeclarationLabel(const FormatToken *UsingTok) {
50   assert(UsingTok && UsingTok->is(tok::kw_using) && "Expecting a using token");
51   std::string Label;
52   const FormatToken *Tok = UsingTok->Next;
53   if (Tok && Tok->is(tok::kw_typename)) {
54     Label.append("typename ");
55     Tok = Tok->Next;
56   }
57   if (Tok && Tok->is(tok::coloncolon)) {
58     Label.append("::");
59     Tok = Tok->Next;
60   }
61   bool HasIdentifier = false;
62   while (Tok && Tok->is(tok::identifier)) {
63     HasIdentifier = true;
64     Label.append(Tok->TokenText.str());
65     Tok = Tok->Next;
66     if (!Tok || Tok->isNot(tok::coloncolon))
67       break;
68     Label.append("::");
69     Tok = Tok->Next;
70   }
71   if (HasIdentifier && Tok && Tok->isOneOf(tok::semi, tok::comma))
72     return Label;
73   return "";
74 }
75 
76 void endUsingDeclarationBlock(
77     SmallVectorImpl<UsingDeclaration> *UsingDeclarations,
78     const SourceManager &SourceMgr, tooling::Replacements *Fixes) {
79   bool BlockAffected = false;
80   for (const UsingDeclaration& Declaration : *UsingDeclarations) {
81     if (Declaration.Line->Affected) {
82       BlockAffected = true;
83       break;
84     }
85   }
86   if (!BlockAffected) {
87     UsingDeclarations->clear();
88     return;
89   }
90   SmallVector<UsingDeclaration, 4> SortedUsingDeclarations(
91       UsingDeclarations->begin(), UsingDeclarations->end());
92   std::stable_sort(SortedUsingDeclarations.begin(),
93                    SortedUsingDeclarations.end());
94   for (size_t I = 0, E = UsingDeclarations->size(); I < E; ++I) {
95     if ((*UsingDeclarations)[I].Line == SortedUsingDeclarations[I].Line)
96       continue;
97     auto Begin = (*UsingDeclarations)[I].Line->First->Tok.getLocation();
98     auto End = (*UsingDeclarations)[I].Line->Last->Tok.getEndLoc();
99     auto SortedBegin =
100         SortedUsingDeclarations[I].Line->First->Tok.getLocation();
101     auto SortedEnd = SortedUsingDeclarations[I].Line->Last->Tok.getEndLoc();
102     StringRef Text(SourceMgr.getCharacterData(SortedBegin),
103                    SourceMgr.getCharacterData(SortedEnd) -
104                        SourceMgr.getCharacterData(SortedBegin));
105     DEBUG({
106       StringRef OldText(SourceMgr.getCharacterData(Begin),
107                         SourceMgr.getCharacterData(End) -
108                             SourceMgr.getCharacterData(Begin));
109       llvm::dbgs() << "Replacing '" << OldText << "' with '" << Text << "'\n";
110     });
111     auto Range = CharSourceRange::getCharRange(Begin, End);
112     auto Err = Fixes->add(tooling::Replacement(SourceMgr, Range, Text));
113     if (Err) {
114       llvm::errs() << "Error while sorting using declarations: "
115                    << llvm::toString(std::move(Err)) << "\n";
116     }
117   }
118   UsingDeclarations->clear();
119 }
120 
121 } // namespace
122 
123 UsingDeclarationsSorter::UsingDeclarationsSorter(const Environment &Env,
124                                                  const FormatStyle &Style)
125     : TokenAnalyzer(Env, Style) {}
126 
127 tooling::Replacements UsingDeclarationsSorter::analyze(
128     TokenAnnotator &Annotator, SmallVectorImpl<AnnotatedLine *> &AnnotatedLines,
129     FormatTokenLexer &Tokens) {
130   const SourceManager &SourceMgr = Env.getSourceManager();
131   AffectedRangeMgr.computeAffectedLines(AnnotatedLines.begin(),
132                                         AnnotatedLines.end());
133   tooling::Replacements Fixes;
134   SmallVector<UsingDeclaration, 4> UsingDeclarations;
135   for (size_t I = 0, E = AnnotatedLines.size(); I != E; ++I) {
136     if (AnnotatedLines[I]->InPPDirective ||
137         !AnnotatedLines[I]->startsWith(tok::kw_using) ||
138         AnnotatedLines[I]->First->Finalized) {
139       endUsingDeclarationBlock(&UsingDeclarations, SourceMgr, &Fixes);
140       continue;
141     }
142     if (AnnotatedLines[I]->First->NewlinesBefore > 1)
143       endUsingDeclarationBlock(&UsingDeclarations, SourceMgr, &Fixes);
144     std::string Label = computeUsingDeclarationLabel(AnnotatedLines[I]->First);
145     if (Label.empty()) {
146       endUsingDeclarationBlock(&UsingDeclarations, SourceMgr, &Fixes);
147       continue;
148     }
149     UsingDeclarations.push_back(UsingDeclaration(AnnotatedLines[I], Label));
150   }
151   endUsingDeclarationBlock(&UsingDeclarations, SourceMgr, &Fixes);
152   return Fixes;
153 }
154 
155 } // namespace format
156 } // namespace clang
157