diff --git a/clang-tools-extra/clangd/refactor/tweaks/RemoveUsingNamespace.cpp b/clang-tools-extra/clangd/refactor/tweaks/RemoveUsingNamespace.cpp --- a/clang-tools-extra/clangd/refactor/tweaks/RemoveUsingNamespace.cpp +++ b/clang-tools-extra/clangd/refactor/tweaks/RemoveUsingNamespace.cpp @@ -54,6 +54,10 @@ : TargetNS(Target.getNominatedNamespace()), TargetCtx(Target.getDeclContext()), Results(Results) {} + FindSameUsings(const NamespaceDecl *TargetNS, const DeclContext *TargetCtx, + std::vector &Results) + : TargetNS(TargetNS), TargetCtx(TargetCtx), Results(Results) {} + bool VisitUsingDirectiveDecl(UsingDirectiveDecl *D) { if (D->getNominatedNamespace() != TargetNS || D->getDeclContext() != TargetCtx) @@ -101,6 +105,40 @@ return D; } +// If the user-defined literal is declared in an inline namespace, +// we add a using-directive for it +void handleUserDefinedLiteral(const DeclContext *D, const DeclContext *Ctx, + ASTContext &AST, + std::vector &UsingToAdd) { + if (D->isInlineNamespace()) { + const auto *ND = cast(D); + // Find if there is already a wanted using-directive, + // if there is, we are done. Otherwise we collect it + std::vector AllDirectives; + FindSameUsings(ND, Ctx, AllDirectives).TraverseAST(AST); + if (AllDirectives.empty()) { + UsingToAdd.push_back(ND->getQualifiedNameAsString()); + } + } +} + +// Produce edit adding 'using namespace xxx::yyy;' +// for user-defined literals declared in an inline namespace +llvm::Optional +addUsingDirectives(const std::vector &UsingToAdd, + SourceManager &SM, SourceLocation Loc) { + if (UsingToAdd.empty()) + return llvm::None; + std::string Directives; + for (auto &Using : UsingToAdd) + Directives += llvm::formatv("using namespace {0};\n", Using); + // Remove the trailing newline + Directives.pop_back(); + // For now we insert the using-directives to where the first using-directive + // to remove stands + return tooling::Replacement(SM, Loc, 0, Directives); +} + bool RemoveUsingNamespace::prepare(const Selection &Inputs) { // Find the 'using namespace' directive under the cursor. auto *CA = Inputs.ASTSelection.commonAncestor(); @@ -144,6 +182,9 @@ // Collect all references to symbols from the namespace for which we're // removing the directive. std::vector IdentsToQualify; + // Collect all namespaces' name to add for user-defined literals declared + // in an inline namespace + std::vector UsingToAdd; for (auto &D : Inputs.AST->getLocalTopLevelDecls()) { findExplicitReferences( D, @@ -164,15 +205,19 @@ // Avoid adding qualifiers before user-defined literals, e.g. // using namespace std; // auto s = "foo"s; // Must not changed to auto s = "foo" std::s; - // FIXME: Add a using-directive for user-defined literals + // And add a using-directive for user-defined literals // declared in an inline namespace, e.g. // using namespace s^td; // int main() { cout << "foo"s; } // change to - // using namespace std::literals; + // using namespace std::string_literals; // int main() { std::cout << "foo"s; } - if (Kind == DeclarationName::NameKind::CXXLiteralOperatorName) + if (Kind == DeclarationName::NameKind::CXXLiteralOperatorName) { + handleUserDefinedLiteral(T->getDeclContext(), + TargetDirective->getDeclContext(), Ctx, + UsingToAdd); return; + } } SourceLocation Loc = Ref.NameLoc; if (Loc.isMacroID()) { @@ -201,6 +246,10 @@ std::unique(IdentsToQualify.begin(), IdentsToQualify.end()), IdentsToQualify.end()); + llvm::sort(UsingToAdd); + UsingToAdd.erase(std::unique(UsingToAdd.begin(), UsingToAdd.end()), + UsingToAdd.end()); + // Produce replacements to remove the using directives. tooling::Replacements R; for (auto *D : AllDirectives) { @@ -217,6 +266,11 @@ /*Length=*/0, Qualifier))) return std::move(Err); } + // Produce replacements to add the using directives for user-defined literals + if (auto AddUsing = + addUsingDirectives(UsingToAdd, SM, FirstUsingDirectiveLoc)) + if (auto Err = R.add(AddUsing.value())) + return std::move(Err); return Effect::mainFileEdit(SM, std::move(R)); } diff --git a/clang-tools-extra/clangd/unittests/tweaks/RemoveUsingNamespaceTests.cpp b/clang-tools-extra/clangd/unittests/tweaks/RemoveUsingNamespaceTests.cpp --- a/clang-tools-extra/clangd/unittests/tweaks/RemoveUsingNamespaceTests.cpp +++ b/clang-tools-extra/clangd/unittests/tweaks/RemoveUsingNamespaceTests.cpp @@ -263,6 +263,26 @@ long double operator "" _w(long double); } + int main() { 1.5_w; } + )cpp"}, + {// Add using-directives for user-defined literals + // declared in an inline namespace + R"cpp( + namespace ns1 { + inline namespace ns2 { + long double operator"" _w(long double); + } + } + using namespace n^s1; + int main() { 1.5_w; } + )cpp", + R"cpp( + namespace ns1 { + inline namespace ns2 { + long double operator"" _w(long double); + } + } + using namespace ns1::ns2; int main() { 1.5_w; } )cpp"}}; for (auto C : Cases)