112 changes: 45 additions & 67 deletions clang-tools-extra/clang-rename/USRLocFinder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,38 +34,18 @@ namespace {
class USRLocFindingASTVisitor
: public clang::RecursiveASTVisitor<USRLocFindingASTVisitor> {
public:
explicit USRLocFindingASTVisitor(StringRef USR, StringRef PrevName)
: USR(USR), PrevName(PrevName) {}
explicit USRLocFindingASTVisitor(StringRef USR, StringRef PrevName,
const ASTContext &Context)
: USR(USR), PrevName(PrevName), Context(Context) {}

// Declaration visitors:

bool VisitNamedDecl(const NamedDecl *Decl) {
if (getUSRForDecl(Decl) == USR) {
LocationsFound.push_back(Decl->getLocation());
}
return true;
}

bool VisitVarDecl(clang::VarDecl *Decl) {
clang::QualType Type = Decl->getType();
const clang::RecordDecl *RecordDecl = Type->getPointeeCXXRecordDecl();
if (RecordDecl) {
if (getUSRForDecl(RecordDecl) == USR) {
// The declaration refers to a type that is to be renamed.
LocationsFound.push_back(Decl->getTypeSpecStartLoc());
}
}
return true;
}

bool VisitCXXConstructorDecl(clang::CXXConstructorDecl *ConstructorDecl) {
const ASTContext &Context = ConstructorDecl->getASTContext();
for (auto &Initializer : ConstructorDecl->inits()) {
if (Initializer->getSourceOrder() == -1) {
// Ignore implicit initializers.
continue;
}

if (const clang::FieldDecl *FieldDecl = Initializer->getAnyMember()) {
if (getUSRForDecl(FieldDecl) == USR) {
// The initializer refers to a field that is to be renamed.
Expand All @@ -81,33 +61,13 @@ class USRLocFindingASTVisitor
}
}
}

if (getUSRForDecl(ConstructorDecl) == USR) {
// This takes care of the class name part of a non-inline ctor definition.
LocationsFound.push_back(ConstructorDecl->getLocStart());
}
return true;
}

bool VisitCXXDestructorDecl(clang::CXXDestructorDecl *DestructorDecl) {
if (getUSRForDecl(DestructorDecl->getParent()) == USR) {
// Handles "~Foo" from "Foo::~Foo".
SourceLocation Location = DestructorDecl->getLocation();
const ASTContext &Context = DestructorDecl->getASTContext();
StringRef LLVM_ATTRIBUTE_UNUSED TokenName = Lexer::getSourceText(
CharSourceRange::getTokenRange(Location), Context.getSourceManager(),
Context.getLangOpts());
// 1 is the length of the "~" string that is not to be touched by the
// rename.
assert(TokenName.startswith("~"));
LocationsFound.push_back(Location.getLocWithOffset(1));

if (DestructorDecl->isThisDeclarationADefinition()) {
// Handles "Foo" from "Foo::~Foo".
LocationsFound.push_back(DestructorDecl->getLocStart());
}
bool VisitNamedDecl(const NamedDecl *Decl) {
if (getUSRForDecl(Decl) == USR) {
checkAndAddLocation(Decl->getLocation());
}

return true;
}

Expand All @@ -116,11 +76,10 @@ class USRLocFindingASTVisitor
bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
const auto *Decl = Expr->getFoundDecl();

checkNestedNameSpecifierLoc(Expr->getQualifierLoc());
if (getUSRForDecl(Decl) == USR) {
const SourceManager &Manager = Decl->getASTContext().getSourceManager();
SourceLocation Location = Manager.getSpellingLoc(Expr->getLocation());
LocationsFound.push_back(Location);
checkAndAddLocation(Location);
}

return true;
Expand All @@ -131,19 +90,8 @@ class USRLocFindingASTVisitor
if (getUSRForDecl(Decl) == USR) {
const SourceManager &Manager = Decl->getASTContext().getSourceManager();
SourceLocation Location = Manager.getSpellingLoc(Expr->getMemberLoc());
LocationsFound.push_back(Location);
}
return true;
}

bool VisitCXXConstructExpr(const CXXConstructExpr *Expr) {
CXXConstructorDecl *Decl = Expr->getConstructor();

if (getUSRForDecl(Decl) == USR) {
// This takes care of 'new <name>' expressions.
LocationsFound.push_back(Expr->getLocation());
checkAndAddLocation(Location);
}

return true;
}

Expand All @@ -163,6 +111,15 @@ class USRLocFindingASTVisitor
return handleCXXNamedCastExpr(Expr);
}

// Other visitors:

bool VisitTypeLoc(const TypeLoc Loc) {
if (getUSRForDecl(Loc.getType()->getAsCXXRecordDecl()) == USR) {
checkAndAddLocation(Loc.getBeginLoc());
}
return true;
}

// Non-visitors:

// \brief Returns a list of unique locations. Duplicate or overlapping
Expand All @@ -171,13 +128,13 @@ class USRLocFindingASTVisitor
return LocationsFound;
}

private:
// Namespace traversal:
void checkNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) {
void handleNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) {
while (NameLoc) {
const auto *Decl = NameLoc.getNestedNameSpecifier()->getAsNamespace();
if (Decl && getUSRForDecl(Decl) == USR)
LocationsFound.push_back(NameLoc.getLocalBeginLoc());
if (Decl && getUSRForDecl(Decl) == USR) {
checkAndAddLocation(NameLoc.getLocalBeginLoc());
}
NameLoc = NameLoc.getPrefix();
}
}
Expand All @@ -194,25 +151,46 @@ class USRLocFindingASTVisitor
if (Decl && getUSRForDecl(Decl) == USR) {
SourceLocation Location =
Expr->getTypeInfoAsWritten()->getTypeLoc().getBeginLoc();
LocationsFound.push_back(Location);
checkAndAddLocation(Location);
}

