text: //===- CIndexHigh.cpp - Higher level API functions ------------------------===//
  //
  //                     The LLVM Compiler Infrastructure
  //
  // This file is distributed under the University of Illinois Open Source
  // License. See LICENSE.TXT for details.
  //
  //===----------------------------------------------------------------------===//
  
  #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();
    }
  
    /// \brief 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 (std::find(TopMethods.begin(), TopMethods.end(), D) !=
            TopMethods.end())
        return true;
  
      TopMethodsTy methods;
      getTopOverriddenMethods(TU, D, methods);
      for (TopMethodsTy::iterator
             I = methods.begin(), E = methods.end(); I != E; ++I) {
        if (std::find(TopMethods.begin(), TopMethods.end(), *I) !=
              TopMethods.end())
          return true;
      }
  
      return false;
    }
  };
  
  } // end anonymous namespace.
  
  /// \brief 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(LLVM_FUNCTION_NAME);
  
    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(LLVM_FUNCTION_NAME);
    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"
  
  