535 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			535 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===- CIndexHigh.cpp - Higher level API functions ------------------------===//
 | |
| //
 | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | |
| // See https://llvm.org/LICENSE.txt for license information.
 | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| #include "CursorVisitor.h"
 | |
| #include "CLog.h"
 | |
| #include "CXCursor.h"
 | |
| #include "CXSourceLocation.h"
 | |
| #include "CXTranslationUnit.h"
 | |
| #include "clang/AST/DeclObjC.h"
 | |
| #include "clang/Frontend/ASTUnit.h"
 | |
| #include "llvm/Support/Compiler.h"
 | |
| 
 | |
| using namespace clang;
 | |
| using namespace cxcursor;
 | |
| using namespace cxindex;
 | |
| 
 | |
| static void getTopOverriddenMethods(CXTranslationUnit TU,
 | |
|                                     const Decl *D,
 | |
|                                     SmallVectorImpl<const Decl *> &Methods) {
 | |
|   if (!D)
 | |
|     return;
 | |
|   if (!isa<ObjCMethodDecl>(D) && !isa<CXXMethodDecl>(D))
 | |
|     return;
 | |
| 
 | |
|   SmallVector<CXCursor, 8> Overridden;
 | |
|   cxcursor::getOverriddenCursors(cxcursor::MakeCXCursor(D, TU), Overridden);
 | |
|   
 | |
|   if (Overridden.empty()) {
 | |
|     Methods.push_back(D->getCanonicalDecl());
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   for (SmallVectorImpl<CXCursor>::iterator
 | |
|          I = Overridden.begin(), E = Overridden.end(); I != E; ++I)
 | |
|     getTopOverriddenMethods(TU, cxcursor::getCursorDecl(*I), Methods);
 | |
| }
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| struct FindFileIdRefVisitData {
 | |
|   CXTranslationUnit TU;
 | |
|   FileID FID;
 | |
|   const Decl *Dcl;
 | |
|   int SelectorIdIdx;
 | |
|   CXCursorAndRangeVisitor visitor;
 | |
| 
 | |
|   typedef SmallVector<const Decl *, 8> TopMethodsTy;
 | |
|   TopMethodsTy TopMethods;
 | |
| 
 | |
|   FindFileIdRefVisitData(CXTranslationUnit TU, FileID FID,
 | |
|                          const Decl *D, int selectorIdIdx,
 | |
|                          CXCursorAndRangeVisitor visitor)
 | |
|     : TU(TU), FID(FID), SelectorIdIdx(selectorIdIdx), visitor(visitor) {
 | |
|     Dcl = getCanonical(D);
 | |
|     getTopOverriddenMethods(TU, Dcl, TopMethods);
 | |
|   }
 | |
| 
 | |
|   ASTContext &getASTContext() const {
 | |
|     return cxtu::getASTUnit(TU)->getASTContext();
 | |
|   }
 | |
| 
 | |
|   /// We are looking to find all semantically relevant identifiers,
 | |
|   /// so the definition of "canonical" here is different than in the AST, e.g.
 | |
|   ///
 | |
|   /// \code
 | |
|   ///   class C {
 | |
|   ///     C() {}
 | |
|   ///   };
 | |
|   /// \endcode
 | |
|   ///
 | |
|   /// we consider the canonical decl of the constructor decl to be the class
 | |
|   /// itself, so both 'C' can be highlighted.
 | |
|   const Decl *getCanonical(const Decl *D) const {
 | |
|     if (!D)
 | |
|       return nullptr;
 | |
| 
 | |
|     D = D->getCanonicalDecl();
 | |
| 
 | |
|     if (const ObjCImplDecl *ImplD = dyn_cast<ObjCImplDecl>(D)) {
 | |
|       if (ImplD->getClassInterface())
 | |
|         return getCanonical(ImplD->getClassInterface());
 | |
| 
 | |
|     } else if (const CXXConstructorDecl *CXXCtorD =
 | |
|                    dyn_cast<CXXConstructorDecl>(D)) {
 | |
|       return getCanonical(CXXCtorD->getParent());
 | |
|     }
 | |
|     
 | |
|     return D;
 | |
|   }
 | |
| 
 | |
|   bool isHit(const Decl *D) const {
 | |
|     if (!D)
 | |
|       return false;
 | |
| 
 | |
|     D = getCanonical(D);
 | |
|     if (D == Dcl)
 | |
|       return true;
 | |
| 
 | |
|     if (isa<ObjCMethodDecl>(D) || isa<CXXMethodDecl>(D))
 | |
|       return isOverriddingMethod(D);
 | |
| 
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
| private:
 | |
|   bool isOverriddingMethod(const Decl *D) const {
 | |
|     if (llvm::find(TopMethods, D) != TopMethods.end())
 | |
|       return true;
 | |
| 
 | |
|     TopMethodsTy methods;
 | |
|     getTopOverriddenMethods(TU, D, methods);
 | |
|     for (TopMethodsTy::iterator
 | |
|            I = methods.begin(), E = methods.end(); I != E; ++I) {
 | |
|       if (llvm::find(TopMethods, *I) != TopMethods.end())
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     return false;
 | |
|   }
 | |
| };
 | |
| 
 | |
| } // end anonymous namespace.
 | |
| 
 | |
| /// For a macro \arg Loc, returns the file spelling location and sets
 | |
| /// to \arg isMacroArg whether the spelling resides inside a macro definition or
 | |
| /// a macro argument.
 | |
| static SourceLocation getFileSpellingLoc(SourceManager &SM,
 | |
|                                          SourceLocation Loc,
 | |
|                                          bool &isMacroArg) {
 | |
|   assert(Loc.isMacroID());
 | |
|   SourceLocation SpellLoc = SM.getImmediateSpellingLoc(Loc);
 | |
|   if (SpellLoc.isMacroID())
 | |
|     return getFileSpellingLoc(SM, SpellLoc, isMacroArg);
 | |
|   
 | |
|   isMacroArg = SM.isMacroArgExpansion(Loc);
 | |
|   return SpellLoc;
 | |
| }
 | |
| 
 | |
| static enum CXChildVisitResult findFileIdRefVisit(CXCursor cursor,
 | |
|                                                   CXCursor parent,
 | |
|                                                   CXClientData client_data) {
 | |
|   CXCursor declCursor = clang_getCursorReferenced(cursor);
 | |
|   if (!clang_isDeclaration(declCursor.kind))
 | |
|     return CXChildVisit_Recurse;
 | |
| 
 | |
|   const Decl *D = cxcursor::getCursorDecl(declCursor);
 | |
|   if (!D)
 | |
|     return CXChildVisit_Continue;
 | |
| 
 | |
|   FindFileIdRefVisitData *data = (FindFileIdRefVisitData *)client_data;
 | |
|   if (data->isHit(D)) {
 | |
|     cursor = cxcursor::getSelectorIdentifierCursor(data->SelectorIdIdx, cursor);
 | |
| 
 | |
|     // We are looking for identifiers to highlight so for objc methods (and
 | |
|     // not a parameter) we can only highlight the selector identifiers.
 | |
|     if ((cursor.kind == CXCursor_ObjCClassMethodDecl ||
 | |
|          cursor.kind == CXCursor_ObjCInstanceMethodDecl) &&
 | |
|          cxcursor::getSelectorIdentifierIndex(cursor) == -1)
 | |
|       return CXChildVisit_Recurse;
 | |
| 
 | |
|     if (clang_isExpression(cursor.kind)) {
 | |
|       if (cursor.kind == CXCursor_DeclRefExpr ||
 | |
|           cursor.kind == CXCursor_MemberRefExpr) {
 | |
|         // continue..
 | |
| 
 | |
|       } else if (cursor.kind == CXCursor_ObjCMessageExpr &&
 | |
|                  cxcursor::getSelectorIdentifierIndex(cursor) != -1) {
 | |
|         // continue..
 | |
|                 
 | |
|       } else
 | |
|         return CXChildVisit_Recurse;
 | |
|     }
 | |
| 
 | |
|     SourceLocation
 | |
|       Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));
 | |
|     SourceLocation SelIdLoc = cxcursor::getSelectorIdentifierLoc(cursor);
 | |
|     if (SelIdLoc.isValid())
 | |
|       Loc = SelIdLoc;
 | |
| 
 | |
|     ASTContext &Ctx = data->getASTContext();
 | |
|     SourceManager &SM = Ctx.getSourceManager();
 | |
|     bool isInMacroDef = false;
 | |
|     if (Loc.isMacroID()) {
 | |
|       bool isMacroArg;
 | |
|       Loc = getFileSpellingLoc(SM, Loc, isMacroArg);
 | |
|       isInMacroDef = !isMacroArg;
 | |
|     }
 | |
| 
 | |
|     // We are looking for identifiers in a specific file.
 | |
|     std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
 | |
|     if (LocInfo.first != data->FID)
 | |
|       return CXChildVisit_Recurse;
 | |
| 
 | |
|     if (isInMacroDef) {
 | |
|       // FIXME: For a macro definition make sure that all expansions
 | |
|       // of it expand to the same reference before allowing to point to it.
 | |
|       return CXChildVisit_Recurse;
 | |
|     }
 | |
| 
 | |
|     if (data->visitor.visit(data->visitor.context, cursor,
 | |
|                         cxloc::translateSourceRange(Ctx, Loc)) == CXVisit_Break)
 | |
|       return CXChildVisit_Break;
 | |
|   }
 | |
|   return CXChildVisit_Recurse;
 | |
| }
 | |
