123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534 |
- //===- 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::is_contained(TopMethods, D))
- return true;
- TopMethodsTy methods;
- getTopOverriddenMethods(TU, D, methods);
- for (TopMethodsTy::iterator
- I = methods.begin(), E = methods.end(); I != E; ++I) {
- if (llvm::is_contained(TopMethods, *I))
- 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"
|