v1nh1shungry updated this revision to Diff 484170.
v1nh1shungry added a comment.

Insert the using-declarations in the outermost `CompoundStmt` instead of
the innermost one. Can reduce some potential rebundancy.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D137817/new/

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
@@ -250,20 +250,34 @@
         foo + 10;
       }
     )cpp"},
-      {// Does not qualify user-defined literals
-       R"cpp(
-      namespace ns {
-      long double operator "" _w(long double);
+      {
+          R"cpp(
+      namespace a {
+      long double operator""_a(long double);
+      inline namespace b {
+      long double operator""_b(long double);
+      } // namespace b
+      } // namespace a
+      using namespace ^a;
+      int main() {
+        1.0_a;
+        1.0_b;
       }
-      using namespace n^s;
-      int main() { 1.5_w; }
     )cpp",
-       R"cpp(
-      namespace ns {
-      long double operator "" _w(long double);
-      }
+          R"cpp(
+      namespace a {
+      long double operator""_a(long double);
+      inline namespace b {
+      long double operator""_b(long double);
+      } // namespace b
+      } // namespace a
       
-      int main() { 1.5_w; }
+      int main() {using a::operator""_a;
+using a::operator""_b;
+
+        1.0_a;
+        1.0_b;
+      }
     )cpp"}};
   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
@@ -13,6 +13,7 @@
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclBase.h"
 #include "clang/AST/DeclCXX.h"
+#include "clang/AST/ParentMapContext.h"
 #include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Tooling/Core/Replacement.h"
@@ -101,6 +102,66 @@
   return D;
 }
 
+void handleUserDefinedLiterals(
+    Decl *D, const llvm::SmallPtrSet<const NamedDecl *, 1> &Literals,
+    std::map<SourceLocation, llvm::StringSet<>> &Result, ASTContext &Ctx) {
+  struct Visitor : RecursiveASTVisitor<Visitor> {
+    Visitor(const llvm::SmallPtrSet<const NamedDecl *, 1> &Literals,
+            std::map<SourceLocation, llvm::StringSet<>> &Result,
+            ASTContext &Ctx)
+        : Literals{Literals}, Result{Result}, Ctx{Ctx} {}
+
+    const llvm::SmallPtrSet<const NamedDecl *, 1> &Literals;
+    std::map<SourceLocation, llvm::StringSet<>> &Result;
+    ASTContext &Ctx;
+
+    bool VisitDeclRefExpr(DeclRefExpr *DRE) {
+      const NamedDecl *ND = DRE->getFoundDecl();
+      // If DRE references to a user-defined literal
+      if (ND->getDeclName().getNameKind() ==
+          DeclarationName::CXXLiteralOperatorName) {
+        for (const NamedDecl *Literal : Literals) {
+          if (ND == Literal) {
+            // If the user-defined literal locates in a CompoundStmt,
+            // we mark the CompoundStmt so that we can add the using-declaration
+            // in it later. If not, the user-defined literal is used in the
+            // top-level, it is reasonable to do nothing for it.
+            if (const CompoundStmt *CS = getCompoundParent(DRE))
+              Result[CS->getLBracLoc()].insert(ND->getQualifiedNameAsString());
+            return true;
+          }
+        }
+      }
+      return true;
+    }
+
+    // Get the outermost CompoundStmt parent of DRE
+    const CompoundStmt *getCompoundParent(DeclRefExpr *DRE) {
+      const CompoundStmt *R = nullptr;
+
+      DynTypedNodeList Parents = Ctx.getParents(*DRE);
+      llvm::SmallVector<DynTypedNode> NodesToProcess{Parents.begin(),
+                                                     Parents.end()};
+
+      while (!NodesToProcess.empty()) {
+        DynTypedNode Node = NodesToProcess.back();
+        NodesToProcess.pop_back();
+
+        if (const auto *CS = Node.get<CompoundStmt>())
+          R = CS;
+        if (Node.get<TranslationUnitDecl>())
+          break;
+        Parents = Ctx.getParents(Node);
+        NodesToProcess.append(Parents.begin(), Parents.end());
+      }
+      return R;
+    }
+  };
+
+  Visitor V{Literals, Result, Ctx};
+  V.TraverseDecl(D);
+}
+
 bool RemoveUsingNamespace::prepare(const Selection &Inputs) {
   // Find the 'using namespace' directive under the cursor.
   auto *CA = Inputs.ASTSelection.commonAncestor();
@@ -144,7 +205,15 @@
   // Collect all references to symbols from the namespace for which we're
   // removing the directive.
   std::vector<SourceLocation> IdentsToQualify;
+  // Collect all user-defined literals from the namespace for which we're
+  // removing the directive
+  std::map<SourceLocation, llvm::StringSet<>> LiteralsToUsing;
+
   for (auto &D : Inputs.AST->getLocalTopLevelDecls()) {
+    if (SM.isBeforeInTranslationUnit(D->getBeginLoc(), FirstUsingDirectiveLoc))
+      continue; // Directive was not visible before this point.
+
+    llvm::SmallPtrSet<const NamedDecl *, 1> Literals;
     findExplicitReferences(
         D,
         [&](ReferenceLoc Ref) {
@@ -164,15 +233,22 @@
             // 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
-            // declared in an inline namespace, e.g.
-            //   using namespace s^td;
-            //   int main() { cout << "foo"s; }
-            // change to
-            //   using namespace std::literals;
-            //   int main() { std::cout << "foo"s; }
-            if (Kind == DeclarationName::NameKind::CXXLiteralOperatorName)
+            // Also add a using-declaration to keep codes well-formed, e.g.
+            //   using namespace std;
+            //   int main() {
+            //     auto s = "foo"s;
+            //   }
+            // will change to
+            //   int main() {
+            //     using std::operator""s;
+            //     auto s = "foo"s;
+            //   }
+            if (Kind == DeclarationName::NameKind::CXXLiteralOperatorName) {
+              // Can't get the AST node from the ReferenceLoc, we have to
+              // traverse D later to handle these user-defined literals
+              Literals.insert(T);
               return;
+            }
           }
           SourceLocation Loc = Ref.NameLoc;
           if (Loc.isMacroID()) {
@@ -189,11 +265,11 @@
           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);
         },
         Inputs.AST->getHeuristicResolver());
+    if (!Literals.empty())
+      handleUserDefinedLiterals(D, Literals, LiteralsToUsing, Ctx);
   }
   // Remove duplicates.
   llvm::sort(IdentsToQualify);
@@ -213,10 +289,20 @@
   // 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,
+    if (auto Err = R.add(tooling::Replacement(SM, Loc,
                                               /*Length=*/0, Qualifier)))
       return std::move(Err);
   }
+  // Produce replacements to add the using-declarations for user-defined
+  // literals
+  for (auto &[Loc, Literals] : LiteralsToUsing) {
+    std::string UsingDeclarations;
+    for (const auto &L : Literals)
+      UsingDeclarations += llvm::formatv("using {0};\n", L.getKey());
+    if (auto Err = R.add(tooling::Replacement(SM, Loc.getLocWithOffset(1), 0,
+                                              UsingDeclarations)))
+      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
  • [PATCH] D137817: [clangd] Imp... Vincent Hong via Phabricator via cfe-commits

Reply via email to