| 
 | |
| static bool findIdRefsInFile(CXTranslationUnit TU, CXCursor declCursor,
 | |
|                              const FileEntry *File,
 | |
|                              CXCursorAndRangeVisitor Visitor) {
 | |
|   assert(clang_isDeclaration(declCursor.kind));
 | |
|   SourceManager &SM = cxtu::getASTUnit(TU)->getSourceManager();
 | |
| 
 | |
|   FileID FID = SM.translateFile(File);
 | |
|   const Decl *Dcl = cxcursor::getCursorDecl(declCursor);
 | |
|   if (!Dcl)
 | |
|     return false;
 | |
| 
 | |
|   FindFileIdRefVisitData data(TU, FID, Dcl,
 | |
|                               cxcursor::getSelectorIdentifierIndex(declCursor),
 | |
|                               Visitor);
 | |
| 
 | |
|   if (const DeclContext *DC = Dcl->getParentFunctionOrMethod()) {
 | |
|     return clang_visitChildren(cxcursor::MakeCXCursor(cast<Decl>(DC), TU),
 | |
|                                findFileIdRefVisit, &data);
 | |
|   }
 | |
| 
 | |
|   SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
 | |
|   CursorVisitor FindIdRefsVisitor(TU,
 | |
|                                   findFileIdRefVisit, &data,
 | |
|                                   /*VisitPreprocessorLast=*/true,
 | |
|                                   /*VisitIncludedEntities=*/false,
 | |
|                                   Range,
 | |
|                                   /*VisitDeclsOnly=*/true);
 | |
|   return FindIdRefsVisitor.visitFileRegion();
 | |
| }
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| struct FindFileMacroRefVisitData {
 | |
|   ASTUnit &Unit;
 | |
|   const FileEntry *File;
 | |
|   const IdentifierInfo *Macro;
 | |
|   CXCursorAndRangeVisitor visitor;
 | |
| 
 | |
|   FindFileMacroRefVisitData(ASTUnit &Unit, const FileEntry *File,
 | |
|                             const IdentifierInfo *Macro,
 | |
|                             CXCursorAndRangeVisitor visitor)
 | |
|     : Unit(Unit), File(File), Macro(Macro), visitor(visitor) { }
 | |
| 
 | |
|   ASTContext &getASTContext() const {
 | |
|     return Unit.getASTContext();
 | |
|   }
 | |
| };
 | |
