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                                    const Decl *D,
25                                    SmallVectorImpl<const 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 (SmallVectorImpl<CXCursor>::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  const Decl *Dcl;
50  int SelectorIdIdx;
51  CXCursorAndRangeVisitor visitor;
52
53  typedef SmallVector<const Decl *, 8> TopMethodsTy;
54  TopMethodsTy TopMethods;
55
56  FindFileIdRefVisitData(CXTranslationUnit TU, FileID FID,
57                         const 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 cxtu::getASTUnit(TU)->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  const Decl *getCanonical(const Decl *D) const {
80    if (!D)
81      return nullptr;
82
83    D = D->getCanonicalDecl();
84
85    if (const ObjCImplDecl *ImplD = dyn_cast<ObjCImplDecl>(D)) {
86      if (ImplD->getClassInterface())
87        return getCanonical(ImplD->getClassInterface());
88
89    } else if (const CXXConstructorDecl *CXXCtorD =
90                   dyn_cast<CXXConstructorDecl>(D)) {
91      return getCanonical(CXXCtorD->getParent());
92    }
93
94    return D;
95  }
96
97  bool isHit(const Decl *D) const {
98    if (!D)
99      return false;
100
101    D = getCanonical(D);
102    if (D == Dcl)
103      return true;
104
105    if (isa<ObjCMethodDecl>(D) || isa<CXXMethodDecl>(D))
106      return isOverriddingMethod(D);
107
108    return false;
109  }
110
111private:
112  bool isOverriddingMethod(const Decl *D) const {
113    if (std::find(TopMethods.begin(), TopMethods.end(), D) !=
114          TopMethods.end())
115      return true;
116
117    TopMethodsTy methods;
118    getTopOverriddenMethods(TU, D, methods);
119    for (TopMethodsTy::iterator
120           I = methods.begin(), E = methods.end(); I != E; ++I) {
121      if (std::find(TopMethods.begin(), TopMethods.end(), *I) !=
122            TopMethods.end())
123        return true;
124    }
125
126    return false;
127  }
128};
129
130} // end anonymous namespace.
131
132/// \brief For a macro \arg Loc, returns the file spelling location and sets
133/// to \arg isMacroArg whether the spelling resides inside a macro definition or
134/// a macro argument.
135static SourceLocation getFileSpellingLoc(SourceManager &SM,
136                                         SourceLocation Loc,
137                                         bool &isMacroArg) {
138  assert(Loc.isMacroID());
139  SourceLocation SpellLoc = SM.getImmediateSpellingLoc(Loc);
140  if (SpellLoc.isMacroID())
141    return getFileSpellingLoc(SM, SpellLoc, isMacroArg);
142
143  isMacroArg = SM.isMacroArgExpansion(Loc);
144  return SpellLoc;
145}
146
147static enum CXChildVisitResult findFileIdRefVisit(CXCursor cursor,
148                                                  CXCursor parent,
149                                                  CXClientData client_data) {
150  CXCursor declCursor = clang_getCursorReferenced(cursor);
151  if (!clang_isDeclaration(declCursor.kind))
152    return CXChildVisit_Recurse;
153
154  const Decl *D = cxcursor::getCursorDecl(declCursor);
155  if (!D)
156    return CXChildVisit_Continue;
157
158  FindFileIdRefVisitData *data = (FindFileIdRefVisitData *)client_data;
159  if (data->isHit(D)) {
160    cursor = cxcursor::getSelectorIdentifierCursor(data->SelectorIdIdx, cursor);
161
162    // We are looking for identifiers to highlight so for objc methods (and
163    // not a parameter) we can only highlight the selector identifiers.
164    if ((cursor.kind == CXCursor_ObjCClassMethodDecl ||
165         cursor.kind == CXCursor_ObjCInstanceMethodDecl) &&
166         cxcursor::getSelectorIdentifierIndex(cursor) == -1)
167      return CXChildVisit_Recurse;
168
169    if (clang_isExpression(cursor.kind)) {
170      if (cursor.kind == CXCursor_DeclRefExpr ||
171          cursor.kind == CXCursor_MemberRefExpr) {
172        // continue..
173
174      } else if (cursor.kind == CXCursor_ObjCMessageExpr &&
175                 cxcursor::getSelectorIdentifierIndex(cursor) != -1) {
176        // continue..
177
178      } else
179        return CXChildVisit_Recurse;
180    }
181
182    SourceLocation
183      Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));
184    SourceLocation SelIdLoc = cxcursor::getSelectorIdentifierLoc(cursor);
185    if (SelIdLoc.isValid())
186      Loc = SelIdLoc;
187
188    ASTContext &Ctx = data->getASTContext();
189    SourceManager &SM = Ctx.getSourceManager();
190    bool isInMacroDef = false;
191    if (Loc.isMacroID()) {
192      bool isMacroArg;
193      Loc = getFileSpellingLoc(SM, Loc, isMacroArg);
194      isInMacroDef = !isMacroArg;
195    }
196
197    // We are looking for identifiers in a specific file.
198    std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
199    if (LocInfo.first != data->FID)
200      return CXChildVisit_Recurse;
201
202    if (isInMacroDef) {
203      // FIXME: For a macro definition make sure that all expansions
204      // of it expand to the same reference before allowing to point to it.
205      return CXChildVisit_Recurse;
206    }
207
208    if (data->visitor.visit(data->visitor.context, cursor,
209                        cxloc::translateSourceRange(Ctx, Loc)) == CXVisit_Break)
210      return CXChildVisit_Break;
211  }
212  return CXChildVisit_Recurse;
213}
214
215static bool findIdRefsInFile(CXTranslationUnit TU, CXCursor declCursor,
216                             const FileEntry *File,
217                             CXCursorAndRangeVisitor Visitor) {
218  assert(clang_isDeclaration(declCursor.kind));
219  SourceManager &SM = cxtu::getASTUnit(TU)->getSourceManager();
220
221  FileID FID = SM.translateFile(File);
222  const Decl *Dcl = cxcursor::getCursorDecl(declCursor);
223  if (!Dcl)
224    return false;
225
226  FindFileIdRefVisitData data(TU, FID, Dcl,
227                              cxcursor::getSelectorIdentifierIndex(declCursor),
228                              Visitor);
229
230  if (const DeclContext *DC = Dcl->getParentFunctionOrMethod()) {
231    return clang_visitChildren(cxcursor::MakeCXCursor(cast<Decl>(DC), TU),
232                               findFileIdRefVisit, &data);
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  return 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 = nullptr;
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  if (data->visitor.visit(data->visitor.context, cursor,
304                        cxloc::translateSourceRange(Ctx, Loc)) == CXVisit_Break)
305    return CXChildVisit_Break;
306  return CXChildVisit_Continue;
307}
308
309static bool findMacroRefsInFile(CXTranslationUnit TU, CXCursor Cursor,
310                                const FileEntry *File,
311                                CXCursorAndRangeVisitor Visitor) {
312  if (Cursor.kind != CXCursor_MacroDefinition &&
313      Cursor.kind != CXCursor_MacroExpansion)
314    return false;
315
316  ASTUnit *Unit = cxtu::getASTUnit(TU);
317  SourceManager &SM = Unit->getSourceManager();
318
319  FileID FID = SM.translateFile(File);
320  const IdentifierInfo *Macro = nullptr;
321  if (Cursor.kind == CXCursor_MacroDefinition)
322    Macro = getCursorMacroDefinition(Cursor)->getName();
323  else
324    Macro = getCursorMacroExpansion(Cursor).getName();
325  if (!Macro)
326    return false;
327
328  FindFileMacroRefVisitData data(*Unit, File, Macro, Visitor);
329
330  SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
331  CursorVisitor FindMacroRefsVisitor(TU,
332                                  findFileMacroRefVisit, &data,
333                                  /*VisitPreprocessorLast=*/false,
334                                  /*VisitIncludedEntities=*/false,
335                                  Range);
336  return FindMacroRefsVisitor.visitPreprocessedEntitiesInRegion();
337}
338
339namespace {
340
341struct FindFileIncludesVisitor {
342  ASTUnit &Unit;
343  const FileEntry *File;
344  CXCursorAndRangeVisitor visitor;
345
346  FindFileIncludesVisitor(ASTUnit &Unit, const FileEntry *File,
347                          CXCursorAndRangeVisitor visitor)
348    : Unit(Unit), File(File), visitor(visitor) { }
349
350  ASTContext &getASTContext() const {
351    return Unit.getASTContext();
352  }
353
354  enum CXChildVisitResult visit(CXCursor cursor, CXCursor parent) {
355    if (cursor.kind != CXCursor_InclusionDirective)
356      return CXChildVisit_Continue;
357
358    SourceLocation
359      Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));
360
361    ASTContext &Ctx = getASTContext();
362    SourceManager &SM = Ctx.getSourceManager();
363
364    // We are looking for includes in a specific file.
365    std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
366    if (SM.getFileEntryForID(LocInfo.first) != File)
367      return CXChildVisit_Continue;
368
369    if (visitor.visit(visitor.context, cursor,
370                      cxloc::translateSourceRange(Ctx, Loc)) == CXVisit_Break)
371      return CXChildVisit_Break;
372    return CXChildVisit_Continue;
373  }
374
375  static enum CXChildVisitResult visit(CXCursor cursor, CXCursor parent,
376                                       CXClientData client_data) {
377    return static_cast<FindFileIncludesVisitor*>(client_data)->
378                                                          visit(cursor, parent);
379  }
380};
381
382} // anonymous namespace
383
384static bool findIncludesInFile(CXTranslationUnit TU, const FileEntry *File,
385                               CXCursorAndRangeVisitor Visitor) {
386  assert(TU && File && Visitor.visit);
387
388  ASTUnit *Unit = cxtu::getASTUnit(TU);
389  SourceManager &SM = Unit->getSourceManager();
390
391  FileID FID = SM.translateFile(File);
392
393  FindFileIncludesVisitor IncludesVisitor(*Unit, File, Visitor);
394
395  SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
396  CursorVisitor InclusionCursorsVisitor(TU,
397                                        FindFileIncludesVisitor::visit,
398                                        &IncludesVisitor,
399                                        /*VisitPreprocessorLast=*/false,
400                                        /*VisitIncludedEntities=*/false,
401                                        Range);
402  return InclusionCursorsVisitor.visitPreprocessedEntitiesInRegion();
403}
404
405
406//===----------------------------------------------------------------------===//
407// libclang public APIs.
408//===----------------------------------------------------------------------===//
409
410extern "C" {
411
412CXResult clang_findReferencesInFile(CXCursor cursor, CXFile file,
413                                    CXCursorAndRangeVisitor visitor) {
414  LogRef Log = Logger::make(LLVM_FUNCTION_NAME);
415
416  if (clang_Cursor_isNull(cursor)) {
417    if (Log)
418      *Log << "Null cursor";
419    return CXResult_Invalid;
420  }
421  if (cursor.kind == CXCursor_NoDeclFound) {
422    if (Log)
423      *Log << "Got CXCursor_NoDeclFound";
424    return CXResult_Invalid;
425  }
426  if (!file) {
427    if (Log)
428      *Log << "Null file";
429    return CXResult_Invalid;
430  }
431  if (!visitor.visit) {
432    if (Log)
433      *Log << "Null visitor";
434    return CXResult_Invalid;
435  }
436
437  if (Log)
438    *Log << cursor << " @" << static_cast<const FileEntry *>(file);
439
440  ASTUnit *CXXUnit = cxcursor::getCursorASTUnit(cursor);
441  if (!CXXUnit)
442    return CXResult_Invalid;
443
444  ASTUnit::ConcurrencyCheck Check(*CXXUnit);
445
446  if (cursor.kind == CXCursor_MacroDefinition ||
447      cursor.kind == CXCursor_MacroExpansion) {
448    if (findMacroRefsInFile(cxcursor::getCursorTU(cursor),
449                            cursor,
450                            static_cast<const FileEntry *>(file),
451                            visitor))
452      return CXResult_VisitBreak;
453    return CXResult_Success;
454  }
455
456  // We are interested in semantics of identifiers so for C++ constructor exprs
457  // prefer type references, e.g.:
458  //
459  //  return MyStruct();
460  //
461  // for 'MyStruct' we'll have a cursor pointing at the constructor decl but
462  // we are actually interested in the type declaration.
463  cursor = cxcursor::getTypeRefCursor(cursor);
464
465  CXCursor refCursor = clang_getCursorReferenced(cursor);
466
467  if (!clang_isDeclaration(refCursor.kind)) {
468    if (Log)
469      *Log << "cursor is not referencing a declaration";
470    return CXResult_Invalid;
471  }
472
473  if (findIdRefsInFile(cxcursor::getCursorTU(cursor),
474                       refCursor,
475                       static_cast<const FileEntry *>(file),
476                       visitor))
477    return CXResult_VisitBreak;
478  return CXResult_Success;
479}
480
481CXResult clang_findIncludesInFile(CXTranslationUnit TU, CXFile file,
482                             CXCursorAndRangeVisitor visitor) {
483  if (cxtu::isNotUsableTU(TU)) {
484    LOG_BAD_TU(TU);
485    return CXResult_Invalid;
486  }
487
488  LogRef Log = Logger::make(LLVM_FUNCTION_NAME);
489  if (!file) {
490    if (Log)
491      *Log << "Null file";
492    return CXResult_Invalid;
493  }
494  if (!visitor.visit) {
495    if (Log)
496      *Log << "Null visitor";
497    return CXResult_Invalid;
498  }
499
500  if (Log)
501    *Log << TU << " @" << static_cast<const FileEntry *>(file);
502
503  ASTUnit *CXXUnit = cxtu::getASTUnit(TU);
504  if (!CXXUnit)
505    return CXResult_Invalid;
506
507  ASTUnit::ConcurrencyCheck Check(*CXXUnit);
508
509  if (findIncludesInFile(TU, static_cast<const FileEntry *>(file), visitor))
510    return CXResult_VisitBreak;
511  return CXResult_Success;
512}
513
514static enum CXVisitorResult _visitCursorAndRange(void *context,
515                                                 CXCursor cursor,
516                                                 CXSourceRange range) {
517  CXCursorAndRangeVisitorBlock block = (CXCursorAndRangeVisitorBlock)context;
518  return INVOKE_BLOCK2(block, cursor, range);
519}
520
521CXResult clang_findReferencesInFileWithBlock(CXCursor cursor,
522                                             CXFile file,
523                                           CXCursorAndRangeVisitorBlock block) {
524  CXCursorAndRangeVisitor visitor = { block,
525                                      block ? _visitCursorAndRange : nullptr };
526  return clang_findReferencesInFile(cursor, file, visitor);
527}
528
529CXResult clang_findIncludesInFileWithBlock(CXTranslationUnit TU,
530                                           CXFile file,
531                                           CXCursorAndRangeVisitorBlock block) {
532  CXCursorAndRangeVisitor visitor = { block,
533                                      block ? _visitCursorAndRange : nullptr };
534  return clang_findIncludesInFile(TU, file, visitor);
535}
536
537} // end: extern "C"
538
539