CIndexHigh.cpp revision c6f5c6a58d34833d1fe458a518d5f59462926c7b
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#include "CLog.h"
15#include "clang/AST/DeclObjC.h"
16#include "clang/Frontend/ASTUnit.h"
17
18using namespace clang;
19using namespace cxcursor;
20using namespace cxindex;
21
22static void getTopOverriddenMethods(CXTranslationUnit TU,
23                                    Decl *D,
24                                    SmallVectorImpl<Decl *> &Methods) {
25  if (!D)
26    return;
27  if (!isa<ObjCMethodDecl>(D) && !isa<CXXMethodDecl>(D))
28    return;
29
30  SmallVector<CXCursor, 8> Overridden;
31  cxcursor::getOverriddenCursors(cxcursor::MakeCXCursor(D, TU), Overridden);
32
33  if (Overridden.empty()) {
34    Methods.push_back(D->getCanonicalDecl());
35    return;
36  }
37
38  for (SmallVector<CXCursor, 8>::iterator
39         I = Overridden.begin(), E = Overridden.end(); I != E; ++I)
40    getTopOverriddenMethods(TU, cxcursor::getCursorDecl(*I), Methods);
41}
42
43namespace {
44
45struct FindFileIdRefVisitData {
46  CXTranslationUnit TU;
47  FileID FID;
48  Decl *Dcl;
49  int SelectorIdIdx;
50  CXCursorAndRangeVisitor visitor;
51
52  typedef SmallVector<Decl *, 8> TopMethodsTy;
53  TopMethodsTy TopMethods;
54
55  FindFileIdRefVisitData(CXTranslationUnit TU, FileID FID,
56                         Decl *D, int selectorIdIdx,
57                         CXCursorAndRangeVisitor visitor)
58    : TU(TU), FID(FID), SelectorIdIdx(selectorIdIdx), visitor(visitor) {
59    Dcl = getCanonical(D);
60    getTopOverriddenMethods(TU, Dcl, TopMethods);
61  }
62
63  ASTContext &getASTContext() const {
64    return static_cast<ASTUnit *>(TU->TUData)->getASTContext();
65  }
66
67  /// \brief We are looking to find all semantically relevant identifiers,
68  /// so the definition of "canonical" here is different than in the AST, e.g.
69  ///
70  /// \code
71  ///   class C {
72  ///     C() {}
73  ///   };
74  /// \endcode
75  ///
76  /// we consider the canonical decl of the constructor decl to be the class
77  /// itself, so both 'C' can be highlighted.
78  Decl *getCanonical(Decl *D) const {
79    if (!D)
80      return 0;
81
82    D = D->getCanonicalDecl();
83
84    if (ObjCImplDecl *ImplD = dyn_cast<ObjCImplDecl>(D)) {
85      if (ImplD->getClassInterface())
86        return getCanonical(ImplD->getClassInterface());
87
88    } else if (CXXConstructorDecl *CXXCtorD = dyn_cast<CXXConstructorDecl>(D)) {
89      return getCanonical(CXXCtorD->getParent());
90    }
91
92    return D;
93  }
94
95  bool isHit(Decl *D) const {
96    if (!D)
97      return false;
98
99    D = getCanonical(D);
100    if (D == Dcl)
101      return true;
102
103    if (isa<ObjCMethodDecl>(D) || isa<CXXMethodDecl>(D))
104      return isOverriddingMethod(D);
105
106    return false;
107  }
108
109private:
110  bool isOverriddingMethod(Decl *D) const {
111    if (std::find(TopMethods.begin(), TopMethods.end(), D) !=
112          TopMethods.end())
113      return true;
114
115    TopMethodsTy methods;
116    getTopOverriddenMethods(TU, D, methods);
117    for (TopMethodsTy::iterator
118           I = methods.begin(), E = methods.end(); I != E; ++I) {
119      if (std::find(TopMethods.begin(), TopMethods.end(), *I) !=
120            TopMethods.end())
121        return true;
122    }
123
124    return false;
125  }
126};
127
128} // end anonymous namespace.
129
130/// \brief For a macro \arg Loc, returns the file spelling location and sets
131/// to \arg isMacroArg whether the spelling resides inside a macro definition or
132/// a macro argument.
133static SourceLocation getFileSpellingLoc(SourceManager &SM,
134                                         SourceLocation Loc,
135                                         bool &isMacroArg) {
136  assert(Loc.isMacroID());
137  SourceLocation SpellLoc = SM.getImmediateSpellingLoc(Loc);
138  if (SpellLoc.isMacroID())
139    return getFileSpellingLoc(SM, SpellLoc, isMacroArg);
140
141  isMacroArg = SM.isMacroArgExpansion(Loc);
142  return SpellLoc;
143}
144
145static enum CXChildVisitResult findFileIdRefVisit(CXCursor cursor,
146                                                  CXCursor parent,
147                                                  CXClientData client_data) {
148  CXCursor declCursor = clang_getCursorReferenced(cursor);
149  if (!clang_isDeclaration(declCursor.kind))
150    return CXChildVisit_Recurse;
151
152  Decl *D = cxcursor::getCursorDecl(declCursor);
153  if (!D)
154    return CXChildVisit_Continue;
155
156  FindFileIdRefVisitData *data = (FindFileIdRefVisitData *)client_data;
157  if (data->isHit(D)) {
158    cursor = cxcursor::getSelectorIdentifierCursor(data->SelectorIdIdx, cursor);
159
160    // We are looking for identifiers to highlight so for objc methods (and
161    // not a parameter) we can only highlight the selector identifiers.
162    if ((cursor.kind == CXCursor_ObjCClassMethodDecl ||
163         cursor.kind == CXCursor_ObjCInstanceMethodDecl) &&
164         cxcursor::getSelectorIdentifierIndex(cursor) == -1)
165      return CXChildVisit_Recurse;
166
167    if (clang_isExpression(cursor.kind)) {
168      if (cursor.kind == CXCursor_DeclRefExpr ||
169          cursor.kind == CXCursor_MemberRefExpr) {
170        // continue..
171
172      } else if (cursor.kind == CXCursor_ObjCMessageExpr &&
173                 cxcursor::getSelectorIdentifierIndex(cursor) != -1) {
174        // continue..
175
176      } else
177        return CXChildVisit_Recurse;
178    }
179
180    SourceLocation
181      Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));
182    SourceLocation SelIdLoc = cxcursor::getSelectorIdentifierLoc(cursor);
183    if (SelIdLoc.isValid())
184      Loc = SelIdLoc;
185
186    ASTContext &Ctx = data->getASTContext();
187    SourceManager &SM = Ctx.getSourceManager();
188    bool isInMacroDef = false;
189    if (Loc.isMacroID()) {
190      bool isMacroArg;
191      Loc = getFileSpellingLoc(SM, Loc, isMacroArg);
192      isInMacroDef = !isMacroArg;
193    }
194
195    // We are looking for identifiers in a specific file.
196    std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
197    if (LocInfo.first != data->FID)
198      return CXChildVisit_Recurse;
199
200    if (isInMacroDef) {
201      // FIXME: For a macro definition make sure that all expansions
202      // of it expand to the same reference before allowing to point to it.
203      return CXChildVisit_Recurse;
204    }
205
206    data->visitor.visit(data->visitor.context, cursor,
207                        cxloc::translateSourceRange(Ctx, Loc));
208  }
209  return CXChildVisit_Recurse;
210}
211
212static void findIdRefsInFile(CXTranslationUnit TU, CXCursor declCursor,
213                           const FileEntry *File,
214                           CXCursorAndRangeVisitor Visitor) {
215  assert(clang_isDeclaration(declCursor.kind));
216  ASTUnit *Unit = static_cast<ASTUnit*>(TU->TUData);
217  SourceManager &SM = Unit->getSourceManager();
218
219  FileID FID = SM.translateFile(File);
220  Decl *Dcl = cxcursor::getCursorDecl(declCursor);
221  if (!Dcl)
222    return;
223
224  FindFileIdRefVisitData data(TU, FID, Dcl,
225                              cxcursor::getSelectorIdentifierIndex(declCursor),
226                              Visitor);
227
228  if (DeclContext *DC = Dcl->getParentFunctionOrMethod()) {
229    clang_visitChildren(cxcursor::MakeCXCursor(cast<Decl>(DC), TU),
230                        findFileIdRefVisit, &data);
231    return;
232  }
233
234  SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
235  CursorVisitor FindIdRefsVisitor(TU,
236                                  findFileIdRefVisit, &data,
237                                  /*VisitPreprocessorLast=*/true,
238                                  /*VisitIncludedEntities=*/false,
239                                  Range,
240                                  /*VisitDeclsOnly=*/true);
241  FindIdRefsVisitor.visitFileRegion();
242}
243
244namespace {
245
246struct FindFileMacroRefVisitData {
247  ASTUnit &Unit;
248  const FileEntry *File;
249  const IdentifierInfo *Macro;
250  CXCursorAndRangeVisitor visitor;
251
252  FindFileMacroRefVisitData(ASTUnit &Unit, const FileEntry *File,
253                            const IdentifierInfo *Macro,
254                            CXCursorAndRangeVisitor visitor)
255    : Unit(Unit), File(File), Macro(Macro), visitor(visitor) { }
256
257  ASTContext &getASTContext() const {
258    return Unit.getASTContext();
259  }
260};
261
262} // anonymous namespace
263
264static enum CXChildVisitResult findFileMacroRefVisit(CXCursor cursor,
265                                                     CXCursor parent,
266                                                     CXClientData client_data) {
267  const IdentifierInfo *Macro = 0;
268  if (cursor.kind == CXCursor_MacroDefinition)
269    Macro = getCursorMacroDefinition(cursor)->getName();
270  else if (cursor.kind == CXCursor_MacroExpansion)
271    Macro = getCursorMacroExpansion(cursor).getName();
272  if (!Macro)
273    return CXChildVisit_Continue;
274
275  FindFileMacroRefVisitData *data = (FindFileMacroRefVisitData *)client_data;
276  if (data->Macro != Macro)
277    return CXChildVisit_Continue;
278
279  SourceLocation
280    Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));
281
282  ASTContext &Ctx = data->getASTContext();
283  SourceManager &SM = Ctx.getSourceManager();
284  bool isInMacroDef = false;
285  if (Loc.isMacroID()) {
286    bool isMacroArg;
287    Loc = getFileSpellingLoc(SM, Loc, isMacroArg);
288    isInMacroDef = !isMacroArg;
289  }
290
291  // We are looking for identifiers in a specific file.
292  std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
293  if (SM.getFileEntryForID(LocInfo.first) != data->File)
294    return CXChildVisit_Continue;
295
296  if (isInMacroDef) {
297    // FIXME: For a macro definition make sure that all expansions
298    // of it expand to the same reference before allowing to point to it.
299    return CXChildVisit_Continue;
300  }
301
302  data->visitor.visit(data->visitor.context, cursor,
303                      cxloc::translateSourceRange(Ctx, Loc));
304  return CXChildVisit_Continue;
305}
306
307static void findMacroRefsInFile(CXTranslationUnit TU, CXCursor Cursor,
308                                const FileEntry *File,
309                                CXCursorAndRangeVisitor Visitor) {
310  if (Cursor.kind != CXCursor_MacroDefinition &&
311      Cursor.kind != CXCursor_MacroExpansion)
312    return;
313
314  ASTUnit *Unit = static_cast<ASTUnit*>(TU->TUData);
315  SourceManager &SM = Unit->getSourceManager();
316
317  FileID FID = SM.translateFile(File);
318  const IdentifierInfo *Macro = 0;
319  if (Cursor.kind == CXCursor_MacroDefinition)
320    Macro = getCursorMacroDefinition(Cursor)->getName();
321  else
322    Macro = getCursorMacroExpansion(Cursor).getName();
323  if (!Macro)
324    return;
325
326  FindFileMacroRefVisitData data(*Unit, File, Macro, Visitor);
327
328  SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
329  CursorVisitor FindMacroRefsVisitor(TU,
330                                  findFileMacroRefVisit, &data,
331                                  /*VisitPreprocessorLast=*/false,
332                                  /*VisitIncludedEntities=*/false,
333                                  Range);
334  FindMacroRefsVisitor.visitPreprocessedEntitiesInRegion();
335}
336
337
338//===----------------------------------------------------------------------===//
339// libclang public APIs.
340//===----------------------------------------------------------------------===//
341
342extern "C" {
343
344void clang_findReferencesInFile(CXCursor cursor, CXFile file,
345                                CXCursorAndRangeVisitor visitor) {
346  LogRef Log = Logger::make(__func__);
347
348  if (clang_Cursor_isNull(cursor)) {
349    if (Log)
350      *Log << "Null cursor";
351    return;
352  }
353  if (cursor.kind == CXCursor_NoDeclFound) {
354    if (Log)
355      *Log << "Got CXCursor_NoDeclFound";
356    return;
357  }
358  if (!file) {
359    if (Log)
360      *Log << "Null file";
361    return;
362  }
363  if (!visitor.visit) {
364    if (Log)
365      *Log << "Null visitor";
366    return;
367  }
368
369  ASTUnit *CXXUnit = cxcursor::getCursorASTUnit(cursor);
370  if (!CXXUnit)
371    return;
372
373  ASTUnit::ConcurrencyCheck Check(*CXXUnit);
374
375  if (cursor.kind == CXCursor_MacroDefinition ||
376      cursor.kind == CXCursor_MacroExpansion) {
377    findMacroRefsInFile(cxcursor::getCursorTU(cursor),
378                        cursor,
379                        static_cast<const FileEntry *>(file),
380                        visitor);
381    return;
382  }
383
384  // We are interested in semantics of identifiers so for C++ constructor exprs
385  // prefer type references, e.g.:
386  //
387  //  return MyStruct();
388  //
389  // for 'MyStruct' we'll have a cursor pointing at the constructor decl but
390  // we are actually interested in the type declaration.
391  cursor = cxcursor::getTypeRefCursor(cursor);
392
393  CXCursor refCursor = clang_getCursorReferenced(cursor);
394
395  if (!clang_isDeclaration(refCursor.kind)) {
396    if (Log)
397      *Log << "cursor is not referencing a declaration";
398    return;
399  }
400
401  findIdRefsInFile(cxcursor::getCursorTU(cursor),
402                   refCursor,
403                   static_cast<const FileEntry *>(file),
404                   visitor);
405}
406
407static enum CXVisitorResult _visitCursorAndRange(void *context,
408                                                 CXCursor cursor,
409                                                 CXSourceRange range) {
410  CXCursorAndRangeVisitorBlock block = (CXCursorAndRangeVisitorBlock)context;
411  return INVOKE_BLOCK2(block, cursor, range);
412}
413
414void clang_findReferencesInFileWithBlock(CXCursor cursor,
415                                         CXFile file,
416                                         CXCursorAndRangeVisitorBlock block) {
417  CXCursorAndRangeVisitor visitor = { block,
418                                      block ? _visitCursorAndRange : 0 };
419  return clang_findReferencesInFile(cursor, file, visitor);
420}
421
422} // end: extern "C"
423
424