| 
 | |
| } // anonymous namespace
 | |
| 
 | |
| static enum CXChildVisitResult findFileMacroRefVisit(CXCursor cursor,
 | |
|                                                      CXCursor parent,
 | |
|                                                      CXClientData client_data) {
 | |
|   const IdentifierInfo *Macro = nullptr;
 | |
|   if (cursor.kind == CXCursor_MacroDefinition)
 | |
|     Macro = getCursorMacroDefinition(cursor)->getName();
 | |
|   else if (cursor.kind == CXCursor_MacroExpansion)
 | |
|     Macro = getCursorMacroExpansion(cursor).getName();
 | |
|   if (!Macro)
 | |
|     return CXChildVisit_Continue;
 | |
| 
 | |
|   FindFileMacroRefVisitData *data = (FindFileMacroRefVisitData *)client_data;
 | |
|   if (data->Macro != Macro)
 | |
|     return CXChildVisit_Continue;
 | |
| 
 | |
|   SourceLocation
 | |
|     Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));
 | |
| 
 | |
|   ASTContext &Ctx = data->getASTContext();
 | |
|   SourceManager &SM = Ctx.getSourceManager();
 | |
|   bool isInMacroDef = false;
 | |
|   if (Loc.isMacroID()) {
 | |
|     bool isMacroArg;
 | |
|     Loc = getFileSpellingLoc(SM, Loc, isMacroArg);
 | |
|     isInMacroDef = !isMacroArg;
 | |
|   }
 | |
