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