usaxena95 created this revision.
usaxena95 added a reviewer: ilya-biryukov.
Herald added subscribers: cfe-commits, kadircet, arphaman, mgrang, jkorous, 
MaskRay.
Herald added a project: clang.

This patch extends the removing using-namespace code action to remove
using-namespace decl that are present inside a namespace.

Goes over all the references that are declared in `TargetNS` and used in
`ContainingNS`. Removes the current qualifer and qualifies it with only
the `TargetNS`. No other qualifier is needed since it was legal to do
`using namespace TargetNS` in the `ContainingNS`. Therefore all
references inside the `ContainingNS` can be qualified with just
`TargetNS`.

Limitations:

- The using decl must be present in TUDecl or directly under NSDecl.
- Only references inside the DeclContext of using-decl are removed. Any

references outisde this DeclContext are retained as is which is
incorrect.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D69162

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

Index: clang-tools-extra/clangd/unittests/TweakTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/TweakTests.cpp
+++ clang-tools-extra/clangd/unittests/TweakTests.cpp
@@ -789,15 +789,23 @@
         map m;
       }
     )cpp"},
-      {// Available only for top level namespace decl.
-       R"cpp(
-        namespace aa {
-          namespace bb { struct map {}; }
-          using namespace b^b;
-        }
-        int main() { aa::map m; }
+      {
+          // FIXME: Does not remove usages outside the enclosing NS.
+          R"cpp(
+      namespace aa {
+      namespace bb { struct map {}; }
+      using namespace b^b;
+      }
+      int main() { aa::map m; }
     )cpp",
-       "unavailable"},
+          R"cpp(
+      namespace aa {
+      namespace bb { struct map {}; }
+      
+      }
+      int main() { aa::map m; }
+    )cpp",
+      },
       {// FIXME: Unavailable for namespaces containing using-namespace decl.
        R"cpp(
       namespace aa {
@@ -875,7 +883,82 @@
       int main() {
         std::vector V;
       }
-    )cpp"}};
+    )cpp"},
+      {// using inner namespaces.
+       R"cpp(
+      namespace A { namespace B { 
+      inline namespace ns1 { namespace std { struct vector {}; } }
+      using namespace st^d;
+      using namespace A::B::std;
+      using namespace B::std;
+      void func() { 
+        vector V1;
+        B::vector V2;
+        A::B::vector V3;
+        A::B::std::vector V4;
+        ::A::B::std::vector V5;
+        ::A::B::ns1::std::vector V6;
+      }
+      }}
+    )cpp",
+       R"cpp(
+      namespace A { namespace B { 
+      inline namespace ns1 { namespace std { struct vector {}; } }
+      
+      
+      
+      void func() { 
+        std::vector V1;
+        std::vector V2;
+        std::vector V3;
+        std::vector V4;
+        std::vector V5;
+        std::vector V6;
+      }
+      }}
+    )cpp"},
+      {// using outer namespaces.
+       R"cpp(
+      namespace std { struct vector {}; }
+      namespace A { namespace B { 
+      using namespace st^d;
+      void func() { 
+        vector V1;
+        B::vector V2;
+        A::B::vector V3;
+        ::A::B::vector V4;
+      }
+      }}
+    )cpp",
+       R"cpp(
+      namespace std { struct vector {}; }
+      namespace A { namespace B { 
+      
+      void func() { 
+        std::vector V1;
+        std::vector V2;
+        std::vector V3;
+        std::vector V4;
+      }
+      }}
+    )cpp"},
+      {// FIXME: No replacements produced for extended namespace.
+       R"cpp(
+      namespace std { struct vector {}; }
+      namespace A { namespace B { using namespace st^d; }}
+      namespace A { namespace B { int func() { vector V1;}}}
+    )cpp",
+       R"cpp(
+      namespace std { struct vector {}; }
+      namespace A { namespace B {  }}
+      namespace A { namespace B { int func() { vector V1;}}}
+    )cpp"},
+      {// Not available for contexts that are not global or namespaces.
+       R"cpp(
+      namespace std { struct vector {}; }
+      int main() { if(true) { using namespace st^d;}}
+    )cpp",
+       "unavailable"}};
   for (auto C : Cases)
     EXPECT_EQ(C.second, apply(C.first)) << C.first;
 }
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
@@ -18,6 +18,7 @@
 #include "clang/Tooling/Core/Replacement.h"
 #include "clang/Tooling/Refactoring/RecursiveSymbolVisitor.h"
 #include "llvm/ADT/ScopeExit.h"
+#include <vector>
 
 namespace clang {
 namespace clangd {
@@ -28,9 +29,10 @@
 ///   vector<int> foo(std::map<int, int>);
 /// Would become:
 ///   std::vector<int> foo(std::map<int, int>);
-/// Currently limited to using namespace directives inside global namespace to
-/// simplify implementation. Also the namespace must not contain using
-/// directives.
+///
+/// Currently limited to using namespace directive that is global or inside a
+/// namespace. Also the namespace must not contain any using directives.
+/// Qualifies all the references that are inside the enclosing namespace.
 class RemoveUsingNamespace : public Tweak {
 public:
   const char *id() const override;
@@ -42,6 +44,7 @@
 
 private:
   const UsingDirectiveDecl *TargetDirective = nullptr;
+  const NamespaceDecl *ContainingNS = nullptr;
 };
 REGISTER_TWEAK(RemoveUsingNamespace)
 
@@ -83,11 +86,6 @@
       "", Ctx.getLangOpts());
 }
 
