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