| 
 | |
|   // We are looking for identifiers in a specific file.
 | |
|   std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
 | |
|   if (SM.getFileEntryForID(LocInfo.first) != data->File)
 | |
|     return CXChildVisit_Continue;
 | |
| 
 | |
|   if (isInMacroDef) {
 | |
|     // FIXME: For a macro definition make sure that all expansions
 | |
|     // of it expand to the same reference before allowing to point to it.
 | |
|     return CXChildVisit_Continue;
 | |
|   }
 | |
| 
 | |
|   if (data->visitor.visit(data->visitor.context, cursor,
 | |
|                         cxloc::translateSourceRange(Ctx, Loc)) == CXVisit_Break)
 | |
|     return CXChildVisit_Break;
 | |
|   return CXChildVisit_Continue;
 | |
| }
 | |
| 
 | |
| static bool findMacroRefsInFile(CXTranslationUnit TU, CXCursor Cursor,
 | |
|                                 const FileEntry *File,
 | |
|                                 CXCursorAndRangeVisitor Visitor) {
 | |
|   if (Cursor.kind != CXCursor_MacroDefinition &&
 | |
|       Cursor.kind != CXCursor_MacroExpansion)
 | |
|     return false;
 | |
| 
 | |
|   ASTUnit *Unit = cxtu::getASTUnit(TU);
 | |
|   SourceManager &SM = Unit->getSourceManager();
 | |
| 
 | |
|   FileID FID = SM.translateFile(File);
 | |
|   const IdentifierInfo *Macro = nullptr;
 | |
|   if (Cursor.kind == CXCursor_MacroDefinition)
 | |
|     Macro = getCursorMacroDefinition(Cursor)->getName();
 | |
|   else
 | |
|     Macro = getCursorMacroExpansion(Cursor).getName();
 | |
|   if (!Macro)
 | |
|     return false;
 | |
| 
 | |
|   FindFileMacroRefVisitData data(*Unit, File, Macro, Visitor);
 | |
| 
 | |
|   SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
 | |
|   CursorVisitor FindMacroRefsVisitor(TU,
 | |
|                                   findFileMacroRefVisit, &data,
 | |
|                                   /*VisitPreprocessorLast=*/false,
 | |
|                                   /*VisitIncludedEntities=*/false,
 | |
|                                   Range);
 | |
|   return FindMacroRefsVisitor.visitPreprocessedEntitiesInRegion();
 | |
| }
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| struct FindFileIncludesVisitor {
 | |
|   ASTUnit &Unit;
 | |
|   const FileEntry *File;
 | |
|   CXCursorAndRangeVisitor visitor;
 | |
| 
 | |
|   FindFileIncludesVisitor(ASTUnit &Unit, const FileEntry *File,
 | |
|                           CXCursorAndRangeVisitor visitor)
 | |
|     : Unit(Unit), File(File), visitor(visitor) { }
 | |
| 
 | |
|   ASTContext &getASTContext() const {
 | |
|     return Unit.getASTContext();
 | |
|   }
 | |
| 
 | |
|   enum CXChildVisitResult visit(CXCursor cursor, CXCursor parent) {
 | |
|     if (cursor.kind != CXCursor_InclusionDirective)
 | |
|       return CXChildVisit_Continue;
 | |
| 
 | |
|     SourceLocation
 | |
|       Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));
 | |
