v1nh1shungry created this revision.
v1nh1shungry added reviewers: tom-anders, kadircet.
Herald added subscribers: arphaman, mgrang.
Herald added a project: All.
v1nh1shungry requested review of this revision.
Herald added subscribers: cfe-commits, MaskRay, ilya-biryukov.
Herald added a project: clang-tools-extra.

If we are removing the namespace `ns1` and there are user-defined literals
declared in an inline namespace `ns2` declared in `ns1`, we can add a
`using namespace ns1::ns2;` to keep the program correct.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D137817

Files:
  clang-tools-extra/clangd/refactor/tweaks/RemoveUsingNamespace.cpp
  clang-tools-extra/clangd/unittests/tweaks/RemoveUsingNamespaceTests.cpp

Index: clang-tools-extra/clangd/unittests/tweaks/RemoveUsingNamespaceTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/tweaks/RemoveUsingNamespaceTests.cpp
+++ 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)
Index: clang-tools-extra/clangd/refactor/tweaks/RemoveUsingNamespace.cpp
===================================================================
--- clang-tools-extra/clangd/refactor/tweaks/RemoveUsingNamespace.cpp
+++ 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<const UsingDirectiveDecl *> &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<std::string> &UsingToAdd) {
+  if (D->isInlineNamespace()) {
+    const auto *ND = cast<NamespaceDecl>(D);
+    // Find if there is already a wanted using-directive,
+    // if there is, we are done. Otherwise we collect it
+    std::vector<const UsingDirectiveDecl *> 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<tooling::Replacement>
+addUsingDirectives(const std::vector<std::string> &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<SourceLocation> IdentsToQualify;
+  // Collect all namespaces' name to add for user-defined literals declared
+  // in an inline namespace
+  std::vector<std::string> 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));
 }
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to