//===- CIndex.cpp - Clang-C Source Indexing Library -----------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file implements the main API hooks in the Clang-C Source Indexing // library. // //===----------------------------------------------------------------------===// #include "CIndexer.h" #include "clang/AST/DeclVisitor.h" #include "clang/AST/StmtVisitor.h" #include "clang/Lex/Lexer.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/System/Program.h" // Needed to define L_TMPNAM on some systems. #include using namespace clang; using namespace idx; //===----------------------------------------------------------------------===// // Crash Reporting. //===----------------------------------------------------------------------===// #ifdef __APPLE__ #ifndef NDEBUG #define USE_CRASHTRACER #include "clang/Analysis/Support/SaveAndRestore.h" // Integrate with crash reporter. extern "C" const char *__crashreporter_info__; #define NUM_CRASH_STRINGS 16 static unsigned crashtracer_counter = 0; static unsigned crashtracer_counter_id[NUM_CRASH_STRINGS] = { 0 }; static const char *crashtracer_strings[NUM_CRASH_STRINGS] = { 0 }; static const char *agg_crashtracer_strings[NUM_CRASH_STRINGS] = { 0 }; static unsigned SetCrashTracerInfo(const char *str, llvm::SmallString<1024> &AggStr) { unsigned slot = 0; while (crashtracer_strings[slot]) { if (++slot == NUM_CRASH_STRINGS) slot = 0; } crashtracer_strings[slot] = str; crashtracer_counter_id[slot] = ++crashtracer_counter; // We need to create an aggregate string because multiple threads // may be in this method at one time. The crash reporter string // will attempt to overapproximate the set of in-flight invocations // of this function. Race conditions can still cause this goal // to not be achieved. { llvm::raw_svector_ostream Out(AggStr); for (unsigned i = 0; i < NUM_CRASH_STRINGS; ++i) if (crashtracer_strings[i]) Out << crashtracer_strings[i] << '\n'; } __crashreporter_info__ = agg_crashtracer_strings[slot] = AggStr.c_str(); return slot; } static void ResetCrashTracerInfo(unsigned slot) { unsigned max_slot = 0; unsigned max_value = 0; crashtracer_strings[slot] = agg_crashtracer_strings[slot] = 0; for (unsigned i = 0 ; i < NUM_CRASH_STRINGS; ++i) if (agg_crashtracer_strings[i] && crashtracer_counter_id[i] > max_value) { max_slot = i; max_value = crashtracer_counter_id[i]; } __crashreporter_info__ = agg_crashtracer_strings[max_slot]; } namespace { class ArgsCrashTracerInfo { llvm::SmallString<1024> CrashString; llvm::SmallString<1024> AggregateString; unsigned crashtracerSlot; public: ArgsCrashTracerInfo(llvm::SmallVectorImpl &Args) : crashtracerSlot(0) { { llvm::raw_svector_ostream Out(CrashString); Out << "ClangCIndex [createTranslationUnitFromSourceFile]: clang"; for (llvm::SmallVectorImpl::iterator I=Args.begin(), E=Args.end(); I!=E; ++I) Out << ' ' << *I; } crashtracerSlot = SetCrashTracerInfo(CrashString.c_str(), AggregateString); } ~ArgsCrashTracerInfo() { ResetCrashTracerInfo(crashtracerSlot); } }; } #endif #endif //===----------------------------------------------------------------------===// // Visitors. //===----------------------------------------------------------------------===// namespace { static enum CXCursorKind TranslateDeclRefExpr(DeclRefExpr *DRE) { NamedDecl *D = DRE->getDecl(); if (isa(D)) return CXCursor_VarRef; else if (isa(D)) return CXCursor_FunctionRef; else if (isa(D)) return CXCursor_EnumConstantRef; else return CXCursor_NotImplemented; } #if 0 // Will be useful one day. class CRefVisitor : public StmtVisitor { CXDecl CDecl; CXDeclIterator Callback; CXClientData CData; void Call(enum CXCursorKind CK, Stmt *SRef) { CXCursor C = { CK, CDecl, SRef }; Callback(CDecl, C, CData); } public: CRefVisitor(CXDecl C, CXDeclIterator cback, CXClientData D) : CDecl(C), Callback(cback), CData(D) {} void VisitStmt(Stmt *S) { for (Stmt::child_iterator C = S->child_begin(), CEnd = S->child_end(); C != CEnd; ++C) Visit(*C); } void VisitDeclRefExpr(DeclRefExpr *Node) { Call(TranslateDeclRefExpr(Node), Node); } void VisitMemberExpr(MemberExpr *Node) { Call(CXCursor_MemberRef, Node); } void VisitObjCMessageExpr(ObjCMessageExpr *Node) { Call(CXCursor_ObjCSelectorRef, Node); } void VisitObjCIvarRefExpr(ObjCIvarRefExpr *Node) { Call(CXCursor_ObjCIvarRef, Node); } }; #endif // Translation Unit Visitor. class TUVisitor : public DeclVisitor { public: typedef void (*Iterator)(void *, CXCursor, CXClientData); private: void *Root; // CXDecl or CXTranslationUnit Iterator Callback; // CXTranslationUnitIterator or CXDeclIterator. CXClientData CData; // MaxPCHLevel - the maximum PCH level of declarations that we will pass on // to the visitor. Declarations with a PCH level greater than this value will // be suppressed. unsigned MaxPCHLevel; void Call(enum CXCursorKind CK, NamedDecl *ND) { // Filter any declarations that have a PCH level greater than what we allow. if (ND->getPCHLevel() > MaxPCHLevel) return; // Filter any implicit declarations (since the source info will be bogus). if (ND->isImplicit()) return; CXCursor C = { CK, ND, 0, 0 }; Callback(Root, C, CData); } public: TUVisitor(void *root, Iterator cback, CXClientData D, unsigned MaxPCHLevel) : Root(root), Callback(cback), CData(D), MaxPCHLevel(MaxPCHLevel) {} void VisitDeclContext(DeclContext *DC); void VisitFunctionDecl(FunctionDecl *ND); void VisitObjCCategoryDecl(ObjCCategoryDecl *ND); void VisitObjCCategoryImplDecl(ObjCCategoryImplDecl *ND); void VisitObjCImplementationDecl(ObjCImplementationDecl *ND); void VisitObjCInterfaceDecl(ObjCInterfaceDecl *ND); void VisitObjCProtocolDecl(ObjCProtocolDecl *ND); void VisitTagDecl(TagDecl *ND); void VisitTranslationUnitDecl(TranslationUnitDecl *D); void VisitTypedefDecl(TypedefDecl *ND); void VisitVarDecl(VarDecl *ND); }; void TUVisitor::VisitDeclContext(DeclContext *DC) { for (DeclContext::decl_iterator I = DC->decls_begin(), E = DC->decls_end(); I != E; ++I) Visit(*I); } void TUVisitor::VisitFunctionDecl(FunctionDecl *ND) { Call(ND->isThisDeclarationADefinition() ? CXCursor_FunctionDefn : CXCursor_FunctionDecl, ND); } void TUVisitor::VisitObjCCategoryDecl(ObjCCategoryDecl *ND) { Call(CXCursor_ObjCCategoryDecl, ND); } void TUVisitor::VisitObjCCategoryImplDecl(ObjCCategoryImplDecl *ND) { Call(CXCursor_ObjCCategoryDefn, ND); } void TUVisitor::VisitObjCImplementationDecl(ObjCImplementationDecl *ND) { Call(CXCursor_ObjCClassDefn, ND); } void TUVisitor::VisitObjCInterfaceDecl(ObjCInterfaceDecl *ND) { Call(CXCursor_ObjCInterfaceDecl, ND); } void TUVisitor::VisitObjCProtocolDecl(ObjCProtocolDecl *ND) { Call(CXCursor_ObjCProtocolDecl, ND); } void TUVisitor::VisitTagDecl(TagDecl *ND) { switch (ND->getTagKind()) { case TagDecl::TK_struct: Call(CXCursor_StructDecl, ND); break; case TagDecl::TK_class: Call(CXCursor_ClassDecl, ND); break; case TagDecl::TK_union: Call(CXCursor_UnionDecl, ND); break; case TagDecl::TK_enum: Call(CXCursor_EnumDecl, ND); break; } } void TUVisitor::VisitTranslationUnitDecl(TranslationUnitDecl *D) { VisitDeclContext(dyn_cast(D)); } void TUVisitor::VisitTypedefDecl(TypedefDecl *ND) { Call(CXCursor_TypedefDecl, ND); } void TUVisitor::VisitVarDecl(VarDecl *ND) { Call(CXCursor_VarDecl, ND); } // Declaration visitor. class CDeclVisitor : public DeclVisitor { CXDecl CDecl; CXDeclIterator Callback; CXClientData CData; // MaxPCHLevel - the maximum PCH level of declarations that we will pass on // to the visitor. Declarations with a PCH level greater than this value will // be suppressed. unsigned MaxPCHLevel; void Call(enum CXCursorKind CK, NamedDecl *ND) { // Disable the callback when the context is equal to the visiting decl. if (CDecl == ND && !clang_isReference(CK)) return; // Filter any declarations that have a PCH level greater than what we allow. if (ND->getPCHLevel() > MaxPCHLevel) return; CXCursor C = { CK, ND, 0, 0 }; Callback(CDecl, C, CData); } public: CDeclVisitor(CXDecl C, CXDeclIterator cback, CXClientData D, unsigned MaxPCHLevel) : CDecl(C), Callback(cback), CData(D), MaxPCHLevel(MaxPCHLevel) {} void VisitDeclContext(DeclContext *DC); void VisitEnumConstantDecl(EnumConstantDecl *ND); void VisitFieldDecl(FieldDecl *ND); void VisitFunctionDecl(FunctionDecl *ND); void VisitObjCCategoryDecl(ObjCCategoryDecl *ND); void VisitObjCCategoryImplDecl(ObjCCategoryImplDecl *D); void VisitObjCImplementationDecl(ObjCImplementationDecl *D); void VisitObjCInterfaceDecl(ObjCInterfaceDecl *D); void VisitObjCIvarDecl(ObjCIvarDecl *ND); void VisitObjCMethodDecl(ObjCMethodDecl *ND); void VisitObjCPropertyDecl(ObjCPropertyDecl *ND); void VisitObjCProtocolDecl(ObjCProtocolDecl *PID); void VisitParmVarDecl(ParmVarDecl *ND); void VisitTagDecl(TagDecl *D); void VisitVarDecl(VarDecl *ND); }; } // end anonymous namespace void CDeclVisitor::VisitDeclContext(DeclContext *DC) { for (DeclContext::decl_iterator I = DC->decls_begin(), E = DC->decls_end(); I != E; ++I) Visit(*I); } void CDeclVisitor::VisitEnumConstantDecl(EnumConstantDecl *ND) { Call(CXCursor_EnumConstantDecl, ND); } void CDeclVisitor::VisitFieldDecl(FieldDecl *ND) { Call(CXCursor_FieldDecl, ND); } void CDeclVisitor::VisitFunctionDecl(FunctionDecl *ND) { if (ND->isThisDeclarationADefinition()) { VisitDeclContext(dyn_cast(ND)); #if 0 // Not currently needed. CompoundStmt *Body = dyn_cast(ND->getBody()); CRefVisitor RVisit(CDecl, Callback, CData); RVisit.Visit(Body); #endif } } void CDeclVisitor::VisitObjCCategoryDecl(ObjCCategoryDecl *ND) { // Issue callbacks for the containing class. Call(CXCursor_ObjCClassRef, ND); // FIXME: Issue callbacks for protocol refs. VisitDeclContext(dyn_cast(ND)); } void CDeclVisitor::VisitObjCCategoryImplDecl(ObjCCategoryImplDecl *D) { VisitDeclContext(dyn_cast(D)); } void CDeclVisitor::VisitObjCImplementationDecl(ObjCImplementationDecl *D) { VisitDeclContext(dyn_cast(D)); } void CDeclVisitor::VisitObjCInterfaceDecl(ObjCInterfaceDecl *D) { // Issue callbacks for super class. if (D->getSuperClass()) Call(CXCursor_ObjCSuperClassRef, D); for (ObjCProtocolDecl::protocol_iterator I = D->protocol_begin(), E = D->protocol_end(); I != E; ++I) Call(CXCursor_ObjCProtocolRef, *I); VisitDeclContext(dyn_cast(D)); } void CDeclVisitor::VisitObjCIvarDecl(ObjCIvarDecl *ND) { Call(CXCursor_ObjCIvarDecl, ND); } void CDeclVisitor::VisitObjCMethodDecl(ObjCMethodDecl *ND) { if (ND->getBody()) { Call(ND->isInstanceMethod() ? CXCursor_ObjCInstanceMethodDefn : CXCursor_ObjCClassMethodDefn, ND); VisitDeclContext(dyn_cast(ND)); } else Call(ND->isInstanceMethod() ? CXCursor_ObjCInstanceMethodDecl : CXCursor_ObjCClassMethodDecl, ND); } void CDeclVisitor::VisitObjCPropertyDecl(ObjCPropertyDecl *ND) { Call(CXCursor_ObjCPropertyDecl, ND); } void CDeclVisitor::VisitObjCProtocolDecl(ObjCProtocolDecl *PID) { for (ObjCProtocolDecl::protocol_iterator I = PID->protocol_begin(), E = PID->protocol_end(); I != E; ++I) Call(CXCursor_ObjCProtocolRef, *I); VisitDeclContext(dyn_cast(PID)); } void CDeclVisitor::VisitParmVarDecl(ParmVarDecl *ND) { Call(CXCursor_ParmDecl, ND); } void CDeclVisitor::VisitTagDecl(TagDecl *D) { VisitDeclContext(dyn_cast(D)); } void CDeclVisitor::VisitVarDecl(VarDecl *ND) { Call(CXCursor_VarDecl, ND); } static SourceLocation getLocationFromCursor(CXCursor C, SourceManager &SourceMgr, NamedDecl *ND) { if (clang_isReference(C.kind)) { switch (C.kind) { case CXCursor_ObjCClassRef: { if (isa(ND)) { // FIXME: This is a hack (storing the parent decl in the stmt slot). NamedDecl *parentDecl = static_cast(C.stmt); return parentDecl->getLocation(); } ObjCCategoryDecl *OID = dyn_cast(ND); assert(OID && "clang_getCursorLine(): Missing category decl"); return OID->getClassInterface()->getLocation(); } case CXCursor_ObjCSuperClassRef: { ObjCInterfaceDecl *OID = dyn_cast(ND); assert(OID && "clang_getCursorLine(): Missing interface decl"); return OID->getSuperClassLoc(); } case CXCursor_ObjCProtocolRef: { ObjCProtocolDecl *OID = dyn_cast(ND); assert(OID && "clang_getCursorLine(): Missing protocol decl"); return OID->getLocation(); } case CXCursor_ObjCSelectorRef: { ObjCMessageExpr *OME = dyn_cast( static_cast(C.stmt)); assert(OME && "clang_getCursorLine(): Missing message expr"); return OME->getLeftLoc(); /* FIXME: should be a range */ } case CXCursor_VarRef: case CXCursor_FunctionRef: case CXCursor_EnumConstantRef: { DeclRefExpr *DRE = dyn_cast( static_cast(C.stmt)); assert(DRE && "clang_getCursorLine(): Missing decl ref expr"); return DRE->getLocation(); } default: return SourceLocation(); } } else { // We have a declaration or a definition. SourceLocation SLoc; switch (ND->getKind()) { case Decl::ObjCInterface: { SLoc = dyn_cast(ND)->getClassLoc(); break; } case Decl::ObjCProtocol: { SLoc = ND->getLocation(); /* FIXME: need to get the name location. */ break; } default: { SLoc = ND->getLocation(); break; } } if (SLoc.isInvalid()) return SourceLocation(); return SourceMgr.getSpellingLoc(SLoc); // handles macro instantiations. } } CXString CIndexer::createCXString(const char *String, bool DupString){ CXString Str; if (DupString) { Str.Spelling = strdup(String); Str.MustFreeString = 1; } else { Str.Spelling = String; Str.MustFreeString = 0; } return Str; } extern "C" { CXIndex clang_createIndex(int excludeDeclarationsFromPCH, int displayDiagnostics) { CIndexer *CIdxr = new CIndexer(new Program()); if (excludeDeclarationsFromPCH) CIdxr->setOnlyLocalDecls(); if (displayDiagnostics) CIdxr->setDisplayDiagnostics(); return CIdxr; } void clang_disposeIndex(CXIndex CIdx) { assert(CIdx && "Passed null CXIndex"); delete static_cast(CIdx); } void clang_setUseExternalASTGeneration(CXIndex CIdx, int value) { assert(CIdx && "Passed null CXIndex"); CIndexer *CXXIdx = static_cast(CIdx); CXXIdx->setUseExternalASTGeneration(value); } // FIXME: need to pass back error info. CXTranslationUnit clang_createTranslationUnit(CXIndex CIdx, const char *ast_filename) { assert(CIdx && "Passed null CXIndex"); CIndexer *CXXIdx = static_cast(CIdx); return ASTUnit::LoadFromPCHFile(ast_filename, CXXIdx->getDiags(), CXXIdx->getOnlyLocalDecls(), /* UseBumpAllocator = */ true); } CXTranslationUnit clang_createTranslationUnitFromSourceFile(CXIndex CIdx, const char *source_filename, int num_command_line_args, const char **command_line_args) { assert(CIdx && "Passed null CXIndex"); CIndexer *CXXIdx = static_cast(CIdx); if (!CXXIdx->getUseExternalASTGeneration()) { llvm::SmallVector Args; // The 'source_filename' argument is optional. If the caller does not // specify it then it is assumed that the source file is specified // in the actual argument list. if (source_filename) Args.push_back(source_filename); Args.insert(Args.end(), command_line_args, command_line_args + num_command_line_args); unsigned NumErrors = CXXIdx->getDiags().getNumErrors(); #ifdef USE_CRASHTRACER ArgsCrashTracerInfo ACTI(Args); #endif llvm::OwningPtr Unit( ASTUnit::LoadFromCommandLine(Args.data(), Args.data() + Args.size(), CXXIdx->getDiags(), CXXIdx->getClangResourcesPath(), CXXIdx->getOnlyLocalDecls(), /* UseBumpAllocator = */ true)); // FIXME: Until we have broader testing, just drop the entire AST if we // encountered an error. if (NumErrors != CXXIdx->getDiags().getNumErrors()) return 0; return Unit.take(); } // Build up the arguments for invoking 'clang'. std::vector argv; // First add the complete path to the 'clang' executable. llvm::sys::Path ClangPath = static_cast(CIdx)->getClangPath(); argv.push_back(ClangPath.c_str()); // Add the '-emit-ast' option as our execution mode for 'clang'. argv.push_back("-emit-ast"); // The 'source_filename' argument is optional. If the caller does not // specify it then it is assumed that the source file is specified // in the actual argument list. if (source_filename) argv.push_back(source_filename); // Generate a temporary name for the AST file. argv.push_back("-o"); char astTmpFile[L_tmpnam]; argv.push_back(tmpnam(astTmpFile)); // Process the compiler options, stripping off '-o', '-c', '-fsyntax-only'. for (int i = 0; i < num_command_line_args; ++i) if (const char *arg = command_line_args[i]) { if (strcmp(arg, "-o") == 0) { ++i; // Also skip the matching argument. continue; } if (strcmp(arg, "-emit-ast") == 0 || strcmp(arg, "-c") == 0 || strcmp(arg, "-fsyntax-only") == 0) { continue; } // Keep the argument. argv.push_back(arg); } // Add the null terminator. argv.push_back(NULL); // Invoke 'clang'. llvm::sys::Path DevNull; // leave empty, causes redirection to /dev/null // on Unix or NUL (Windows). std::string ErrMsg; const llvm::sys::Path *Redirects[] = { &DevNull, &DevNull, &DevNull, NULL }; llvm::sys::Program::ExecuteAndWait(ClangPath, &argv[0], /* env */ NULL, /* redirects */ !CXXIdx->getDisplayDiagnostics() ? &Redirects[0] : NULL, /* secondsToWait */ 0, /* memoryLimits */ 0, &ErrMsg); if (CXXIdx->getDisplayDiagnostics() && !ErrMsg.empty()) { llvm::errs() << "clang_createTranslationUnitFromSourceFile: " << ErrMsg << '\n' << "Arguments: \n"; for (std::vector::iterator I = argv.begin(), E = argv.end(); I!=E; ++I) { if (*I) llvm::errs() << ' ' << *I << '\n'; } llvm::errs() << '\n'; } // Finally, we create the translation unit from the ast file. ASTUnit *ATU = static_cast( clang_createTranslationUnit(CIdx, astTmpFile)); if (ATU) ATU->unlinkTemporaryFile(); return ATU; } void clang_disposeTranslationUnit(CXTranslationUnit CTUnit) { assert(CTUnit && "Passed null CXTranslationUnit"); delete static_cast(CTUnit); } CXString clang_getTranslationUnitSpelling(CXTranslationUnit CTUnit) { assert(CTUnit && "Passed null CXTranslationUnit"); ASTUnit *CXXUnit = static_cast(CTUnit); return CIndexer::createCXString(CXXUnit->getOriginalSourceFileName().c_str(), true); } void clang_loadTranslationUnit(CXTranslationUnit CTUnit, CXTranslationUnitIterator callback, CXClientData CData) { assert(CTUnit && "Passed null CXTranslationUnit"); ASTUnit *CXXUnit = static_cast(CTUnit); ASTContext &Ctx = CXXUnit->getASTContext(); unsigned PCHLevel = Decl::MaxPCHLevel; // Set the PCHLevel to filter out unwanted decls if requested. if (CXXUnit->getOnlyLocalDecls()) { PCHLevel = 0; // If the main input was an AST, bump the level. if (CXXUnit->isMainFileAST()) ++PCHLevel; } TUVisitor DVisit(CTUnit, callback, CData, PCHLevel); // If using a non-AST based ASTUnit, iterate over the stored list of top-level // decls. if (!CXXUnit->isMainFileAST() && CXXUnit->getOnlyLocalDecls()) { const std::vector &TLDs = CXXUnit->getTopLevelDecls(); for (std::vector::const_iterator it = TLDs.begin(), ie = TLDs.end(); it != ie; ++it) { DVisit.Visit(*it); } } else DVisit.Visit(Ctx.getTranslationUnitDecl()); } void clang_loadDeclaration(CXDecl Dcl, CXDeclIterator callback, CXClientData CData) { assert(Dcl && "Passed null CXDecl"); CDeclVisitor DVisit(Dcl, callback, CData, static_cast(Dcl)->getPCHLevel()); DVisit.Visit(static_cast(Dcl)); } } // end: extern "C" //===----------------------------------------------------------------------===// // CXDecl Operations. //===----------------------------------------------------------------------===// static const FileEntry *getFileEntryFromSourceLocation(SourceManager &SMgr, SourceLocation SLoc) { FileID FID; if (SLoc.isFileID()) FID = SMgr.getFileID(SLoc); else FID = SMgr.getDecomposedSpellingLoc(SLoc).first; return SMgr.getFileEntryForID(FID); } extern "C" { CXString clang_getDeclSpelling(CXDecl AnonDecl) { assert(AnonDecl && "Passed null CXDecl"); NamedDecl *ND = static_cast(AnonDecl); if (ObjCMethodDecl *OMD = dyn_cast(ND)) return CIndexer::createCXString(OMD->getSelector().getAsString().c_str(), true); if (ObjCCategoryImplDecl *CIMP = dyn_cast(ND)) // No, this isn't the same as the code below. getIdentifier() is non-virtual // and returns different names. NamedDecl returns the class name and // ObjCCategoryImplDecl returns the category name. return CIndexer::createCXString(CIMP->getIdentifier()->getNameStart()); if (ND->getIdentifier()) return CIndexer::createCXString(ND->getIdentifier()->getNameStart()); return CIndexer::createCXString(""); } unsigned clang_getDeclLine(CXDecl AnonDecl) { assert(AnonDecl && "Passed null CXDecl"); NamedDecl *ND = static_cast(AnonDecl); SourceManager &SourceMgr = ND->getASTContext().getSourceManager(); return SourceMgr.getSpellingLineNumber(ND->getLocation()); } unsigned clang_getDeclColumn(CXDecl AnonDecl) { assert(AnonDecl && "Passed null CXDecl"); NamedDecl *ND = static_cast(AnonDecl); SourceManager &SourceMgr = ND->getASTContext().getSourceManager(); return SourceMgr.getSpellingColumnNumber(ND->getLocation()); } CXDeclExtent clang_getDeclExtent(CXDecl AnonDecl) { assert(AnonDecl && "Passed null CXDecl"); NamedDecl *ND = static_cast(AnonDecl); SourceManager &SM = ND->getASTContext().getSourceManager(); SourceRange R = ND->getSourceRange(); SourceLocation Begin = SM.getInstantiationLoc(R.getBegin()); SourceLocation End = SM.getInstantiationLoc(R.getEnd()); if (!Begin.isValid()) { CXDeclExtent extent = { { 0, 0 }, { 0, 0 } }; return extent; } // FIXME: This is largely copy-paste from ///TextDiagnosticPrinter::HighlightRange. When it is clear that this is // what we want the two routines should be refactored. // If the End location and the start location are the same and are a macro // location, then the range was something that came from a macro expansion // or _Pragma. If this is an object-like macro, the best we can do is to // get the range. If this is a function-like macro, we'd also like to // get the arguments. if (Begin == End && R.getEnd().isMacroID()) End = SM.getInstantiationRange(R.getEnd()).second; assert(SM.getFileID(Begin) == SM.getFileID(End)); unsigned StartLineNo = SM.getInstantiationLineNumber(Begin); unsigned EndLineNo = SM.getInstantiationLineNumber(End); // Compute the column number of the start. Keep the column based at 1. unsigned StartColNo = SM.getInstantiationColumnNumber(Begin); // Compute the column number of the end. unsigned EndColNo = SM.getInstantiationColumnNumber(End); if (EndColNo) { // Offset the end column by 1 so that we point to the last character // in the last token. --EndColNo; // Add in the length of the token, so that we cover multi-char tokens. ASTContext &Ctx = ND->getTranslationUnitDecl()->getASTContext(); const LangOptions &LOpts = Ctx.getLangOptions(); EndColNo += Lexer::MeasureTokenLength(End, SM, LOpts); } // Package up the line/column data and return to the caller. CXDeclExtent extent = { { StartLineNo, StartColNo }, { EndLineNo, EndColNo } }; return extent; } const char *clang_getDeclSource(CXDecl AnonDecl) { assert(AnonDecl && "Passed null CXDecl"); FileEntry *FEnt = static_cast(clang_getDeclSourceFile(AnonDecl)); assert(FEnt && "Cannot find FileEntry for Decl"); return clang_getFileName(FEnt); } CXFile clang_getDeclSourceFile(CXDecl AnonDecl) { assert(AnonDecl && "Passed null CXDecl"); NamedDecl *ND = static_cast(AnonDecl); SourceManager &SourceMgr = ND->getASTContext().getSourceManager(); return (void *)getFileEntryFromSourceLocation(SourceMgr, ND->getLocation()); } } // end: extern "C" //===----------------------------------------------------------------------===// // CXFile Operations. //===----------------------------------------------------------------------===// extern "C" { const char *clang_getFileName(CXFile SFile) { assert(SFile && "Passed null CXFile"); FileEntry *FEnt = static_cast(SFile); return FEnt->getName(); } time_t clang_getFileTime(CXFile SFile) { assert(SFile && "Passed null CXFile"); FileEntry *FEnt = static_cast(SFile); return FEnt->getModificationTime(); } } // end: extern "C" //===----------------------------------------------------------------------===// // CXCursor Operations. //===----------------------------------------------------------------------===// static enum CXCursorKind TranslateKind(Decl *D) { switch (D->getKind()) { case Decl::Function: return CXCursor_FunctionDecl; case Decl::Typedef: return CXCursor_TypedefDecl; case Decl::Enum: return CXCursor_EnumDecl; case Decl::EnumConstant: return CXCursor_EnumConstantDecl; case Decl::Record: return CXCursor_StructDecl; // FIXME: union/class case Decl::Field: return CXCursor_FieldDecl; case Decl::Var: return CXCursor_VarDecl; case Decl::ParmVar: return CXCursor_ParmDecl; case Decl::ObjCInterface: return CXCursor_ObjCInterfaceDecl; case Decl::ObjCCategory: return CXCursor_ObjCCategoryDecl; case Decl::ObjCProtocol: return CXCursor_ObjCProtocolDecl; case Decl::ObjCMethod: { ObjCMethodDecl *MD = dyn_cast(D); if (MD->isInstanceMethod()) return CXCursor_ObjCInstanceMethodDecl; return CXCursor_ObjCClassMethodDecl; } default: break; } return CXCursor_NotImplemented; } static CXCursor MakeCXCursor(CXCursorKind K, Decl *D) { CXCursor C = { K, D, 0, 0 }; return C; } static CXCursor MakeCXCursor(CXCursorKind K, Decl *D, Stmt *S) { assert(clang_isReference(K)); CXCursor C = { K, D, S, 0 }; return C; } static Decl *getDeclFromExpr(Stmt *E) { if (DeclRefExpr *RefExpr = dyn_cast(E)) return RefExpr->getDecl(); if (MemberExpr *ME = dyn_cast(E)) return ME->getMemberDecl(); if (ObjCIvarRefExpr *RE = dyn_cast(E)) return RE->getDecl(); if (CallExpr *CE = dyn_cast(E)) return getDeclFromExpr(CE->getCallee()); if (CastExpr *CE = dyn_cast(E)) return getDeclFromExpr(CE->getSubExpr()); if (ObjCMessageExpr *OME = dyn_cast(E)) return OME->getMethodDecl(); return 0; } extern "C" { CXString clang_getCursorSpelling(CXCursor C) { assert(C.decl && "CXCursor has null decl"); NamedDecl *ND = static_cast(C.decl); if (clang_isReference(C.kind)) { switch (C.kind) { case CXCursor_ObjCSuperClassRef: { ObjCInterfaceDecl *OID = dyn_cast(ND); assert(OID && "clang_getCursorLine(): Missing interface decl"); return CIndexer::createCXString(OID->getSuperClass()->getIdentifier() ->getNameStart()); } case CXCursor_ObjCClassRef: { if (ObjCInterfaceDecl *OID = dyn_cast(ND)) return CIndexer::createCXString(OID->getIdentifier()->getNameStart()); ObjCCategoryDecl *OCD = dyn_cast(ND); assert(OCD && "clang_getCursorLine(): Missing category decl"); return CIndexer::createCXString(OCD->getClassInterface()->getIdentifier() ->getNameStart()); } case CXCursor_ObjCProtocolRef: { ObjCProtocolDecl *OID = dyn_cast(ND); assert(OID && "clang_getCursorLine(): Missing protocol decl"); return CIndexer::createCXString(OID->getIdentifier()->getNameStart()); } case CXCursor_ObjCSelectorRef: { ObjCMessageExpr *OME = dyn_cast( static_cast(C.stmt)); assert(OME && "clang_getCursorLine(): Missing message expr"); return CIndexer::createCXString(OME->getSelector().getAsString().c_str(), true); } case CXCursor_VarRef: case CXCursor_FunctionRef: case CXCursor_EnumConstantRef: { DeclRefExpr *DRE = dyn_cast( static_cast(C.stmt)); assert(DRE && "clang_getCursorLine(): Missing decl ref expr"); return CIndexer::createCXString(DRE->getDecl()->getIdentifier() ->getNameStart()); } default: return CIndexer::createCXString(""); } } return clang_getDeclSpelling(C.decl); } const char *clang_getCursorKindSpelling(enum CXCursorKind Kind) { switch (Kind) { case CXCursor_FunctionDecl: return "FunctionDecl"; case CXCursor_TypedefDecl: return "TypedefDecl"; case CXCursor_EnumDecl: return "EnumDecl"; case CXCursor_EnumConstantDecl: return "EnumConstantDecl"; case CXCursor_StructDecl: return "StructDecl"; case CXCursor_UnionDecl: return "UnionDecl"; case CXCursor_ClassDecl: return "ClassDecl"; case CXCursor_FieldDecl: return "FieldDecl"; case CXCursor_VarDecl: return "VarDecl"; case CXCursor_ParmDecl: return "ParmDecl"; case CXCursor_ObjCInterfaceDecl: return "ObjCInterfaceDecl"; case CXCursor_ObjCCategoryDecl: return "ObjCCategoryDecl"; case CXCursor_ObjCProtocolDecl: return "ObjCProtocolDecl"; case CXCursor_ObjCPropertyDecl: return "ObjCPropertyDecl"; case CXCursor_ObjCIvarDecl: return "ObjCIvarDecl"; case CXCursor_ObjCInstanceMethodDecl: return "ObjCInstanceMethodDecl"; case CXCursor_ObjCClassMethodDecl: return "ObjCClassMethodDecl"; case CXCursor_FunctionDefn: return "FunctionDefn"; case CXCursor_ObjCInstanceMethodDefn: return "ObjCInstanceMethodDefn"; case CXCursor_ObjCClassMethodDefn: return "ObjCClassMethodDefn"; case CXCursor_ObjCClassDefn: return "ObjCClassDefn"; case CXCursor_ObjCCategoryDefn: return "ObjCCategoryDefn"; case CXCursor_ObjCSuperClassRef: return "ObjCSuperClassRef"; case CXCursor_ObjCProtocolRef: return "ObjCProtocolRef"; case CXCursor_ObjCClassRef: return "ObjCClassRef"; case CXCursor_ObjCSelectorRef: return "ObjCSelectorRef"; case CXCursor_VarRef: return "VarRef"; case CXCursor_FunctionRef: return "FunctionRef"; case CXCursor_EnumConstantRef: return "EnumConstantRef"; case CXCursor_MemberRef: return "MemberRef"; case CXCursor_InvalidFile: return "InvalidFile"; case CXCursor_NoDeclFound: return "NoDeclFound"; case CXCursor_NotImplemented: return "NotImplemented"; default: return ""; } } CXCursor clang_getCursor(CXTranslationUnit CTUnit, const char *source_name, unsigned line, unsigned column) { assert(CTUnit && "Passed null CXTranslationUnit"); ASTUnit *CXXUnit = static_cast(CTUnit); FileManager &FMgr = CXXUnit->getFileManager(); const FileEntry *File = FMgr.getFile(source_name, source_name+strlen(source_name)); if (!File) return clang_getNullCursor(); SourceLocation SLoc = CXXUnit->getSourceManager().getLocation(File, line, column); ASTLocation LastLoc = CXXUnit->getLastASTLocation(); ASTLocation ALoc = ResolveLocationInAST(CXXUnit->getASTContext(), SLoc, &LastLoc); // FIXME: This doesn't look thread-safe. if (ALoc.isValid()) CXXUnit->setLastASTLocation(ALoc); Decl *Dcl = ALoc.getParentDecl(); if (ALoc.isNamedRef()) Dcl = ALoc.AsNamedRef().ND; Stmt *Stm = ALoc.dyn_AsStmt(); if (Dcl) { if (Stm) { if (DeclRefExpr *DRE = dyn_cast(Stm)) return MakeCXCursor(TranslateDeclRefExpr(DRE), Dcl, Stm); else if (ObjCMessageExpr *MExp = dyn_cast(Stm)) return MakeCXCursor(CXCursor_ObjCSelectorRef, Dcl, MExp); // Fall through...treat as a decl, not a ref. } if (ALoc.isNamedRef()) { if (isa(Dcl)) { CXCursor C = { CXCursor_ObjCClassRef, Dcl, ALoc.getParentDecl(), 0 }; return C; } if (isa(Dcl)) { CXCursor C = { CXCursor_ObjCProtocolRef, Dcl, ALoc.getParentDecl(), 0 }; return C; } } return MakeCXCursor(TranslateKind(Dcl), Dcl); } return MakeCXCursor(CXCursor_NoDeclFound, 0); } CXCursor clang_getNullCursor(void) { return MakeCXCursor(CXCursor_InvalidFile, 0); } unsigned clang_equalCursors(CXCursor X, CXCursor Y) { return X.kind == Y.kind && X.decl == Y.decl && X.stmt == Y.stmt && X.referringDecl == Y.referringDecl; } CXCursor clang_getCursorFromDecl(CXDecl AnonDecl) { assert(AnonDecl && "Passed null CXDecl"); NamedDecl *ND = static_cast(AnonDecl); return MakeCXCursor(TranslateKind(ND), ND); } unsigned clang_isInvalid(enum CXCursorKind K) { return K >= CXCursor_FirstInvalid && K <= CXCursor_LastInvalid; } unsigned clang_isDeclaration(enum CXCursorKind K) { return K >= CXCursor_FirstDecl && K <= CXCursor_LastDecl; } unsigned clang_isReference(enum CXCursorKind K) { return K >= CXCursor_FirstRef && K <= CXCursor_LastRef; } unsigned clang_isDefinition(enum CXCursorKind K) { return K >= CXCursor_FirstDefn && K <= CXCursor_LastDefn; } CXCursorKind clang_getCursorKind(CXCursor C) { return C.kind; } CXDecl clang_getCursorDecl(CXCursor C) { if (clang_isDeclaration(C.kind)) return C.decl; if (clang_isReference(C.kind)) { if (C.stmt) { if (C.kind == CXCursor_ObjCClassRef || C.kind == CXCursor_ObjCProtocolRef) return static_cast(C.stmt); else return getDeclFromExpr(static_cast(C.stmt)); } else return C.decl; } return 0; } unsigned clang_getCursorLine(CXCursor C) { assert(C.decl && "CXCursor has null decl"); NamedDecl *ND = static_cast(C.decl); SourceManager &SourceMgr = ND->getASTContext().getSourceManager(); SourceLocation SLoc = getLocationFromCursor(C, SourceMgr, ND); return SourceMgr.getSpellingLineNumber(SLoc); } unsigned clang_getCursorColumn(CXCursor C) { assert(C.decl && "CXCursor has null decl"); NamedDecl *ND = static_cast(C.decl); SourceManager &SourceMgr = ND->getASTContext().getSourceManager(); SourceLocation SLoc = getLocationFromCursor(C, SourceMgr, ND); return SourceMgr.getSpellingColumnNumber(SLoc); } const char *clang_getCursorSource(CXCursor C) { assert(C.decl && "CXCursor has null decl"); NamedDecl *ND = static_cast(C.decl); SourceManager &SourceMgr = ND->getASTContext().getSourceManager(); SourceLocation SLoc = getLocationFromCursor(C, SourceMgr, ND); if (SLoc.isFileID()) { const char *bufferName = SourceMgr.getBufferName(SLoc); return bufferName[0] == '<' ? NULL : bufferName; } // Retrieve the file in which the macro was instantiated, then provide that // buffer name. // FIXME: Do we want to give specific macro-instantiation information? const llvm::MemoryBuffer *Buffer = SourceMgr.getBuffer(SourceMgr.getDecomposedSpellingLoc(SLoc).first); if (!Buffer) return 0; return Buffer->getBufferIdentifier(); } CXFile clang_getCursorSourceFile(CXCursor C) { assert(C.decl && "CXCursor has null decl"); NamedDecl *ND = static_cast(C.decl); SourceManager &SourceMgr = ND->getASTContext().getSourceManager(); return (void *) getFileEntryFromSourceLocation(SourceMgr, getLocationFromCursor(C,SourceMgr, ND)); } void clang_getDefinitionSpellingAndExtent(CXCursor C, const char **startBuf, const char **endBuf, unsigned *startLine, unsigned *startColumn, unsigned *endLine, unsigned *endColumn) { assert(C.decl && "CXCursor has null decl"); NamedDecl *ND = static_cast(C.decl); FunctionDecl *FD = dyn_cast(ND); CompoundStmt *Body = dyn_cast(FD->getBody()); SourceManager &SM = FD->getASTContext().getSourceManager(); *startBuf = SM.getCharacterData(Body->getLBracLoc()); *endBuf = SM.getCharacterData(Body->getRBracLoc()); *startLine = SM.getSpellingLineNumber(Body->getLBracLoc()); *startColumn = SM.getSpellingColumnNumber(Body->getLBracLoc()); *endLine = SM.getSpellingLineNumber(Body->getRBracLoc()); *endColumn = SM.getSpellingColumnNumber(Body->getRBracLoc()); } } // end: extern "C" //===----------------------------------------------------------------------===// // CXString Operations. //===----------------------------------------------------------------------===// extern "C" { const char *clang_getCString(CXString string) { return string.Spelling; } void clang_disposeString(CXString string) { if (string.MustFreeString && string.Spelling) free((void*)string.Spelling); } } // end: extern "C"