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