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