Skip to content

Commit

Permalink
[clang-rename] add support for overridden functions
Browse files Browse the repository at this point in the history
Reviewers: klimek

Differential Revision: https://reviews.llvm.org/D22408

llvm-svn: 275958
  • Loading branch information
kirillbobyrev committed Jul 19, 2016
1 parent 5260c04 commit 953a978
Showing 1 changed file with 102 additions and 42 deletions.
144 changes: 102 additions & 42 deletions clang-tools-extra/clang-rename/USRFindingAction.cpp
Expand Up @@ -8,7 +8,8 @@
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief Provides an action to rename every symbol at a point.
/// \brief Provides an action to find USR for the symbol at <offset>, as well as
/// all additional USRs.
///
//===----------------------------------------------------------------------===//

Expand All @@ -17,6 +18,9 @@
#include "clang/AST/AST.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Basic/FileManager.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendAction.h"
Expand All @@ -25,47 +29,107 @@
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Refactoring.h"
#include "clang/Tooling/Tooling.h"
#include <algorithm>
#include <string>
#include <set>
#include <vector>


using namespace llvm;
using namespace clang::ast_matchers;

namespace clang {
namespace rename {

// Get the USRs for the constructors of the class.
static std::vector<std::string> getAllConstructorUSRs(
const CXXRecordDecl *Decl) {
std::vector<std::string> USRs;

// We need to get the definition of the record (as opposed to any forward
// declarations) in order to find the constructor and destructor.
const auto *RecordDecl = Decl->getDefinition();

// Iterate over all the constructors and add their USRs.
for (const auto *CtorDecl : RecordDecl->ctors())
USRs.push_back(getUSRForDecl(CtorDecl));

// Ignore destructors. GetLocationsOfUSR will find the declaration of and
// explicit calls to a destructor through TagTypeLoc (and it is better for the
// purpose of renaming).
//
// For example, in the following code segment,
// 1 class C {
// 2 ~C();
// 3 };
// At line 3, there is a NamedDecl starting from '~' and a TagTypeLoc starting
// from 'C'.

return USRs;
}
namespace {
// \brief NamedDeclFindingConsumer should delegate finding USRs of given Decl to
// AdditionalUSRFinder. AdditionalUSRFinder adds USRs of ctor and dtor if given
// Decl refers to class and adds USRs of all overridden methods if Decl refers
// to virtual method.
//
// FIXME: It's better to match ctors/dtors via typeLoc's instead of adding
// their USRs to the storage, because we can also match CXXConversionDecl's by
// typeLoc and we won't have to "manually" handle them here.
class AdditionalUSRFinder : public MatchFinder::MatchCallback {
public:
explicit AdditionalUSRFinder(const Decl *FoundDecl, ASTContext &Context,
std::vector<std::string> *USRs)
: FoundDecl(FoundDecl), Context(Context), USRs(USRs), USRSet(), Finder() {}

void Find() {
USRSet.insert(getUSRForDecl(FoundDecl));
addUSRsFromOverrideSetsAndCtorDtors();
addMatchers();
Finder.matchAST(Context);
USRs->insert(USRs->end(), USRSet.begin(), USRSet.end());
}

private:
void addMatchers() {
const auto CXXMethodDeclMatcher =
cxxMethodDecl(isVirtual()).bind("cxxMethodDecl");
Finder.addMatcher(CXXMethodDeclMatcher, this);
}

// FIXME: Implement hasOverriddenMethod and matchesUSR matchers to make
// lookups more efficient.
virtual void run(const MatchFinder::MatchResult &Result) {
const auto *VirtualMethod =
Result.Nodes.getNodeAs<CXXMethodDecl>("cxxMethodDecl");
bool Found = false;
for (const auto &OverriddenMethod : VirtualMethod->overridden_methods()) {
if (USRSet.find(getUSRForDecl(OverriddenMethod)) != USRSet.end()) {
Found = true;
}
}
if (Found) {
USRSet.insert(getUSRForDecl(VirtualMethod));
}
}

void addUSRsFromOverrideSetsAndCtorDtors() {
// If D is CXXRecordDecl we should add all USRs of its constructors.
if (const auto *RecordDecl = dyn_cast<CXXRecordDecl>(FoundDecl)) {
// Ignore destructors. Find the declaration of and explicit calls to a
// destructor through TagTypeLoc (and it is better for the purpose of
// renaming).
//
// For example, in the following code segment,
// 1 class C {
// 2 ~C();
// 3 };
//
// At line 3, there is a NamedDecl starting from '~' and a TagTypeLoc
// starting from 'C'.
RecordDecl = RecordDecl->getDefinition();

// Iterate over all the constructors and add their USRs.
for (const auto *CtorDecl : RecordDecl->ctors()) {
USRSet.insert(getUSRForDecl(CtorDecl));
}
}
// If D is CXXMethodDecl we should add all USRs of its overriden methods.
if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(FoundDecl)) {
for (auto &OverriddenMethod : MethodDecl->overridden_methods()) {
USRSet.insert(getUSRForDecl(OverriddenMethod));
}
}
}

const Decl *FoundDecl;
ASTContext &Context;
std::vector<std::string> *USRs;
std::set<std::string> USRSet;
MatchFinder Finder;
};
} // namespace

struct NamedDeclFindingConsumer : public ASTConsumer {
void HandleTranslationUnit(ASTContext &Context) override {
const auto &SourceMgr = Context.getSourceManager();
// The file we look for the USR in will always be the main source file.
const auto Point = SourceMgr.getLocForStartOfFile(
SourceMgr.getMainFileID()).getLocWithOffset(SymbolOffset);
const auto Point = SourceMgr.getLocForStartOfFile(SourceMgr.getMainFileID())
.getLocWithOffset(SymbolOffset);
if (!Point.isValid())
return;
const NamedDecl *FoundDecl = nullptr;
Expand All @@ -84,20 +148,17 @@ struct NamedDeclFindingConsumer : public ASTConsumer {
return;
}

// If the decl is a constructor or destructor, we want to instead take the
// decl of the parent record.
if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(FoundDecl))
// If FoundDecl is a constructor or destructor, we want to instead take the
// Decl of the corresponding class.
if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(FoundDecl)) {
FoundDecl = CtorDecl->getParent();
else if (const auto *DtorDecl = dyn_cast<CXXDestructorDecl>(FoundDecl))
} else if (const auto *DtorDecl = dyn_cast<CXXDestructorDecl>(FoundDecl)) {
FoundDecl = DtorDecl->getParent();

// If the decl is in any way relatedpp to a class, we want to make sure we
// search for the constructor and destructor as well as everything else.
if (const auto *Record = dyn_cast<CXXRecordDecl>(FoundDecl))
*USRs = getAllConstructorUSRs(Record);

USRs->push_back(getUSRForDecl(FoundDecl));
}
*SpellingName = FoundDecl->getNameAsString();

AdditionalUSRFinder Finder(FoundDecl, Context, USRs);
Finder.Find();
}

unsigned SymbolOffset;
Expand All @@ -106,8 +167,7 @@ struct NamedDeclFindingConsumer : public ASTConsumer {
std::vector<std::string> *USRs;
};

std::unique_ptr<ASTConsumer>
USRFindingAction::newASTConsumer() {
std::unique_ptr<ASTConsumer> USRFindingAction::newASTConsumer() {
std::unique_ptr<NamedDeclFindingConsumer> Consumer(
new NamedDeclFindingConsumer);
SpellingName = "";
Expand Down

0 comments on commit 953a978

Please sign in to comment.