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