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