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