diff options
author | Argyrios Kyrtzidis <akyrtzi@gmail.com> | 2011-10-06 07:00:54 +0000 |
---|---|---|
committer | Argyrios Kyrtzidis <akyrtzi@gmail.com> | 2011-10-06 07:00:54 +0000 |
commit | aed123ec3cc37e457fe20a6158fdadf8849ad916 (patch) | |
tree | 3c966b7fabc78150142d829f20b4be00cceb3d0a /tools/libclang/CIndexHigh.cpp | |
parent | b11be041e4f05519a2eabf6a99429ba6110f1ca9 (diff) | |
download | clang-aed123ec3cc37e457fe20a6158fdadf8849ad916.tar.gz clang-aed123ec3cc37e457fe20a6158fdadf8849ad916.tar.bz2 clang-aed123ec3cc37e457fe20a6158fdadf8849ad916.tar.xz |
[libclang] Introduce clang_findReferencesInFile which accepts a cursor, a file,
and a callback and finds all identifier references of the cursor in the file.
rdar://7948304
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@141277 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'tools/libclang/CIndexHigh.cpp')
-rw-r--r-- | tools/libclang/CIndexHigh.cpp | 315 |
1 files changed, 315 insertions, 0 deletions
diff --git a/tools/libclang/CIndexHigh.cpp b/tools/libclang/CIndexHigh.cpp new file mode 100644 index 0000000000..b5a05eaafc --- /dev/null +++ b/tools/libclang/CIndexHigh.cpp @@ -0,0 +1,315 @@ +//===- 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 "Index_Internal.h" +#include "CXCursor.h" +#include "CXSourceLocation.h" +#include "CXTranslationUnit.h" + +#include "clang/Frontend/ASTUnit.h" +#include "clang/AST/DeclObjC.h" + +using namespace clang; + +static void getTopOverriddenMethods(CXTranslationUnit TU, + Decl *D, + SmallVectorImpl<Decl *> &Methods) { + 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 (SmallVector<CXCursor, 8>::iterator + I = Overridden.begin(), E = Overridden.end(); I != E; ++I) + getTopOverriddenMethods(TU, cxcursor::getCursorDecl(*I), Methods); +} + +namespace { + +struct FindFileIdRefVisitData { + CXTranslationUnit TU; + FileID FID; + Decl *Dcl; + int SelectorIdIdx; + CXCursorAndRangeVisitor visitor; + + typedef SmallVector<Decl *, 8> TopMethodsTy; + TopMethodsTy TopMethods; + + FindFileIdRefVisitData(CXTranslationUnit TU, FileID FID, + 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 static_cast<ASTUnit *>(TU->TUData)->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. + Decl *getCanonical(Decl *D) const { + D = D->getCanonicalDecl(); + + if (ObjCImplDecl *ImplD = dyn_cast<ObjCImplDecl>(D)) + return getCanonical(ImplD->getClassInterface()); + if (CXXConstructorDecl *CXXCtorD = dyn_cast<CXXConstructorDecl>(D)) + return getCanonical(CXXCtorD->getParent()); + + return D; + } + + bool isHit(Decl *D) const { + D = getCanonical(D); + if (D == Dcl) + return true; + + if (isa<ObjCMethodDecl>(D) || isa<CXXMethodDecl>(D)) + return isOverriddingMethod(D); + + return false; + } + +private: + bool isOverriddingMethod(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; + + Decl *D = cxcursor::getCursorDecl(declCursor); + 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; + + SourceManager &SM = data->getASTContext().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. + Loc = SourceLocation(); + } + + data->visitor.visit(data->visitor.context, cursor, + cxloc::translateSourceRange(D->getASTContext(), Loc)); + } + return CXChildVisit_Recurse; +} + +static void findIdRefsInFile(CXTranslationUnit TU, CXCursor declCursor, + const FileEntry *File, + CXCursorAndRangeVisitor Visitor) { + assert(clang_isDeclaration(declCursor.kind)); + ASTUnit *Unit = static_cast<ASTUnit*>(TU->TUData); + ASTContext &Ctx = Unit->getASTContext(); + SourceManager &SM = Unit->getSourceManager(); + + FileID FID = SM.translateFile(File); + Decl *Dcl = cxcursor::getCursorDecl(declCursor); + FindFileIdRefVisitData data(TU, FID, Dcl, + cxcursor::getSelectorIdentifierIndex(declCursor), + Visitor); + + if (DeclContext *DC = Dcl->getParentFunctionOrMethod()) { + clang_visitChildren(cxcursor::MakeCXCursor(cast<Decl>(DC), TU), + findFileIdRefVisit, &data); + return; + } + + if (FID == SM.getMainFileID() && !Unit->isMainFileAST()) { + SourceLocation FileLoc = SM.getLocForStartOfFile(FID); + TranslationUnitDecl *TUD = Ctx.getTranslationUnitDecl(); + CXCursor TUCursor = clang_getTranslationUnitCursor(TU); + for (DeclContext::decl_iterator + I = TUD->noload_decls_begin(), E = TUD->noload_decls_end(); + I != E; ++I) { + Decl *D = *I; + + SourceRange R = D->getSourceRange(); + if (R.isInvalid()) + continue; + if (SM.isBeforeInTranslationUnit(R.getEnd(), FileLoc)) + continue; + + if (TagDecl *TD = dyn_cast<TagDecl>(D)) + if (!TD->isFreeStanding()) + continue; + + CXCursor CurCursor = cxcursor::MakeCXCursor(D, TU); + findFileIdRefVisit(CurCursor, TUCursor, &data); + clang_visitChildren(CurCursor, findFileIdRefVisit, &data); + } + return; + } + + clang_visitChildren(clang_getTranslationUnitCursor(TU), + findFileIdRefVisit, &data); +} + + +//===----------------------------------------------------------------------===// +// libclang public APIs. +//===----------------------------------------------------------------------===// + +extern "C" { + +void clang_findReferencesInFile(CXCursor cursor, CXFile file, + CXCursorAndRangeVisitor visitor) { + bool Logging = ::getenv("LIBCLANG_LOGGING"); + + if (clang_Cursor_isNull(cursor)) { + if (Logging) + llvm::errs() << "clang_findReferencesInFile: Null cursor\n"; + return; + } + if (!file) { + if (Logging) + llvm::errs() << "clang_findReferencesInFile: Null file\n"; + return; + } + if (!visitor.visit) { + if (Logging) + llvm::errs() << "clang_findReferencesInFile: Null visitor\n"; + return; + } + + // 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 (Logging) + llvm::errs() << "clang_findReferencesInFile: cursor is not referencing a " + "declaration\n"; + return; + } + + ASTUnit *CXXUnit = cxcursor::getCursorASTUnit(cursor); + ASTUnit::ConcurrencyCheck Check(*CXXUnit); + + findIdRefsInFile(cxcursor::getCursorTU(cursor), + refCursor, + static_cast<const FileEntry *>(file), + visitor); +} + +static enum CXVisitorResult _visitCursorAndRange(void *context, + CXCursor cursor, + CXSourceRange range) { + CXCursorAndRangeVisitorBlock block = (CXCursorAndRangeVisitorBlock)context; + return INVOKE_BLOCK2(block, cursor, range); +} + +void clang_findReferencesInFileWithBlock(CXCursor cursor, + CXFile file, + CXCursorAndRangeVisitorBlock block) { + CXCursorAndRangeVisitor visitor = { block, + block ? _visitCursorAndRange : 0 }; + return clang_findReferencesInFile(cursor, file, visitor); +} + +} // end: extern "C" + |