kbobyrev updated this revision to Diff 303940. kbobyrev added a comment. Finish fields canonicalization.
Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D71880/new/ https://reviews.llvm.org/D71880 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 @@ -91,10 +91,86 @@ } TEST(RenameTest, WithinFileRename) { - // rename is runnning on all "^" points, and "[[]]" ranges point to the - // identifier that is being renamed. + // For each "^" this test moves cursor to its location and applies renaming + // while checking that all identifiers enclosed in [[]] ranges are handled + // correctly. llvm::StringRef Tests[] = { - // Function. + // Templated static method instantiation. + R"cpp( + template<typename T> + class Foo { + public: + static T [[f^oo]]() {} + }; + + void bar() { + Foo<int>::[[f^oo]](); + } + )cpp", + + // Templated method instantiation. + R"cpp( + template<typename T> + class Foo { + public: + T [[f^oo]]() {} + }; + + void bar() { + Foo<int>().[[f^oo]](); + } + )cpp", + + // Class template (partial) specialization forward declarations. + R"cpp( + template<typename T, typename U=bool> + class [[Foo^]]; + + template<typename T, typename U> + class [[Foo^]] {}; + + template<typename T=int, typename U> + class [[Foo^]]; + )cpp", + + // Class template (full) specialization forward declaration. + R"cpp( + template<typename T=float, typename U=int> + class [[Foo^]]; + + template<typename T, typename U> + class [[Foo^]] {}; + )cpp", + + // Function template specialization forward declaration. + R"cpp( + template<typename T=int, typename U=bool> + U [[foo^]](); + + template<typename T, typename U> + U [[foo^]]() {}; + )cpp", + + // Function template specialization forward declaration. + R"cpp( + template<typename T, typename U> + U [[foo^]]() {}; + + template<typename T=int, typename U=bool> + U [[foo^]](); + )cpp", + + // Function template specialization forward declaration without function + // definition. + R"cpp( + template<typename T=int, typename U=bool> + U [[foo^]](); + + template<typename T, typename U> + U [[foo^]](); + )cpp", + + // Simple recursive function. R"cpp( void [[foo^]]() { [[fo^o]](); @@ -104,7 +180,7 @@ // Type. R"cpp( struct [[foo^]] {}; - [[foo]] test() { + [[foo^]] test() { [[f^oo]] x; return x; } @@ -114,20 +190,21 @@ R"cpp( void bar() { if (auto [[^foo]] = 5) { - [[foo]] = 3; + [[fo^o]] = 3; } } )cpp", - // Rename class, including constructor/destructor. + // Class, its constructor and destructor. R"cpp( class [[F^oo]] { + public: [[F^oo]](); - ~[[Foo]](); + ~[[Fo^o]](); void foo(int x); }; - [[Foo]]::[[Fo^o]]() {} - void [[Foo]]::foo(int x) {} + [[Fo^o]]::[[Fo^o]]() {} + void [[Fo^o]]::foo(int x) {} )cpp", // Rename template class, including constructor/destructor. @@ -199,9 +276,9 @@ class [[F^oo]]<T*> {}; void test() { - [[Foo]]<int> x; - [[Foo]]<bool> y; - [[Foo]]<int*> z; + [[F^oo]]<int> x; + [[Fo^o]]<bool> y; + [[Foo^]]<int*> z; } )cpp", @@ -360,8 +437,8 @@ void boo(int); void qoo() { - [[foo]](); - boo([[foo]]()); + [[f^oo]](); + boo([[fo^o]]()); M1(); boo(M1()); M2([[foo]]()); @@ -454,7 +531,7 @@ } )cpp", - // template class in template argument list. + // Template class in template argument list. R"cpp( template<typename T> class [[Fo^o]] {}; @@ -510,6 +587,263 @@ } } +TEST(RenameTest, Alias) { + // For each "^" this test moves cursor to its location and applies renaming + // while checking that all identifiers enclosed in [[]] ranges are handled + // correctly. + llvm::StringRef Tests[] = { + R"cpp( + class X {}; + typedef X [[Fo^o]]; + )cpp", + + R"cpp( + class X {}; + using [[U^Old]] = X; + )cpp", + + R"cpp( + template <typename T> + class X { T t; }; + + template <typename T> + using [[O^ld]] = X<T>; + )cpp", + + R"cpp( + namespace x { class X {}; } + namespace ns { + using [[F^oo]] = x::X; + } + )cpp", + + R"cpp( + namespace x { class Old {}; } + namespace ns { + #define REF(alias) alias alias_var; + + #define ALIAS(old) \ + using old##Alias = x::old; \ + REF(old##Alias); + + ALIAS(Old); + + [[Ol^dAlias]] old_alias; + } + )cpp", + }; + for (llvm::StringRef T : Tests) { + SCOPED_TRACE(T); + Annotations Code(T); + auto TU = TestTU::withCode(Code.code()); + TU.ExtraArgs.push_back("-fno-delayed-template-parsing"); + auto AST = TU.build(); + llvm::StringRef NewName = "abcde"; + for (const auto &RenamePos : Code.points()) { + auto RenameResult = + rename({RenamePos, NewName, AST, testPath(TU.Filename)}); + ASSERT_TRUE(bool(RenameResult)) << RenameResult.takeError(); + ASSERT_EQ(1u, RenameResult->GlobalChanges.size()); + EXPECT_EQ( + applyEdits(std::move(RenameResult->GlobalChanges)).front().second, + expectedResult(Code, NewName)); + } + } +} + +TEST(RenameTest, Member) { + // For each "^" this test moves cursor to its location and applies renaming + // while checking that all identifiers enclosed in [[]] ranges are handled + // correctly. + llvm::StringRef Tests[] = { + R"cpp( + struct X { + int [[M^oo]]; + void Baz() { [[M^oo]] = 1; } + }; + )cpp", + + R"cpp( + struct X { + void [[F^oo]]() {} + void Baz() { [[Foo^]](); } + }; + )cpp", + + R"cpp( + struct A {}; + struct B {}; + class X { + public: + X(); + A [[a^]]; + A a2; + B b; + }; + + X::X():[[a^]](), b() {} + )cpp", + + R"cpp( + class Foo { + public: + Foo(int Variable) : [[Variabl^e]](Variable) {} + + int [[Va^riable]] = 42; + + private: + void foo() { ++[[Vari^able]]; } + }; + + void bar() { + Foo f(9000); + f.[[Variable^]] = -1; + } + )cpp", + + R"cpp( + template<typename T> + struct Foo { + T [[Vari^able]] = 42; + }; + + void foo() { + Foo<int> f; + f.[[Varia^ble]] = 9000; + } + )cpp", + + R"cpp( + template<typename T, typename U> + struct Foo { + T [[Variab^le]] = 42; + U Another; + + void bar() {} + }; + + // Variables here are not related to the ones in the fully templated + // class. + template<typename T> + struct Foo<T, bool> { + T Variable; + void bar() { ++Variable; } + }; + template<> + struct Foo<int, bool> { + int Variable; + void bar() { ++Variable; } + }; + + void foo() { + Foo<unsigned, char> f; + f.[[Varia^ble]] = 9000; + } + )cpp", + + R"cpp( + template<typename T, typename U> + struct Foo { + T Variable[42]; + U Another; + + void bar() {} + }; + + template<typename T> + struct Foo<T, bool> { + T [[Var^iable]]; + void bar() { ++[[Var^iable]]; } + }; + + void foo() { + Foo<unsigned, bool> f; + f.[[Var^iable]] = 9000; + } + )cpp", + + R"cpp( + template<typename T, typename U> + struct Foo { + T Variable[42]; + U Another; + + void bar() {} + }; + + template<typename T> + struct Foo<T, bool> { + T Variable; + void bar() { ++Variable; } + }; + + template<> + struct Foo<unsigned, bool> { + unsigned [[Var^iable]]; + void bar() { ++[[Var^iable]]; } + }; + + void foo() { + Foo<unsigned, bool> f; + f.[[Var^iable]] = 9000; + } + )cpp", + }; + for (llvm::StringRef T : Tests) { + SCOPED_TRACE(T); + Annotations Code(T); + auto TU = TestTU::withCode(Code.code()); + TU.ExtraArgs.push_back("-fno-delayed-template-parsing"); + auto AST = TU.build(); + llvm::StringRef NewName = "abcde"; + for (const auto &RenamePos : Code.points()) { + auto RenameResult = + rename({RenamePos, NewName, AST, testPath(TU.Filename)}); + ASSERT_TRUE(bool(RenameResult)) << RenameResult.takeError(); + ASSERT_EQ(1u, RenameResult->GlobalChanges.size()); + EXPECT_EQ( + applyEdits(std::move(RenameResult->GlobalChanges)).front().second, + expectedResult(Code, NewName)); + } + } +} + +TEST(RenameTest, Enum) { + // For each "^" this test moves cursor to its location and applies renaming + // while checking that all identifiers enclosed in [[]] ranges are handled + // correctly. + llvm::StringRef Tests[] = { + R"cpp( + namespace ns { + enum [[Old^]] { Blue }; + } + )cpp", + + R"cpp( + namespace ns { + enum class [[Old^]] { Blue }; + } + )cpp", + }; + for (llvm::StringRef T : Tests) { + SCOPED_TRACE(T); + Annotations Code(T); + auto TU = TestTU::withCode(Code.code()); + TU.ExtraArgs.push_back("-fno-delayed-template-parsing"); + auto AST = TU.build(); + llvm::StringRef NewName = "abcde"; + for (const auto &RenamePos : Code.points()) { + auto RenameResult = + rename({RenamePos, NewName, AST, testPath(TU.Filename)}); + ASSERT_TRUE(bool(RenameResult)) << RenameResult.takeError(); + ASSERT_EQ(1u, RenameResult->GlobalChanges.size()); + EXPECT_EQ( + applyEdits(std::move(RenameResult->GlobalChanges)).front().second, + expectedResult(Code, NewName)); + } + } +} + TEST(RenameTest, Renameable) { struct Case { const char *Code; Index: clang-tools-extra/clangd/refactor/Rename.cpp =================================================================== --- clang-tools-extra/clangd/refactor/Rename.cpp +++ clang-tools-extra/clangd/refactor/Rename.cpp @@ -92,6 +92,7 @@ targetDecl(SelectedNode->ASTNode, DeclRelation::Alias | DeclRelation::TemplatePattern)) { // Get to CXXRecordDecl from constructor or destructor. + // FIXME(kirillbobyrev): Just use getRenameRoot? D = tooling::getCanonicalSymbolDeclaration(D); Result.insert(D); } @@ -219,23 +220,80 @@ return error("Cannot rename symbol: {0}", Message(Reason)); } +const Decl *getRenameRootDecl(const ClassTemplateSpecializationDecl *D) { + return D->getSpecializedTemplate()->getTemplatedDecl(); +} + +const Decl *getRenameRootDecl(const TemplateDecl *D) { + return D->getTemplatedDecl(); +} + +const Decl *getRenameRootDecl(const CXXMethodDecl *D) { + const auto *Result = D; + if (const auto *InstantiatedMethod = D->getInstantiatedFromMemberFunction()) + Result = cast<CXXMethodDecl>(InstantiatedMethod); + while (Result->isVirtual() && Result->size_overridden_methods()) + Result = *Result->overridden_methods().begin(); + return Result; +} + +const Decl *getRenameRootDecl(const FunctionDecl *D) { + const auto *Definition = D->getDefinition(); + const auto *Candidate = Definition ? Definition : D->getMostRecentDecl(); + return Candidate->isTemplateInstantiation() + ? Candidate->getPrimaryTemplate()->getTemplatedDecl() + : Candidate; +} + +const Decl *getRenameRootDecl(const FieldDecl *D) { + // This is a hacky way to do something like + // CXXMethodDecl::getINstantiatedFromMemberFunction for the field because + // Clang AST does not store relevant information about the field that is + // instantiated. + const auto *TemplateSpec = + dyn_cast<ClassTemplateSpecializationDecl>(D->getParent()); + if (!TemplateSpec) + return D; + const auto *FieldParent = TemplateSpec->getTemplateInstantiationPattern(); + if (!FieldParent) + return D; + for (const auto *Field : FieldParent->fields()) + if (Field->getFieldIndex() == D->getFieldIndex() && + Field->getLocation() == D->getLocation()) + return Field; + return D; +} + +// FIXME(kirillbobyrev): Write documentation. +const Decl *getRenameRootDecl(const Decl *D) { + const auto *Candidate = D; + if (const auto *RD = dyn_cast<RecordDecl>(D)) { + const auto *Definition = RD->getDefinition(); + Candidate = Definition ? Definition : RD->getMostRecentDecl(); + } + if (const auto *Template = dyn_cast<TemplateDecl>(Candidate)) + return getRenameRootDecl(Template); + if (const auto *ClassTemplateSpecialization = + dyn_cast<ClassTemplateSpecializationDecl>(Candidate)) + return getRenameRootDecl(ClassTemplateSpecialization); + if (const auto *Constructor = dyn_cast<CXXConstructorDecl>(Candidate)) + return getRenameRootDecl(Constructor->getParent()); + if (const auto *Destructor = dyn_cast<CXXDestructorDecl>(Candidate)) + return getRenameRootDecl(Destructor->getParent()); + if (const auto *Method = dyn_cast<CXXMethodDecl>(Candidate)) + return getRenameRootDecl(Method); + if (const auto *Function = dyn_cast<FunctionDecl>(Candidate)) + return getRenameRootDecl(Function); + if (const auto *Field = dyn_cast<FieldDecl>(Candidate)) + return getRenameRootDecl(Field); + return Candidate; +} + // Return all rename occurrences in the main file. std::vector<SourceLocation> findOccurrencesWithinFile(ParsedAST &AST, const NamedDecl &ND) { trace::Span Tracer("FindOccurrenceeWithinFile"); - // If the cursor is at the underlying CXXRecordDecl of the - // ClassTemplateDecl, ND will be the CXXRecordDecl. In this case, we need to - // get the primary template manually. - // getUSRsForDeclaration will find other related symbols, e.g. virtual and its - // overriddens, primary template and all explicit specializations. - // FIXME: Get rid of the remaining tooling APIs. - const auto *RenameDecl = - ND.getDescribedTemplate() ? ND.getDescribedTemplate() : &ND; - std::vector<std::string> RenameUSRs = - tooling::getUSRsForDeclaration(RenameDecl, AST.getASTContext()); - llvm::DenseSet<SymbolID> TargetIDs; - for (auto &USR : RenameUSRs) - TargetIDs.insert(SymbolID(USR)); + const auto *RenameDeclRoot = getRenameRootDecl(&ND); std::vector<SourceLocation> Results; for (Decl *TopLevelDecl : AST.getLocalTopLevelDecls()) { @@ -243,11 +301,11 @@ if (Ref.Targets.empty()) return; for (const auto *Target : Ref.Targets) { - auto ID = getSymbolID(Target); - if (!ID || TargetIDs.find(ID) == TargetIDs.end()) + if (getRenameRootDecl(Target) == RenameDeclRoot) { + Results.push_back(Ref.NameLoc); return; + } } - Results.push_back(Ref.NameLoc); }); }
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits