arphaman created this revision.
Herald added a subscriber: mgorny.
The core engine of clang-rename will be used for local and global renames in
the new refactoring engine
(http://lists.llvm.org/pipermail/cfe-dev/2017-June/054286.html).
I haven't renamed the files/cleaned up the code in this patch, do you think I
should? E.g. SymbolOccurrenceFinder instead of USRLocFinder.
Repository:
rL LLVM
https://reviews.llvm.org/D34696
Files:
include/clang/Tooling/Refactoring/USRFinder.h
include/clang/Tooling/Refactoring/USRFindingAction.h
include/clang/Tooling/Refactoring/USRLocFinder.h
include/clang/module.modulemap
lib/Tooling/Refactoring/CMakeLists.txt
lib/Tooling/Refactoring/USRFinder.cpp
lib/Tooling/Refactoring/USRFindingAction.cpp
lib/Tooling/Refactoring/USRLocFinder.cpp
tools/extra/clang-rename/CMakeLists.txt
tools/extra/clang-rename/RenamingAction.cpp
tools/extra/clang-rename/USRFinder.cpp
tools/extra/clang-rename/USRFinder.h
tools/extra/clang-rename/USRFindingAction.cpp
tools/extra/clang-rename/USRFindingAction.h
tools/extra/clang-rename/USRLocFinder.cpp
tools/extra/clang-rename/USRLocFinder.h
tools/extra/clang-rename/tool/ClangRename.cpp
Index: tools/extra/clang-rename/tool/ClangRename.cpp
===================================================================
--- tools/extra/clang-rename/tool/ClangRename.cpp
+++ tools/extra/clang-rename/tool/ClangRename.cpp
@@ -14,7 +14,6 @@
//===----------------------------------------------------------------------===//
#include "../RenamingAction.h"
-#include "../USRFindingAction.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticOptions.h"
#include "clang/Basic/FileManager.h"
@@ -26,6 +25,7 @@
#include "clang/Rewrite/Core/Rewriter.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Refactoring/USRFindingAction.h"
#include "clang/Tooling/ReplacementsYaml.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
@@ -160,7 +160,7 @@
auto Files = OP.getSourcePathList();
tooling::RefactoringTool Tool(OP.getCompilations(), Files);
- rename::USRFindingAction FindingAction(SymbolOffsets, QualifiedNames, Force);
+ tooling::USRFindingAction FindingAction(SymbolOffsets, QualifiedNames, Force);
Tool.run(tooling::newFrontendActionFactory(&FindingAction).get());
const std::vector<std::vector<std::string>> &USRList =
FindingAction.getUSRList();
Index: tools/extra/clang-rename/USRLocFinder.h
===================================================================
--- tools/extra/clang-rename/USRLocFinder.h
+++ /dev/null
@@ -1,49 +0,0 @@
-//===--- tools/extra/clang-rename/USRLocFinder.h - Clang rename tool ------===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// \brief Provides functionality for finding all instances of a USR in a given
-/// AST.
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_LOC_FINDER_H
-#define LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_LOC_FINDER_H
-
-#include "clang/AST/AST.h"
-#include "clang/Tooling/Core/Replacement.h"
-#include "clang/Tooling/Refactoring/AtomicChange.h"
-#include "llvm/ADT/StringRef.h"
-#include <string>
-#include <vector>
-
-namespace clang {
-namespace rename {
-
-/// Create atomic changes for renaming all symbol references which are
-/// identified by the USRs set to a given new name.
-///
-/// \param USRs The set containing USRs of a particular old symbol.
-/// \param NewName The new name to replace old symbol name.
-/// \param TranslationUnitDecl The translation unit declaration.
-///
-/// \return Atomic changes for renaming.
-std::vector<tooling::AtomicChange>
-createRenameAtomicChanges(llvm::ArrayRef<std::string> USRs,
- llvm::StringRef NewName, Decl *TranslationUnitDecl);
-
-// FIXME: make this an AST matcher. Wouldn't that be awesome??? I agree!
-std::vector<SourceLocation>
-getLocationsOfUSRs(const std::vector<std::string> &USRs,
- llvm::StringRef PrevName, Decl *Decl);
-
-} // namespace rename
-} // namespace clang
-
-#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_LOC_FINDER_H
Index: tools/extra/clang-rename/USRLocFinder.cpp
===================================================================
--- tools/extra/clang-rename/USRLocFinder.cpp
+++ /dev/null
@@ -1,509 +0,0 @@
-//===--- tools/extra/clang-rename/USRLocFinder.cpp - Clang rename tool ----===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// \brief Mehtods for finding all instances of a USR. Our strategy is very
-/// simple; we just compare the USR at every relevant AST node with the one
-/// provided.
-///
-//===----------------------------------------------------------------------===//
-
-#include "USRLocFinder.h"
-#include "USRFinder.h"
-#include "clang/AST/ASTContext.h"
-#include "clang/AST/RecursiveASTVisitor.h"
-#include "clang/Basic/LLVM.h"
-#include "clang/Basic/SourceLocation.h"
-#include "clang/Basic/SourceManager.h"
-#include "clang/Lex/Lexer.h"
-#include "clang/Tooling/Core/Lookup.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/Support/Casting.h"
-#include <cstddef>
-#include <set>
-#include <string>
-#include <vector>
-
-using namespace llvm;
-
-namespace clang {
-namespace rename {
-
-namespace {
-
-// \brief This visitor recursively searches for all instances of a USR in a
-// translation unit and stores them for later usage.
-class USRLocFindingASTVisitor
- : public clang::RecursiveASTVisitor<USRLocFindingASTVisitor> {
-public:
- explicit USRLocFindingASTVisitor(const std::vector<std::string> &USRs,
- StringRef PrevName,
- const ASTContext &Context)
- : USRSet(USRs.begin(), USRs.end()), PrevName(PrevName), Context(Context) {
- }
-
- // Declaration visitors:
-
- bool VisitCXXConstructorDecl(clang::CXXConstructorDecl *ConstructorDecl) {
- for (const auto *Initializer : ConstructorDecl->inits()) {
- // Ignore implicit initializers.
- if (!Initializer->isWritten())
- continue;
- if (const clang::FieldDecl *FieldDecl = Initializer->getMember()) {
- if (USRSet.find(getUSRForDecl(FieldDecl)) != USRSet.end())
- LocationsFound.push_back(Initializer->getSourceLocation());
- }
- }
- return true;
- }
-
- bool VisitNamedDecl(const NamedDecl *Decl) {
- if (USRSet.find(getUSRForDecl(Decl)) != USRSet.end())
- checkAndAddLocation(Decl->getLocation());
- return true;
- }
-
- // Expression visitors:
-
- bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
- const NamedDecl *Decl = Expr->getFoundDecl();
-
- if (USRSet.find(getUSRForDecl(Decl)) != USRSet.end()) {
- const SourceManager &Manager = Decl->getASTContext().getSourceManager();
- SourceLocation Location = Manager.getSpellingLoc(Expr->getLocation());
- checkAndAddLocation(Location);
- }
-
- return true;
- }
-
- bool VisitMemberExpr(const MemberExpr *Expr) {
- const NamedDecl *Decl = Expr->getFoundDecl().getDecl();
- if (USRSet.find(getUSRForDecl(Decl)) != USRSet.end()) {
- const SourceManager &Manager = Decl->getASTContext().getSourceManager();
- SourceLocation Location = Manager.getSpellingLoc(Expr->getMemberLoc());
- checkAndAddLocation(Location);
- }
- return true;
- }
-
- // Other visitors:
-
- bool VisitTypeLoc(const TypeLoc Loc) {
- if (USRSet.find(getUSRForDecl(Loc.getType()->getAsCXXRecordDecl())) !=
- USRSet.end())
- checkAndAddLocation(Loc.getBeginLoc());
- if (const auto *TemplateTypeParm =
- dyn_cast<TemplateTypeParmType>(Loc.getType())) {
- if (USRSet.find(getUSRForDecl(TemplateTypeParm->getDecl())) !=
- USRSet.end())
- checkAndAddLocation(Loc.getBeginLoc());
- }
- return true;
- }
-
- // Non-visitors:
-
- // \brief Returns a list of unique locations. Duplicate or overlapping
- // locations are erroneous and should be reported!
- const std::vector<clang::SourceLocation> &getLocationsFound() const {
- return LocationsFound;
- }
-
- // Namespace traversal:
- void handleNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) {
- while (NameLoc) {
- const NamespaceDecl *Decl =
- NameLoc.getNestedNameSpecifier()->getAsNamespace();
- if (Decl && USRSet.find(getUSRForDecl(Decl)) != USRSet.end())
- checkAndAddLocation(NameLoc.getLocalBeginLoc());
- NameLoc = NameLoc.getPrefix();
- }
- }
-
-private:
- void checkAndAddLocation(SourceLocation Loc) {
- const SourceLocation BeginLoc = Loc;
- const SourceLocation 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);
-
- // The token of the source location we find actually has the old
- // name.
- if (Offset != StringRef::npos)
- LocationsFound.push_back(BeginLoc.getLocWithOffset(Offset));
- }
-
- const std::set<std::string> USRSet;
- const std::string PrevName;
- std::vector<clang::SourceLocation> LocationsFound;
- const ASTContext &Context;
-};
-
-SourceLocation StartLocationForType(TypeLoc TL) {
- // For elaborated types (e.g. `struct a::A`) we want the portion after the
- // `struct` but including the namespace qualifier, `a::`.
- if (auto ElaboratedTypeLoc = TL.getAs<clang::ElaboratedTypeLoc>()) {
- NestedNameSpecifierLoc NestedNameSpecifier =
- ElaboratedTypeLoc.getQualifierLoc();
- if (NestedNameSpecifier.getNestedNameSpecifier())
- return NestedNameSpecifier.getBeginLoc();
- TL = TL.getNextTypeLoc();
- }
- return TL.getLocStart();
-}
-
-SourceLocation EndLocationForType(TypeLoc TL) {
- // Dig past any namespace or keyword qualifications.
- while (TL.getTypeLocClass() == TypeLoc::Elaborated ||
- TL.getTypeLocClass() == TypeLoc::Qualified)
- TL = TL.getNextTypeLoc();
-
- // The location for template specializations (e.g. Foo<int>) includes the
- // templated types in its location range. We want to restrict this to just
- // before the `<` character.
- if (TL.getTypeLocClass() == TypeLoc::TemplateSpecialization) {
- return TL.castAs<TemplateSpecializationTypeLoc>()
- .getLAngleLoc()
- .getLocWithOffset(-1);
- }
- return TL.getEndLoc();
-}
-
-NestedNameSpecifier *GetNestedNameForType(TypeLoc TL) {
- // Dig past any keyword qualifications.
- while (TL.getTypeLocClass() == TypeLoc::Qualified)
- TL = TL.getNextTypeLoc();
-
- // For elaborated types (e.g. `struct a::A`) we want the portion after the
- // `struct` but including the namespace qualifier, `a::`.
- if (auto ElaboratedTypeLoc = TL.getAs<clang::ElaboratedTypeLoc>())
- return ElaboratedTypeLoc.getQualifierLoc().getNestedNameSpecifier();
- return nullptr;
-}
-
-// Find all locations identified by the given USRs for rename.
-//
-// This class will traverse the AST and find every AST node whose USR is in the
-// given USRs' set.
-class RenameLocFinder : public RecursiveASTVisitor<RenameLocFinder> {
-public:
- RenameLocFinder(llvm::ArrayRef<std::string> USRs, ASTContext &Context)
- : USRSet(USRs.begin(), USRs.end()), Context(Context) {}
-
- // A structure records all information of a symbol reference being renamed.
- // We try to add as few prefix qualifiers as possible.
- struct RenameInfo {
- // The begin location of a symbol being renamed.
- SourceLocation Begin;
- // The end location of a symbol being renamed.
- SourceLocation End;
- // The declaration of a symbol being renamed (can be nullptr).
- const NamedDecl *FromDecl;
- // The declaration in which the nested name is contained (can be nullptr).
- const Decl *Context;
- // The nested name being replaced (can be nullptr).
- const NestedNameSpecifier *Specifier;
- };
-
- // FIXME: Currently, prefix qualifiers will be added to the renamed symbol
- // definition (e.g. "class Foo {};" => "class b::Bar {};" when renaming
- // "a::Foo" to "b::Bar").
- // For renaming declarations/definitions, prefix qualifiers should be filtered
- // out.
- bool VisitNamedDecl(const NamedDecl *Decl) {
- // UsingDecl has been handled in other place.
- if (llvm::isa<UsingDecl>(Decl))
- return true;
-
- // DestructorDecl has been handled in Typeloc.
- if (llvm::isa<CXXDestructorDecl>(Decl))
- return true;
-
- if (Decl->isImplicit())
- return true;
-
- if (isInUSRSet(Decl)) {
- RenameInfo Info = {Decl->getLocation(), Decl->getLocation(), nullptr,
- nullptr, nullptr};
- RenameInfos.push_back(Info);
- }
- return true;
- }
-
- bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
- const NamedDecl *Decl = Expr->getFoundDecl();
- if (isInUSRSet(Decl)) {
- RenameInfo Info = {Expr->getSourceRange().getBegin(),
- Expr->getSourceRange().getEnd(), Decl,
- getClosestAncestorDecl(*Expr), Expr->getQualifier()};
- RenameInfos.push_back(Info);
- }
-
- return true;
- }
-
- bool VisitUsingDecl(const UsingDecl *Using) {
- for (const auto *UsingShadow : Using->shadows()) {
- if (isInUSRSet(UsingShadow->getTargetDecl())) {
- UsingDecls.push_back(Using);
- break;
- }
- }
- return true;
- }
-
- bool VisitNestedNameSpecifierLocations(NestedNameSpecifierLoc NestedLoc) {
- if (!NestedLoc.getNestedNameSpecifier()->getAsType())
- return true;
- if (IsTypeAliasWhichWillBeRenamedElsewhere(NestedLoc.getTypeLoc()))
- return true;
-
- if (const auto *TargetDecl =
- getSupportedDeclFromTypeLoc(NestedLoc.getTypeLoc())) {
- if (isInUSRSet(TargetDecl)) {
- RenameInfo Info = {NestedLoc.getBeginLoc(),
- EndLocationForType(NestedLoc.getTypeLoc()),
- TargetDecl, getClosestAncestorDecl(NestedLoc),
- NestedLoc.getNestedNameSpecifier()->getPrefix()};
- RenameInfos.push_back(Info);
- }
- }
- return true;
- }
-
- bool VisitTypeLoc(TypeLoc Loc) {
- if (IsTypeAliasWhichWillBeRenamedElsewhere(Loc))
- return true;
-
- auto Parents = Context.getParents(Loc);
- TypeLoc ParentTypeLoc;
- if (!Parents.empty()) {
- // Handle cases of nested name specificier locations.
- //
- // The VisitNestedNameSpecifierLoc interface is not impelmented in
- // RecursiveASTVisitor, we have to handle it explicitly.
- if (const auto *NSL = Parents[0].get<NestedNameSpecifierLoc>()) {
- VisitNestedNameSpecifierLocations(*NSL);
- return true;
- }
-
- if (const auto *TL = Parents[0].get<TypeLoc>())
- ParentTypeLoc = *TL;
- }
-
- // Handle the outermost TypeLoc which is directly linked to the interesting
- // declaration and don't handle nested name specifier locations.
- if (const auto *TargetDecl = getSupportedDeclFromTypeLoc(Loc)) {
- if (isInUSRSet(TargetDecl)) {
- // Only handle the outermost typeLoc.
- //
- // For a type like "a::Foo", there will be two typeLocs for it.
- // One ElaboratedType, the other is RecordType:
- //
- // ElaboratedType 0x33b9390 'a::Foo' sugar
- // `-RecordType 0x338fef0 'class a::Foo'
- // `-CXXRecord 0x338fe58 'Foo'
- //
- // Skip if this is an inner typeLoc.
- if (!ParentTypeLoc.isNull() &&
- isInUSRSet(getSupportedDeclFromTypeLoc(ParentTypeLoc)))
- return true;
- RenameInfo Info = {StartLocationForType(Loc), EndLocationForType(Loc),
- TargetDecl, getClosestAncestorDecl(Loc),
- GetNestedNameForType(Loc)};
- RenameInfos.push_back(Info);
- return true;
- }
- }
-
- // Handle specific template class specialiation cases.
- if (const auto *TemplateSpecType =
- dyn_cast<TemplateSpecializationType>(Loc.getType())) {
- TypeLoc TargetLoc = Loc;
- if (!ParentTypeLoc.isNull()) {
- if (llvm::isa<ElaboratedType>(ParentTypeLoc.getType()))
- TargetLoc = ParentTypeLoc;
- }
-
- if (isInUSRSet(TemplateSpecType->getTemplateName().getAsTemplateDecl())) {
- TypeLoc TargetLoc = Loc;
- // FIXME: Find a better way to handle this case.
- // For the qualified template class specification type like
- // "ns::Foo<int>" in "ns::Foo<int>& f();", we want the parent typeLoc
- // (ElaboratedType) of the TemplateSpecializationType in order to
- // catch the prefix qualifiers "ns::".
- if (!ParentTypeLoc.isNull() &&
- llvm::isa<ElaboratedType>(ParentTypeLoc.getType()))
- TargetLoc = ParentTypeLoc;
- RenameInfo Info = {
- StartLocationForType(TargetLoc), EndLocationForType(TargetLoc),
- TemplateSpecType->getTemplateName().getAsTemplateDecl(),
- getClosestAncestorDecl(
- ast_type_traits::DynTypedNode::create(TargetLoc)),
- GetNestedNameForType(TargetLoc)};
- RenameInfos.push_back(Info);
- }
- }
- return true;
- }
-
- // Returns a list of RenameInfo.
- const std::vector<RenameInfo> &getRenameInfos() const { return RenameInfos; }
-
- // Returns a list of using declarations which are needed to update.
- const std::vector<const UsingDecl *> &getUsingDecls() const {
- return UsingDecls;
- }
-
-private:
- // FIXME: This method may not be suitable for renaming other types like alias
- // types. Need to figure out a way to handle it.
- bool IsTypeAliasWhichWillBeRenamedElsewhere(TypeLoc TL) const {
- while (!TL.isNull()) {
- // SubstTemplateTypeParm is the TypeLocation class for a substituted type
- // inside a template expansion so we ignore these. For example:
- //
- // template<typename T> struct S {
- // T t; // <-- this T becomes a TypeLoc(int) with class
- // // SubstTemplateTypeParm when S<int> is instantiated
- // }
- if (TL.getTypeLocClass() == TypeLoc::SubstTemplateTypeParm)
- return true;
-
- // Typedef is the TypeLocation class for a type which is a typedef to the
- // type we want to replace. We ignore the use of the typedef as we will
- // replace the definition of it. For example:
- //
- // typedef int T;
- // T a; // <--- This T is a TypeLoc(int) with class Typedef.
- if (TL.getTypeLocClass() == TypeLoc::Typedef)
- return true;
- TL = TL.getNextTypeLoc();
- }
- return false;
- }
-
- // Get the supported declaration from a given typeLoc. If the declaration type
- // is not supported, returns nullptr.
- //
- // FIXME: support more types, e.g. enum, type alias.
- const NamedDecl *getSupportedDeclFromTypeLoc(TypeLoc Loc) {
- if (const auto *RD = Loc.getType()->getAsCXXRecordDecl())
- return RD;
- return nullptr;
- }
-
- // Get the closest ancester which is a declaration of a given AST node.
- template <typename ASTNodeType>
- const Decl *getClosestAncestorDecl(const ASTNodeType &Node) {
- auto Parents = Context.getParents(Node);
- // FIXME: figure out how to handle it when there are multiple parents.
- if (Parents.size() != 1)
- return nullptr;
- if (ast_type_traits::ASTNodeKind::getFromNodeKind<Decl>().isBaseOf(
- Parents[0].getNodeKind()))
- return Parents[0].template get<Decl>();
- return getClosestAncestorDecl(Parents[0]);
- }
-
- // Get the parent typeLoc of a given typeLoc. If there is no such parent,
- // return nullptr.
- const TypeLoc *getParentTypeLoc(TypeLoc Loc) const {
- auto Parents = Context.getParents(Loc);
- // FIXME: figure out how to handle it when there are multiple parents.
- if (Parents.size() != 1)
- return nullptr;
- return Parents[0].get<TypeLoc>();
- }
-
- // Check whether the USR of a given Decl is in the USRSet.
- bool isInUSRSet(const Decl *Decl) const {
- auto USR = getUSRForDecl(Decl);
- if (USR.empty())
- return false;
- return llvm::is_contained(USRSet, USR);
- }
-
- const std::set<std::string> USRSet;
- ASTContext &Context;
- std::vector<RenameInfo> RenameInfos;
- // Record all interested using declarations which contains the using-shadow
- // declarations of the symbol declarations being renamed.
- std::vector<const UsingDecl *> UsingDecls;
-};
-
-} // namespace
-
-std::vector<SourceLocation>
-getLocationsOfUSRs(const std::vector<std::string> &USRs, StringRef PrevName,
- Decl *Decl) {
- USRLocFindingASTVisitor Visitor(USRs, PrevName, Decl->getASTContext());
- Visitor.TraverseDecl(Decl);
- NestedNameSpecifierLocFinder Finder(Decl->getASTContext());
-
- for (const auto &Location : Finder.getNestedNameSpecifierLocations())
- Visitor.handleNestedNameSpecifierLoc(Location);
-
- return Visitor.getLocationsFound();
-}
-
-std::vector<tooling::AtomicChange>
-createRenameAtomicChanges(llvm::ArrayRef<std::string> USRs,
- llvm::StringRef NewName, Decl *TranslationUnitDecl) {
- RenameLocFinder Finder(USRs, TranslationUnitDecl->getASTContext());
- Finder.TraverseDecl(TranslationUnitDecl);
-
- const SourceManager &SM =
- TranslationUnitDecl->getASTContext().getSourceManager();
-
- std::vector<tooling::AtomicChange> AtomicChanges;
- auto Replace = [&](SourceLocation Start, SourceLocation End,
- llvm::StringRef Text) {
- tooling::AtomicChange ReplaceChange = tooling::AtomicChange(SM, Start);
- llvm::Error Err = ReplaceChange.replace(
- SM, CharSourceRange::getTokenRange(Start, End), Text);
- if (Err) {
- llvm::errs() << "Faile to add replacement to AtomicChange: "
- << llvm::toString(std::move(Err)) << "\n";
- return;
- }
- AtomicChanges.push_back(std::move(ReplaceChange));
- };
-
- for (const auto &RenameInfo : Finder.getRenameInfos()) {
- std::string ReplacedName = NewName.str();
- if (RenameInfo.FromDecl && RenameInfo.Context) {
- if (!llvm::isa<clang::TranslationUnitDecl>(
- RenameInfo.Context->getDeclContext())) {
- ReplacedName = tooling::replaceNestedName(
- RenameInfo.Specifier, RenameInfo.Context->getDeclContext(),
- RenameInfo.FromDecl,
- NewName.startswith("::") ? NewName.str() : ("::" + NewName).str());
- }
- }
- // If the NewName contains leading "::", add it back.
- if (NewName.startswith("::") && NewName.substr(2) == ReplacedName)
- ReplacedName = NewName.str();
- Replace(RenameInfo.Begin, RenameInfo.End, ReplacedName);
- }
-
- // Hanlde using declarations explicitly as "using a::Foo" don't trigger
- // typeLoc for "a::Foo".
- for (const auto *Using : Finder.getUsingDecls())
- Replace(Using->getLocStart(), Using->getLocEnd(), "using " + NewName.str());
-
- return AtomicChanges;
-}
-
-} // namespace rename
-} // namespace clang
Index: tools/extra/clang-rename/USRFindingAction.h
===================================================================
--- tools/extra/clang-rename/USRFindingAction.h
+++ /dev/null
@@ -1,54 +0,0 @@
-//===--- tools/extra/clang-rename/USRFindingAction.h - Clang rename tool --===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// \brief Provides an action to find all relevant USRs at a point.
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDING_ACTION_H
-#define LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDING_ACTION_H
-
-#include "clang/Basic/LLVM.h"
-#include "llvm/ADT/ArrayRef.h"
-
-#include <string>
-#include <vector>
-
-namespace clang {
-class ASTConsumer;
-class CompilerInstance;
-class NamedDecl;
-
-namespace rename {
-
-struct USRFindingAction {
- USRFindingAction(ArrayRef<unsigned> SymbolOffsets,
- ArrayRef<std::string> QualifiedNames, bool Force)
- : SymbolOffsets(SymbolOffsets), QualifiedNames(QualifiedNames),
- ErrorOccurred(false), Force(Force) {}
- std::unique_ptr<ASTConsumer> newASTConsumer();
-
- ArrayRef<std::string> getUSRSpellings() { return SpellingNames; }
- ArrayRef<std::vector<std::string>> getUSRList() { return USRList; }
- bool errorOccurred() { return ErrorOccurred; }
-
-private:
- std::vector<unsigned> SymbolOffsets;
- std::vector<std::string> QualifiedNames;
- std::vector<std::string> SpellingNames;
- std::vector<std::vector<std::string>> USRList;
- bool ErrorOccurred;
- bool Force;
-};
-
-} // namespace rename
-} // namespace clang
-
-#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDING_ACTION_H
Index: tools/extra/clang-rename/USRFindingAction.cpp
===================================================================
--- tools/extra/clang-rename/USRFindingAction.cpp
+++ /dev/null
@@ -1,236 +0,0 @@
-//===--- tools/extra/clang-rename/USRFindingAction.cpp - Clang rename tool ===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// \brief Provides an action to find USR for the symbol at <offset>, as well as
-/// all additional USRs.
-///
-//===----------------------------------------------------------------------===//
-
-#include "USRFindingAction.h"
-#include "USRFinder.h"
-#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/Basic/FileManager.h"
-#include "clang/Frontend/CompilerInstance.h"
-#include "clang/Frontend/FrontendAction.h"
-#include "clang/Lex/Lexer.h"
-#include "clang/Lex/Preprocessor.h"
-#include "clang/Tooling/CommonOptionsParser.h"
-#include "clang/Tooling/Refactoring.h"
-#include "clang/Tooling/Tooling.h"
-
-#include <algorithm>
-#include <set>
-#include <string>
-#include <vector>
-
-using namespace llvm;
-
-namespace clang {
-namespace rename {
-
-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.
-class AdditionalUSRFinder : public RecursiveASTVisitor<AdditionalUSRFinder> {
-public:
- AdditionalUSRFinder(const Decl *FoundDecl, ASTContext &Context)
- : FoundDecl(FoundDecl), Context(Context) {}
-
- std::vector<std::string> Find() {
- // Fill OverriddenMethods and PartialSpecs storages.
- TraverseDecl(Context.getTranslationUnitDecl());
- if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(FoundDecl)) {
- addUSRsOfOverridenFunctions(MethodDecl);
- for (const auto &OverriddenMethod : OverriddenMethods) {
- if (checkIfOverriddenFunctionAscends(OverriddenMethod))
- USRSet.insert(getUSRForDecl(OverriddenMethod));
- }
- } else if (const auto *RecordDecl = dyn_cast<CXXRecordDecl>(FoundDecl)) {
- handleCXXRecordDecl(RecordDecl);
- } else if (const auto *TemplateDecl =
- dyn_cast<ClassTemplateDecl>(FoundDecl)) {
- handleClassTemplateDecl(TemplateDecl);
- } else {
- USRSet.insert(getUSRForDecl(FoundDecl));
- }
- return std::vector<std::string>(USRSet.begin(), USRSet.end());
- }
-
- bool VisitCXXMethodDecl(const CXXMethodDecl *MethodDecl) {
- if (MethodDecl->isVirtual())
- OverriddenMethods.push_back(MethodDecl);
- return true;
- }
-
- bool VisitClassTemplatePartialSpecializationDecl(
- const ClassTemplatePartialSpecializationDecl *PartialSpec) {
- PartialSpecs.push_back(PartialSpec);
- return true;
- }
-
-private:
- void handleCXXRecordDecl(const CXXRecordDecl *RecordDecl) {
- RecordDecl = RecordDecl->getDefinition();
- if (const auto *ClassTemplateSpecDecl =
- dyn_cast<ClassTemplateSpecializationDecl>(RecordDecl))
- handleClassTemplateDecl(ClassTemplateSpecDecl->getSpecializedTemplate());
- addUSRsOfCtorDtors(RecordDecl);
- }
-
- void handleClassTemplateDecl(const ClassTemplateDecl *TemplateDecl) {
- for (const auto *Specialization : TemplateDecl->specializations())
- addUSRsOfCtorDtors(Specialization);
-
- for (const auto *PartialSpec : PartialSpecs) {
- if (PartialSpec->getSpecializedTemplate() == TemplateDecl)
- addUSRsOfCtorDtors(PartialSpec);
- }
- addUSRsOfCtorDtors(TemplateDecl->getTemplatedDecl());
- }
-
- void addUSRsOfCtorDtors(const CXXRecordDecl *RecordDecl) {
- RecordDecl = RecordDecl->getDefinition();
-
- // Skip if the CXXRecordDecl doesn't have definition.
- if (!RecordDecl)
- return;
-
- for (const auto *CtorDecl : RecordDecl->ctors())
- USRSet.insert(getUSRForDecl(CtorDecl));
-
- USRSet.insert(getUSRForDecl(RecordDecl->getDestructor()));
- USRSet.insert(getUSRForDecl(RecordDecl));
- }
-
- void addUSRsOfOverridenFunctions(const CXXMethodDecl *MethodDecl) {
- USRSet.insert(getUSRForDecl(MethodDecl));
- // Recursively visit each OverridenMethod.
- for (const auto &OverriddenMethod : MethodDecl->overridden_methods())
- addUSRsOfOverridenFunctions(OverriddenMethod);
- }
-
- bool checkIfOverriddenFunctionAscends(const CXXMethodDecl *MethodDecl) {
- for (const auto &OverriddenMethod : MethodDecl->overridden_methods()) {
- if (USRSet.find(getUSRForDecl(OverriddenMethod)) != USRSet.end())
- return true;
- return checkIfOverriddenFunctionAscends(OverriddenMethod);
- }
- return false;
- }
-
- const Decl *FoundDecl;
- ASTContext &Context;
- std::set<std::string> USRSet;
- std::vector<const CXXMethodDecl *> OverriddenMethods;
- std::vector<const ClassTemplatePartialSpecializationDecl *> PartialSpecs;
-};
-} // namespace
-
-class NamedDeclFindingConsumer : public ASTConsumer {
-public:
- NamedDeclFindingConsumer(ArrayRef<unsigned> SymbolOffsets,
- ArrayRef<std::string> QualifiedNames,
- std::vector<std::string> &SpellingNames,
- std::vector<std::vector<std::string>> &USRList,
- bool Force, bool &ErrorOccurred)
- : SymbolOffsets(SymbolOffsets), QualifiedNames(QualifiedNames),
- SpellingNames(SpellingNames), USRList(USRList), Force(Force),
- ErrorOccurred(ErrorOccurred) {}
-
-private:
- bool FindSymbol(ASTContext &Context, const SourceManager &SourceMgr,
- unsigned SymbolOffset, const std::string &QualifiedName) {
- DiagnosticsEngine &Engine = Context.getDiagnostics();
- const FileID MainFileID = SourceMgr.getMainFileID();
-
- if (SymbolOffset >= SourceMgr.getFileIDSize(MainFileID)) {
- ErrorOccurred = true;
- unsigned InvalidOffset = Engine.getCustomDiagID(
- DiagnosticsEngine::Error,
- "SourceLocation in file %0 at offset %1 is invalid");
- Engine.Report(SourceLocation(), InvalidOffset)
- << SourceMgr.getFileEntryForID(MainFileID)->getName() << SymbolOffset;
- return false;
- }
-
- const SourceLocation Point = SourceMgr.getLocForStartOfFile(MainFileID)
- .getLocWithOffset(SymbolOffset);
- const NamedDecl *FoundDecl = QualifiedName.empty()
- ? getNamedDeclAt(Context, Point)
- : getNamedDeclFor(Context, QualifiedName);
-
- if (FoundDecl == nullptr) {
- if (QualifiedName.empty()) {
- FullSourceLoc FullLoc(Point, SourceMgr);
- unsigned CouldNotFindSymbolAt = Engine.getCustomDiagID(
- DiagnosticsEngine::Error,
- "clang-rename could not find symbol (offset %0)");
- Engine.Report(Point, CouldNotFindSymbolAt) << SymbolOffset;
- ErrorOccurred = true;
- return false;
- }
-
- if (Force)
- return true;
-
- unsigned CouldNotFindSymbolNamed = Engine.getCustomDiagID(
- DiagnosticsEngine::Error, "clang-rename could not find symbol %0");
- Engine.Report(CouldNotFindSymbolNamed) << QualifiedName;
- ErrorOccurred = true;
- return false;
- }
-
- // 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))
- FoundDecl = DtorDecl->getParent();
-
- SpellingNames.push_back(FoundDecl->getNameAsString());
- AdditionalUSRFinder Finder(FoundDecl, Context);
- USRList.push_back(Finder.Find());
- return true;
- }
-
- void HandleTranslationUnit(ASTContext &Context) override {
- const SourceManager &SourceMgr = Context.getSourceManager();
- for (unsigned Offset : SymbolOffsets) {
- if (!FindSymbol(Context, SourceMgr, Offset, ""))
- return;
- }
- for (const std::string &QualifiedName : QualifiedNames) {
- if (!FindSymbol(Context, SourceMgr, 0, QualifiedName))
- return;
- }
- }
-
- ArrayRef<unsigned> SymbolOffsets;
- ArrayRef<std::string> QualifiedNames;
- std::vector<std::string> &SpellingNames;
- std::vector<std::vector<std::string>> &USRList;
- bool Force;
- bool &ErrorOccurred;
-};
-
-std::unique_ptr<ASTConsumer> USRFindingAction::newASTConsumer() {
- return llvm::make_unique<NamedDeclFindingConsumer>(
- SymbolOffsets, QualifiedNames, SpellingNames, USRList, Force,
- ErrorOccurred);
-}
-
-} // namespace rename
-} // namespace clang
Index: tools/extra/clang-rename/USRFinder.h
===================================================================
--- tools/extra/clang-rename/USRFinder.h
+++ /dev/null
@@ -1,84 +0,0 @@
-//===--- tools/extra/clang-rename/USRFinder.h - Clang rename tool ---------===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// \brief Methods for determining the USR of a symbol at a location in source
-/// code.
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDER_H
-#define LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDER_H
-
-#include "clang/AST/AST.h"
-#include "clang/AST/ASTContext.h"
-#include "clang/ASTMatchers/ASTMatchFinder.h"
-#include <string>
-#include <vector>
-
-using namespace llvm;
-using namespace clang::ast_matchers;
-
-namespace clang {
-
-class ASTContext;
-class Decl;
-class SourceLocation;
-class NamedDecl;
-
-namespace rename {
-
-// Given an AST context and a point, returns a NamedDecl identifying the symbol
-// at the point. Returns null if nothing is found at the point.
-const NamedDecl *getNamedDeclAt(const ASTContext &Context,
- const SourceLocation Point);
-
-// Given an AST context and a fully qualified name, returns a NamedDecl
-// identifying the symbol with a matching name. Returns null if nothing is
-// found for the name.
-const NamedDecl *getNamedDeclFor(const ASTContext &Context,
- const std::string &Name);
-
-// Converts a Decl into a USR.
-std::string getUSRForDecl(const Decl *Decl);
-
-// FIXME: Implement RecursiveASTVisitor<T>::VisitNestedNameSpecifier instead.
-class NestedNameSpecifierLocFinder : public MatchFinder::MatchCallback {
-public:
- explicit NestedNameSpecifierLocFinder(ASTContext &Context)
- : Context(Context) {}
-
- std::vector<NestedNameSpecifierLoc> getNestedNameSpecifierLocations() {
- addMatchers();
- Finder.matchAST(Context);
- return Locations;
- }
-
-private:
- void addMatchers() {
- const auto NestedNameSpecifierLocMatcher =
- nestedNameSpecifierLoc().bind("nestedNameSpecifierLoc");
- Finder.addMatcher(NestedNameSpecifierLocMatcher, this);
- }
-
- void run(const MatchFinder::MatchResult &Result) override {
- const auto *NNS = Result.Nodes.getNodeAs<NestedNameSpecifierLoc>(
- "nestedNameSpecifierLoc");
- Locations.push_back(*NNS);
- }
-
- ASTContext &Context;
- std::vector<NestedNameSpecifierLoc> Locations;
- MatchFinder Finder;
-};
-
-} // namespace rename
-} // namespace clang
-
-#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDER_H
Index: tools/extra/clang-rename/USRFinder.cpp
===================================================================
--- tools/extra/clang-rename/USRFinder.cpp
+++ /dev/null
@@ -1,213 +0,0 @@
-//===--- tools/extra/clang-rename/USRFinder.cpp - Clang rename tool -------===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file Implements a recursive AST visitor that finds the USR of a symbol at a
-/// point.
-///
-//===----------------------------------------------------------------------===//
-
-#include "USRFinder.h"
-#include "clang/AST/AST.h"
-#include "clang/AST/ASTContext.h"
-#include "clang/AST/RecursiveASTVisitor.h"
-#include "clang/Index/USRGeneration.h"
-#include "clang/Lex/Lexer.h"
-#include "llvm/ADT/SmallVector.h"
-
-using namespace llvm;
-
-namespace clang {
-namespace rename {
-
-// NamedDeclFindingASTVisitor recursively visits each AST node to find the
-// symbol underneath the cursor.
-// FIXME: move to separate .h/.cc file if this gets too large.
-namespace {
-class NamedDeclFindingASTVisitor
- : public clang::RecursiveASTVisitor<NamedDeclFindingASTVisitor> {
-public:
- // \brief Finds the NamedDecl at a point in the source.
- // \param Point the location in the source to search for the NamedDecl.
- explicit NamedDeclFindingASTVisitor(const SourceLocation Point,
- const ASTContext &Context)
- : Result(nullptr), Point(Point), Context(Context) {}
-
- // \brief Finds the NamedDecl for a name in the source.
- // \param Name the fully qualified name.
- explicit NamedDeclFindingASTVisitor(const std::string &Name,
- const ASTContext &Context)
- : Result(nullptr), Name(Name), Context(Context) {}
-
- // Declaration visitors:
-
- // \brief Checks if the point falls within the NameDecl. This covers every
- // declaration of a named entity that we may come across. Usually, just
- // checking if the point lies within the length of the name of the declaration
- // and the start location is sufficient.
- bool VisitNamedDecl(const NamedDecl *Decl) {
- return dyn_cast<CXXConversionDecl>(Decl)
- ? true
- : setResult(Decl, Decl->getLocation(),
- Decl->getNameAsString().length());
- }
-
- // Expression visitors:
-
- bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
- const NamedDecl *Decl = Expr->getFoundDecl();
- return setResult(Decl, Expr->getLocation(),
- Decl->getNameAsString().length());
- }
-
- bool VisitMemberExpr(const MemberExpr *Expr) {
- const NamedDecl *Decl = Expr->getFoundDecl().getDecl();
- return setResult(Decl, Expr->getMemberLoc(),
- Decl->getNameAsString().length());
- }
-
- // Other visitors:
-
- bool VisitTypeLoc(const TypeLoc Loc) {
- const SourceLocation TypeBeginLoc = Loc.getBeginLoc();
- const SourceLocation TypeEndLoc = Lexer::getLocForEndOfToken(
- TypeBeginLoc, 0, Context.getSourceManager(), Context.getLangOpts());
- if (const auto *TemplateTypeParm =
- dyn_cast<TemplateTypeParmType>(Loc.getType()))
- return setResult(TemplateTypeParm->getDecl(), TypeBeginLoc, TypeEndLoc);
- if (const auto *TemplateSpecType =
- dyn_cast<TemplateSpecializationType>(Loc.getType())) {
- return setResult(TemplateSpecType->getTemplateName().getAsTemplateDecl(),
- TypeBeginLoc, TypeEndLoc);
- }
- return setResult(Loc.getType()->getAsCXXRecordDecl(), TypeBeginLoc,
- TypeEndLoc);
- }
-
- bool VisitCXXConstructorDecl(clang::CXXConstructorDecl *ConstructorDecl) {
- for (const auto *Initializer : ConstructorDecl->inits()) {
- // Ignore implicit initializers.
- if (!Initializer->isWritten())
- continue;
- if (const clang::FieldDecl *FieldDecl = Initializer->getMember()) {
- const SourceLocation InitBeginLoc = Initializer->getSourceLocation(),
- InitEndLoc = Lexer::getLocForEndOfToken(
- InitBeginLoc, 0, Context.getSourceManager(),
- Context.getLangOpts());
- if (!setResult(FieldDecl, InitBeginLoc, InitEndLoc))
- return false;
- }
- }
- return true;
- }
-
- // Other:
-
- const NamedDecl *getNamedDecl() { return Result; }
-
- // \brief Determines if a namespace qualifier contains the point.
- // \returns false on success and sets Result.
- void handleNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) {
- while (NameLoc) {
- const NamespaceDecl *Decl =
- NameLoc.getNestedNameSpecifier()->getAsNamespace();
- setResult(Decl, NameLoc.getLocalBeginLoc(), NameLoc.getLocalEndLoc());
- NameLoc = NameLoc.getPrefix();
- }
- }
-
-private:
- // \brief Sets Result to Decl if the Point is within Start and End.
- // \returns false on success.
- bool setResult(const NamedDecl *Decl, SourceLocation Start,
- SourceLocation End) {
- if (!Decl)
- return true;
- if (Name.empty()) {
- // Offset is used to find the declaration.
- if (!Start.isValid() || !Start.isFileID() || !End.isValid() ||
- !End.isFileID() || !isPointWithin(Start, End))
- return true;
- } else {
- // Fully qualified name is used to find the declaration.
- if (Name != Decl->getQualifiedNameAsString() &&
- Name != "::" + Decl->getQualifiedNameAsString())
- return true;
- }
- Result = Decl;
- return false;
- }
-
- // \brief Sets Result to Decl if Point is within Loc and Loc + Offset.
- // \returns false on success.
- bool setResult(const NamedDecl *Decl, SourceLocation Loc, unsigned Offset) {
- // FIXME: Add test for Offset == 0. Add test for Offset - 1 (vs -2 etc).
- return Offset == 0 ||
- setResult(Decl, Loc, Loc.getLocWithOffset(Offset - 1));
- }
-
- // \brief Determines if the Point is within Start and End.
- bool isPointWithin(const SourceLocation Start, const SourceLocation End) {
- // FIXME: Add tests for Point == End.
- return Point == Start || Point == End ||
- (Context.getSourceManager().isBeforeInTranslationUnit(Start,
- Point) &&
- Context.getSourceManager().isBeforeInTranslationUnit(Point, End));
- }
-
- const NamedDecl *Result;
- const SourceLocation Point; // The location to find the NamedDecl.
- const std::string Name;
- const ASTContext &Context;
-};
-} // namespace
-
-const NamedDecl *getNamedDeclAt(const ASTContext &Context,
- const SourceLocation Point) {
- const SourceManager &SM = Context.getSourceManager();
- NamedDeclFindingASTVisitor Visitor(Point, Context);
-
- // Try to be clever about pruning down the number of top-level declarations we
- // see. If both start and end is either before or after the point we're
- // looking for the point cannot be inside of this decl. Don't even look at it.
- for (auto *CurrDecl : Context.getTranslationUnitDecl()->decls()) {
- SourceLocation StartLoc = CurrDecl->getLocStart();
- SourceLocation EndLoc = CurrDecl->getLocEnd();
- if (StartLoc.isValid() && EndLoc.isValid() &&
- SM.isBeforeInTranslationUnit(StartLoc, Point) !=
- SM.isBeforeInTranslationUnit(EndLoc, Point))
- Visitor.TraverseDecl(CurrDecl);
- }
-
- NestedNameSpecifierLocFinder Finder(const_cast<ASTContext &>(Context));
- for (const auto &Location : Finder.getNestedNameSpecifierLocations())
- Visitor.handleNestedNameSpecifierLoc(Location);
-
- return Visitor.getNamedDecl();
-}
-
-const NamedDecl *getNamedDeclFor(const ASTContext &Context,
- const std::string &Name) {
- NamedDeclFindingASTVisitor Visitor(Name, Context);
- Visitor.TraverseDecl(Context.getTranslationUnitDecl());
-
- return Visitor.getNamedDecl();
-}
-
-std::string getUSRForDecl(const Decl *Decl) {
- llvm::SmallVector<char, 128> Buff;
-
- // FIXME: Add test for the nullptr case.
- if (Decl == nullptr || index::generateUSRForDecl(Decl, Buff))
- return "";
-
- return std::string(Buff.data(), Buff.size());
-}
-
-} // namespace rename
-} // namespace clang
Index: tools/extra/clang-rename/RenamingAction.cpp
===================================================================
--- tools/extra/clang-rename/RenamingAction.cpp
+++ tools/extra/clang-rename/RenamingAction.cpp
@@ -13,7 +13,6 @@
//===----------------------------------------------------------------------===//
#include "RenamingAction.h"
-#include "USRLocFinder.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/Basic/FileManager.h"
@@ -23,6 +22,7 @@
#include "clang/Lex/Preprocessor.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Refactoring/USRLocFinder.h"
#include "clang/Tooling/Tooling.h"
#include <string>
#include <vector>
@@ -55,8 +55,8 @@
std::vector<SourceLocation> RenamingCandidates;
std::vector<SourceLocation> NewCandidates;
- NewCandidates =
- getLocationsOfUSRs(USRs, PrevName, Context.getTranslationUnitDecl());
+ NewCandidates = tooling::getLocationsOfUSRs(
+ USRs, PrevName, Context.getTranslationUnitDecl());
RenamingCandidates.insert(RenamingCandidates.end(), NewCandidates.begin(),
NewCandidates.end());
@@ -101,7 +101,7 @@
for (unsigned I = 0; I < NewNames.size(); ++I) {
// FIXME: Apply AtomicChanges directly once the refactoring APIs are
// ready.
- auto AtomicChanges = createRenameAtomicChanges(
+ auto AtomicChanges = tooling::createRenameAtomicChanges(
USRList[I], NewNames[I], Context.getTranslationUnitDecl());
for (const auto AtomicChange : AtomicChanges) {
for (const auto &Replace : AtomicChange.getReplacements()) {
Index: tools/extra/clang-rename/CMakeLists.txt
===================================================================
--- tools/extra/clang-rename/CMakeLists.txt
+++ tools/extra/clang-rename/CMakeLists.txt
@@ -1,9 +1,6 @@
set(LLVM_LINK_COMPONENTS support)
add_clang_library(clangRename
- USRFinder.cpp
- USRFindingAction.cpp
- USRLocFinder.cpp
RenamingAction.cpp
LINK_LIBS
Index: lib/Tooling/Refactoring/USRLocFinder.cpp
===================================================================
--- /dev/null
+++ lib/Tooling/Refactoring/USRLocFinder.cpp
@@ -0,0 +1,509 @@
+//===--- USRLocFinder.cpp - Clang refactoring library ---------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Mehtods for finding all instances of a USR. Our strategy is very
+/// simple; we just compare the USR at every relevant AST node with the one
+/// provided.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/Refactoring/USRLocFinder.h"
+#include "clang/Tooling/Refactoring/USRFinder.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Tooling/Core/Lookup.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Casting.h"
+#include <cstddef>
+#include <set>
+#include <string>
+#include <vector>
+
+using namespace llvm;
+
+namespace clang {
+namespace tooling {
+
+namespace {
+
+// \brief This visitor recursively searches for all instances of a USR in a
+// translation unit and stores them for later usage.
+class USRLocFindingASTVisitor
+ : public clang::RecursiveASTVisitor<USRLocFindingASTVisitor> {
+public:
+ explicit USRLocFindingASTVisitor(const std::vector<std::string> &USRs,
+ StringRef PrevName,
+ const ASTContext &Context)
+ : USRSet(USRs.begin(), USRs.end()), PrevName(PrevName), Context(Context) {
+ }
+
+ // Declaration visitors:
+
+ bool VisitCXXConstructorDecl(clang::CXXConstructorDecl *ConstructorDecl) {
+ for (const auto *Initializer : ConstructorDecl->inits()) {
+ // Ignore implicit initializers.
+ if (!Initializer->isWritten())
+ continue;
+ if (const clang::FieldDecl *FieldDecl = Initializer->getMember()) {
+ if (USRSet.find(getUSRForDecl(FieldDecl)) != USRSet.end())
+ LocationsFound.push_back(Initializer->getSourceLocation());
+ }
+ }
+ return true;
+ }
+
+ bool VisitNamedDecl(const NamedDecl *Decl) {
+ if (USRSet.find(getUSRForDecl(Decl)) != USRSet.end())
+ checkAndAddLocation(Decl->getLocation());
+ return true;
+ }
+
+ // Expression visitors:
+
+ bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
+ const NamedDecl *Decl = Expr->getFoundDecl();
+
+ if (USRSet.find(getUSRForDecl(Decl)) != USRSet.end()) {
+ const SourceManager &Manager = Decl->getASTContext().getSourceManager();
+ SourceLocation Location = Manager.getSpellingLoc(Expr->getLocation());
+ checkAndAddLocation(Location);
+ }
+
+ return true;
+ }
+
+ bool VisitMemberExpr(const MemberExpr *Expr) {
+ const NamedDecl *Decl = Expr->getFoundDecl().getDecl();
+ if (USRSet.find(getUSRForDecl(Decl)) != USRSet.end()) {
+ const SourceManager &Manager = Decl->getASTContext().getSourceManager();
+ SourceLocation Location = Manager.getSpellingLoc(Expr->getMemberLoc());
+ checkAndAddLocation(Location);
+ }
+ return true;
+ }
+
+ // Other visitors:
+
+ bool VisitTypeLoc(const TypeLoc Loc) {
+ if (USRSet.find(getUSRForDecl(Loc.getType()->getAsCXXRecordDecl())) !=
+ USRSet.end())
+ checkAndAddLocation(Loc.getBeginLoc());
+ if (const auto *TemplateTypeParm =
+ dyn_cast<TemplateTypeParmType>(Loc.getType())) {
+ if (USRSet.find(getUSRForDecl(TemplateTypeParm->getDecl())) !=
+ USRSet.end())
+ checkAndAddLocation(Loc.getBeginLoc());
+ }
+ return true;
+ }
+
+ // Non-visitors:
+
+ // \brief Returns a list of unique locations. Duplicate or overlapping
+ // locations are erroneous and should be reported!
+ const std::vector<clang::SourceLocation> &getLocationsFound() const {
+ return LocationsFound;
+ }
+
+ // Namespace traversal:
+ void handleNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) {
+ while (NameLoc) {
+ const NamespaceDecl *Decl =
+ NameLoc.getNestedNameSpecifier()->getAsNamespace();
+ if (Decl && USRSet.find(getUSRForDecl(Decl)) != USRSet.end())
+ checkAndAddLocation(NameLoc.getLocalBeginLoc());
+ NameLoc = NameLoc.getPrefix();
+ }
+ }
+
+private:
+ void checkAndAddLocation(SourceLocation Loc) {
+ const SourceLocation BeginLoc = Loc;
+ const SourceLocation 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);
+
+ // The token of the source location we find actually has the old
+ // name.
+ if (Offset != StringRef::npos)
+ LocationsFound.push_back(BeginLoc.getLocWithOffset(Offset));
+ }
+
+ const std::set<std::string> USRSet;
+ const std::string PrevName;
+ std::vector<clang::SourceLocation> LocationsFound;
+ const ASTContext &Context;
+};
+
+SourceLocation StartLocationForType(TypeLoc TL) {
+ // For elaborated types (e.g. `struct a::A`) we want the portion after the
+ // `struct` but including the namespace qualifier, `a::`.
+ if (auto ElaboratedTypeLoc = TL.getAs<clang::ElaboratedTypeLoc>()) {
+ NestedNameSpecifierLoc NestedNameSpecifier =
+ ElaboratedTypeLoc.getQualifierLoc();
+ if (NestedNameSpecifier.getNestedNameSpecifier())
+ return NestedNameSpecifier.getBeginLoc();
+ TL = TL.getNextTypeLoc();
+ }
+ return TL.getLocStart();
+}
+
+SourceLocation EndLocationForType(TypeLoc TL) {
+ // Dig past any namespace or keyword qualifications.
+ while (TL.getTypeLocClass() == TypeLoc::Elaborated ||
+ TL.getTypeLocClass() == TypeLoc::Qualified)
+ TL = TL.getNextTypeLoc();
+
+ // The location for template specializations (e.g. Foo<int>) includes the
+ // templated types in its location range. We want to restrict this to just
+ // before the `<` character.
+ if (TL.getTypeLocClass() == TypeLoc::TemplateSpecialization) {
+ return TL.castAs<TemplateSpecializationTypeLoc>()
+ .getLAngleLoc()
+ .getLocWithOffset(-1);
+ }
+ return TL.getEndLoc();
+}
+
+NestedNameSpecifier *GetNestedNameForType(TypeLoc TL) {
+ // Dig past any keyword qualifications.
+ while (TL.getTypeLocClass() == TypeLoc::Qualified)
+ TL = TL.getNextTypeLoc();
+
+ // For elaborated types (e.g. `struct a::A`) we want the portion after the
+ // `struct` but including the namespace qualifier, `a::`.
+ if (auto ElaboratedTypeLoc = TL.getAs<clang::ElaboratedTypeLoc>())
+ return ElaboratedTypeLoc.getQualifierLoc().getNestedNameSpecifier();
+ return nullptr;
+}
+
+// Find all locations identified by the given USRs for rename.
+//
+// This class will traverse the AST and find every AST node whose USR is in the
+// given USRs' set.
+class RenameLocFinder : public RecursiveASTVisitor<RenameLocFinder> {
+public:
+ RenameLocFinder(llvm::ArrayRef<std::string> USRs, ASTContext &Context)
+ : USRSet(USRs.begin(), USRs.end()), Context(Context) {}
+
+ // A structure records all information of a symbol reference being renamed.
+ // We try to add as few prefix qualifiers as possible.
+ struct RenameInfo {
+ // The begin location of a symbol being renamed.
+ SourceLocation Begin;
+ // The end location of a symbol being renamed.
+ SourceLocation End;
+ // The declaration of a symbol being renamed (can be nullptr).
+ const NamedDecl *FromDecl;
+ // The declaration in which the nested name is contained (can be nullptr).
+ const Decl *Context;
+ // The nested name being replaced (can be nullptr).
+ const NestedNameSpecifier *Specifier;
+ };
+
+ // FIXME: Currently, prefix qualifiers will be added to the renamed symbol
+ // definition (e.g. "class Foo {};" => "class b::Bar {};" when renaming
+ // "a::Foo" to "b::Bar").
+ // For renaming declarations/definitions, prefix qualifiers should be filtered
+ // out.
+ bool VisitNamedDecl(const NamedDecl *Decl) {
+ // UsingDecl has been handled in other place.
+ if (llvm::isa<UsingDecl>(Decl))
+ return true;
+
+ // DestructorDecl has been handled in Typeloc.
+ if (llvm::isa<CXXDestructorDecl>(Decl))
+ return true;
+
+ if (Decl->isImplicit())
+ return true;
+
+ if (isInUSRSet(Decl)) {
+ RenameInfo Info = {Decl->getLocation(), Decl->getLocation(), nullptr,
+ nullptr, nullptr};
+ RenameInfos.push_back(Info);
+ }
+ return true;
+ }
+
+ bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
+ const NamedDecl *Decl = Expr->getFoundDecl();
+ if (isInUSRSet(Decl)) {
+ RenameInfo Info = {Expr->getSourceRange().getBegin(),
+ Expr->getSourceRange().getEnd(), Decl,
+ getClosestAncestorDecl(*Expr), Expr->getQualifier()};
+ RenameInfos.push_back(Info);
+ }
+
+ return true;
+ }
+
+ bool VisitUsingDecl(const UsingDecl *Using) {
+ for (const auto *UsingShadow : Using->shadows()) {
+ if (isInUSRSet(UsingShadow->getTargetDecl())) {
+ UsingDecls.push_back(Using);
+ break;
+ }
+ }
+ return true;
+ }
+
+ bool VisitNestedNameSpecifierLocations(NestedNameSpecifierLoc NestedLoc) {
+ if (!NestedLoc.getNestedNameSpecifier()->getAsType())
+ return true;
+ if (IsTypeAliasWhichWillBeRenamedElsewhere(NestedLoc.getTypeLoc()))
+ return true;
+
+ if (const auto *TargetDecl =
+ getSupportedDeclFromTypeLoc(NestedLoc.getTypeLoc())) {
+ if (isInUSRSet(TargetDecl)) {
+ RenameInfo Info = {NestedLoc.getBeginLoc(),
+ EndLocationForType(NestedLoc.getTypeLoc()),
+ TargetDecl, getClosestAncestorDecl(NestedLoc),
+ NestedLoc.getNestedNameSpecifier()->getPrefix()};
+ RenameInfos.push_back(Info);
+ }
+ }
+ return true;
+ }
+
+ bool VisitTypeLoc(TypeLoc Loc) {
+ if (IsTypeAliasWhichWillBeRenamedElsewhere(Loc))
+ return true;
+
+ auto Parents = Context.getParents(Loc);
+ TypeLoc ParentTypeLoc;
+ if (!Parents.empty()) {
+ // Handle cases of nested name specificier locations.
+ //
+ // The VisitNestedNameSpecifierLoc interface is not impelmented in
+ // RecursiveASTVisitor, we have to handle it explicitly.
+ if (const auto *NSL = Parents[0].get<NestedNameSpecifierLoc>()) {
+ VisitNestedNameSpecifierLocations(*NSL);
+ return true;
+ }
+
+ if (const auto *TL = Parents[0].get<TypeLoc>())
+ ParentTypeLoc = *TL;
+ }
+
+ // Handle the outermost TypeLoc which is directly linked to the interesting
+ // declaration and don't handle nested name specifier locations.
+ if (const auto *TargetDecl = getSupportedDeclFromTypeLoc(Loc)) {
+ if (isInUSRSet(TargetDecl)) {
+ // Only handle the outermost typeLoc.
+ //
+ // For a type like "a::Foo", there will be two typeLocs for it.
+ // One ElaboratedType, the other is RecordType:
+ //
+ // ElaboratedType 0x33b9390 'a::Foo' sugar
+ // `-RecordType 0x338fef0 'class a::Foo'
+ // `-CXXRecord 0x338fe58 'Foo'
+ //
+ // Skip if this is an inner typeLoc.
+ if (!ParentTypeLoc.isNull() &&
+ isInUSRSet(getSupportedDeclFromTypeLoc(ParentTypeLoc)))
+ return true;
+ RenameInfo Info = {StartLocationForType(Loc), EndLocationForType(Loc),
+ TargetDecl, getClosestAncestorDecl(Loc),
+ GetNestedNameForType(Loc)};
+ RenameInfos.push_back(Info);
+ return true;
+ }
+ }
+
+ // Handle specific template class specialiation cases.
+ if (const auto *TemplateSpecType =
+ dyn_cast<TemplateSpecializationType>(Loc.getType())) {
+ TypeLoc TargetLoc = Loc;
+ if (!ParentTypeLoc.isNull()) {
+ if (llvm::isa<ElaboratedType>(ParentTypeLoc.getType()))
+ TargetLoc = ParentTypeLoc;
+ }
+
+ if (isInUSRSet(TemplateSpecType->getTemplateName().getAsTemplateDecl())) {
+ TypeLoc TargetLoc = Loc;
+ // FIXME: Find a better way to handle this case.
+ // For the qualified template class specification type like
+ // "ns::Foo<int>" in "ns::Foo<int>& f();", we want the parent typeLoc
+ // (ElaboratedType) of the TemplateSpecializationType in order to
+ // catch the prefix qualifiers "ns::".
+ if (!ParentTypeLoc.isNull() &&
+ llvm::isa<ElaboratedType>(ParentTypeLoc.getType()))
+ TargetLoc = ParentTypeLoc;
+ RenameInfo Info = {
+ StartLocationForType(TargetLoc), EndLocationForType(TargetLoc),
+ TemplateSpecType->getTemplateName().getAsTemplateDecl(),
+ getClosestAncestorDecl(
+ ast_type_traits::DynTypedNode::create(TargetLoc)),
+ GetNestedNameForType(TargetLoc)};
+ RenameInfos.push_back(Info);
+ }
+ }
+ return true;
+ }
+
+ // Returns a list of RenameInfo.
+ const std::vector<RenameInfo> &getRenameInfos() const { return RenameInfos; }
+
+ // Returns a list of using declarations which are needed to update.
+ const std::vector<const UsingDecl *> &getUsingDecls() const {
+ return UsingDecls;
+ }
+
+private:
+ // FIXME: This method may not be suitable for renaming other types like alias
+ // types. Need to figure out a way to handle it.
+ bool IsTypeAliasWhichWillBeRenamedElsewhere(TypeLoc TL) const {
+ while (!TL.isNull()) {
+ // SubstTemplateTypeParm is the TypeLocation class for a substituted type
+ // inside a template expansion so we ignore these. For example:
+ //
+ // template<typename T> struct S {
+ // T t; // <-- this T becomes a TypeLoc(int) with class
+ // // SubstTemplateTypeParm when S<int> is instantiated
+ // }
+ if (TL.getTypeLocClass() == TypeLoc::SubstTemplateTypeParm)
+ return true;
+
+ // Typedef is the TypeLocation class for a type which is a typedef to the
+ // type we want to replace. We ignore the use of the typedef as we will
+ // replace the definition of it. For example:
+ //
+ // typedef int T;
+ // T a; // <--- This T is a TypeLoc(int) with class Typedef.
+ if (TL.getTypeLocClass() == TypeLoc::Typedef)
+ return true;
+ TL = TL.getNextTypeLoc();
+ }
+ return false;
+ }
+
+ // Get the supported declaration from a given typeLoc. If the declaration type
+ // is not supported, returns nullptr.
+ //
+ // FIXME: support more types, e.g. enum, type alias.
+ const NamedDecl *getSupportedDeclFromTypeLoc(TypeLoc Loc) {
+ if (const auto *RD = Loc.getType()->getAsCXXRecordDecl())
+ return RD;
+ return nullptr;
+ }
+
+ // Get the closest ancester which is a declaration of a given AST node.
+ template <typename ASTNodeType>
+ const Decl *getClosestAncestorDecl(const ASTNodeType &Node) {
+ auto Parents = Context.getParents(Node);
+ // FIXME: figure out how to handle it when there are multiple parents.
+ if (Parents.size() != 1)
+ return nullptr;
+ if (ast_type_traits::ASTNodeKind::getFromNodeKind<Decl>().isBaseOf(
+ Parents[0].getNodeKind()))
+ return Parents[0].template get<Decl>();
+ return getClosestAncestorDecl(Parents[0]);
+ }
+
+ // Get the parent typeLoc of a given typeLoc. If there is no such parent,
+ // return nullptr.
+ const TypeLoc *getParentTypeLoc(TypeLoc Loc) const {
+ auto Parents = Context.getParents(Loc);
+ // FIXME: figure out how to handle it when there are multiple parents.
+ if (Parents.size() != 1)
+ return nullptr;
+ return Parents[0].get<TypeLoc>();
+ }
+
+ // Check whether the USR of a given Decl is in the USRSet.
+ bool isInUSRSet(const Decl *Decl) const {
+ auto USR = getUSRForDecl(Decl);
+ if (USR.empty())
+ return false;
+ return llvm::is_contained(USRSet, USR);
+ }
+
+ const std::set<std::string> USRSet;
+ ASTContext &Context;
+ std::vector<RenameInfo> RenameInfos;
+ // Record all interested using declarations which contains the using-shadow
+ // declarations of the symbol declarations being renamed.
+ std::vector<const UsingDecl *> UsingDecls;
+};
+
+} // namespace
+
+std::vector<SourceLocation>
+getLocationsOfUSRs(const std::vector<std::string> &USRs, StringRef PrevName,
+ Decl *Decl) {
+ USRLocFindingASTVisitor Visitor(USRs, PrevName, Decl->getASTContext());
+ Visitor.TraverseDecl(Decl);
+ NestedNameSpecifierLocFinder Finder(Decl->getASTContext());
+
+ for (const auto &Location : Finder.getNestedNameSpecifierLocations())
+ Visitor.handleNestedNameSpecifierLoc(Location);
+
+ return Visitor.getLocationsFound();
+}
+
+std::vector<tooling::AtomicChange>
+createRenameAtomicChanges(llvm::ArrayRef<std::string> USRs,
+ llvm::StringRef NewName, Decl *TranslationUnitDecl) {
+ RenameLocFinder Finder(USRs, TranslationUnitDecl->getASTContext());
+ Finder.TraverseDecl(TranslationUnitDecl);
+
+ const SourceManager &SM =
+ TranslationUnitDecl->getASTContext().getSourceManager();
+
+ std::vector<tooling::AtomicChange> AtomicChanges;
+ auto Replace = [&](SourceLocation Start, SourceLocation End,
+ llvm::StringRef Text) {
+ tooling::AtomicChange ReplaceChange = tooling::AtomicChange(SM, Start);
+ llvm::Error Err = ReplaceChange.replace(
+ SM, CharSourceRange::getTokenRange(Start, End), Text);
+ if (Err) {
+ llvm::errs() << "Faile to add replacement to AtomicChange: "
+ << llvm::toString(std::move(Err)) << "\n";
+ return;
+ }
+ AtomicChanges.push_back(std::move(ReplaceChange));
+ };
+
+ for (const auto &RenameInfo : Finder.getRenameInfos()) {
+ std::string ReplacedName = NewName.str();
+ if (RenameInfo.FromDecl && RenameInfo.Context) {
+ if (!llvm::isa<clang::TranslationUnitDecl>(
+ RenameInfo.Context->getDeclContext())) {
+ ReplacedName = tooling::replaceNestedName(
+ RenameInfo.Specifier, RenameInfo.Context->getDeclContext(),
+ RenameInfo.FromDecl,
+ NewName.startswith("::") ? NewName.str() : ("::" + NewName).str());
+ }
+ }
+ // If the NewName contains leading "::", add it back.
+ if (NewName.startswith("::") && NewName.substr(2) == ReplacedName)
+ ReplacedName = NewName.str();
+ Replace(RenameInfo.Begin, RenameInfo.End, ReplacedName);
+ }
+
+ // Hanlde using declarations explicitly as "using a::Foo" don't trigger
+ // typeLoc for "a::Foo".
+ for (const auto *Using : Finder.getUsingDecls())
+ Replace(Using->getLocStart(), Using->getLocEnd(), "using " + NewName.str());
+
+ return AtomicChanges;
+}
+
+} // end namespace tooling
+} // end namespace clang
Index: lib/Tooling/Refactoring/USRFindingAction.cpp
===================================================================
--- /dev/null
+++ lib/Tooling/Refactoring/USRFindingAction.cpp
@@ -0,0 +1,236 @@
+//===--- USRFindingAction.cpp - Clang refactoring library -----------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Provides an action to find USR for the symbol at <offset>, as well as
+/// all additional USRs.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/Refactoring/USRFindingAction.h"
+#include "clang/Tooling/Refactoring/USRFinder.h"
+#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/Basic/FileManager.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendAction.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Tooling.h"
+
+#include <algorithm>
+#include <set>
+#include <string>
+#include <vector>
+
+using namespace llvm;
+
+namespace clang {
+namespace tooling {
+
+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.
+class AdditionalUSRFinder : public RecursiveASTVisitor<AdditionalUSRFinder> {
+public:
+ AdditionalUSRFinder(const Decl *FoundDecl, ASTContext &Context)
+ : FoundDecl(FoundDecl), Context(Context) {}
+
+ std::vector<std::string> Find() {
+ // Fill OverriddenMethods and PartialSpecs storages.
+ TraverseDecl(Context.getTranslationUnitDecl());
+ if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(FoundDecl)) {
+ addUSRsOfOverridenFunctions(MethodDecl);
+ for (const auto &OverriddenMethod : OverriddenMethods) {
+ if (checkIfOverriddenFunctionAscends(OverriddenMethod))
+ USRSet.insert(getUSRForDecl(OverriddenMethod));
+ }
+ } else if (const auto *RecordDecl = dyn_cast<CXXRecordDecl>(FoundDecl)) {
+ handleCXXRecordDecl(RecordDecl);
+ } else if (const auto *TemplateDecl =
+ dyn_cast<ClassTemplateDecl>(FoundDecl)) {
+ handleClassTemplateDecl(TemplateDecl);
+ } else {
+ USRSet.insert(getUSRForDecl(FoundDecl));
+ }
+ return std::vector<std::string>(USRSet.begin(), USRSet.end());
+ }
+
+ bool VisitCXXMethodDecl(const CXXMethodDecl *MethodDecl) {
+ if (MethodDecl->isVirtual())
+ OverriddenMethods.push_back(MethodDecl);
+ return true;
+ }
+
+ bool VisitClassTemplatePartialSpecializationDecl(
+ const ClassTemplatePartialSpecializationDecl *PartialSpec) {
+ PartialSpecs.push_back(PartialSpec);
+ return true;
+ }
+
+private:
+ void handleCXXRecordDecl(const CXXRecordDecl *RecordDecl) {
+ RecordDecl = RecordDecl->getDefinition();
+ if (const auto *ClassTemplateSpecDecl =
+ dyn_cast<ClassTemplateSpecializationDecl>(RecordDecl))
+ handleClassTemplateDecl(ClassTemplateSpecDecl->getSpecializedTemplate());
+ addUSRsOfCtorDtors(RecordDecl);
+ }
+
+ void handleClassTemplateDecl(const ClassTemplateDecl *TemplateDecl) {
+ for (const auto *Specialization : TemplateDecl->specializations())
+ addUSRsOfCtorDtors(Specialization);
+
+ for (const auto *PartialSpec : PartialSpecs) {
+ if (PartialSpec->getSpecializedTemplate() == TemplateDecl)
+ addUSRsOfCtorDtors(PartialSpec);
+ }
+ addUSRsOfCtorDtors(TemplateDecl->getTemplatedDecl());
+ }
+
+ void addUSRsOfCtorDtors(const CXXRecordDecl *RecordDecl) {
+ RecordDecl = RecordDecl->getDefinition();
+
+ // Skip if the CXXRecordDecl doesn't have definition.
+ if (!RecordDecl)
+ return;
+
+ for (const auto *CtorDecl : RecordDecl->ctors())
+ USRSet.insert(getUSRForDecl(CtorDecl));
+
+ USRSet.insert(getUSRForDecl(RecordDecl->getDestructor()));
+ USRSet.insert(getUSRForDecl(RecordDecl));
+ }
+
+ void addUSRsOfOverridenFunctions(const CXXMethodDecl *MethodDecl) {
+ USRSet.insert(getUSRForDecl(MethodDecl));
+ // Recursively visit each OverridenMethod.
+ for (const auto &OverriddenMethod : MethodDecl->overridden_methods())
+ addUSRsOfOverridenFunctions(OverriddenMethod);
+ }
+
+ bool checkIfOverriddenFunctionAscends(const CXXMethodDecl *MethodDecl) {
+ for (const auto &OverriddenMethod : MethodDecl->overridden_methods()) {
+ if (USRSet.find(getUSRForDecl(OverriddenMethod)) != USRSet.end())
+ return true;
+ return checkIfOverriddenFunctionAscends(OverriddenMethod);
+ }
+ return false;
+ }
+
+ const Decl *FoundDecl;
+ ASTContext &Context;
+ std::set<std::string> USRSet;
+ std::vector<const CXXMethodDecl *> OverriddenMethods;
+ std::vector<const ClassTemplatePartialSpecializationDecl *> PartialSpecs;
+};
+} // namespace
+
+class NamedDeclFindingConsumer : public ASTConsumer {
+public:
+ NamedDeclFindingConsumer(ArrayRef<unsigned> SymbolOffsets,
+ ArrayRef<std::string> QualifiedNames,
+ std::vector<std::string> &SpellingNames,
+ std::vector<std::vector<std::string>> &USRList,
+ bool Force, bool &ErrorOccurred)
+ : SymbolOffsets(SymbolOffsets), QualifiedNames(QualifiedNames),
+ SpellingNames(SpellingNames), USRList(USRList), Force(Force),
+ ErrorOccurred(ErrorOccurred) {}
+
+private:
+ bool FindSymbol(ASTContext &Context, const SourceManager &SourceMgr,
+ unsigned SymbolOffset, const std::string &QualifiedName) {
+ DiagnosticsEngine &Engine = Context.getDiagnostics();
+ const FileID MainFileID = SourceMgr.getMainFileID();
+
+ if (SymbolOffset >= SourceMgr.getFileIDSize(MainFileID)) {
+ ErrorOccurred = true;
+ unsigned InvalidOffset = Engine.getCustomDiagID(
+ DiagnosticsEngine::Error,
+ "SourceLocation in file %0 at offset %1 is invalid");
+ Engine.Report(SourceLocation(), InvalidOffset)
+ << SourceMgr.getFileEntryForID(MainFileID)->getName() << SymbolOffset;
+ return false;
+ }
+
+ const SourceLocation Point = SourceMgr.getLocForStartOfFile(MainFileID)
+ .getLocWithOffset(SymbolOffset);
+ const NamedDecl *FoundDecl = QualifiedName.empty()
+ ? getNamedDeclAt(Context, Point)
+ : getNamedDeclFor(Context, QualifiedName);
+
+ if (FoundDecl == nullptr) {
+ if (QualifiedName.empty()) {
+ FullSourceLoc FullLoc(Point, SourceMgr);
+ unsigned CouldNotFindSymbolAt = Engine.getCustomDiagID(
+ DiagnosticsEngine::Error,
+ "clang-rename could not find symbol (offset %0)");
+ Engine.Report(Point, CouldNotFindSymbolAt) << SymbolOffset;
+ ErrorOccurred = true;
+ return false;
+ }
+
+ if (Force)
+ return true;
+
+ unsigned CouldNotFindSymbolNamed = Engine.getCustomDiagID(
+ DiagnosticsEngine::Error, "clang-rename could not find symbol %0");
+ Engine.Report(CouldNotFindSymbolNamed) << QualifiedName;
+ ErrorOccurred = true;
+ return false;
+ }
+
+ // 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))
+ FoundDecl = DtorDecl->getParent();
+
+ SpellingNames.push_back(FoundDecl->getNameAsString());
+ AdditionalUSRFinder Finder(FoundDecl, Context);
+ USRList.push_back(Finder.Find());
+ return true;
+ }
+
+ void HandleTranslationUnit(ASTContext &Context) override {
+ const SourceManager &SourceMgr = Context.getSourceManager();
+ for (unsigned Offset : SymbolOffsets) {
+ if (!FindSymbol(Context, SourceMgr, Offset, ""))
+ return;
+ }
+ for (const std::string &QualifiedName : QualifiedNames) {
+ if (!FindSymbol(Context, SourceMgr, 0, QualifiedName))
+ return;
+ }
+ }
+
+ ArrayRef<unsigned> SymbolOffsets;
+ ArrayRef<std::string> QualifiedNames;
+ std::vector<std::string> &SpellingNames;
+ std::vector<std::vector<std::string>> &USRList;
+ bool Force;
+ bool &ErrorOccurred;
+};
+
+std::unique_ptr<ASTConsumer> USRFindingAction::newASTConsumer() {
+ return llvm::make_unique<NamedDeclFindingConsumer>(
+ SymbolOffsets, QualifiedNames, SpellingNames, USRList, Force,
+ ErrorOccurred);
+}
+
+} // end namespace tooling
+} // end namespace clang
Index: lib/Tooling/Refactoring/USRFinder.cpp
===================================================================
--- /dev/null
+++ lib/Tooling/Refactoring/USRFinder.cpp
@@ -0,0 +1,213 @@
+//===--- USRFinder.cpp - Clang refactoring library ------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file Implements a recursive AST visitor that finds the USR of a symbol at a
+/// point.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/Refactoring/USRFinder.h"
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Index/USRGeneration.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/SmallVector.h"
+
+using namespace llvm;
+
+namespace clang {
+namespace tooling {
+
+// NamedDeclFindingASTVisitor recursively visits each AST node to find the
+// symbol underneath the cursor.
+// FIXME: move to separate .h/.cc file if this gets too large.
+namespace {
+class NamedDeclFindingASTVisitor
+ : public clang::RecursiveASTVisitor<NamedDeclFindingASTVisitor> {
+public:
+ // \brief Finds the NamedDecl at a point in the source.
+ // \param Point the location in the source to search for the NamedDecl.
+ explicit NamedDeclFindingASTVisitor(const SourceLocation Point,
+ const ASTContext &Context)
+ : Result(nullptr), Point(Point), Context(Context) {}
+
+ // \brief Finds the NamedDecl for a name in the source.
+ // \param Name the fully qualified name.
+ explicit NamedDeclFindingASTVisitor(const std::string &Name,
+ const ASTContext &Context)
+ : Result(nullptr), Name(Name), Context(Context) {}
+
+ // Declaration visitors:
+
+ // \brief Checks if the point falls within the NameDecl. This covers every
+ // declaration of a named entity that we may come across. Usually, just
+ // checking if the point lies within the length of the name of the declaration
+ // and the start location is sufficient.
+ bool VisitNamedDecl(const NamedDecl *Decl) {
+ return dyn_cast<CXXConversionDecl>(Decl)
+ ? true
+ : setResult(Decl, Decl->getLocation(),
+ Decl->getNameAsString().length());
+ }
+
+ // Expression visitors:
+
+ bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
+ const NamedDecl *Decl = Expr->getFoundDecl();
+ return setResult(Decl, Expr->getLocation(),
+ Decl->getNameAsString().length());
+ }
+
+ bool VisitMemberExpr(const MemberExpr *Expr) {
+ const NamedDecl *Decl = Expr->getFoundDecl().getDecl();
+ return setResult(Decl, Expr->getMemberLoc(),
+ Decl->getNameAsString().length());
+ }
+
+ // Other visitors:
+
+ bool VisitTypeLoc(const TypeLoc Loc) {
+ const SourceLocation TypeBeginLoc = Loc.getBeginLoc();
+ const SourceLocation TypeEndLoc = Lexer::getLocForEndOfToken(
+ TypeBeginLoc, 0, Context.getSourceManager(), Context.getLangOpts());
+ if (const auto *TemplateTypeParm =
+ dyn_cast<TemplateTypeParmType>(Loc.getType()))
+ return setResult(TemplateTypeParm->getDecl(), TypeBeginLoc, TypeEndLoc);
+ if (const auto *TemplateSpecType =
+ dyn_cast<TemplateSpecializationType>(Loc.getType())) {
+ return setResult(TemplateSpecType->getTemplateName().getAsTemplateDecl(),
+ TypeBeginLoc, TypeEndLoc);
+ }
+ return setResult(Loc.getType()->getAsCXXRecordDecl(), TypeBeginLoc,
+ TypeEndLoc);
+ }
+
+ bool VisitCXXConstructorDecl(clang::CXXConstructorDecl *ConstructorDecl) {
+ for (const auto *Initializer : ConstructorDecl->inits()) {
+ // Ignore implicit initializers.
+ if (!Initializer->isWritten())
+ continue;
+ if (const clang::FieldDecl *FieldDecl = Initializer->getMember()) {
+ const SourceLocation InitBeginLoc = Initializer->getSourceLocation(),
+ InitEndLoc = Lexer::getLocForEndOfToken(
+ InitBeginLoc, 0, Context.getSourceManager(),
+ Context.getLangOpts());
+ if (!setResult(FieldDecl, InitBeginLoc, InitEndLoc))
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // Other:
+
+ const NamedDecl *getNamedDecl() { return Result; }
+
+ // \brief Determines if a namespace qualifier contains the point.
+ // \returns false on success and sets Result.
+ void handleNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) {
+ while (NameLoc) {
+ const NamespaceDecl *Decl =
+ NameLoc.getNestedNameSpecifier()->getAsNamespace();
+ setResult(Decl, NameLoc.getLocalBeginLoc(), NameLoc.getLocalEndLoc());
+ NameLoc = NameLoc.getPrefix();
+ }
+ }
+
+private:
+ // \brief Sets Result to Decl if the Point is within Start and End.
+ // \returns false on success.
+ bool setResult(const NamedDecl *Decl, SourceLocation Start,
+ SourceLocation End) {
+ if (!Decl)
+ return true;
+ if (Name.empty()) {
+ // Offset is used to find the declaration.
+ if (!Start.isValid() || !Start.isFileID() || !End.isValid() ||
+ !End.isFileID() || !isPointWithin(Start, End))
+ return true;
+ } else {
+ // Fully qualified name is used to find the declaration.
+ if (Name != Decl->getQualifiedNameAsString() &&
+ Name != "::" + Decl->getQualifiedNameAsString())
+ return true;
+ }
+ Result = Decl;
+ return false;
+ }
+
+ // \brief Sets Result to Decl if Point is within Loc and Loc + Offset.
+ // \returns false on success.
+ bool setResult(const NamedDecl *Decl, SourceLocation Loc, unsigned Offset) {
+ // FIXME: Add test for Offset == 0. Add test for Offset - 1 (vs -2 etc).
+ return Offset == 0 ||
+ setResult(Decl, Loc, Loc.getLocWithOffset(Offset - 1));
+ }
+
+ // \brief Determines if the Point is within Start and End.
+ bool isPointWithin(const SourceLocation Start, const SourceLocation End) {
+ // FIXME: Add tests for Point == End.
+ return Point == Start || Point == End ||
+ (Context.getSourceManager().isBeforeInTranslationUnit(Start,
+ Point) &&
+ Context.getSourceManager().isBeforeInTranslationUnit(Point, End));
+ }
+
+ const NamedDecl *Result;
+ const SourceLocation Point; // The location to find the NamedDecl.
+ const std::string Name;
+ const ASTContext &Context;
+};
+} // namespace
+
+const NamedDecl *getNamedDeclAt(const ASTContext &Context,
+ const SourceLocation Point) {
+ const SourceManager &SM = Context.getSourceManager();
+ NamedDeclFindingASTVisitor Visitor(Point, Context);
+
+ // Try to be clever about pruning down the number of top-level declarations we
+ // see. If both start and end is either before or after the point we're
+ // looking for the point cannot be inside of this decl. Don't even look at it.
+ for (auto *CurrDecl : Context.getTranslationUnitDecl()->decls()) {
+ SourceLocation StartLoc = CurrDecl->getLocStart();
+ SourceLocation EndLoc = CurrDecl->getLocEnd();
+ if (StartLoc.isValid() && EndLoc.isValid() &&
+ SM.isBeforeInTranslationUnit(StartLoc, Point) !=
+ SM.isBeforeInTranslationUnit(EndLoc, Point))
+ Visitor.TraverseDecl(CurrDecl);
+ }
+
+ NestedNameSpecifierLocFinder Finder(const_cast<ASTContext &>(Context));
+ for (const auto &Location : Finder.getNestedNameSpecifierLocations())
+ Visitor.handleNestedNameSpecifierLoc(Location);
+
+ return Visitor.getNamedDecl();
+}
+
+const NamedDecl *getNamedDeclFor(const ASTContext &Context,
+ const std::string &Name) {
+ NamedDeclFindingASTVisitor Visitor(Name, Context);
+ Visitor.TraverseDecl(Context.getTranslationUnitDecl());
+
+ return Visitor.getNamedDecl();
+}
+
+std::string getUSRForDecl(const Decl *Decl) {
+ llvm::SmallVector<char, 128> Buff;
+
+ // FIXME: Add test for the nullptr case.
+ if (Decl == nullptr || index::generateUSRForDecl(Decl, Buff))
+ return "";
+
+ return std::string(Buff.data(), Buff.size());
+}
+
+} // end namespace tooling
+} // end namespace clang
Index: lib/Tooling/Refactoring/CMakeLists.txt
===================================================================
--- lib/Tooling/Refactoring/CMakeLists.txt
+++ lib/Tooling/Refactoring/CMakeLists.txt
@@ -5,6 +5,9 @@
add_clang_library(clangToolingRefactor
AtomicChange.cpp
+ USRFinder.cpp
+ USRFindingAction.cpp
+ USRLocFinder.cpp
LINK_LIBS
clangBasic
Index: include/clang/module.modulemap
===================================================================
--- include/clang/module.modulemap
+++ include/clang/module.modulemap
@@ -133,9 +133,10 @@
module Clang_Tooling {
requires cplusplus umbrella "Tooling" module * { export * }
- // FIXME: Exclude this header to avoid pulling all of the AST matchers
+ // FIXME: Exclude these headers to avoid pulling all of the AST matchers
// library into clang-format. Due to inline key functions in the headers,
// importing the AST matchers library gives a link dependency on the AST
// matchers (and thus the AST), which clang-format should not have.
exclude header "Tooling/RefactoringCallbacks.h"
+ exclude header "Tooling/Refactoring/USRFinder.h"
}
Index: include/clang/Tooling/Refactoring/USRLocFinder.h
===================================================================
--- /dev/null
+++ include/clang/Tooling/Refactoring/USRLocFinder.h
@@ -0,0 +1,49 @@
+//===--- USRLocFinder.h - Clang refactoring library -----------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Provides functionality for finding all instances of a USR in a given
+/// AST.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_REFACTOR_USR_LOC_FINDER_H
+#define LLVM_CLANG_TOOLING_REFACTOR_USR_LOC_FINDER_H
+
+#include "clang/AST/AST.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include "clang/Tooling/Refactoring/AtomicChange.h"
+#include "llvm/ADT/StringRef.h"
+#include <string>
+#include <vector>
+
+namespace clang {
+namespace tooling {
+
+/// Create atomic changes for renaming all symbol references which are
+/// identified by the USRs set to a given new name.
+///
+/// \param USRs The set containing USRs of a particular old symbol.
+/// \param NewName The new name to replace old symbol name.
+/// \param TranslationUnitDecl The translation unit declaration.
+///
+/// \return Atomic changes for renaming.
+std::vector<tooling::AtomicChange>
+createRenameAtomicChanges(llvm::ArrayRef<std::string> USRs,
+ llvm::StringRef NewName, Decl *TranslationUnitDecl);
+
+// FIXME: make this an AST matcher. Wouldn't that be awesome??? I agree!
+std::vector<SourceLocation>
+getLocationsOfUSRs(const std::vector<std::string> &USRs,
+ llvm::StringRef PrevName, Decl *Decl);
+
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_REFACTOR_USR_LOC_FINDER_H
Index: include/clang/Tooling/Refactoring/USRFindingAction.h
===================================================================
--- /dev/null
+++ include/clang/Tooling/Refactoring/USRFindingAction.h
@@ -0,0 +1,54 @@
+//===--- USRFindingAction.h - Clang refactoring library -------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Provides an action to find all relevant USRs at a point.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_REFACTOR_USR_FINDING_ACTION_H
+#define LLVM_CLANG_TOOLING_REFACTOR_USR_FINDING_ACTION_H
+
+#include "clang/Basic/LLVM.h"
+#include "llvm/ADT/ArrayRef.h"
+
+#include <string>
+#include <vector>
+
+namespace clang {
+class ASTConsumer;
+class CompilerInstance;
+class NamedDecl;
+
+namespace tooling {
+
+struct USRFindingAction {
+ USRFindingAction(ArrayRef<unsigned> SymbolOffsets,
+ ArrayRef<std::string> QualifiedNames, bool Force)
+ : SymbolOffsets(SymbolOffsets), QualifiedNames(QualifiedNames),
+ ErrorOccurred(false), Force(Force) {}
+ std::unique_ptr<ASTConsumer> newASTConsumer();
+
+ ArrayRef<std::string> getUSRSpellings() { return SpellingNames; }
+ ArrayRef<std::vector<std::string>> getUSRList() { return USRList; }
+ bool errorOccurred() { return ErrorOccurred; }
+
+private:
+ std::vector<unsigned> SymbolOffsets;
+ std::vector<std::string> QualifiedNames;
+ std::vector<std::string> SpellingNames;
+ std::vector<std::vector<std::string>> USRList;
+ bool ErrorOccurred;
+ bool Force;
+};
+
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_REFACTOR_USR_FINDING_ACTION_H
Index: include/clang/Tooling/Refactoring/USRFinder.h
===================================================================
--- /dev/null
+++ include/clang/Tooling/Refactoring/USRFinder.h
@@ -0,0 +1,84 @@
+//===--- USRFinder.h - Clang refactoring library --------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Methods for determining the USR of a symbol at a location in source
+/// code.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_REFACTOR_USR_FINDER_H
+#define LLVM_CLANG_TOOLING_REFACTOR_USR_FINDER_H
+
+#include "clang/AST/AST.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include <string>
+#include <vector>
+
+using namespace llvm;
+using namespace clang::ast_matchers;
+
+namespace clang {
+
+class ASTContext;
+class Decl;
+class SourceLocation;
+class NamedDecl;
+
+namespace tooling {
+
+// Given an AST context and a point, returns a NamedDecl identifying the symbol
+// at the point. Returns null if nothing is found at the point.
+const NamedDecl *getNamedDeclAt(const ASTContext &Context,
+ const SourceLocation Point);
+
+// Given an AST context and a fully qualified name, returns a NamedDecl
+// identifying the symbol with a matching name. Returns null if nothing is
+// found for the name.
+const NamedDecl *getNamedDeclFor(const ASTContext &Context,
+ const std::string &Name);
+
+// Converts a Decl into a USR.
+std::string getUSRForDecl(const Decl *Decl);
+
+// FIXME: Implement RecursiveASTVisitor<T>::VisitNestedNameSpecifier instead.
+class NestedNameSpecifierLocFinder : public MatchFinder::MatchCallback {
+public:
+ explicit NestedNameSpecifierLocFinder(ASTContext &Context)
+ : Context(Context) {}
+
+ std::vector<NestedNameSpecifierLoc> getNestedNameSpecifierLocations() {
+ addMatchers();
+ Finder.matchAST(Context);
+ return Locations;
+ }
+
+private:
+ void addMatchers() {
+ const auto NestedNameSpecifierLocMatcher =
+ nestedNameSpecifierLoc().bind("nestedNameSpecifierLoc");
+ Finder.addMatcher(NestedNameSpecifierLocMatcher, this);
+ }
+
+ void run(const MatchFinder::MatchResult &Result) override {
+ const auto *NNS = Result.Nodes.getNodeAs<NestedNameSpecifierLoc>(
+ "nestedNameSpecifierLoc");
+ Locations.push_back(*NNS);
+ }
+
+ ASTContext &Context;
+ std::vector<NestedNameSpecifierLoc> Locations;
+ MatchFinder Finder;
+};
+
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_REFACTOR_USR_FINDER_H
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits