1 //===--- USRFinder.cpp - Clang refactoring library ------------------------===//
2 //
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 
15 #include "clang/Tooling/Refactoring/Rename/USRFinder.h"
16 #include "clang/AST/AST.h"
17 #include "clang/AST/ASTContext.h"
18 #include "clang/AST/RecursiveASTVisitor.h"
19 #include "clang/Index/USRGeneration.h"
20 #include "clang/Lex/Lexer.h"
21 #include "llvm/ADT/SmallVector.h"
22 
23 using namespace llvm;
24 
25 namespace clang {
26 namespace tooling {
27 
28 // NamedDeclFindingASTVisitor recursively visits each AST node to find the
29 // symbol underneath the cursor.
30 // FIXME: move to separate .h/.cc file if this gets too large.
31 namespace {
32 class NamedDeclFindingASTVisitor
33     : public clang::RecursiveASTVisitor<NamedDeclFindingASTVisitor> {
34 public:
35   // \brief Finds the NamedDecl at a point in the source.
36   // \param Point the location in the source to search for the NamedDecl.
37   explicit NamedDeclFindingASTVisitor(const SourceLocation Point,
38                                       const ASTContext &Context)
39       : Result(nullptr), Point(Point), Context(Context) {}
40 
41   // \brief Finds the NamedDecl for a name in the source.
42   // \param Name the fully qualified name.
43   explicit NamedDeclFindingASTVisitor(const std::string &Name,
44                                       const ASTContext &Context)
45       : Result(nullptr), Name(Name), Context(Context) {}
46 
47   // Declaration visitors:
48 
49   // \brief Checks if the point falls within the NameDecl. This covers every
50   // declaration of a named entity that we may come across. Usually, just
51   // checking if the point lies within the length of the name of the declaration
52   // and the start location is sufficient.
53   bool VisitNamedDecl(const NamedDecl *Decl) {
54     return dyn_cast<CXXConversionDecl>(Decl)
55                ? true
56                : setResult(Decl, Decl->getLocation(),
57                            Decl->getNameAsString().length());
58   }
59 
60   // Expression visitors:
61 
62   bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
63     const NamedDecl *Decl = Expr->getFoundDecl();
64     return setResult(Decl, Expr->getLocation(),
65                      Decl->getNameAsString().length());
66   }
67 
68   bool VisitMemberExpr(const MemberExpr *Expr) {
69     const NamedDecl *Decl = Expr->getFoundDecl().getDecl();
70     return setResult(Decl, Expr->getMemberLoc(),
71                      Decl->getNameAsString().length());
72   }
73 
74   // Other visitors:
75 
76   bool VisitTypeLoc(const TypeLoc Loc) {
77     const SourceLocation TypeBeginLoc = Loc.getBeginLoc();
78     const SourceLocation TypeEndLoc = Lexer::getLocForEndOfToken(
79         TypeBeginLoc, 0, Context.getSourceManager(), Context.getLangOpts());
80     if (const auto *TemplateTypeParm =
81             dyn_cast<TemplateTypeParmType>(Loc.getType()))
82       return setResult(TemplateTypeParm->getDecl(), TypeBeginLoc, TypeEndLoc);
83     if (const auto *TemplateSpecType =
84             dyn_cast<TemplateSpecializationType>(Loc.getType())) {
85       return setResult(TemplateSpecType->getTemplateName().getAsTemplateDecl(),
86                        TypeBeginLoc, TypeEndLoc);
87     }
88     return setResult(Loc.getType()->getAsCXXRecordDecl(), TypeBeginLoc,
89                      TypeEndLoc);
90   }
91 
92   bool VisitCXXConstructorDecl(clang::CXXConstructorDecl *ConstructorDecl) {
93     for (const auto *Initializer : ConstructorDecl->inits()) {
94       // Ignore implicit initializers.
95       if (!Initializer->isWritten())
96         continue;
97       if (const clang::FieldDecl *FieldDecl = Initializer->getMember()) {
98         const SourceLocation InitBeginLoc = Initializer->getSourceLocation(),
99                              InitEndLoc = Lexer::getLocForEndOfToken(
100                                  InitBeginLoc, 0, Context.getSourceManager(),
101                                  Context.getLangOpts());
102         if (!setResult(FieldDecl, InitBeginLoc, InitEndLoc))
103           return false;
104       }
105     }
106     return true;
107   }
108 
109   // Other:
110 
111   const NamedDecl *getNamedDecl() { return Result; }
112 
113   // \brief Determines if a namespace qualifier contains the point.
114   // \returns false on success and sets Result.
115   void handleNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) {
116     while (NameLoc) {
117       const NamespaceDecl *Decl =
118           NameLoc.getNestedNameSpecifier()->getAsNamespace();
119       setResult(Decl, NameLoc.getLocalBeginLoc(), NameLoc.getLocalEndLoc());
120       NameLoc = NameLoc.getPrefix();
121     }
122   }
123 
124 private:
125   // \brief Sets Result to Decl if the Point is within Start and End.
126   // \returns false on success.
127   bool setResult(const NamedDecl *Decl, SourceLocation Start,
128                  SourceLocation End) {
129     if (!Decl)
130       return true;
131     if (Name.empty()) {
132       // Offset is used to find the declaration.
133       if (!Start.isValid() || !Start.isFileID() || !End.isValid() ||
134           !End.isFileID() || !isPointWithin(Start, End))
135         return true;
136     } else {
137       // Fully qualified name is used to find the declaration.
138       if (Name != Decl->getQualifiedNameAsString() &&
139           Name != "::" + Decl->getQualifiedNameAsString())
140         return true;
141     }
142     Result = Decl;
143     return false;
144   }
145 
146   // \brief Sets Result to Decl if Point is within Loc and Loc + Offset.
147   // \returns false on success.
148   bool setResult(const NamedDecl *Decl, SourceLocation Loc, unsigned Offset) {
149     // FIXME: Add test for Offset == 0. Add test for Offset - 1 (vs -2 etc).
150     return Offset == 0 ||
151            setResult(Decl, Loc, Loc.getLocWithOffset(Offset - 1));
152   }
153 
154   // \brief Determines if the Point is within Start and End.
155   bool isPointWithin(const SourceLocation Start, const SourceLocation End) {
156     // FIXME: Add tests for Point == End.
157     return Point == Start || Point == End ||
158            (Context.getSourceManager().isBeforeInTranslationUnit(Start,
159                                                                  Point) &&
160             Context.getSourceManager().isBeforeInTranslationUnit(Point, End));
161   }
162 
163   const NamedDecl *Result;
164   const SourceLocation Point; // The location to find the NamedDecl.
165   const std::string Name;
166   const ASTContext &Context;
167 };
168 } // namespace
169 
170 const NamedDecl *getNamedDeclAt(const ASTContext &Context,
171                                 const SourceLocation Point) {
172   const SourceManager &SM = Context.getSourceManager();
173   NamedDeclFindingASTVisitor Visitor(Point, Context);
174 
175   // Try to be clever about pruning down the number of top-level declarations we
176   // see. If both start and end is either before or after the point we're
177   // looking for the point cannot be inside of this decl. Don't even look at it.
178   for (auto *CurrDecl : Context.getTranslationUnitDecl()->decls()) {
179     SourceLocation StartLoc = CurrDecl->getLocStart();
180     SourceLocation EndLoc = CurrDecl->getLocEnd();
181     if (StartLoc.isValid() && EndLoc.isValid() &&
182         SM.isBeforeInTranslationUnit(StartLoc, Point) !=
183             SM.isBeforeInTranslationUnit(EndLoc, Point))
184       Visitor.TraverseDecl(CurrDecl);
185   }
186 
187   NestedNameSpecifierLocFinder Finder(const_cast<ASTContext &>(Context));
188   for (const auto &Location : Finder.getNestedNameSpecifierLocations())
189     Visitor.handleNestedNameSpecifierLoc(Location);
190 
191   return Visitor.getNamedDecl();
192 }
193 
194 const NamedDecl *getNamedDeclFor(const ASTContext &Context,
195                                  const std::string &Name) {
196   NamedDeclFindingASTVisitor Visitor(Name, Context);
197   Visitor.TraverseDecl(Context.getTranslationUnitDecl());
198 
199   return Visitor.getNamedDecl();
200 }
201 
202 std::string getUSRForDecl(const Decl *Decl) {
203   llvm::SmallVector<char, 128> Buff;
204 
205   // FIXME: Add test for the nullptr case.
206   if (Decl == nullptr || index::generateUSRForDecl(Decl, Buff))
207     return "";
208 
209   return std::string(Buff.data(), Buff.size());
210 }
211 
212 } // end namespace tooling
213 } // end namespace clang
214