diff options
Diffstat (limited to 'lib/Format/UsingDeclarationsSorter.cpp')
-rw-r--r-- | lib/Format/UsingDeclarationsSorter.cpp | 91 |
1 files changed, 82 insertions, 9 deletions
diff --git a/lib/Format/UsingDeclarationsSorter.cpp b/lib/Format/UsingDeclarationsSorter.cpp index fb4f59fbc9bc..ef0c7a7d5a45 100644 --- a/lib/Format/UsingDeclarationsSorter.cpp +++ b/lib/Format/UsingDeclarationsSorter.cpp @@ -26,6 +26,45 @@ namespace format { namespace { +// The order of using declaration is defined as follows: +// Split the strings by "::" and discard any initial empty strings. The last +// element of each list is a non-namespace name; all others are namespace +// names. Sort the lists of names lexicographically, where the sort order of +// individual names is that all non-namespace names come before all namespace +// names, and within those groups, names are in case-insensitive lexicographic +// order. +int compareLabels(StringRef A, StringRef B) { + SmallVector<StringRef, 2> NamesA; + A.split(NamesA, "::", /*MaxSplit=*/-1, /*KeepEmpty=*/false); + SmallVector<StringRef, 2> NamesB; + B.split(NamesB, "::", /*MaxSplit=*/-1, /*KeepEmpty=*/false); + size_t SizeA = NamesA.size(); + size_t SizeB = NamesB.size(); + for (size_t I = 0, E = std::min(SizeA, SizeB); I < E; ++I) { + if (I + 1 == SizeA) { + // I is the last index of NamesA and NamesA[I] is a non-namespace name. + + // Non-namespace names come before all namespace names. + if (SizeB > SizeA) + return -1; + + // Two names within a group compare case-insensitively. + return NamesA[I].compare_lower(NamesB[I]); + } + + // I is the last index of NamesB and NamesB[I] is a non-namespace name. + // Non-namespace names come before all namespace names. + if (I + 1 == SizeB) + return 1; + + // Two namespaces names within a group compare case-insensitively. + int C = NamesA[I].compare_lower(NamesB[I]); + if (C != 0) + return C; + } + return 0; +} + struct UsingDeclaration { const AnnotatedLine *Line; std::string Label; @@ -34,7 +73,7 @@ struct UsingDeclaration { : Line(Line), Label(Label) {} bool operator<(const UsingDeclaration &Other) const { - return Label < Other.Label; + return compareLabels(Label, Other.Label) < 0; } }; @@ -76,10 +115,42 @@ std::string computeUsingDeclarationLabel(const FormatToken *UsingTok) { void endUsingDeclarationBlock( SmallVectorImpl<UsingDeclaration> *UsingDeclarations, const SourceManager &SourceMgr, tooling::Replacements *Fixes) { + bool BlockAffected = false; + for (const UsingDeclaration &Declaration : *UsingDeclarations) { + if (Declaration.Line->Affected) { + BlockAffected = true; + break; + } + } + if (!BlockAffected) { + UsingDeclarations->clear(); + return; + } SmallVector<UsingDeclaration, 4> SortedUsingDeclarations( UsingDeclarations->begin(), UsingDeclarations->end()); - std::sort(SortedUsingDeclarations.begin(), SortedUsingDeclarations.end()); + std::stable_sort(SortedUsingDeclarations.begin(), + SortedUsingDeclarations.end()); + SortedUsingDeclarations.erase( + std::unique(SortedUsingDeclarations.begin(), + SortedUsingDeclarations.end(), + [](const UsingDeclaration &a, const UsingDeclaration &b) { + return a.Label == b.Label; + }), + SortedUsingDeclarations.end()); for (size_t I = 0, E = UsingDeclarations->size(); I < E; ++I) { + if (I >= SortedUsingDeclarations.size()) { + // This using declaration has been deduplicated, delete it. + auto Begin = + (*UsingDeclarations)[I].Line->First->WhitespaceRange.getBegin(); + auto End = (*UsingDeclarations)[I].Line->Last->Tok.getEndLoc(); + auto Range = CharSourceRange::getCharRange(Begin, End); + auto Err = Fixes->add(tooling::Replacement(SourceMgr, Range, "")); + if (Err) { + llvm::errs() << "Error while sorting using declarations: " + << llvm::toString(std::move(Err)) << "\n"; + } + continue; + } if ((*UsingDeclarations)[I].Line == SortedUsingDeclarations[I].Line) continue; auto Begin = (*UsingDeclarations)[I].Line->First->Tok.getLocation(); @@ -112,7 +183,7 @@ UsingDeclarationsSorter::UsingDeclarationsSorter(const Environment &Env, const FormatStyle &Style) : TokenAnalyzer(Env, Style) {} -tooling::Replacements UsingDeclarationsSorter::analyze( +std::pair<tooling::Replacements, unsigned> UsingDeclarationsSorter::analyze( TokenAnnotator &Annotator, SmallVectorImpl<AnnotatedLine *> &AnnotatedLines, FormatTokenLexer &Tokens) { const SourceManager &SourceMgr = Env.getSourceManager(); @@ -121,15 +192,17 @@ tooling::Replacements UsingDeclarationsSorter::analyze( tooling::Replacements Fixes; SmallVector<UsingDeclaration, 4> UsingDeclarations; for (size_t I = 0, E = AnnotatedLines.size(); I != E; ++I) { - if (!AnnotatedLines[I]->Affected || AnnotatedLines[I]->InPPDirective || - !AnnotatedLines[I]->startsWith(tok::kw_using) || - AnnotatedLines[I]->First->Finalized) { + const auto *FirstTok = AnnotatedLines[I]->First; + if (AnnotatedLines[I]->InPPDirective || + !AnnotatedLines[I]->startsWith(tok::kw_using) || FirstTok->Finalized) { endUsingDeclarationBlock(&UsingDeclarations, SourceMgr, &Fixes); continue; } - if (AnnotatedLines[I]->First->NewlinesBefore > 1) + if (FirstTok->NewlinesBefore > 1) endUsingDeclarationBlock(&UsingDeclarations, SourceMgr, &Fixes); - std::string Label = computeUsingDeclarationLabel(AnnotatedLines[I]->First); + const auto *UsingTok = + FirstTok->is(tok::comment) ? FirstTok->getNextNonComment() : FirstTok; + std::string Label = computeUsingDeclarationLabel(UsingTok); if (Label.empty()) { endUsingDeclarationBlock(&UsingDeclarations, SourceMgr, &Fixes); continue; @@ -137,7 +210,7 @@ tooling::Replacements UsingDeclarationsSorter::analyze( UsingDeclarations.push_back(UsingDeclaration(AnnotatedLines[I], Label)); } endUsingDeclarationBlock(&UsingDeclarations, SourceMgr, &Fixes); - return Fixes; + return {Fixes, 0}; } } // namespace format |