-// Returns true iff the parent of the Node is a TUDecl.
-bool isTopLevelDecl(const SelectionTree::Node *Node) {
-  return Node->Parent && Node->Parent->ASTNode.get<TranslationUnitDecl>();
-}
-
 // Returns the first visible context that contains this DeclContext.
 // For example: Returns ns1 for S1 and a.
 // namespace ns1 {
@@ -108,7 +106,8 @@
   TargetDirective = CA->ASTNode.get<UsingDirectiveDecl>();
   if (!TargetDirective)
     return false;
-  if (!dyn_cast<Decl>(TargetDirective->getDeclContext()))
+  auto DeclCtx = TargetDirective->getDeclContext();
+  if (!dyn_cast<Decl>(DeclCtx))
     return false;
   // FIXME: Unavailable for namespaces containing using-namespace decl.
   // It is non-trivial to deal with cases where identifiers come from the inner
@@ -122,7 +121,9 @@
   // We need to make this aware of the transitive using-namespace decls.
   if (!TargetDirective->getNominatedNamespace()->using_directives().empty())
     return false;
-  return isTopLevelDecl(CA);
+  // The containing context must be a namespace or a TU decl.
+  ContainingNS = dyn_cast<NamespaceDecl>(DeclCtx);
+  return ContainingNS || DeclCtx->isTranslationUnit();;
 }
 
 Expected<Tweak::Effect> RemoveUsingNamespace::apply(const Selection &Inputs) {
@@ -141,43 +142,45 @@
   }
 
   // Collect all references to symbols from the namespace for which we're
-  // removing the directive.
-  std::vector<SourceLocation> IdentsToQualify;
-  for (auto &D : Inputs.AST.getLocalTopLevelDecls()) {
-    findExplicitReferences(D, [&](ReferenceLoc Ref) {
-      if (Ref.Qualifier)
-        return; // This reference is already qualified.
-
-      for (auto *T : Ref.Targets) {
-        if (!visibleContext(T->getDeclContext())
-                 ->Equals(TargetDirective->getNominatedNamespace()))
-          return;
-      }
-      SourceLocation Loc = Ref.NameLoc;
-      if (Loc.isMacroID()) {
-        // Avoid adding qualifiers before macro expansions, it's probably
-        // incorrect, e.g.
-        //   namespace std { int foo(); }
-        //   #define FOO 1 + foo()
-        //   using namespace foo; // provides matrix
-        //   auto x = FOO; // Must not changed to auto x = std::FOO
-        if (!SM.isMacroArgExpansion(Loc))
-          return; // FIXME: report a warning to the users.
-        Loc = SM.getFileLoc(Ref.NameLoc);
-      }
-      assert(Loc.isFileID());
-      if (SM.getFileID(Loc) != SM.getMainFileID())
-        return; // FIXME: report these to the user as warnings?
-      if (SM.isBeforeInTranslationUnit(Loc, FirstUsingDirectiveLoc))
-        return; // Directive was not visible before this point.
-      IdentsToQualify.push_back(Loc);
-    });
-  }
-  // Remove duplicates.
-  llvm::sort(IdentsToQualify);
-  IdentsToQualify.erase(
-      std::unique(IdentsToQualify.begin(), IdentsToQualify.end()),
-      IdentsToQualify.end());
+  // removing the directive. Replace their current qualifier with the TargetNS.
+  std::vector<CharSourceRange> QualifierReplacements;
+  auto SelectRefToQualify = [&](ReferenceLoc Ref) {
+    if (Ref.Targets.empty())
+      return;
+    for (auto *T : Ref.Targets) {
+      if (!visibleContext(T->getDeclContext())
+               ->Equals(TargetDirective->getNominatedNamespace()))
+        return;
+    }
+    SourceLocation Loc = Ref.NameLoc;
+    if (Loc.isMacroID()) {
+      // Avoid adding qualifiers before macro expansions, it's probably
+      // incorrect, e.g.
+      //   namespace std { int foo(); }
+      //   #define FOO 1 + foo()
+      //   using namespace foo; // provides matrix
+      //   auto x = FOO; // Must not changed to auto x = std::FOO
+      if (!SM.isMacroArgExpansion(Loc))
+        return; // FIXME: report a warning to the users.
+      Loc = SM.getFileLoc(Ref.NameLoc);
+    }
+    assert(Loc.isFileID());
+    if (SM.getFileID(Loc) != SM.getMainFileID())
+      return; // FIXME: report these to the user as warnings?
+    if (SM.isBeforeInTranslationUnit(Loc, FirstUsingDirectiveLoc))
+      return; // Directive was not visible before this point.
+
+    CharSourceRange RemoveQualifier;
+    RemoveQualifier.setBegin(Ref.Qualifier ? Ref.Qualifier.getBeginLoc() : Loc);
+    RemoveQualifier.setEnd(Loc);
+    QualifierReplacements.push_back(RemoveQualifier);
+  };
+
+  if (ContainingNS)
+    findExplicitReferences(ContainingNS, SelectRefToQualify);
+  else
+    for (auto &D : Inputs.AST.getLocalTopLevelDecls())
+      findExplicitReferences(D, SelectRefToQualify);
 
   // Produce replacements to remove the using directives.
   tooling::Replacements R;
@@ -190,9 +193,9 @@
   }
   // Produce replacements to add the qualifiers.
   std::string Qualifier = printUsingNamespaceName(Ctx, *TargetDirective) + "::";
-  for (auto Loc : IdentsToQualify) {
-    if (auto Err = R.add(tooling::Replacement(Ctx.getSourceManager(), Loc,
-                                              /*Length=*/0, Qualifier)))
+  for (auto RemoveRange : QualifierReplacements) {
+    if (auto Err = R.add(tooling::Replacement(Ctx.getSourceManager(),
+                                              RemoveRange, Qualifier)))
       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