Skip to content

Commit f8bfb34

Browse files
committedOct 25, 2017
[rename] support renaming class member.
Reviewers: ioeric Reviewed By: ioeric Subscribers: klimek, cfe-commits, mgorny Differential Revision: https://reviews.llvm.org/D39178 llvm-svn: 316571
1 parent b350221 commit f8bfb34

File tree

3 files changed

+279
-0
lines changed

3 files changed

+279
-0
lines changed
 

‎clang/lib/Tooling/Refactoring/Rename/USRLocFinder.cpp

+49
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,41 @@ class RenameLocFinder : public RecursiveASTVisitor<RenameLocFinder> {
212212
return true;
213213
}
214214

215+
bool VisitMemberExpr(const MemberExpr *Expr) {
216+
const NamedDecl *Decl = Expr->getFoundDecl();
217+
auto StartLoc = Expr->getMemberLoc();
218+
auto EndLoc = Expr->getMemberLoc();
219+
if (isInUSRSet(Decl)) {
220+
RenameInfos.push_back({StartLoc, EndLoc,
221+
/*FromDecl=*/nullptr,
222+
/*Context=*/nullptr,
223+
/*Specifier=*/nullptr,
224+
/*IgnorePrefixQualifiers=*/true});
225+
}
226+
return true;
227+
}
228+
229+
bool VisitCXXConstructorDecl(const CXXConstructorDecl *CD) {
230+
// Fix the constructor initializer when renaming class members.
231+
for (const auto *Initializer : CD->inits()) {
232+
// Ignore implicit initializers.
233+
if (!Initializer->isWritten())
234+
continue;
235+
236+
if (const FieldDecl *FD = Initializer->getMember()) {
237+
if (isInUSRSet(FD)) {
238+
auto Loc = Initializer->getSourceLocation();
239+
RenameInfos.push_back({Loc, Loc,
240+
/*FromDecl=*/nullptr,
241+
/*Context=*/nullptr,
242+
/*Specifier=*/nullptr,
243+
/*IgnorePrefixQualifiers=*/true});
244+
}
245+
}
246+
}
247+
return true;
248+
}
249+
215250
bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
216251
const NamedDecl *Decl = Expr->getFoundDecl();
217252
// Get the underlying declaration of the shadow declaration introduced by a
@@ -227,6 +262,20 @@ class RenameLocFinder : public RecursiveASTVisitor<RenameLocFinder> {
227262
? Expr->getLAngleLoc().getLocWithOffset(-1)
228263
: Expr->getLocEnd();
229264

265+
if (const auto *MD = llvm::dyn_cast<CXXMethodDecl>(Decl)) {
266+
if (isInUSRSet(MD)) {
267+
// Handle renaming static template class methods, we only rename the
268+
// name without prefix qualifiers and restrict the source range to the
269+
// name.
270+
RenameInfos.push_back({EndLoc, EndLoc,
271+
/*FromDecl=*/nullptr,
272+
/*Context=*/nullptr,
273+
/*Specifier=*/nullptr,
274+
/*IgnorePrefixQualifiers=*/true});
275+
return true;
276+
}
277+
}
278+
230279
// In case of renaming an enum declaration, we have to explicitly handle
231280
// unscoped enum constants referenced in expressions (e.g.
232281
// "auto r = ns1::ns2::Green" where Green is an enum constant of an unscoped

‎clang/unittests/Rename/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ add_clang_unittest(ClangRenameTests
99
RenameClassTest.cpp
1010
RenameEnumTest.cpp
1111
RenameAliasTest.cpp
12+
RenameMemberTest.cpp
1213
RenameFunctionTest.cpp
1314
)
1415

+229
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
//===-- ClangMemberTests.cpp - unit tests for renaming class members ------===//
2+
//
3+
// The LLVM Compiler Infrastructure
4+
//
5+
// This file is distributed under the University of Illinois Open Source
6+
// License. See LICENSE.TXT for details.
7+
//
8+
//===----------------------------------------------------------------------===//
9+
10+
#include "ClangRenameTest.h"
11+
12+
namespace clang {
13+
namespace clang_rename {
14+
namespace test {
15+
namespace {
16+
17+
class RenameMemberTest : public ClangRenameTest {
18+
public:
19+
RenameMemberTest() {
20+
AppendToHeader(R"(
21+
struct NA {
22+
void Foo();
23+
void NotFoo();
24+
static void SFoo();
25+
static void SNotFoo();
26+
int Moo;
27+
};
28+
struct A {
29+
virtual void Foo();
30+
void NotFoo();
31+
static void SFoo();
32+
static void SNotFoo();
33+
int Moo;
34+
int NotMoo;
35+
static int SMoo;
36+
};
37+
struct B : public A {
38+
void Foo() override;
39+
};
40+
template <typename T> struct TA {
41+
T* Foo();
42+
T* NotFoo();
43+
static T* SFoo();
44+
static T* NotSFoo();
45+
};
46+
template <typename T> struct TB : public TA<T> {};
47+
namespace ns {
48+
template <typename T> struct TA {
49+
T* Foo();
50+
T* NotFoo();
51+
static T* SFoo();
52+
static T* NotSFoo();
53+
static int SMoo;
54+
};
55+
template <typename T> struct TB : public TA<T> {};
56+
struct A {
57+
void Foo();
58+
void NotFoo();
59+
static void SFoo();
60+
static void SNotFoo();
61+
};
62+
struct B : public A {};
63+
struct C {
64+
template <class T>
65+
void SFoo(const T& t) {}
66+
template <class T>
67+
void Foo() {}
68+
};
69+
})");
70+
}
71+
};
72+
73+
INSTANTIATE_TEST_CASE_P(
74+
DISABLED_RenameTemplatedClassStaticVariableTest, RenameMemberTest,
75+
testing::ValuesIn(std::vector<Case>({
76+
// FIXME: support renaming static variables for template classes.
77+
{"void f() { ns::TA<int>::SMoo; }",
78+
"void f() { ns::TA<int>::SMeh; }", "ns::TA::SMoo", "ns::TA::SMeh"},
79+
})), );
80+
81+
INSTANTIATE_TEST_CASE_P(
82+
RenameMemberTest, RenameMemberTest,
83+
testing::ValuesIn(std::vector<Case>({
84+
// Normal methods and fields.
85+
{"void f() { A a; a.Foo(); }", "void f() { A a; a.Bar(); }", "A::Foo",
86+
"A::Bar"},
87+
{"void f() { ns::A a; a.Foo(); }", "void f() { ns::A a; a.Bar(); }",
88+
"ns::A::Foo", "ns::A::Bar"},
89+
{"void f() { A a; int x = a.Moo; }", "void f() { A a; int x = a.Meh; }",
90+
"A::Moo", "A::Meh"},
91+
{"void f() { B b; b.Foo(); }", "void f() { B b; b.Bar(); }", "B::Foo",
92+
"B::Bar"},
93+
{"void f() { ns::B b; b.Foo(); }", "void f() { ns::B b; b.Bar(); }",
94+
"ns::A::Foo", "ns::A::Bar"},
95+
{"void f() { B b; int x = b.Moo; }", "void f() { B b; int x = b.Meh; }",
96+
"A::Moo", "A::Meh"},
97+
98+
// Static methods.
99+
{"void f() { A::SFoo(); }", "void f() { A::SBar(); }", "A::SFoo",
100+
"A::SBar"},
101+
{"void f() { ns::A::SFoo(); }", "void f() { ns::A::SBar(); }",
102+
"ns::A::SFoo", "ns::A::SBar"},
103+
{"void f() { TA<int>::SFoo(); }", "void f() { TA<int>::SBar(); }",
104+
"TA::SFoo", "TA::SBar"},
105+
{"void f() { ns::TA<int>::SFoo(); }",
106+
"void f() { ns::TA<int>::SBar(); }", "ns::TA::SFoo", "ns::TA::SBar"},
107+
108+
// Static variables.
109+
{"void f() { A::SMoo; }",
110+
"void f() { A::SMeh; }", "A::SMoo", "A::SMeh"},
111+
112+
// Templated methods.
113+
{"void f() { TA<int> a; a.Foo(); }", "void f() { TA<int> a; a.Bar(); }",
114+
"TA::Foo", "TA::Bar"},
115+
{"void f() { ns::TA<int> a; a.Foo(); }",
116+
"void f() { ns::TA<int> a; a.Bar(); }", "ns::TA::Foo", "ns::TA::Bar"},
117+
{"void f() { TB<int> b; b.Foo(); }", "void f() { TB<int> b; b.Bar(); }",
118+
"TA::Foo", "TA::Bar"},
119+
{"void f() { ns::TB<int> b; b.Foo(); }",
120+
"void f() { ns::TB<int> b; b.Bar(); }", "ns::TA::Foo", "ns::TA::Bar"},
121+
{"void f() { ns::C c; int x; c.SFoo(x); }",
122+
"void f() { ns::C c; int x; c.SBar(x); }", "ns::C::SFoo",
123+
"ns::C::SBar"},
124+
{"void f() { ns::C c; c.Foo<int>(); }",
125+
"void f() { ns::C c; c.Bar<int>(); }", "ns::C::Foo", "ns::C::Bar"},
126+
127+
// Pointers to methods.
128+
{"void f() { auto p = &A::Foo; }", "void f() { auto p = &A::Bar; }",
129+
"A::Foo", "A::Bar"},
130+
{"void f() { auto p = &A::SFoo; }", "void f() { auto p = &A::SBar; }",
131+
"A::SFoo", "A::SBar"},
132+
{"void f() { auto p = &B::Foo; }", "void f() { auto p = &B::Bar; }",
133+
"B::Foo", "B::Bar"},
134+
{"void f() { auto p = &ns::A::Foo; }",
135+
"void f() { auto p = &ns::A::Bar; }", "ns::A::Foo", "ns::A::Bar"},
136+
{"void f() { auto p = &ns::A::SFoo; }",
137+
"void f() { auto p = &ns::A::SBar; }", "ns::A::SFoo", "ns::A::SBar"},
138+
{"void f() { auto p = &ns::C::SFoo<int>; }",
139+
"void f() { auto p = &ns::C::SBar<int>; }", "ns::C::SFoo",
140+
"ns::C::SBar"},
141+
142+
// These methods are not declared or overrided in the subclass B, we
143+
// have to use the qualified name with parent class A to identify them.
144+
{"void f() { auto p = &ns::B::Foo; }",
145+
"void f() { auto p = &ns::B::Bar; }", "ns::A::Foo", "ns::B::Bar"},
146+
{"void f() { B::SFoo(); }", "void f() { B::SBar(); }", "A::SFoo",
147+
"B::SBar"},
148+
{"void f() { ns::B::SFoo(); }", "void f() { ns::B::SBar(); }",
149+
"ns::A::SFoo", "ns::B::SBar"},
150+
{"void f() { auto p = &B::SFoo; }", "void f() { auto p = &B::SBar; }",
151+
"A::SFoo", "B::SBar"},
152+
{"void f() { auto p = &ns::B::SFoo; }",
153+
"void f() { auto p = &ns::B::SBar; }", "ns::A::SFoo", "ns::B::SBar"},
154+
{"void f() { TB<int>::SFoo(); }", "void f() { TB<int>::SBar(); }",
155+
"TA::SFoo", "TB::SBar"},
156+
{"void f() { ns::TB<int>::SFoo(); }",
157+
"void f() { ns::TB<int>::SBar(); }", "ns::TA::SFoo", "ns::TB::SBar"},
158+
})), );
159+
160+
TEST_P(RenameMemberTest, RenameMembers) {
161+
auto Param = GetParam();
162+
assert(!Param.OldName.empty());
163+
assert(!Param.NewName.empty());
164+
std::string Actual =
165+
runClangRenameOnCode(Param.Before, Param.OldName, Param.NewName);
166+
CompareSnippets(Param.After, Actual);
167+
}
168+
169+
TEST_F(RenameMemberTest, RenameMemberInsideClassMethods) {
170+
std::string Before = R"(
171+
struct X {
172+
int Moo;
173+
void Baz() { Moo = 1; }
174+
};)";
175+
std::string Expected = R"(
176+
struct X {
177+
int Meh;
178+
void Baz() { Meh = 1; }
179+
};)";
180+
std::string After = runClangRenameOnCode(Before, "X::Moo", "Y::Meh");
181+
CompareSnippets(Expected, After);
182+
}
183+
184+
TEST_F(RenameMemberTest, RenameMethodInsideClassMethods) {
185+
std::string Before = R"(
186+
struct X {
187+
void Foo() {}
188+
void Baz() { Foo(); }
189+
};)";
190+
std::string Expected = R"(
191+
struct X {
192+
void Bar() {}
193+
void Baz() { Bar(); }
194+
};)";
195+
std::string After = runClangRenameOnCode(Before, "X::Foo", "X::Bar");
196+
CompareSnippets(Expected, After);
197+
}
198+
199+
TEST_F(RenameMemberTest, RenameCtorInitializer) {
200+
std::string Before = R"(
201+
class X {
202+
public:
203+
X();
204+
A a;
205+
A a2;
206+
B b;
207+
};
208+
209+
X::X():a(), b() {}
210+
)";
211+
std::string Expected = R"(
212+
class X {
213+
public:
214+
X();
215+
A bar;
216+
A a2;
217+
B b;
218+
};
219+
220+
X::X():bar(), b() {}
221+
)";
222+
std::string After = runClangRenameOnCode(Before, "X::a", "X::bar");
223+
CompareSnippets(Expected, After);
224+
}
225+
226+
} // anonymous namespace
227+
} // namespace test
228+
} // namespace clang_rename
229+
} // namesdpace clang

0 commit comments

Comments
 (0)
Please sign in to comment.