//===--- 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/Rename/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 "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 { public: // \brief 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 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: // \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 = 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->getLocStart(); SourceLocation EndLoc = CurrDecl->getLocEnd(); 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 { 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::SmallVector 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