Clang Project

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