| 
 | |
|     ASTContext &Ctx = getASTContext();
 | |
|     SourceManager &SM = Ctx.getSourceManager();
 | |
| 
 | |
|     // We are looking for includes in a specific file.
 | |
|     std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
 | |
|     if (SM.getFileEntryForID(LocInfo.first) != File)
 | |
|       return CXChildVisit_Continue;
 | |
| 
 | |
|     if (visitor.visit(visitor.context, cursor,
 | |
|                       cxloc::translateSourceRange(Ctx, Loc)) == CXVisit_Break)
 | |
|       return CXChildVisit_Break;
 | |
|     return CXChildVisit_Continue;
 | |
|   }
 | |
| 
 | |
|   static enum CXChildVisitResult visit(CXCursor cursor, CXCursor parent,
 | |
|                                        CXClientData client_data) {
 | |
|     return static_cast<FindFileIncludesVisitor*>(client_data)->
 | |
|                                                           visit(cursor, parent);
 | |
|   }
 | |
| };
 | |
| 
 | |
| } // anonymous namespace
 | |
| 
 | |
| static bool findIncludesInFile(CXTranslationUnit TU, const FileEntry *File,
 | |
|                                CXCursorAndRangeVisitor Visitor) {
 | |
|   assert(TU && File && Visitor.visit);
 | |
| 
 | |
|   ASTUnit *Unit = cxtu::getASTUnit(TU);
 | |
|   SourceManager &SM = Unit->getSourceManager();
 | |
| 
 | |
|   FileID FID = SM.translateFile(File);
 | |
| 
 | |
|   FindFileIncludesVisitor IncludesVisitor(*Unit, File, Visitor);
 | |
| 
 | |
|   SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
 | |
|   CursorVisitor InclusionCursorsVisitor(TU,
 | |
|                                         FindFileIncludesVisitor::visit,
 | |
|                                         &IncludesVisitor,
 | |
|                                         /*VisitPreprocessorLast=*/false,
 | |
|                                         /*VisitIncludedEntities=*/false,
 | |
|                                         Range);
 | |
|   return InclusionCursorsVisitor.visitPreprocessedEntitiesInRegion();
 | |
| }
 | |
| 
 | |
| 
 | |
| //===----------------------------------------------------------------------===//
 | |
