blob: 3bfb5bbe35e4adc4cb485813408f3ddf20a27fa0 [file] [log] [blame]
Alex Lorenz4abbd922017-06-30 16:36:09 +00001//===--- USRFinder.cpp - Clang refactoring library ------------------------===//
Manuel Klimekde237262014-08-20 01:39:05 +00002//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9///
10/// \file Implements a recursive AST visitor that finds the USR of a symbol at a
11/// point.
12///
13//===----------------------------------------------------------------------===//
14
Alex Lorenz4abbd922017-06-30 16:36:09 +000015#include "clang/Tooling/Refactoring/Rename/USRFinder.h"
Manuel Klimekde237262014-08-20 01:39:05 +000016#include "clang/AST/AST.h"
17#include "clang/AST/ASTContext.h"
18#include "clang/AST/RecursiveASTVisitor.h"
Manuel Klimekde237262014-08-20 01:39:05 +000019#include "clang/Index/USRGeneration.h"
Chandler Carruth3cbd71c2015-01-14 11:24:38 +000020#include "clang/Lex/Lexer.h"
Alex Lorenz98394f82017-07-13 10:36:33 +000021#include "clang/Tooling/Refactoring/RecursiveSymbolVisitor.h"
Manuel Klimekde237262014-08-20 01:39:05 +000022#include "llvm/ADT/SmallVector.h"
23
24using namespace llvm;
25
26namespace clang {
Alex Lorenz4abbd922017-06-30 16:36:09 +000027namespace tooling {
Manuel Klimekde237262014-08-20 01:39:05 +000028
Manuel Klimekde237262014-08-20 01:39:05 +000029namespace {
Alex Lorenz98394f82017-07-13 10:36:33 +000030
31/// Recursively visits each AST node to find the symbol underneath the cursor.
32class NamedDeclOccurrenceFindingVisitor
33 : public RecursiveSymbolVisitor<NamedDeclOccurrenceFindingVisitor> {
Manuel Klimekde237262014-08-20 01:39:05 +000034public:
35 // \brief Finds the NamedDecl at a point in the source.
36 // \param Point the location in the source to search for the NamedDecl.
Alex Lorenz98394f82017-07-13 10:36:33 +000037 explicit NamedDeclOccurrenceFindingVisitor(const SourceLocation Point,
38 const ASTContext &Context)
39 : RecursiveSymbolVisitor(Context.getSourceManager(),
40 Context.getLangOpts()),
41 Point(Point), Context(Context) {}
Manuel Klimekde237262014-08-20 01:39:05 +000042
Alex Lorenz98394f82017-07-13 10:36:33 +000043 bool visitSymbolOccurrence(const NamedDecl *ND,
44 ArrayRef<SourceRange> NameRanges) {
45 if (!ND)
Kirill Bobyrev1f0ea352016-07-28 09:05:06 +000046 return true;
Alex Lorenz98394f82017-07-13 10:36:33 +000047 for (const auto &Range : NameRanges) {
48 SourceLocation Start = Range.getBegin();
49 SourceLocation End = Range.getEnd();
Miklos Vajna47bd4632016-06-21 19:48:57 +000050 if (!Start.isValid() || !Start.isFileID() || !End.isValid() ||
Kirill Bobyrev87697782016-09-04 22:50:41 +000051 !End.isFileID() || !isPointWithin(Start, End))
Miklos Vajna47bd4632016-06-21 19:48:57 +000052 return true;
Manuel Klimekde237262014-08-20 01:39:05 +000053 }
Alex Lorenz98394f82017-07-13 10:36:33 +000054 Result = ND;
Manuel Klimekde237262014-08-20 01:39:05 +000055 return false;
56 }
57
Alex Lorenz98394f82017-07-13 10:36:33 +000058 const NamedDecl *getNamedDecl() const { return Result; }
Manuel Klimekde237262014-08-20 01:39:05 +000059
Alex Lorenz98394f82017-07-13 10:36:33 +000060private:
Manuel Klimekde237262014-08-20 01:39:05 +000061 // \brief Determines if the Point is within Start and End.
62 bool isPointWithin(const SourceLocation Start, const SourceLocation End) {
63 // FIXME: Add tests for Point == End.
64 return Point == Start || Point == End ||
Kirill Bobyrev1f0ea352016-07-28 09:05:06 +000065 (Context.getSourceManager().isBeforeInTranslationUnit(Start,
66 Point) &&
67 Context.getSourceManager().isBeforeInTranslationUnit(Point, End));
Manuel Klimekde237262014-08-20 01:39:05 +000068 }
69
Alex Lorenz98394f82017-07-13 10:36:33 +000070 const NamedDecl *Result = nullptr;
Manuel Klimekde237262014-08-20 01:39:05 +000071 const SourceLocation Point; // The location to find the NamedDecl.
Kirill Bobyrev1f0ea352016-07-28 09:05:06 +000072 const ASTContext &Context;
Manuel Klimekde237262014-08-20 01:39:05 +000073};
Alex Lorenz98394f82017-07-13 10:36:33 +000074
75} // end anonymous namespace
Manuel Klimekde237262014-08-20 01:39:05 +000076
77const NamedDecl *getNamedDeclAt(const ASTContext &Context,
78 const SourceLocation Point) {
Benjamin Kramerb568d992016-11-22 17:29:45 +000079 const SourceManager &SM = Context.getSourceManager();
Alex Lorenz98394f82017-07-13 10:36:33 +000080 NamedDeclOccurrenceFindingVisitor Visitor(Point, Context);
Manuel Klimekde237262014-08-20 01:39:05 +000081
Benjamin Kramerb568d992016-11-22 17:29:45 +000082 // Try to be clever about pruning down the number of top-level declarations we
83 // see. If both start and end is either before or after the point we're
84 // looking for the point cannot be inside of this decl. Don't even look at it.
Kirill Bobyrevbc84fc92016-08-16 06:19:06 +000085 for (auto *CurrDecl : Context.getTranslationUnitDecl()->decls()) {
Benjamin Kramerb568d992016-11-22 17:29:45 +000086 SourceLocation StartLoc = CurrDecl->getLocStart();
87 SourceLocation EndLoc = CurrDecl->getLocEnd();
88 if (StartLoc.isValid() && EndLoc.isValid() &&
89 SM.isBeforeInTranslationUnit(StartLoc, Point) !=
90 SM.isBeforeInTranslationUnit(EndLoc, Point))
Manuel Klimekde237262014-08-20 01:39:05 +000091 Visitor.TraverseDecl(CurrDecl);
Manuel Klimekde237262014-08-20 01:39:05 +000092 }
93
Kirill Bobyreva3432fa2016-07-22 13:41:09 +000094 return Visitor.getNamedDecl();
Manuel Klimekde237262014-08-20 01:39:05 +000095}
96
Alex Lorenz98394f82017-07-13 10:36:33 +000097namespace {
98
99/// Recursively visits each NamedDecl node to find the declaration with a
100/// specific name.
101class NamedDeclFindingVisitor
102 : public RecursiveASTVisitor<NamedDeclFindingVisitor> {
103public:
104 explicit NamedDeclFindingVisitor(StringRef Name) : Name(Name) {}
105
106 // We don't have to traverse the uses to find some declaration with a
107 // specific name, so just visit the named declarations.
108 bool VisitNamedDecl(const NamedDecl *ND) {
109 if (!ND)
110 return true;
111 // Fully qualified name is used to find the declaration.
112 if (Name != ND->getQualifiedNameAsString() &&
113 Name != "::" + ND->getQualifiedNameAsString())
114 return true;
115 Result = ND;
116 return false;
117 }
118
119 const NamedDecl *getNamedDecl() const { return Result; }
120
121private:
122 const NamedDecl *Result = nullptr;
123 StringRef Name;
124};
125
126} // end anonymous namespace
127
Miklos Vajna47bd4632016-06-21 19:48:57 +0000128const NamedDecl *getNamedDeclFor(const ASTContext &Context,
129 const std::string &Name) {
Alex Lorenz98394f82017-07-13 10:36:33 +0000130 NamedDeclFindingVisitor Visitor(Name);
Kirill Bobyreva3432fa2016-07-22 13:41:09 +0000131 Visitor.TraverseDecl(Context.getTranslationUnitDecl());
Kirill Bobyreva3432fa2016-07-22 13:41:09 +0000132 return Visitor.getNamedDecl();
Miklos Vajna47bd4632016-06-21 19:48:57 +0000133}
134
Manuel Klimekde237262014-08-20 01:39:05 +0000135std::string getUSRForDecl(const Decl *Decl) {
136 llvm::SmallVector<char, 128> Buff;
137
138 // FIXME: Add test for the nullptr case.
139 if (Decl == nullptr || index::generateUSRForDecl(Decl, Buff))
140 return "";
141
142 return std::string(Buff.data(), Buff.size());
143}
144
Alex Lorenz4abbd922017-06-30 16:36:09 +0000145} // end namespace tooling
146} // end namespace clang