147 lines
5.0 KiB
C++
147 lines
5.0 KiB
C++
//===--- USRFinder.cpp - Clang refactoring library ------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
///
|
|
/// \file Implements a recursive AST visitor that finds the USR of a symbol at a
|
|
/// point.
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/Tooling/Refactoring/Rename/USRFinder.h"
|
|
#include "clang/AST/AST.h"
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/AST/RecursiveASTVisitor.h"
|
|
#include "clang/Basic/SourceManager.h"
|
|
#include "clang/Index/USRGeneration.h"
|
|
#include "clang/Lex/Lexer.h"
|
|
#include "clang/Tooling/Refactoring/RecursiveSymbolVisitor.h"
|
|
#include "llvm/ADT/SmallVector.h"
|
|
|
|
using namespace llvm;
|
|
|
|
namespace clang {
|
|
namespace tooling {
|
|
|
|
namespace {
|
|
|
|
/// Recursively visits each AST node to find the symbol underneath the cursor.
|
|
class NamedDeclOccurrenceFindingVisitor
|
|
: public RecursiveSymbolVisitor<NamedDeclOccurrenceFindingVisitor> {
|
|
public:
|
|
// Finds the NamedDecl at a point in the source.
|
|
// \param Point the location in the source to search for the NamedDecl.
|
|
explicit NamedDeclOccurrenceFindingVisitor(const SourceLocation Point,
|
|
const ASTContext &Context)
|
|
: RecursiveSymbolVisitor(Context.getSourceManager(),
|
|
Context.getLangOpts()),
|
|
Point(Point), Context(Context) {}
|
|
|
|
bool visitSymbolOccurrence(const NamedDecl *ND,
|
|
ArrayRef<SourceRange> NameRanges) {
|
|
if (!ND)
|
|
return true;
|
|
for (const auto &Range : NameRanges) {
|
|
SourceLocation Start = Range.getBegin();
|
|
SourceLocation End = Range.getEnd();
|
|
if (!Start.isValid() || !Start.isFileID() || !End.isValid() ||
|
|
!End.isFileID() || !isPointWithin(Start, End))
|
|
return true;
|
|
}
|
|
Result = ND;
|
|
return false;
|
|
}
|
|
|
|
const NamedDecl *getNamedDecl() const { return Result; }
|
|
|
|
private:
|
|
// 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 = nullptr;
|
|
const SourceLocation Point; // The location to find the NamedDecl.
|
|
const ASTContext &Context;
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
const NamedDecl *getNamedDeclAt(const ASTContext &Context,
|
|
const SourceLocation Point) {
|
|
const SourceManager &SM = Context.getSourceManager();
|
|
NamedDeclOccurrenceFindingVisitor 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->getBeginLoc();
|
|
SourceLocation EndLoc = CurrDecl->getEndLoc();
|
|
if (StartLoc.isValid() && EndLoc.isValid() &&
|
|
SM.isBeforeInTranslationUnit(StartLoc, Point) !=
|
|
SM.isBeforeInTranslationUnit(EndLoc, Point))
|
|
Visitor.TraverseDecl(CurrDecl);
|
|
}
|
|
|
|
return Visitor.getNamedDecl();
|
|
}
|
|
|
|
namespace {
|
|
|
|
/// Recursively visits each NamedDecl node to find the declaration with a
|
|
/// specific name.
|
|
class NamedDeclFindingVisitor
|
|
: public RecursiveASTVisitor<NamedDeclFindingVisitor> {
|
|
public:
|
|
explicit NamedDeclFindingVisitor(StringRef Name) : Name(Name) {}
|
|
|
|
// We don't have to traverse the uses to find some declaration with a
|
|
// specific name, so just visit the named declarations.
|
|
bool VisitNamedDecl(const NamedDecl *ND) {
|
|
if (!ND)
|
|
return true;
|
|
// Fully qualified name is used to find the declaration.
|
|
if (Name != ND->getQualifiedNameAsString() &&
|
|
Name != "::" + ND->getQualifiedNameAsString())
|
|
return true;
|
|
Result = ND;
|
|
return false;
|
|
}
|
|
|
|
const NamedDecl *getNamedDecl() const { return Result; }
|
|
|
|
private:
|
|
const NamedDecl *Result = nullptr;
|
|
StringRef Name;
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
const NamedDecl *getNamedDeclFor(const ASTContext &Context,
|
|
const std::string &Name) {
|
|
NamedDeclFindingVisitor Visitor(Name);
|
|
Visitor.TraverseDecl(Context.getTranslationUnitDecl());
|
|
return Visitor.getNamedDecl();
|
|
}
|
|
|
|
std::string getUSRForDecl(const Decl *Decl) {
|
|
llvm::SmallString<128> Buff;
|
|
|
|
// FIXME: Add test for the nullptr case.
|
|
if (Decl == nullptr || index::generateUSRForDecl(Decl, Buff))
|
|
return "";
|
|
|
|
return std::string(Buff);
|
|
}
|
|
|
|
} // end namespace tooling
|
|
} // end namespace clang
|