1 //===- CIndexHigh.cpp - Higher level API functions ------------------------===//
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 #include "CursorVisitor.h"
11 #include "CXCursor.h"
12 #include "CXSourceLocation.h"
13 #include "CXTranslationUnit.h"
14 
15 #include "clang/Frontend/ASTUnit.h"
16 #include "clang/AST/DeclObjC.h"
17 
18 using namespace clang;
19 using namespace cxcursor;
20 
21 static void getTopOverriddenMethods(CXTranslationUnit TU,
22                                     Decl *D,
23                                     SmallVectorImpl<Decl *> &Methods) {
24   if (!isa<ObjCMethodDecl>(D) && !isa<CXXMethodDecl>(D))
25     return;
26 
27   SmallVector<CXCursor, 8> Overridden;
28   cxcursor::getOverriddenCursors(cxcursor::MakeCXCursor(D, TU), Overridden);
29 
30   if (Overridden.empty()) {
31     Methods.push_back(D->getCanonicalDecl());
32     return;
33   }
34 
35   for (SmallVector<CXCursor, 8>::iterator
36          I = Overridden.begin(), E = Overridden.end(); I != E; ++I)
37     getTopOverriddenMethods(TU, cxcursor::getCursorDecl(*I), Methods);
38 }
39 
40 namespace {
41 
42 struct FindFileIdRefVisitData {
43   CXTranslationUnit TU;
44   FileID FID;
45   Decl *Dcl;
46   int SelectorIdIdx;
47   CXCursorAndRangeVisitor visitor;
48 
49   typedef SmallVector<Decl *, 8> TopMethodsTy;
50   TopMethodsTy TopMethods;
51 
52   FindFileIdRefVisitData(CXTranslationUnit TU, FileID FID,
53                          Decl *D, int selectorIdIdx,
54                          CXCursorAndRangeVisitor visitor)
55     : TU(TU), FID(FID), SelectorIdIdx(selectorIdIdx), visitor(visitor) {
56     Dcl = getCanonical(D);
57     getTopOverriddenMethods(TU, Dcl, TopMethods);
58   }
59 
60   ASTContext &getASTContext() const {
61     return static_cast<ASTUnit *>(TU->TUData)->getASTContext();
62   }
63 
64   /// \brief We are looking to find all semantically relevant identifiers,
65   /// so the definition of "canonical" here is different than in the AST, e.g.
66   ///
67   /// \code
68   ///   class C {
69   ///     C() {}
70   ///   };
71   /// \endcode
72   ///
73   /// we consider the canonical decl of the constructor decl to be the class
74   /// itself, so both 'C' can be highlighted.
75   Decl *getCanonical(Decl *D) const {
76     D = D->getCanonicalDecl();
77 
78     if (ObjCImplDecl *ImplD = dyn_cast<ObjCImplDecl>(D))
79       return getCanonical(ImplD->getClassInterface());
80     if (CXXConstructorDecl *CXXCtorD = dyn_cast<CXXConstructorDecl>(D))
81       return getCanonical(CXXCtorD->getParent());
82 
83     return D;
84   }
85 
86   bool isHit(Decl *D) const {
87     D = getCanonical(D);
88     if (D == Dcl)
89       return true;
90 
91     if (isa<ObjCMethodDecl>(D) || isa<CXXMethodDecl>(D))
92       return isOverriddingMethod(D);
93 
94     return false;
95   }
96 
97 private:
98   bool isOverriddingMethod(Decl *D) const {
99     if (std::find(TopMethods.begin(), TopMethods.end(), D) !=
100           TopMethods.end())
101       return true;
102 
103     TopMethodsTy methods;
104     getTopOverriddenMethods(TU, D, methods);
105     for (TopMethodsTy::iterator
106            I = methods.begin(), E = methods.end(); I != E; ++I) {
107       if (std::find(TopMethods.begin(), TopMethods.end(), *I) !=
108             TopMethods.end())
109         return true;
110     }
111 
112     return false;
113   }
114 };
115 
116 } // end anonymous namespace.
117 
118 /// \brief For a macro \arg Loc, returns the file spelling location and sets
119 /// to \arg isMacroArg whether the spelling resides inside a macro definition or
120 /// a macro argument.
121 static SourceLocation getFileSpellingLoc(SourceManager &SM,
122                                          SourceLocation Loc,
123                                          bool &isMacroArg) {
124   assert(Loc.isMacroID());
125   SourceLocation SpellLoc = SM.getImmediateSpellingLoc(Loc);
126   if (SpellLoc.isMacroID())
127     return getFileSpellingLoc(SM, SpellLoc, isMacroArg);
128 
129   isMacroArg = SM.isMacroArgExpansion(Loc);
130   return SpellLoc;
131 }
132 
133 static enum CXChildVisitResult findFileIdRefVisit(CXCursor cursor,
134                                                   CXCursor parent,
135                                                   CXClientData client_data) {
136   CXCursor declCursor = clang_getCursorReferenced(cursor);
137   if (!clang_isDeclaration(declCursor.kind))
138     return CXChildVisit_Recurse;
139 
140   Decl *D = cxcursor::getCursorDecl(declCursor);
141   FindFileIdRefVisitData *data = (FindFileIdRefVisitData *)client_data;
142   if (data->isHit(D)) {
143     cursor = cxcursor::getSelectorIdentifierCursor(data->SelectorIdIdx, cursor);
144 
145     // We are looking for identifiers to highlight so for objc methods (and
146     // not a parameter) we can only highlight the selector identifiers.
147     if ((cursor.kind == CXCursor_ObjCClassMethodDecl ||
148          cursor.kind == CXCursor_ObjCInstanceMethodDecl) &&
149          cxcursor::getSelectorIdentifierIndex(cursor) == -1)
150       return CXChildVisit_Recurse;
151 
152     if (clang_isExpression(cursor.kind)) {
153       if (cursor.kind == CXCursor_DeclRefExpr ||
154           cursor.kind == CXCursor_MemberRefExpr) {
155         // continue..
156 
157       } else if (cursor.kind == CXCursor_ObjCMessageExpr &&
158                  cxcursor::getSelectorIdentifierIndex(cursor) != -1) {
159         // continue..
160 
161       } else
162         return CXChildVisit_Recurse;
163     }
164 
165     SourceLocation
166       Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));
167     SourceLocation SelIdLoc = cxcursor::getSelectorIdentifierLoc(cursor);
168     if (SelIdLoc.isValid())
169       Loc = SelIdLoc;
170 
171     SourceManager &SM = data->getASTContext().getSourceManager();
172     bool isInMacroDef = false;
173     if (Loc.isMacroID()) {
174       bool isMacroArg;
175       Loc = getFileSpellingLoc(SM, Loc, isMacroArg);
176       isInMacroDef = !isMacroArg;
177     }
178 
179     // We are looking for identifiers in a specific file.
180     std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
181     if (LocInfo.first != data->FID)
182       return CXChildVisit_Recurse;
183 
184     if (isInMacroDef) {
185       // FIXME: For a macro definition make sure that all expansions
186       // of it expand to the same reference before allowing to point to it.
187       Loc = SourceLocation();
188     }
189 
190     data->visitor.visit(data->visitor.context, cursor,
191                         cxloc::translateSourceRange(D->getASTContext(), Loc));
192   }
193   return CXChildVisit_Recurse;
194 }
195 
196 static void findIdRefsInFile(CXTranslationUnit TU, CXCursor declCursor,
197                            const FileEntry *File,
198                            CXCursorAndRangeVisitor Visitor) {
199   assert(clang_isDeclaration(declCursor.kind));
200   ASTUnit *Unit = static_cast<ASTUnit*>(TU->TUData);
201   SourceManager &SM = Unit->getSourceManager();
202 
203   FileID FID = SM.translateFile(File);
204   Decl *Dcl = cxcursor::getCursorDecl(declCursor);
205   FindFileIdRefVisitData data(TU, FID, Dcl,
206                               cxcursor::getSelectorIdentifierIndex(declCursor),
207                               Visitor);
208 
209   if (DeclContext *DC = Dcl->getParentFunctionOrMethod()) {
210     clang_visitChildren(cxcursor::MakeCXCursor(cast<Decl>(DC), TU),
211                         findFileIdRefVisit, &data);
212     return;
213   }
214 
215   SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
216   CursorVisitor FindIdRefsVisitor(TU,
217                                   findFileIdRefVisit, &data,
218                                   /*VisitPreprocessorLast=*/true,
219                                   /*VisitIncludedEntities=*/false,
220                                   Range);
221   FindIdRefsVisitor.visitFileRegion();
222 }
223 
224 
225 //===----------------------------------------------------------------------===//
226 // libclang public APIs.
227 //===----------------------------------------------------------------------===//
228 
229 extern "C" {
230 
231 void clang_findReferencesInFile(CXCursor cursor, CXFile file,
232                                 CXCursorAndRangeVisitor visitor) {
233   bool Logging = ::getenv("LIBCLANG_LOGGING");
234 
235   if (clang_Cursor_isNull(cursor)) {
236     if (Logging)
237       llvm::errs() << "clang_findReferencesInFile: Null cursor\n";
238     return;
239   }
240   if (!file) {
241     if (Logging)
242       llvm::errs() << "clang_findReferencesInFile: Null file\n";
243     return;
244   }
245   if (!visitor.visit) {
246     if (Logging)
247       llvm::errs() << "clang_findReferencesInFile: Null visitor\n";
248     return;
249   }
250 
251   // We are interested in semantics of identifiers so for C++ constructor exprs
252   // prefer type references, e.g.:
253   //
254   //  return MyStruct();
255   //
256   // for 'MyStruct' we'll have a cursor pointing at the constructor decl but
257   // we are actually interested in the type declaration.
258   cursor = cxcursor::getTypeRefCursor(cursor);
259 
260   CXCursor refCursor = clang_getCursorReferenced(cursor);
261 
262   if (!clang_isDeclaration(refCursor.kind)) {
263     if (Logging)
264       llvm::errs() << "clang_findReferencesInFile: cursor is not referencing a "
265                       "declaration\n";
266     return;
267   }
268 
269   ASTUnit *CXXUnit = cxcursor::getCursorASTUnit(cursor);
270   ASTUnit::ConcurrencyCheck Check(*CXXUnit);
271 
272   findIdRefsInFile(cxcursor::getCursorTU(cursor),
273                    refCursor,
274                    static_cast<const FileEntry *>(file),
275                    visitor);
276 }
277 
278 static enum CXVisitorResult _visitCursorAndRange(void *context,
279                                                  CXCursor cursor,
280                                                  CXSourceRange range) {
281   CXCursorAndRangeVisitorBlock block = (CXCursorAndRangeVisitorBlock)context;
282   return INVOKE_BLOCK2(block, cursor, range);
283 }
284 
285 void clang_findReferencesInFileWithBlock(CXCursor cursor,
286                                          CXFile file,
287                                          CXCursorAndRangeVisitorBlock block) {
288   CXCursorAndRangeVisitor visitor = { block,
289                                       block ? _visitCursorAndRange : 0 };
290   return clang_findReferencesInFile(cursor, file, visitor);
291 }
292 
293 } // end: extern "C"
294 
295