return true;
}

private:
void checkAndAddLocation(SourceLocation Loc) {
const auto BeginLoc = Loc;
const auto EndLoc = Lexer::getLocForEndOfToken(
BeginLoc, 0, Context.getSourceManager(),
Context.getLangOpts());
StringRef TokenName =
Lexer::getSourceText(CharSourceRange::getTokenRange(BeginLoc, EndLoc),
Context.getSourceManager(), Context.getLangOpts());
size_t Offset = TokenName.find(PrevName);
if (Offset != StringRef::npos) {
// The token of the source location we find actually has the old
// name.
LocationsFound.push_back(BeginLoc.getLocWithOffset(Offset));
}
}

// All the locations of the USR were found.
const std::string USR;
// Old name that is renamed.
const std::string PrevName;
std::vector<clang::SourceLocation> LocationsFound;
const ASTContext &Context;
};
} // namespace

std::vector<SourceLocation> getLocationsOfUSR(StringRef USR, StringRef PrevName,
Decl *Decl) {
USRLocFindingASTVisitor Visitor(USR, PrevName);

USRLocFindingASTVisitor Visitor(USR, PrevName, Decl->getASTContext());
Visitor.TraverseDecl(Decl);
NestedNameSpecifierLocFinder Finder(Decl->getASTContext());
for (const auto &Location : Finder.getNestedNameSpecifierLocations()) {
Visitor.handleNestedNameSpecifierLoc(Location);
}
return Visitor.getLocationsFound();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// RUN: cat %s > %t.cpp
// RUN: clang-rename -offset=136 -new-name=Bar %t.cpp -i --
// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s

class Foo {}; // CHECK: class Bar {};

template <typename T>
void func() {}

template <typename T>
class Baz {};

int main() {
func<Foo>(); // CHECK: func<Bar>();
Baz<Foo> obj; // CHECK: Baz<Bar> obj;
return 0;
}

// Use grep -FUbo 'Foo' <file> to get the correct offset of Foo when changing
// this file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// RUN: cat %s > %t.cpp
// RUN: clang-rename -offset=304 -new-name=Bar %t.cpp -i --
// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s

class Foo {}; // CHECK: class Bar {};

template <typename T>
void func() {}

template <typename T>
class Baz {};

int main() {
func<Foo>(); // CHECK: func<Bar>();
Baz<Foo> obj; // CHECK: Baz<Bar> obj;
return 0;
}

// Use grep -FUbo 'Foo' <file> to get the correct offset of Cla when changing
// this file.
Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
// Currently unsupported test.
// RUN: cat %s > %t.cpp
// FIXME: clang-rename doesn't recognize symbol in class function definition.
// RUN: clang-rename -offset=136 -new-name=Bar %t.cpp -i --
// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s

class Foo {
class Foo { // CHECK: class Bar {
public:
void foo(int x);
};

void Foo::foo(int x) {}
// ^ this one

int main() {
Foo obj;
obj.foo(0);
return 0;
}
void Foo::foo(int x) {} // CHECK: void Bar::foo(int x) {}
23 changes: 12 additions & 11 deletions clang-tools-extra/test/clang-rename/ComplicatedClassType.cpp
Original file line number Diff line number Diff line change
@@ -1,30 +1,31 @@
// Unsupported test.
// RUN: cat %s > %t.cpp
// FIXME: This test contains very simple constructions likely to be seen in any
// project and therefore passing this test is a slight sign of success.
// Currently, the test fails badly.
// RUN: clang-rename -offset=220 -new-name=Bar %t.cpp -i --
// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s

// Forward declaration.
class Foo; // CHECK: class Bar;

class Foo { // CHECK: class Bar {
public:
Foo(int value = 0) : x(value) {} // Bar(int value=0) : x(value) {}
public:
Foo(int value = 0) : x(value) {} // CHECK: Bar(int value = 0) : x(value) {}

Foo& operator++(int) { // Bar& operator++(int) {
Foo &operator++(int) { // CHECK: Bar &operator++(int) {
x++;
return *this;
}

bool operator<(Foo const& rhs) { // bool operator<(Bar const &rhs) {
bool operator<(Foo const &rhs) { // CHECK: bool operator<(Bar const &rhs) {
return this->x < rhs.x;
}

private:
private:
int x;
};

int main() {
Foo* Pointer = 0; // CHECK: Bar *Pointer = 0;
Foo *Pointer = 0; // CHECK: Bar *Pointer = 0;
Foo Variable = Foo(10); // CHECK: Bar Variable = Bar(10);
for (Foo it; it < Variable; it++) { // for (Bar it; it < Variable; it++) {}
for (Foo it; it < Variable; it++) { // CHECK: for (Bar it; it < Variable; it++) {
}
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// RUN: cat %s > %t.cpp
// RUN: clang-rename -offset=287 -new-name=Bar %t.cpp -i --
// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s

// Currently unsupported test.
// FIXME: clang-rename should be able to rename classes with templates
// correctly.
// XFAIL: *

template <typename T>
class Foo { // CHECK: class Bar;
public:
T foo(T arg, T& ref, T* ptr) {
T value;
int number = 42;
value = (T)number;
value = static_cast<T>(number);
return value;
}
static void foo(T value) {}
T member;
};

template <typename T>
void func() {
Foo<T> obj; // CHECK: Bar<T> obj;
obj.member = T();
Foo<T>::foo(); // CHECK: Bar<T>::foo();
}

int main() {
Foo<int> i; // CHECK: Bar<int> i;
i.member = 0;
Foo<int>::foo(0); // CHECK: Bar<int>::foo(0);

Foo<bool> b; // CHECK: Bar<bool> b;
b.member = false;
Foo<bool>::foo(false); // CHECK: Bar<bool>::foo(false);

return 0;
}

// Use grep -FUbo 'C' <file> to get the correct offset of foo when changing
// this file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// RUN: cat %s > %t.cpp
// RUN: clang-rename -offset=703 -new-name=Bar %t.cpp -i --
// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s

// Currently unsupported test.
// FIXME: clang-rename should be able to rename classes with templates
// correctly.
// XFAIL: *

template <typename T>
class Foo { // CHECK: class Bar;
public:
T foo(T arg, T& ref, T* ptr) {
T value;
int number = 42;
value = (T)number;
value = static_cast<T>(number);
return value;
}
static void foo(T value) {}
T member;
};

template <typename T>
void func() {
Foo<T> obj; // CHECK: Bar<T> obj;
obj.member = T();
Foo<T>::foo(); // CHECK: Bar<T>::foo();
}

int main() {
Foo<int> i; // CHECK: Bar<int> i;
i.member = 0;
Foo<int>::foo(0); // CHECK: Bar<int>::foo(0);

Foo<bool> b; // CHECK: Bar<bool> b;
b.member = false;
Foo<bool>::foo(false); // CHECK: Bar<bool>::foo(false);

return 0;
}

// Use grep -FUbo 'Foo' <file> to get the correct offset of foo when changing
// this file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// RUN: cat %s > %t.cpp
// RUN: clang-rename -offset=241 -new-name=bar %t.cpp -i --
// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s

// FIXME: clang-rename should be able to rename functions with templates.
// XFAIL: *

template <typename T>
T foo(T value) { // CHECK: T boo(T value) {
return value;
}

int main() {
foo<bool>(false); // CHECK: bar<bool>(false);
foo<int>(0); // CHECK: bar<int>(0);
return 0;
}

// Use grep -FUbo 'foo' <file> to get the correct offset of foo when changing
// this file.
17 changes: 17 additions & 0 deletions clang-tools-extra/test/clang-rename/TemplateFunctionFindByUse.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// RUN: cat %s > %t.cpp
// RUN: clang-rename -offset=290 -new-name=Bar %t.cpp -i --
// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s

// FIXME: clang-rename should be able to rename functions with templates.
// XFAIL: *

template <typename T>
T foo(T value) {
return value;
}

int main() {
foo<bool>(false);
foo<int>(0);
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// RUN: cat %s > %t.cpp
// RUN: clang-rename -offset=270 -new-name=U %t.cpp -i --
// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s

// Currently unsupported test.
// FIXME: clang-rename should be able to rename template parameters correctly.
// XFAIL: *

template <typename T> // CHECK: template <typename U>
class Foo {
T foo(T arg, T& ref, T* ptr) { // CHECK: U foo(U arg, U& ref, U* ptr) {
T value; // CHECK: U value;
int number = 42;
value = (T)number; // CHECK: value = (U)number;
value = static_cast<T>(number); // CHECK: value = static_cast<U>(number);
return value;
}

static void foo(T value) {} // CHECK: static void foo(U value) {}

T member; // CHECK: U member;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// RUN: cat %s > %t.cpp
// RUN: clang-rename -offset=350 -new-name=U %t.cpp -i --
// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s

// Currently unsupported test.
// FIXME: clang-rename should be able to rename template parameters correctly.
// XFAIL: *

template <typename T> // CHECK: template <typename U>
class Foo {
T foo(T arg, T& ref, T* ptr) { // CHECK: U foo(U arg, U& ref, U* ptr) {
T value; // CHECK: U value;
int number = 42;
value = (T)number; // CHECK: value = (U)number;
value = static_cast<T>(number); // CHECK: value = static_cast<U>(number);
return value;
}

static void foo(T value) {} // CHECK: static void foo(U value) {}

T member; // CHECK: U member;
};
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Currently unsupported test.
// RUN: cat %s > %t.cpp
// FIXME: while renaming class/struct clang-rename should be able to change
// this type name corresponding user-defined conversions, too.
// RUN: clang-rename -offset=136 -new-name=Bar %t.cpp -i --
// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s

class Foo { // CHECK: class Bar {
// ^ offset must be here
Expand All @@ -22,3 +21,6 @@ int main() {
Foo foo = static_cast<Foo>(boo); // CHECK: Bar foo = static_cast<Bar>(boo);
return 0;
}

// Use grep -FUbo 'Foo' <file> to get the correct offset of Cla when changing
// this file.