1e5dd7070Spatrick //===--- USRFinder.cpp - Clang refactoring library ------------------------===//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick ///
9e5dd7070Spatrick /// \file Implements a recursive AST visitor that finds the USR of a symbol at a
10e5dd7070Spatrick /// point.
11e5dd7070Spatrick ///
12e5dd7070Spatrick //===----------------------------------------------------------------------===//
13e5dd7070Spatrick
14e5dd7070Spatrick #include "clang/Tooling/Refactoring/Rename/USRFinder.h"
15e5dd7070Spatrick #include "clang/AST/AST.h"
16e5dd7070Spatrick #include "clang/AST/ASTContext.h"
17e5dd7070Spatrick #include "clang/AST/RecursiveASTVisitor.h"
18ec727ea7Spatrick #include "clang/Basic/SourceManager.h"
19e5dd7070Spatrick #include "clang/Index/USRGeneration.h"
20e5dd7070Spatrick #include "clang/Lex/Lexer.h"
21e5dd7070Spatrick #include "clang/Tooling/Refactoring/RecursiveSymbolVisitor.h"
22e5dd7070Spatrick #include "llvm/ADT/SmallVector.h"
23e5dd7070Spatrick
24e5dd7070Spatrick using namespace llvm;
25e5dd7070Spatrick
26e5dd7070Spatrick namespace clang {
27e5dd7070Spatrick namespace tooling {
28e5dd7070Spatrick
29e5dd7070Spatrick namespace {
30e5dd7070Spatrick
31e5dd7070Spatrick /// Recursively visits each AST node to find the symbol underneath the cursor.
32e5dd7070Spatrick class NamedDeclOccurrenceFindingVisitor
33e5dd7070Spatrick : public RecursiveSymbolVisitor<NamedDeclOccurrenceFindingVisitor> {
34e5dd7070Spatrick public:
35e5dd7070Spatrick // Finds the NamedDecl at a point in the source.
36e5dd7070Spatrick // \param Point the location in the source to search for the NamedDecl.
NamedDeclOccurrenceFindingVisitor(const SourceLocation Point,const ASTContext & Context)37e5dd7070Spatrick explicit NamedDeclOccurrenceFindingVisitor(const SourceLocation Point,
38e5dd7070Spatrick const ASTContext &Context)
39e5dd7070Spatrick : RecursiveSymbolVisitor(Context.getSourceManager(),
40e5dd7070Spatrick Context.getLangOpts()),
41e5dd7070Spatrick Point(Point), Context(Context) {}
42e5dd7070Spatrick
visitSymbolOccurrence(const NamedDecl * ND,ArrayRef<SourceRange> NameRanges)43e5dd7070Spatrick bool visitSymbolOccurrence(const NamedDecl *ND,
44e5dd7070Spatrick ArrayRef<SourceRange> NameRanges) {
45e5dd7070Spatrick if (!ND)
46e5dd7070Spatrick return true;
47e5dd7070Spatrick for (const auto &Range : NameRanges) {
48e5dd7070Spatrick SourceLocation Start = Range.getBegin();
49e5dd7070Spatrick SourceLocation End = Range.getEnd();
50e5dd7070Spatrick if (!Start.isValid() || !Start.isFileID() || !End.isValid() ||
51e5dd7070Spatrick !End.isFileID() || !isPointWithin(Start, End))
52e5dd7070Spatrick return true;
53e5dd7070Spatrick }
54e5dd7070Spatrick Result = ND;
55e5dd7070Spatrick return false;
56e5dd7070Spatrick }
57e5dd7070Spatrick
getNamedDecl() const58e5dd7070Spatrick const NamedDecl *getNamedDecl() const { return Result; }
59e5dd7070Spatrick
60e5dd7070Spatrick private:
61e5dd7070Spatrick // Determines if the Point is within Start and End.
isPointWithin(const SourceLocation Start,const SourceLocation End)62e5dd7070Spatrick bool isPointWithin(const SourceLocation Start, const SourceLocation End) {
63e5dd7070Spatrick // FIXME: Add tests for Point == End.
64e5dd7070Spatrick return Point == Start || Point == End ||
65e5dd7070Spatrick (Context.getSourceManager().isBeforeInTranslationUnit(Start,
66e5dd7070Spatrick Point) &&
67e5dd7070Spatrick Context.getSourceManager().isBeforeInTranslationUnit(Point, End));
68e5dd7070Spatrick }
69e5dd7070Spatrick
70e5dd7070Spatrick const NamedDecl *Result = nullptr;
71e5dd7070Spatrick const SourceLocation Point; // The location to find the NamedDecl.
72e5dd7070Spatrick const ASTContext &Context;
73e5dd7070Spatrick };
74e5dd7070Spatrick
75e5dd7070Spatrick } // end anonymous namespace
76e5dd7070Spatrick
getNamedDeclAt(const ASTContext & Context,const SourceLocation Point)77e5dd7070Spatrick const NamedDecl *getNamedDeclAt(const ASTContext &Context,
78e5dd7070Spatrick const SourceLocation Point) {
79e5dd7070Spatrick const SourceManager &SM = Context.getSourceManager();
80e5dd7070Spatrick NamedDeclOccurrenceFindingVisitor Visitor(Point, Context);
81e5dd7070Spatrick
82e5dd7070Spatrick // Try to be clever about pruning down the number of top-level declarations we
83e5dd7070Spatrick // see. If both start and end is either before or after the point we're
84e5dd7070Spatrick // looking for the point cannot be inside of this decl. Don't even look at it.
85e5dd7070Spatrick for (auto *CurrDecl : Context.getTranslationUnitDecl()->decls()) {
86e5dd7070Spatrick SourceLocation StartLoc = CurrDecl->getBeginLoc();
87e5dd7070Spatrick SourceLocation EndLoc = CurrDecl->getEndLoc();
88e5dd7070Spatrick if (StartLoc.isValid() && EndLoc.isValid() &&
89e5dd7070Spatrick SM.isBeforeInTranslationUnit(StartLoc, Point) !=
90e5dd7070Spatrick SM.isBeforeInTranslationUnit(EndLoc, Point))
91e5dd7070Spatrick Visitor.TraverseDecl(CurrDecl);
92e5dd7070Spatrick }
93e5dd7070Spatrick
94e5dd7070Spatrick return Visitor.getNamedDecl();
95e5dd7070Spatrick }
96e5dd7070Spatrick
97e5dd7070Spatrick namespace {
98e5dd7070Spatrick
99e5dd7070Spatrick /// Recursively visits each NamedDecl node to find the declaration with a
100e5dd7070Spatrick /// specific name.
101e5dd7070Spatrick class NamedDeclFindingVisitor
102e5dd7070Spatrick : public RecursiveASTVisitor<NamedDeclFindingVisitor> {
103e5dd7070Spatrick public:
NamedDeclFindingVisitor(StringRef Name)104e5dd7070Spatrick explicit NamedDeclFindingVisitor(StringRef Name) : Name(Name) {}
105e5dd7070Spatrick
106e5dd7070Spatrick // We don't have to traverse the uses to find some declaration with a
107e5dd7070Spatrick // specific name, so just visit the named declarations.
VisitNamedDecl(const NamedDecl * ND)108e5dd7070Spatrick bool VisitNamedDecl(const NamedDecl *ND) {
109e5dd7070Spatrick if (!ND)
110e5dd7070Spatrick return true;
111e5dd7070Spatrick // Fully qualified name is used to find the declaration.
112e5dd7070Spatrick if (Name != ND->getQualifiedNameAsString() &&
113e5dd7070Spatrick Name != "::" + ND->getQualifiedNameAsString())
114e5dd7070Spatrick return true;
115e5dd7070Spatrick Result = ND;
116e5dd7070Spatrick return false;
117e5dd7070Spatrick }
118e5dd7070Spatrick
getNamedDecl() const119e5dd7070Spatrick const NamedDecl *getNamedDecl() const { return Result; }
120e5dd7070Spatrick
121e5dd7070Spatrick private:
122e5dd7070Spatrick const NamedDecl *Result = nullptr;
123e5dd7070Spatrick StringRef Name;
124e5dd7070Spatrick };
125e5dd7070Spatrick
126e5dd7070Spatrick } // end anonymous namespace
127e5dd7070Spatrick
getNamedDeclFor(const ASTContext & Context,const std::string & Name)128e5dd7070Spatrick const NamedDecl *getNamedDeclFor(const ASTContext &Context,
129e5dd7070Spatrick const std::string &Name) {
130e5dd7070Spatrick NamedDeclFindingVisitor Visitor(Name);
131e5dd7070Spatrick Visitor.TraverseDecl(Context.getTranslationUnitDecl());
132e5dd7070Spatrick return Visitor.getNamedDecl();
133e5dd7070Spatrick }
134e5dd7070Spatrick
getUSRForDecl(const Decl * Decl)135e5dd7070Spatrick std::string getUSRForDecl(const Decl *Decl) {
136*a9ac8606Spatrick llvm::SmallString<128> Buff;
137e5dd7070Spatrick
138e5dd7070Spatrick // FIXME: Add test for the nullptr case.
139e5dd7070Spatrick if (Decl == nullptr || index::generateUSRForDecl(Decl, Buff))
140e5dd7070Spatrick return "";
141e5dd7070Spatrick
142*a9ac8606Spatrick return std::string(Buff);
143e5dd7070Spatrick }
144e5dd7070Spatrick
145e5dd7070Spatrick } // end namespace tooling
146e5dd7070Spatrick } // end namespace clang
147