You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
535 lines
17 KiB
535 lines
17 KiB
//===- 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"
|