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,43 @@ 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, llvm::StringSet<> &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.insert(ND->getQualifiedNameAsString()); + } + } +} + +// Produce edit adding 'using namespace xxx::yyy;' +// for user-defined literals declared in an inline namespace +llvm::Optional +addUsingDirectives(const llvm::StringSet<> &UsingToAdd, SourceManager &SM, + SourceLocation Loc) { + if (UsingToAdd.empty()) + return llvm::None; + // FIXME: We simply join all the using-directives with newline, + // so it can't handle indent. For now this is correct + // because the code action only runs on the using-directive + // declared in the top level + std::string Directives; + for (const auto &Using : UsingToAdd) + Directives += llvm::formatv("using namespace {0};\n", Using.getKey()); + // 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 +185,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 + llvm::StringSet<> UsingToAdd; for (auto &D : Inputs.AST->getLocalTopLevelDecls()) { findExplicitReferences( D, @@ -164,15 +208,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()) { @@ -217,6 +265,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 @@ -264,6 +264,39 @@ } 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); +} +inline namespace ns3 { +long double operator "" _h(long double); +} +} +using namespace n^s1; +int main() { + 1.5_h; + 1.5_w; +} + )cpp", + R"cpp( +namespace ns1 { +inline namespace ns2 { +long double operator "" _w(long double); +} +inline namespace ns3 { +long double operator "" _h(long double); +} +} +using namespace ns1::ns2; +using namespace ns1::ns3; +int main() { + 1.5_h; + 1.5_w; +} )cpp"}}; for (auto C : Cases) EXPECT_EQ(C.second, apply(C.first)) << C.first;