kbobyrev created this revision.
kbobyrev added a reviewer: hokein.
Herald added subscribers: usaxena95, kadircet, arphaman.
kbobyrev requested review of this revision.
Herald added subscribers: cfe-commits, MaskRay, ilya-biryukov.
Herald added a project: clang.

This patch allows detecting conflicts with variables defined in the current
CompoundStmt or If/While/For variable init statements.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D95925

Files:
  clang-tools-extra/clangd/refactor/Rename.cpp
  clang-tools-extra/clangd/unittests/RenameTests.cpp

Index: clang-tools-extra/clangd/unittests/RenameTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/RenameTests.cpp
+++ clang-tools-extra/clangd/unittests/RenameTests.cpp
@@ -1010,13 +1010,41 @@
       )cpp",
        "conflict", !HeaderFile, nullptr, "Conflict"},
 
-      {R"cpp(// FIXME: detecting local variables is not supported yet.
+      {R"cpp(
         void func() {
           int Conflict;
-          int [[V^ar]];
+          int V^ar;
+        }
+      )cpp",
+       "conflict", !HeaderFile, nullptr, "Conflict"},
+
+      {R"cpp(
+        void func() {
+          if (int Conflict = 42) {
+            int V^ar;
+          }
         }
       )cpp",
-       nullptr, !HeaderFile, nullptr, "Conflict"},
+       "conflict", !HeaderFile, nullptr, "Conflict"},
+
+      {R"cpp(
+        void func() {
+          while (int V^ar = 10) {
+            bool Conflict = true;
+          }
+        }
+      )cpp",
+       "conflict", !HeaderFile, nullptr, "Conflict"},
+
+      {R"cpp(
+        void func() {
+          for (int Something = 9000, Anything = 14, Conflict = 42; Anything > 9;
+               ++Something) {
+            int V^ar;
+          }
+        }
+      )cpp",
+       "conflict", !HeaderFile, nullptr, "Conflict"},
 
       {R"cpp(// Trying to rename into the same name, SameName == SameName.
         void func() {
Index: clang-tools-extra/clangd/refactor/Rename.cpp
===================================================================
--- clang-tools-extra/clangd/refactor/Rename.cpp
+++ clang-tools-extra/clangd/refactor/Rename.cpp
@@ -15,8 +15,12 @@
 #include "index/SymbolCollector.h"
 #include "support/Logger.h"
 #include "support/Trace.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/ASTTypeTraits.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclTemplate.h"
+#include "clang/AST/ParentMapContext.h"
+#include "clang/AST/Stmt.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Tooling/Syntax/Tokens.h"
 #include "llvm/ADT/None.h"
@@ -318,13 +322,72 @@
   return Results;
 }
 
+// Detect name conflict with othter DeclStmts in the same enclosing scope.
+const NamedDecl *lookupSiblingWithinEnclosingScope(ASTContext &Ctx,
+                                                   const NamedDecl &RenamedDecl,
+                                                   StringRef NewName) {
+  DynTypedNodeList Parents = Ctx.getParents(RenamedDecl);
+  if (Parents.size() != 1 || !Parents.begin()->get<DeclStmt>())
+    return nullptr;
+  Parents = Ctx.getParents(*Parents.begin()->get<DeclStmt>());
+  if (Parents.size() != 1)
+    return nullptr;
+  // For now, only CompoundStmts are supported;
+  const auto *ParentNode = Parents.begin();
+  const CompoundStmt *EnclosingScope = ParentNode->get<CompoundStmt>();
+  if (const auto *If = ParentNode->get<IfStmt>())
+    if (const auto *Then = dyn_cast<CompoundStmt>(If->getThen()))
+      EnclosingScope = Then;
+  if (const auto *While = ParentNode->get<WhileStmt>())
+    if (const auto *Body = dyn_cast<CompoundStmt>(While->getBody()))
+      EnclosingScope = Body;
+  if (const auto *For = ParentNode->get<ForStmt>())
+    if (const auto *Body = dyn_cast<CompoundStmt>(For->getBody()))
+      EnclosingScope = Body;
+  if (!EnclosingScope)
+    return nullptr;
+  // This helper checks if any statement within DeclStmt has NewName.
+  auto CheckDeclStmt = [&](const DeclStmt *DS) -> const NamedDecl * {
+    for (const auto &Child : DS->getDeclGroup())
+      if (const auto *VD = dyn_cast<VarDecl>(Child))
+        if (VD != &RenamedDecl && VD->getName() == NewName)
+          return VD;
+    return nullptr;
+  };
+  for (const auto *Node : EnclosingScope->children())
+    if (const auto *DS = dyn_cast<DeclStmt>(Node))
+      if (const auto *Result = CheckDeclStmt(DS))
+        return Result;
+  Parents = Ctx.getParents(*EnclosingScope);
+  if (Parents.size() != 1)
+    return nullptr;
+  // This helper checks if there is a condition variable has NewName.
+  auto CheckConditionVariable = [&](const auto *Scope) -> const NamedDecl * {
+    if (!Scope)
+      return nullptr;
+    if (const auto *ConditionDS = Scope->getConditionVariableDeclStmt())
+      if (const auto *Result = CheckDeclStmt(ConditionDS))
+        return Result;
+    return nullptr;
+  };
+  const auto *Parent = Parents.begin();
+  if (const auto *Result = CheckConditionVariable(Parent->get<IfStmt>()))
+    return Result;
+  if (const auto *Result = CheckConditionVariable(Parent->get<WhileStmt>()))
+    return Result;
+  if (const auto *For = Parent->get<ForStmt>())
+    if (const auto *Init = dyn_cast<DeclStmt>(For->getInit()))
+      if (const auto *Result = CheckDeclStmt(Init))
+        return Result;
+  return nullptr;
+}
+
 // Lookup the declarations (if any) with the given Name in the context of
 // RenameDecl.
-const NamedDecl *lookupSiblingWithName(const ASTContext &Ctx,
-                                       const NamedDecl &RenamedDecl,
-                                       llvm::StringRef Name) {
-  trace::Span Tracer("LookupSiblingWithName");
-  const auto &II = Ctx.Idents.get(Name);
+const NamedDecl *lookupSiblingsWithinContext(ASTContext &Ctx,
+                                             const NamedDecl &RenamedDecl,
+                                             llvm::StringRef NewName) {
+  const auto &II = Ctx.Idents.get(NewName);
   DeclarationName LookupName(&II);
   DeclContextLookupResult LookupResult;
   const auto *DC = RenamedDecl.getDeclContext();
@@ -356,6 +419,16 @@
   return nullptr;
 }
 
+const NamedDecl *lookupSiblingWithName(ASTContext &Ctx,
+                                       const NamedDecl &RenamedDecl,
+                                       llvm::StringRef NewName) {
+  trace::Span Tracer("LookupSiblingWithName");
+  if (const auto *Result =
+          lookupSiblingsWithinContext(Ctx, RenamedDecl, NewName))
+    return Result;
+  return lookupSiblingWithinEnclosingScope(Ctx, RenamedDecl, NewName);
+}
+
 struct InvalidName {
   enum Kind {
     Keywords,
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to