return true;
}
+ bool VisitMemberExpr(const MemberExpr *Expr) {
+ const NamedDecl *Decl = Expr->getFoundDecl();
+ auto StartLoc = Expr->getMemberLoc();
+ auto EndLoc = Expr->getMemberLoc();
+ if (isInUSRSet(Decl)) {
+ RenameInfos.push_back({StartLoc, EndLoc,
+ /*FromDecl=*/nullptr,
+ /*Context=*/nullptr,
+ /*Specifier=*/nullptr,
+ /*IgnorePrefixQualifiers=*/true});
+ }
+ return true;
+ }
+
+ bool VisitCXXConstructorDecl(const CXXConstructorDecl *CD) {
+ // Fix the constructor initializer when renaming class members.
+ for (const auto *Initializer : CD->inits()) {
+ // Ignore implicit initializers.
+ if (!Initializer->isWritten())
+ continue;
+
+ if (const FieldDecl *FD = Initializer->getMember()) {
+ if (isInUSRSet(FD)) {
+ auto Loc = Initializer->getSourceLocation();
+ RenameInfos.push_back({Loc, Loc,
+ /*FromDecl=*/nullptr,
+ /*Context=*/nullptr,
+ /*Specifier=*/nullptr,
+ /*IgnorePrefixQualifiers=*/true});
+ }
+ }
+ }
+ return true;
+ }
+
bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
const NamedDecl *Decl = Expr->getFoundDecl();
// Get the underlying declaration of the shadow declaration introduced by a
? Expr->getLAngleLoc().getLocWithOffset(-1)
: Expr->getLocEnd();
+ if (const auto *MD = llvm::dyn_cast<CXXMethodDecl>(Decl)) {
+ if (isInUSRSet(MD)) {
+ // Handle renaming static template class methods, we only rename the
+ // name without prefix qualifiers and restrict the source range to the
+ // name.
+ RenameInfos.push_back({EndLoc, EndLoc,
+ /*FromDecl=*/nullptr,
+ /*Context=*/nullptr,
+ /*Specifier=*/nullptr,
+ /*IgnorePrefixQualifiers=*/true});
+ return true;
+ }
+ }
+
// In case of renaming an enum declaration, we have to explicitly handle
// unscoped enum constants referenced in expressions (e.g.
// "auto r = ns1::ns2::Green" where Green is an enum constant of an unscoped
--- /dev/null
+//===-- ClangMemberTests.cpp - unit tests for renaming class members ------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangRenameTest.h"
+
+namespace clang {
+namespace clang_rename {
+namespace test {
+namespace {
+
+class RenameMemberTest : public ClangRenameTest {
+public:
+ RenameMemberTest() {
+ AppendToHeader(R"(
+ struct NA {
+ void Foo();
+ void NotFoo();
+ static void SFoo();
+ static void SNotFoo();
+ int Moo;
+ };
+ struct A {
+ virtual void Foo();
+ void NotFoo();
+ static void SFoo();
+ static void SNotFoo();
+ int Moo;
+ int NotMoo;
+ static int SMoo;
+ };
+ struct B : public A {
+ void Foo() override;
+ };
+ template <typename T> struct TA {
+ T* Foo();
+ T* NotFoo();
+ static T* SFoo();
+ static T* NotSFoo();
+ };
+ template <typename T> struct TB : public TA<T> {};
+ namespace ns {
+ template <typename T> struct TA {
+ T* Foo();
+ T* NotFoo();
+ static T* SFoo();
+ static T* NotSFoo();
+ static int SMoo;
+ };
+ template <typename T> struct TB : public TA<T> {};
+ struct A {
+ void Foo();
+ void NotFoo();
+ static void SFoo();
+ static void SNotFoo();
+ };
+ struct B : public A {};
+ struct C {
+ template <class T>
+ void SFoo(const T& t) {}
+ template <class T>
+ void Foo() {}
+ };
+ })");
+ }
+};
+
+INSTANTIATE_TEST_CASE_P(
+ DISABLED_RenameTemplatedClassStaticVariableTest, RenameMemberTest,
+ testing::ValuesIn(std::vector<Case>({
+ // FIXME: support renaming static variables for template classes.
+ {"void f() { ns::TA<int>::SMoo; }",
+ "void f() { ns::TA<int>::SMeh; }", "ns::TA::SMoo", "ns::TA::SMeh"},
+ })), );
+
+INSTANTIATE_TEST_CASE_P(
+ RenameMemberTest, RenameMemberTest,
+ testing::ValuesIn(std::vector<Case>({
+ // Normal methods and fields.
+ {"void f() { A a; a.Foo(); }", "void f() { A a; a.Bar(); }", "A::Foo",
+ "A::Bar"},
+ {"void f() { ns::A a; a.Foo(); }", "void f() { ns::A a; a.Bar(); }",
+ "ns::A::Foo", "ns::A::Bar"},
+ {"void f() { A a; int x = a.Moo; }", "void f() { A a; int x = a.Meh; }",
+ "A::Moo", "A::Meh"},
+ {"void f() { B b; b.Foo(); }", "void f() { B b; b.Bar(); }", "B::Foo",
+ "B::Bar"},
+ {"void f() { ns::B b; b.Foo(); }", "void f() { ns::B b; b.Bar(); }",
+ "ns::A::Foo", "ns::A::Bar"},
+ {"void f() { B b; int x = b.Moo; }", "void f() { B b; int x = b.Meh; }",
+ "A::Moo", "A::Meh"},
+
+ // Static methods.
+ {"void f() { A::SFoo(); }", "void f() { A::SBar(); }", "A::SFoo",
+ "A::SBar"},
+ {"void f() { ns::A::SFoo(); }", "void f() { ns::A::SBar(); }",
+ "ns::A::SFoo", "ns::A::SBar"},
+ {"void f() { TA<int>::SFoo(); }", "void f() { TA<int>::SBar(); }",
+ "TA::SFoo", "TA::SBar"},
+ {"void f() { ns::TA<int>::SFoo(); }",
+ "void f() { ns::TA<int>::SBar(); }", "ns::TA::SFoo", "ns::TA::SBar"},
+
+ // Static variables.
+ {"void f() { A::SMoo; }",
+ "void f() { A::SMeh; }", "A::SMoo", "A::SMeh"},
+
+ // Templated methods.
+ {"void f() { TA<int> a; a.Foo(); }", "void f() { TA<int> a; a.Bar(); }",
+ "TA::Foo", "TA::Bar"},
+ {"void f() { ns::TA<int> a; a.Foo(); }",
+ "void f() { ns::TA<int> a; a.Bar(); }", "ns::TA::Foo", "ns::TA::Bar"},
+ {"void f() { TB<int> b; b.Foo(); }", "void f() { TB<int> b; b.Bar(); }",
+ "TA::Foo", "TA::Bar"},
+ {"void f() { ns::TB<int> b; b.Foo(); }",
+ "void f() { ns::TB<int> b; b.Bar(); }", "ns::TA::Foo", "ns::TA::Bar"},
+ {"void f() { ns::C c; int x; c.SFoo(x); }",
+ "void f() { ns::C c; int x; c.SBar(x); }", "ns::C::SFoo",
+ "ns::C::SBar"},
+ {"void f() { ns::C c; c.Foo<int>(); }",
+ "void f() { ns::C c; c.Bar<int>(); }", "ns::C::Foo", "ns::C::Bar"},
+
+ // Pointers to methods.
+ {"void f() { auto p = &A::Foo; }", "void f() { auto p = &A::Bar; }",
+ "A::Foo", "A::Bar"},
+ {"void f() { auto p = &A::SFoo; }", "void f() { auto p = &A::SBar; }",
+ "A::SFoo", "A::SBar"},
+ {"void f() { auto p = &B::Foo; }", "void f() { auto p = &B::Bar; }",
+ "B::Foo", "B::Bar"},
+ {"void f() { auto p = &ns::A::Foo; }",
+ "void f() { auto p = &ns::A::Bar; }", "ns::A::Foo", "ns::A::Bar"},
+ {"void f() { auto p = &ns::A::SFoo; }",
+ "void f() { auto p = &ns::A::SBar; }", "ns::A::SFoo", "ns::A::SBar"},
+ {"void f() { auto p = &ns::C::SFoo<int>; }",
+ "void f() { auto p = &ns::C::SBar<int>; }", "ns::C::SFoo",
+ "ns::C::SBar"},
+
+ // These methods are not declared or overrided in the subclass B, we
+ // have to use the qualified name with parent class A to identify them.
+ {"void f() { auto p = &ns::B::Foo; }",
+ "void f() { auto p = &ns::B::Bar; }", "ns::A::Foo", "ns::B::Bar"},
+ {"void f() { B::SFoo(); }", "void f() { B::SBar(); }", "A::SFoo",
+ "B::SBar"},
+ {"void f() { ns::B::SFoo(); }", "void f() { ns::B::SBar(); }",
+ "ns::A::SFoo", "ns::B::SBar"},
+ {"void f() { auto p = &B::SFoo; }", "void f() { auto p = &B::SBar; }",
+ "A::SFoo", "B::SBar"},
+ {"void f() { auto p = &ns::B::SFoo; }",
+ "void f() { auto p = &ns::B::SBar; }", "ns::A::SFoo", "ns::B::SBar"},
+ {"void f() { TB<int>::SFoo(); }", "void f() { TB<int>::SBar(); }",
+ "TA::SFoo", "TB::SBar"},
+ {"void f() { ns::TB<int>::SFoo(); }",
+ "void f() { ns::TB<int>::SBar(); }", "ns::TA::SFoo", "ns::TB::SBar"},
+ })), );
+
+TEST_P(RenameMemberTest, RenameMembers) {
+ auto Param = GetParam();
+ assert(!Param.OldName.empty());
+ assert(!Param.NewName.empty());
+ std::string Actual =
+ runClangRenameOnCode(Param.Before, Param.OldName, Param.NewName);
+ CompareSnippets(Param.After, Actual);
+}
+
+TEST_F(RenameMemberTest, RenameMemberInsideClassMethods) {
+ std::string Before = R"(
+ struct X {
+ int Moo;
+ void Baz() { Moo = 1; }
+ };)";
+ std::string Expected = R"(
+ struct X {
+ int Meh;
+ void Baz() { Meh = 1; }
+ };)";
+ std::string After = runClangRenameOnCode(Before, "X::Moo", "Y::Meh");
+ CompareSnippets(Expected, After);
+}
+
+TEST_F(RenameMemberTest, RenameMethodInsideClassMethods) {
+ std::string Before = R"(
+ struct X {
+ void Foo() {}
+ void Baz() { Foo(); }
+ };)";
+ std::string Expected = R"(
+ struct X {
+ void Bar() {}
+ void Baz() { Bar(); }
+ };)";
+ std::string After = runClangRenameOnCode(Before, "X::Foo", "X::Bar");
+ CompareSnippets(Expected, After);
+}
+
+TEST_F(RenameMemberTest, RenameCtorInitializer) {
+ std::string Before = R"(
+ class X {
+ public:
+ X();
+ A a;
+ A a2;
+ B b;
+ };
+
+ X::X():a(), b() {}
+ )";
+ std::string Expected = R"(
+ class X {
+ public:
+ X();
+ A bar;
+ A a2;
+ B b;
+ };
+
+ X::X():bar(), b() {}
+ )";
+ std::string After = runClangRenameOnCode(Before, "X::a", "X::bar");
+ CompareSnippets(Expected, After);
+}
+
+} // anonymous namespace
+} // namespace test
+} // namespace clang_rename
+} // namesdpace clang