| // libclang public APIs.
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| extern "C" {
 | |
| 
 | |
| CXResult clang_findReferencesInFile(CXCursor cursor, CXFile file,
 | |
|                                     CXCursorAndRangeVisitor visitor) {
 | |
|   LogRef Log = Logger::make(__func__);
 | |
| 
 | |
|   if (clang_Cursor_isNull(cursor)) {
 | |
|     if (Log)
 | |
|       *Log << "Null cursor";
 | |
|     return CXResult_Invalid;
 | |
|   }
 | |
|   if (cursor.kind == CXCursor_NoDeclFound) {
 | |
|     if (Log)
 | |
|       *Log << "Got CXCursor_NoDeclFound";
 | |
|     return CXResult_Invalid;
 | |
|   }
 | |
|   if (!file) {
 | |
|     if (Log)
 | |
|       *Log << "Null file";
 | |
|     return CXResult_Invalid;
 | |
|   }
 | |
|   if (!visitor.visit) {
 | |
|     if (Log)
 | |
|       *Log << "Null visitor";
 | |
|     return CXResult_Invalid;
 | |
|   }
 | |
| 
 | |
|   if (Log)
 | |
|     *Log << cursor << " @" << static_cast<const FileEntry *>(file);
 | |
| 
 | |
|   ASTUnit *CXXUnit = cxcursor::getCursorASTUnit(cursor);
 | |
|   if (!CXXUnit)
 | |
|     return CXResult_Invalid;
 | |
| 
 | |
|   ASTUnit::ConcurrencyCheck Check(*CXXUnit);
 | |
| 
 | |
|   if (cursor.kind == CXCursor_MacroDefinition ||
 | |
|       cursor.kind == CXCursor_MacroExpansion) {
 | |
|     if (findMacroRefsInFile(cxcursor::getCursorTU(cursor),
 | |
|                             cursor,
 | |
|                             static_cast<const FileEntry *>(file),
 | |
|                             visitor))
 | |
|       return CXResult_VisitBreak;
 | |
|     return CXResult_Success;
 | |
|   }
 | |
| 
 | |
|   // We are interested in semantics of identifiers so for C++ constructor exprs
 | |
|   // prefer type references, e.g.:
 | |
|   //
 | |
|   //  return MyStruct();
 | |
|   //
 | |
|   // for 'MyStruct' we'll have a cursor pointing at the constructor decl but
 | |
|   // we are actually interested in the type declaration.
 | |
|   cursor = cxcursor::getTypeRefCursor(cursor);
 | |
| 
 | |
|   CXCursor refCursor = clang_getCursorReferenced(cursor);
 | |
| 
 | |
|   if (!clang_isDeclaration(refCursor.kind)) {
 | |
|     if (Log)
 | |
|       *Log << "cursor is not referencing a declaration";
 | |
|     return CXResult_Invalid;
 | |
|   }
 | |
| 
 | |
|   if (findIdRefsInFile(cxcursor::getCursorTU(cursor),
 | |
|                        refCursor,
 | |
|                        static_cast<const FileEntry *>(file),
 | |
|                        visitor))
 | |
|     return CXResult_VisitBreak;
 | |
|   return CXResult_Success;
 | |
| }
 | |
| 
 | |
| CXResult clang_findIncludesInFile(CXTranslationUnit TU, CXFile file,
 | |
|                              CXCursorAndRangeVisitor visitor) {
 | |
|   if (cxtu::isNotUsableTU(TU)) {
 | |
|     LOG_BAD_TU(TU);
 | |
|     return CXResult_Invalid;
 | |
|   }
 | |
| 
 | |
|   LogRef Log = Logger::make(__func__);
 | |
|   if (!file) {
 | |
|     if (Log)
 | |
|       *Log << "Null file";
 | |
|     return CXResult_Invalid;
 | |
|   }
 | |
|   if (!visitor.visit) {
 | |
|     if (Log)
 | |
|       *Log << "Null visitor";
 | |
|     return CXResult_Invalid;
 | |
|   }
 | |
| 
 | |
|   if (Log)
 | |
|     *Log << TU << " @" << static_cast<const FileEntry *>(file);
 | |
| 
 | |
|   ASTUnit *CXXUnit = cxtu::getASTUnit(TU);
 | |
|   if (!CXXUnit)
 | |
|     return CXResult_Invalid;
 | |
| 
 | |
|   ASTUnit::ConcurrencyCheck Check(*CXXUnit);
 | |
| 
 | |
|   if (findIncludesInFile(TU, static_cast<const FileEntry *>(file), visitor))
 | |
|     return CXResult_VisitBreak;
 | |
|   return CXResult_Success;
 | |
| }
 | |
| 
 | |
| static enum CXVisitorResult _visitCursorAndRange(void *context,
 | |
|                                                  CXCursor cursor,
 | |
|                                                  CXSourceRange range) {
 | |
|   CXCursorAndRangeVisitorBlock block = (CXCursorAndRangeVisitorBlock)context;
 | |
|   return INVOKE_BLOCK2(block, cursor, range);
 | |
| }
 | |
| 
 | |
| CXResult clang_findReferencesInFileWithBlock(CXCursor cursor,
 | |
|                                              CXFile file,
 | |
|                                            CXCursorAndRangeVisitorBlock block) {
 | |
|   CXCursorAndRangeVisitor visitor = { block,
 | |
|                                       block ? _visitCursorAndRange : nullptr };
 | |
|   return clang_findReferencesInFile(cursor, file, visitor);
 | |
| }
 | |
| 
 | |
| CXResult clang_findIncludesInFileWithBlock(CXTranslationUnit TU,
 | |
|                                            CXFile file,
 | |
|                                            CXCursorAndRangeVisitorBlock block) {
 | |
|   CXCursorAndRangeVisitor visitor = { block,
 | |
|                                       block ? _visitCursorAndRange : nullptr };
 | |
|   return clang_findIncludesInFile(TU, file, visitor);
 | |
| }
 | |
| 
 | |
| } // end: extern "C"
 |