xref: /openbsd-src/gnu/llvm/clang/lib/Tooling/Refactoring/Rename/USRFinder.cpp (revision a9ac8606c53d55cee9c3a39778b249c51df111ef)
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