diff options
author | rdivacky <rdivacky@FreeBSD.org> | 2010-05-04 16:12:48 +0000 |
---|---|---|
committer | rdivacky <rdivacky@FreeBSD.org> | 2010-05-04 16:12:48 +0000 |
commit | 8aaf5818a64e9f7687798852af5945b053c68a54 (patch) | |
tree | d6a70c3518b8dea8be7062438d7e8676820ed17f /tools | |
parent | 71438373cd57f0d5d8c93bb5cf690844a0fbc9d0 (diff) | |
download | FreeBSD-src-8aaf5818a64e9f7687798852af5945b053c68a54.zip FreeBSD-src-8aaf5818a64e9f7687798852af5945b053c68a54.tar.gz |
Update clang to r103004.
Diffstat (limited to 'tools')
37 files changed, 5632 insertions, 163 deletions
diff --git a/tools/CIndex/CIndex.cpp b/tools/CIndex/CIndex.cpp index 01130e2..9cdb965 100644 --- a/tools/CIndex/CIndex.cpp +++ b/tools/CIndex/CIndex.cpp @@ -22,8 +22,12 @@ #include "clang/AST/DeclVisitor.h" #include "clang/AST/StmtVisitor.h" #include "clang/AST/TypeLocVisitor.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Lex/Lexer.h" +#include "clang/Lex/PreprocessingRecord.h" #include "clang/Lex/Preprocessor.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/System/Program.h" @@ -35,7 +39,6 @@ using namespace clang; using namespace clang::cxcursor; using namespace clang::cxstring; -using namespace idx; //===----------------------------------------------------------------------===// // Crash Reporting. @@ -242,6 +245,7 @@ public: // Declaration visitors bool VisitAttributes(Decl *D); + bool VisitBlockDecl(BlockDecl *B); bool VisitDeclContext(DeclContext *DC); bool VisitTranslationUnitDecl(TranslationUnitDecl *D); bool VisitTypedefDecl(TypedefDecl *D); @@ -297,10 +301,12 @@ public: bool VisitForStmt(ForStmt *S); // Expression visitors - bool VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *E); - bool VisitExplicitCastExpr(ExplicitCastExpr *E); + bool VisitBlockExpr(BlockExpr *B); bool VisitCompoundLiteralExpr(CompoundLiteralExpr *E); + bool VisitExplicitCastExpr(ExplicitCastExpr *E); bool VisitObjCMessageExpr(ObjCMessageExpr *E); + bool VisitObjCEncodeExpr(ObjCEncodeExpr *E); + bool VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *E); }; } // end anonymous namespace @@ -484,6 +490,15 @@ bool CursorVisitor::VisitChildren(CXCursor Cursor) { return false; } +bool CursorVisitor::VisitBlockDecl(BlockDecl *B) { + for (BlockDecl::param_iterator I=B->param_begin(), E=B->param_end(); I!=E;++I) + if (Decl *D = *I) + if (Visit(D)) + return true; + + return Visit(MakeCXCursor(B->getBody(), StmtParent, TU)); +} + bool CursorVisitor::VisitDeclContext(DeclContext *DC) { for (DeclContext::decl_iterator I = DC->decls_begin(), E = DC->decls_end(); I != E; ++I) { @@ -814,8 +829,9 @@ bool CursorVisitor::VisitFunctionTypeLoc(FunctionTypeLoc TL) { return true; for (unsigned I = 0, N = TL.getNumArgs(); I != N; ++I) - if (Visit(MakeCXCursor(TL.getArg(I), TU))) - return true; + if (Decl *D = TL.getArg(I)) + if (Visit(MakeCXCursor(D, TU))) + return true; return false; } @@ -923,6 +939,10 @@ bool CursorVisitor::VisitForStmt(ForStmt *S) { return false; } +bool CursorVisitor::VisitBlockExpr(BlockExpr *B) { + return Visit(B->getBlockDecl()); +} + bool CursorVisitor::VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *E) { if (E->isArgumentType()) { if (TypeSourceInfo *TSInfo = E->getArgumentTypeInfo()) @@ -951,13 +971,18 @@ bool CursorVisitor::VisitCompoundLiteralExpr(CompoundLiteralExpr *E) { } bool CursorVisitor::VisitObjCMessageExpr(ObjCMessageExpr *E) { - ObjCMessageExpr::ClassInfo CI = E->getClassInfo(); - if (CI.Decl && Visit(MakeCursorObjCClassRef(CI.Decl, CI.Loc, TU))) - return true; + if (TypeSourceInfo *TSInfo = E->getClassReceiverTypeInfo()) + if (Visit(TSInfo->getTypeLoc())) + return true; return VisitExpr(E); } +bool CursorVisitor::VisitObjCEncodeExpr(ObjCEncodeExpr *E) { + return Visit(E->getEncodedTypeSourceInfo()->getTypeLoc()); +} + + bool CursorVisitor::VisitAttributes(Decl *D) { for (const Attr *A = D->getAttrs(); A; A = A->getNext()) if (Visit(MakeCXCursor(A, D, TU))) @@ -1539,6 +1564,8 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) { return createCXString("ObjCImplementationDecl"); case CXCursor_ObjCCategoryImplDecl: return createCXString("ObjCCategoryImplDecl"); + case CXCursor_CXXMethod: + return createCXString("CXXMethod"); case CXCursor_UnexposedDecl: return createCXString("UnexposedDecl"); case CXCursor_ObjCSuperClassRef: @@ -1551,6 +1578,8 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) { return createCXString("TypeRef"); case CXCursor_UnexposedExpr: return createCXString("UnexposedExpr"); + case CXCursor_BlockExpr: + return createCXString("BlockExpr"); case CXCursor_DeclRefExpr: return createCXString("DeclRefExpr"); case CXCursor_MemberRefExpr: @@ -1892,8 +1921,6 @@ CXCursor clang_getCursorDefinition(CXCursor C) { // Declaration kinds that don't make any sense here, but are // nonetheless harmless. case Decl::TranslationUnit: - case Decl::Template: - case Decl::ObjCContainer: break; // Declaration kinds for which the definition is not resolvable. @@ -2444,6 +2471,67 @@ CXLinkageKind clang_getCursorLinkage(CXCursor cursor) { } // end: extern "C" //===----------------------------------------------------------------------===// +// Operations for querying language of a cursor. +//===----------------------------------------------------------------------===// + +static CXLanguageKind getDeclLanguage(const Decl *D) { + switch (D->getKind()) { + default: + break; + case Decl::ImplicitParam: + case Decl::ObjCAtDefsField: + case Decl::ObjCCategory: + case Decl::ObjCCategoryImpl: + case Decl::ObjCClass: + case Decl::ObjCCompatibleAlias: + case Decl::ObjCForwardProtocol: + case Decl::ObjCImplementation: + case Decl::ObjCInterface: + case Decl::ObjCIvar: + case Decl::ObjCMethod: + case Decl::ObjCProperty: + case Decl::ObjCPropertyImpl: + case Decl::ObjCProtocol: + return CXLanguage_ObjC; + case Decl::CXXConstructor: + case Decl::CXXConversion: + case Decl::CXXDestructor: + case Decl::CXXMethod: + case Decl::CXXRecord: + case Decl::ClassTemplate: + case Decl::ClassTemplatePartialSpecialization: + case Decl::ClassTemplateSpecialization: + case Decl::Friend: + case Decl::FriendTemplate: + case Decl::FunctionTemplate: + case Decl::LinkageSpec: + case Decl::Namespace: + case Decl::NamespaceAlias: + case Decl::NonTypeTemplateParm: + case Decl::StaticAssert: + case Decl::TemplateTemplateParm: + case Decl::TemplateTypeParm: + case Decl::UnresolvedUsingTypename: + case Decl::UnresolvedUsingValue: + case Decl::Using: + case Decl::UsingDirective: + case Decl::UsingShadow: + return CXLanguage_CPlusPlus; + } + + return CXLanguage_C; +} + +extern "C" { +CXLanguageKind clang_getCursorLanguage(CXCursor cursor) { + if (clang_isDeclaration(cursor.kind)) + return getDeclLanguage(cxcursor::getCursorDecl(cursor)); + + return CXLanguage_Invalid; +} +} // end: extern "C" + +//===----------------------------------------------------------------------===// // CXString Operations. //===----------------------------------------------------------------------===// diff --git a/tools/CIndex/CIndex.darwin.exports b/tools/CIndex/CIndex.darwin.exports new file mode 100644 index 0000000..b361168 --- /dev/null +++ b/tools/CIndex/CIndex.darwin.exports @@ -0,0 +1,81 @@ +_clang_annotateTokens +_clang_codeComplete +_clang_codeCompleteGetDiagnostic +_clang_codeCompleteGetNumDiagnostics +_clang_constructUSR_ObjCCategory +_clang_constructUSR_ObjCClass +_clang_constructUSR_ObjCIvar +_clang_constructUSR_ObjCMethod +_clang_constructUSR_ObjCProperty +_clang_constructUSR_ObjCProtocol +_clang_createIndex +_clang_createTranslationUnit +_clang_createTranslationUnitFromSourceFile +_clang_defaultDiagnosticDisplayOptions +_clang_disposeCodeCompleteResults +_clang_disposeDiagnostic +_clang_disposeIndex +_clang_disposeString +_clang_disposeTokens +_clang_disposeTranslationUnit +_clang_enableStackTraces +_clang_equalCursors +_clang_equalLocations +_clang_formatDiagnostic +_clang_getCString +_clang_getClangVersion +_clang_getCompletionChunkCompletionString +_clang_getCompletionChunkKind +_clang_getCompletionChunkText +_clang_getCursor +_clang_getCursorDefinition +_clang_getCursorExtent +_clang_getCursorKind +_clang_getCursorKindSpelling +_clang_getCursorLanguage +_clang_getCursorLinkage +_clang_getCursorLocation +_clang_getCursorReferenced +_clang_getCursorSpelling +_clang_getCursorUSR +_clang_getDefinitionSpellingAndExtent +_clang_getDiagnostic +_clang_getDiagnosticFixIt +_clang_getDiagnosticLocation +_clang_getDiagnosticNumFixIts +_clang_getDiagnosticNumRanges +_clang_getDiagnosticRange +_clang_getDiagnosticSeverity +_clang_getDiagnosticSpelling +_clang_getFile +_clang_getFileName +_clang_getFileTime +_clang_getInclusions +_clang_getInstantiationLocation +_clang_getLocation +_clang_getNullCursor +_clang_getNullLocation +_clang_getNullRange +_clang_getNumCompletionChunks +_clang_getNumDiagnostics +_clang_getRange +_clang_getRangeEnd +_clang_getRangeStart +_clang_getTokenExtent +_clang_getTokenKind +_clang_getTokenLocation +_clang_getTokenSpelling +_clang_getTranslationUnitCursor +_clang_getTranslationUnitSpelling +_clang_isCursorDefinition +_clang_isDeclaration +_clang_isExpression +_clang_isInvalid +_clang_isPreprocessing +_clang_isReference +_clang_isStatement +_clang_isTranslationUnit +_clang_isUnexposed +_clang_setUseExternalASTGeneration +_clang_tokenize +_clang_visitChildren diff --git a/tools/CIndex/CIndex.exports b/tools/CIndex/CIndex.exports index d036e5c..991bb06 100644 --- a/tools/CIndex/CIndex.exports +++ b/tools/CIndex/CIndex.exports @@ -1,80 +1,81 @@ -_clang_annotateTokens -_clang_codeComplete -_clang_codeCompleteGetDiagnostic -_clang_codeCompleteGetNumDiagnostics -_clang_constructUSR_ObjCClass -_clang_constructUSR_ObjCCategory -_clang_constructUSR_ObjCIvar -_clang_constructUSR_ObjCMethod -_clang_constructUSR_ObjCProtocol -_clang_constructUSR_ObjCProperty -_clang_createIndex -_clang_createTranslationUnit -_clang_createTranslationUnitFromSourceFile -_clang_defaultDiagnosticDisplayOptions -_clang_disposeCodeCompleteResults -_clang_disposeDiagnostic -_clang_disposeIndex -_clang_disposeString -_clang_disposeTokens -_clang_disposeTranslationUnit -_clang_enableStackTraces -_clang_equalCursors -_clang_equalLocations -_clang_formatDiagnostic -_clang_getClangVersion -_clang_getCString -_clang_getCompletionChunkCompletionString -_clang_getCompletionChunkKind -_clang_getCompletionChunkText -_clang_getCursor -_clang_getCursorDefinition -_clang_getCursorExtent -_clang_getCursorKind -_clang_getCursorKindSpelling -_clang_getCursorLinkage -_clang_getCursorLocation -_clang_getCursorReferenced -_clang_getCursorSpelling -_clang_getCursorUSR -_clang_getDefinitionSpellingAndExtent -_clang_getDiagnostic -_clang_getDiagnosticFixIt -_clang_getDiagnosticLocation -_clang_getDiagnosticNumFixIts -_clang_getDiagnosticNumRanges -_clang_getDiagnosticRange -_clang_getDiagnosticSeverity -_clang_getDiagnosticSpelling -_clang_getFile -_clang_getFileName -_clang_getFileTime -_clang_getInclusions -_clang_getInstantiationLocation -_clang_getLocation -_clang_getNullCursor -_clang_getNullLocation -_clang_getNullRange -_clang_getNumCompletionChunks -_clang_getNumDiagnostics -_clang_getRange -_clang_getRangeEnd -_clang_getRangeStart -_clang_getTokenExtent -_clang_getTokenKind -_clang_getTokenLocation -_clang_getTokenSpelling -_clang_getTranslationUnitCursor -_clang_getTranslationUnitSpelling -_clang_isCursorDefinition -_clang_isDeclaration -_clang_isExpression -_clang_isInvalid -_clang_isPreprocessing -_clang_isReference -_clang_isStatement -_clang_isTranslationUnit -_clang_isUnexposed -_clang_setUseExternalASTGeneration -_clang_tokenize -_clang_visitChildren +clang_annotateTokens +clang_codeComplete +clang_codeCompleteGetDiagnostic +clang_codeCompleteGetNumDiagnostics +clang_constructUSR_ObjCCategory +clang_constructUSR_ObjCClass +clang_constructUSR_ObjCIvar +clang_constructUSR_ObjCMethod +clang_constructUSR_ObjCProperty +clang_constructUSR_ObjCProtocol +clang_createIndex +clang_createTranslationUnit +clang_createTranslationUnitFromSourceFile +clang_defaultDiagnosticDisplayOptions +clang_disposeCodeCompleteResults +clang_disposeDiagnostic +clang_disposeIndex +clang_disposeString +clang_disposeTokens +clang_disposeTranslationUnit +clang_enableStackTraces +clang_equalCursors +clang_equalLocations +clang_formatDiagnostic +clang_getCString +clang_getClangVersion +clang_getCompletionChunkCompletionString +clang_getCompletionChunkKind +clang_getCompletionChunkText +clang_getCursor +clang_getCursorDefinition +clang_getCursorExtent +clang_getCursorKind +clang_getCursorKindSpelling +clang_getCursorLanguage +clang_getCursorLinkage +clang_getCursorLocation +clang_getCursorReferenced +clang_getCursorSpelling +clang_getCursorUSR +clang_getDefinitionSpellingAndExtent +clang_getDiagnostic +clang_getDiagnosticFixIt +clang_getDiagnosticLocation +clang_getDiagnosticNumFixIts +clang_getDiagnosticNumRanges +clang_getDiagnosticRange +clang_getDiagnosticSeverity +clang_getDiagnosticSpelling +clang_getFile +clang_getFileName +clang_getFileTime +clang_getInclusions +clang_getInstantiationLocation +clang_getLocation +clang_getNullCursor +clang_getNullLocation +clang_getNullRange +clang_getNumCompletionChunks +clang_getNumDiagnostics +clang_getRange +clang_getRangeEnd +clang_getRangeStart +clang_getTokenExtent +clang_getTokenKind +clang_getTokenLocation +clang_getTokenSpelling +clang_getTranslationUnitCursor +clang_getTranslationUnitSpelling +clang_isCursorDefinition +clang_isDeclaration +clang_isExpression +clang_isInvalid +clang_isPreprocessing +clang_isReference +clang_isStatement +clang_isTranslationUnit +clang_isUnexposed +clang_setUseExternalASTGeneration +clang_tokenize +clang_visitChildren diff --git a/tools/CIndex/CIndexCodeCompletion.cpp b/tools/CIndex/CIndexCodeCompletion.cpp index d11b8df..a21614c 100644 --- a/tools/CIndex/CIndexCodeCompletion.cpp +++ b/tools/CIndex/CIndexCodeCompletion.cpp @@ -14,12 +14,26 @@ #include "CIndexer.h" #include "CIndexDiagnostic.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/FileManager.h" +#include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Sema/CodeCompleteConsumer.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/System/Program.h" +#ifdef UDP_CODE_COMPLETION_LOGGER +#include "clang/Basic/Version.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/raw_ostream.h" +#include <arpa/inet.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <unistd.h> +#endif + using namespace clang; using namespace clang::cxstring; @@ -226,6 +240,12 @@ CXCodeCompleteResults *clang_codeComplete(CXIndex CIdx, const char *complete_filename, unsigned complete_line, unsigned complete_column) { +#ifdef UDP_CODE_COMPLETION_LOGGER +#ifdef UDP_CODE_COMPLETION_LOGGER_PORT + const llvm::TimeRecord &StartTime = llvm::TimeRecord::getCurrentTime(); +#endif +#endif + // The indexer, which is mainly used to determine where diagnostics go. CIndexer *CXXIdx = static_cast<CIndexer *>(CIdx); @@ -383,6 +403,78 @@ CXCodeCompleteResults *clang_codeComplete(CXIndex CIdx, // destroyed. Results->TemporaryFiles.swap(TemporaryFiles); +#ifdef UDP_CODE_COMPLETION_LOGGER +#ifdef UDP_CODE_COMPLETION_LOGGER_PORT + const llvm::TimeRecord &EndTime = llvm::TimeRecord::getCurrentTime(); + llvm::SmallString<256> LogResult; + llvm::raw_svector_ostream os(LogResult); + + // Figure out the language and whether or not it uses PCH. + const char *lang = 0; + bool usesPCH = false; + + for (std::vector<const char*>::iterator I = argv.begin(), E = argv.end(); + I != E; ++I) { + if (*I == 0) + continue; + if (strcmp(*I, "-x") == 0) { + if (I + 1 != E) { + lang = *(++I); + continue; + } + } + else if (strcmp(*I, "-include") == 0) { + if (I+1 != E) { + const char *arg = *(++I); + llvm::SmallString<512> pchName; + { + llvm::raw_svector_ostream os(pchName); + os << arg << ".pth"; + } + pchName.push_back('\0'); + struct stat stat_results; + if (stat(pchName.data(), &stat_results) == 0) + usesPCH = true; + continue; + } + } + } + + os << "{ "; + os << "\"wall\": " << (EndTime.getWallTime() - StartTime.getWallTime()); + os << ", \"numRes\": " << Results->NumResults; + os << ", \"diags\": " << Results->Diagnostics.size(); + os << ", \"pch\": " << (usesPCH ? "true" : "false"); + os << ", \"lang\": \"" << (lang ? lang : "<unknown>") << '"'; + const char *name = getlogin(); + os << ", \"user\": \"" << (name ? name : "unknown") << '"'; + os << ", \"clangVer\": \"" << getClangFullVersion() << '"'; + os << " }"; + + llvm::StringRef res = os.str(); + if (res.size() > 0) { + do { + // Setup the UDP socket. + struct sockaddr_in servaddr; + bzero(&servaddr, sizeof(servaddr)); + servaddr.sin_family = AF_INET; + servaddr.sin_port = htons(UDP_CODE_COMPLETION_LOGGER_PORT); + if (inet_pton(AF_INET, UDP_CODE_COMPLETION_LOGGER, + &servaddr.sin_addr) <= 0) + break; + + int sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) + break; + + sendto(sockfd, res.data(), res.size(), 0, + (struct sockaddr *)&servaddr, sizeof(servaddr)); + close(sockfd); + } + while (false); + } +#endif +#endif return Results; } diff --git a/tools/CIndex/CIndexDiagnostic.cpp b/tools/CIndex/CIndexDiagnostic.cpp index bcf066d..3db37b9 100644 --- a/tools/CIndex/CIndexDiagnostic.cpp +++ b/tools/CIndex/CIndexDiagnostic.cpp @@ -14,6 +14,7 @@ #include "CIndexer.h" #include "CXSourceLocation.h" +#include "clang/Frontend/ASTUnit.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/Twine.h" diff --git a/tools/CIndex/CIndexDiagnostic.h b/tools/CIndex/CIndexDiagnostic.h index 79a5df0..919c21c 100644 --- a/tools/CIndex/CIndexDiagnostic.h +++ b/tools/CIndex/CIndexDiagnostic.h @@ -13,20 +13,21 @@ #ifndef LLVM_CLANG_CINDEX_DIAGNOSTIC_H #define LLVM_CLANG_CINDEX_DIAGNOSTIC_H -#include "clang-c/Index.h" -#include "clang/Basic/Diagnostic.h" -#include "clang/Basic/LangOptions.h" -#include "llvm/ADT/SmallVector.h" +struct CXUnsavedFile; -namespace llvm { namespace sys { -class Path; -} } +namespace llvm { +template<typename T> class SmallVectorImpl; +namespace sys { class Path; } +} namespace clang { class Diagnostic; +class FileManager; class LangOptions; class Preprocessor; +class StoredDiagnostic; +class SourceManager; /// \brief The storage behind a CXDiagnostic struct CXStoredDiagnostic { diff --git a/tools/CIndex/CIndexInclusionStack.cpp b/tools/CIndex/CIndexInclusionStack.cpp index 4a884e5..e863239 100644 --- a/tools/CIndex/CIndexInclusionStack.cpp +++ b/tools/CIndex/CIndexInclusionStack.cpp @@ -15,8 +15,10 @@ #include "CIndexer.h" #include "CXSourceLocation.h" #include "clang/AST/DeclVisitor.h" +#include "clang/Frontend/ASTUnit.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" +using namespace clang; extern "C" { void clang_getInclusions(CXTranslationUnit TU, CXInclusionVisitor CB, diff --git a/tools/CIndex/CIndexUSRs.cpp b/tools/CIndex/CIndexUSRs.cpp index 379320c..58870b9 100644 --- a/tools/CIndex/CIndexUSRs.cpp +++ b/tools/CIndex/CIndexUSRs.cpp @@ -14,9 +14,12 @@ #include "CIndexer.h" #include "CXCursor.h" #include "clang/AST/DeclVisitor.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Lex/PreprocessingRecord.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/raw_ostream.h" +using namespace clang; using namespace clang::cxstring; //===----------------------------------------------------------------------===// @@ -27,8 +30,10 @@ namespace { class USRGenerator : public DeclVisitor<USRGenerator> { llvm::raw_ostream &Out; bool IgnoreResults; + ASTUnit *AU; public: - USRGenerator(llvm::raw_ostream &out) : Out(out), IgnoreResults(false) {} + USRGenerator(ASTUnit *au, llvm::raw_ostream &out) + : Out(out), IgnoreResults(false), AU(au) {} bool ignoreResults() const { return IgnoreResults; } @@ -39,12 +44,19 @@ public: void VisitFunctionDecl(FunctionDecl *D); void VisitNamedDecl(NamedDecl *D); void VisitNamespaceDecl(NamespaceDecl *D); + void VisitObjCClassDecl(ObjCClassDecl *CD); void VisitObjCContainerDecl(ObjCContainerDecl *CD); + void VisitObjCForwardProtocolDecl(ObjCForwardProtocolDecl *P); void VisitObjCMethodDecl(ObjCMethodDecl *MD); void VisitObjCPropertyDecl(ObjCPropertyDecl *D); + void VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *D); void VisitTagDecl(TagDecl *D); void VisitTypedefDecl(TypedefDecl *D); + void VisitVarDecl(VarDecl *D); + /// Generate the string component containing the location of the + /// declaration. + void GenLoc(const Decl *D); /// String generation methods used both by the visitation methods /// and from other clients that want to directly generate USRs. These @@ -78,10 +90,10 @@ private: llvm::raw_svector_ostream Out; USRGenerator UG; public: - StringUSRGenerator() - : Out(StrBuf), UG(Out) { + StringUSRGenerator(const CXCursor *C = 0) + : Out(StrBuf), UG(C ? cxcursor::getCursorASTUnit(*C) : 0, Out) { // Add the USR space prefix. - Out << "c:"; + Out << "c:"; } llvm::StringRef str() { @@ -106,7 +118,7 @@ public: void USRGenerator::VisitBlockDecl(BlockDecl *D) { VisitDeclContext(D->getDeclContext()); // FIXME: Better support for anonymous blocks. - Out << "@B^anon"; + Out << "@B@anon"; } void USRGenerator::VisitDeclContext(DeclContext *DC) { @@ -122,24 +134,48 @@ void USRGenerator::VisitFieldDecl(FieldDecl *D) { return; } VisitDeclContext(D->getDeclContext()); - Out << "@^FI^" << s; + Out << (isa<ObjCIvarDecl>(D) ? "@" : "@FI@") << s; } void USRGenerator::VisitFunctionDecl(FunctionDecl *D) { VisitDeclContext(D->getDeclContext()); - Out << "@F^" << D->getNameAsString(); + Out << "@F@" << D; } void USRGenerator::VisitNamedDecl(NamedDecl *D) { VisitDeclContext(D->getDeclContext()); const std::string &s = D->getNameAsString(); - // assert(!s.empty()); - GenNamedDecl(s); + // The string can be empty if the declaration has no name; e.g., it is + // the ParmDecl with no name for declaration of a function pointer type, e.g.: + // void (*f)(void *); + // In this case, don't generate a USR. + if (s.empty()) + IgnoreResults = true; + else + GenNamedDecl(s); +} + +void USRGenerator::VisitVarDecl(VarDecl *D) { + // VarDecls can be declared 'extern' within a function or method body, + // but their enclosing DeclContext is the function, not the TU. We need + // to check the storage class to correctly generate the USR. + if (!D->hasExternalStorage()) + VisitDeclContext(D->getDeclContext()); + + const std::string &s = D->getNameAsString(); + // The string can be empty if the declaration has no name; e.g., it is + // the ParmDecl with no name for declaration of a function pointer type, e.g.: + // void (*f)(void *); + // In this case, don't generate a USR. + if (s.empty()) + IgnoreResults = true; + else + GenNamedDecl(s); } void USRGenerator::VisitNamespaceDecl(NamespaceDecl *D) { VisitDeclContext(D->getDeclContext()); - Out << "@N^" << D->getNameAsString(); + Out << "@N@" << D; } void USRGenerator::VisitObjCMethodDecl(ObjCMethodDecl *D) { @@ -148,6 +184,18 @@ void USRGenerator::VisitObjCMethodDecl(ObjCMethodDecl *D) { D->isInstanceMethod()); } +void USRGenerator::VisitObjCClassDecl(ObjCClassDecl *D) { + // FIXME: @class declarations can refer to multiple classes. We need + // to be able to traverse these. + IgnoreResults = true; +} + +void USRGenerator::VisitObjCForwardProtocolDecl(ObjCForwardProtocolDecl *D) { + // FIXME: @protocol declarations can refer to multiple protocols. We need + // to be able to traverse these. + IgnoreResults = true; +} + void USRGenerator::VisitObjCContainerDecl(ObjCContainerDecl *D) { switch (D->getKind()) { default: @@ -195,32 +243,85 @@ void USRGenerator::VisitObjCPropertyDecl(ObjCPropertyDecl *D) { GenObjCProperty(D->getName()); } +void USRGenerator::VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *D) { + if (ObjCPropertyDecl *PD = D->getPropertyDecl()) { + VisitObjCPropertyDecl(PD); + return; + } + + IgnoreResults = true; +} + void USRGenerator::VisitTagDecl(TagDecl *D) { + D = D->getCanonicalDecl(); VisitDeclContext(D->getDeclContext()); switch (D->getTagKind()) { - case TagDecl::TK_struct: Out << "@S^"; break; - case TagDecl::TK_class: Out << "@C^"; break; - case TagDecl::TK_union: Out << "@U^"; break; - case TagDecl::TK_enum: Out << "@E^"; break; + case TagDecl::TK_struct: Out << "@S"; break; + case TagDecl::TK_class: Out << "@C"; break; + case TagDecl::TK_union: Out << "@U"; break; + case TagDecl::TK_enum: Out << "@E"; break; } - // FIXME: Better support for anonymous structures and enums. const std::string &s = D->getNameAsString(); + const TypedefDecl *TD = 0; if (s.empty()) { - if (TypedefDecl *TD = D->getTypedefForAnonDecl()) - Out << "^anontd^" << TD->getNameAsString(); - else - Out << "^anon"; + TD = D->getTypedefForAnonDecl(); + Out << (TD ? 'A' : 'a'); + } + + // Add the location of the tag decl to handle resolution across + // translation units. + if (D->getLinkage() == NoLinkage) { + Out << '@'; + GenLoc(D); + if (IgnoreResults) + return; + } + + if (s.empty()) { + if (TD) + Out << '@' << TD; } else - Out << s; + Out << '@' << s; } void USRGenerator::VisitTypedefDecl(TypedefDecl *D) { DeclContext *DC = D->getDeclContext(); if (NamedDecl *DCN = dyn_cast<NamedDecl>(DC)) Visit(DCN); - Out << "typedef@" << D->getName(); + Out << "@T@"; + if (D->getLinkage() == NoLinkage) { + GenLoc(D); + if (IgnoreResults) + return; + Out << '@'; + } + Out << D->getName(); +} + +void USRGenerator::GenLoc(const Decl *D) { + const SourceManager &SM = AU->getSourceManager(); + SourceLocation L = D->getLocStart(); + if (L.isInvalid()) { + IgnoreResults = true; + return; + } + L = SM.getInstantiationLoc(L); + const std::pair<FileID, unsigned> &Decomposed = SM.getDecomposedLoc(L); + const FileEntry *FE = SM.getFileEntryForID(Decomposed.first); + if (FE) { + llvm::sys::Path P(FE->getName()); + Out << P.getLast(); + } + else { + // This case really isn't interesting. + IgnoreResults = true; + return; + } + Out << '@' + << SM.getLineNumber(Decomposed.first, Decomposed.second) << ':' + << SM.getColumnNumber(Decomposed.first, Decomposed.second); } //===----------------------------------------------------------------------===// @@ -228,7 +329,7 @@ void USRGenerator::VisitTypedefDecl(TypedefDecl *D) { //===----------------------------------------------------------------------===// void USRGenerator::GenNamedDecl(llvm::StringRef name) { - Out << "@^" << name; + Out << "@" << name; } void USRGenerator::GenObjCClass(llvm::StringRef cls) { @@ -236,7 +337,7 @@ void USRGenerator::GenObjCClass(llvm::StringRef cls) { } void USRGenerator::GenObjCCategory(llvm::StringRef cls, llvm::StringRef cat) { - Out << "objc(cy)" << cls << '^' << cat; + Out << "objc(cy)" << cls << '@' << cat; } void USRGenerator::GenObjCIvar(llvm::StringRef ivar) { @@ -259,29 +360,69 @@ void USRGenerator::GenObjCProtocol(llvm::StringRef prot) { // API hooks. //===----------------------------------------------------------------------===// -extern "C" { +static inline llvm::StringRef extractUSRSuffix(llvm::StringRef s) { + return s.startswith("c:") ? s.substr(2) : ""; +} -CXString clang_getCursorUSR(CXCursor C) { +static CXString getDeclCursorUSR(const CXCursor &C) { Decl *D = cxcursor::getCursorDecl(C); - if (!D) - return createCXString(NULL); - StringUSRGenerator SUG; - SUG->Visit(static_cast<Decl*>(D)); + // Don't generate USRs for things with invalid locations. + if (!D || D->getLocStart().isInvalid()) + return createCXString(""); + + // Check if the cursor has 'NoLinkage'. + if (const NamedDecl *ND = dyn_cast<NamedDecl>(D)) + switch (ND->getLinkage()) { + case ExternalLinkage: + // Generate USRs for all entities with external linkage. + break; + case NoLinkage: + // We allow enums, typedefs, and structs that have no linkage to + // have USRs that are anchored to the file they were defined in + // (e.g., the header). This is a little gross, but in principal + // enums/anonymous structs/etc. defined in a common header file + // are referred to across multiple translation units. + if (isa<TagDecl>(ND) || isa<TypedefDecl>(ND) || + isa<EnumConstantDecl>(ND) || isa<FieldDecl>(ND)) + break; + // Fall-through. + case InternalLinkage: + case UniqueExternalLinkage: + return createCXString(""); + } + + StringUSRGenerator SUG(&C); + SUG->Visit(D); if (SUG->ignoreResults()) return createCXString(""); - // Return a copy of the string that must be disposed by the caller. + // For development testing. + // assert(SUG.str().size() > 2); + + // Return a copy of the string that must be disposed by the caller. return createCXString(SUG.str(), true); } -static inline llvm::StringRef extractUSRSuffix(llvm::StringRef s) { - if (!(s.size() >= 2 && s[0] == 'c' && s[1] == ':')) - return ""; - return s.substr(2); +extern "C" { + +CXString clang_getCursorUSR(CXCursor C) { + const CXCursorKind &K = clang_getCursorKind(C); + + if (clang_isDeclaration(K)) + return getDeclCursorUSR(C); + + if (K == CXCursor_MacroDefinition) { + StringUSRGenerator SUG(&C); + SUG << "macro@" + << cxcursor::getCursorMacroDefinition(C)->getName()->getNameStart(); + return createCXString(SUG.str(), true); + } + + return createCXString(""); } - + CXString clang_constructUSR_ObjCIvar(const char *name, CXString classUSR) { StringUSRGenerator SUG; SUG << extractUSRSuffix(clang_getCString(classUSR)); diff --git a/tools/CIndex/CIndexer.cpp b/tools/CIndex/CIndexer.cpp index 0774ae2..d5131ff 100644 --- a/tools/CIndex/CIndexer.cpp +++ b/tools/CIndex/CIndexer.cpp @@ -38,7 +38,6 @@ #endif using namespace clang; -using namespace idx; const llvm::sys::Path& CIndexer::getClangPath() { // Did we already compute the path? diff --git a/tools/CIndex/CIndexer.h b/tools/CIndex/CIndexer.h index 1fa3ca9..31bf779 100644 --- a/tools/CIndex/CIndexer.h +++ b/tools/CIndex/CIndexer.h @@ -16,14 +16,10 @@ #define LLVM_CLANG_CINDEXER_H #include "clang-c/Index.h" -#include "clang/Frontend/CompilerInstance.h" -#include "clang/Frontend/ASTUnit.h" #include "llvm/ADT/StringRef.h" #include "llvm/System/Path.h" #include <vector> -using namespace clang; - namespace clang { namespace cxstring { CXString createCXString(const char *String, bool DupString = false); diff --git a/tools/CIndex/CMakeLists.txt b/tools/CIndex/CMakeLists.txt index 6aa7a57..609719e 100644 --- a/tools/CIndex/CMakeLists.txt +++ b/tools/CIndex/CMakeLists.txt @@ -32,9 +32,13 @@ add_clang_library(CIndex if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") # FIXME: Deal with LLVM_SUBMIT_VERSION? + # FIXME: This uses a special darwin-specific exports file in order to + # get underscore-prefixed names. It would be better to have build rules + # which know how to produce a darwin-suitable exports file from the + # regular exports file. set_target_properties(CIndex PROPERTIES - LINK_FLAGS "-avoid-version -Wl,-exported_symbols_list -Wl,${CMAKE_CURRENT_SOURCE_DIR}/CIndex.exports -Wl,-dead_strip -Wl,-seg1addr -Wl,0xE0000000" + LINK_FLAGS "-avoid-version -Wl,-exported_symbols_list -Wl,${CMAKE_CURRENT_SOURCE_DIR}/CIndex.darwin.exports -Wl,-dead_strip -Wl,-seg1addr -Wl,0xE0000000" INSTALL_NAME_DIR "@executable_path/../lib" ) endif() diff --git a/tools/CIndex/CXCursor.cpp b/tools/CIndex/CXCursor.cpp index cbf9d7e..3bc5d01 100644 --- a/tools/CIndex/CXCursor.cpp +++ b/tools/CIndex/CXCursor.cpp @@ -50,6 +50,7 @@ static CXCursorKind GetCursorKind(Decl *D) { case Decl::ObjCMethod: return cast<ObjCMethodDecl>(D)->isInstanceMethod() ? CXCursor_ObjCInstanceMethodDecl : CXCursor_ObjCClassMethodDecl; + case Decl::CXXMethod: return CXCursor_CXXMethod; case Decl::ObjCProperty: return CXCursor_ObjCPropertyDecl; case Decl::ObjCProtocol: return CXCursor_ObjCProtocolDecl; case Decl::ParmVar: return CXCursor_ParmDecl; @@ -159,7 +160,6 @@ CXCursor cxcursor::MakeCXCursor(Stmt *S, Decl *Parent, ASTUnit *TU) { case Stmt::TypesCompatibleExprClass: case Stmt::ChooseExprClass: case Stmt::GNUNullExprClass: - case Stmt::CXXNamedCastExprClass: case Stmt::CXXStaticCastExprClass: case Stmt::CXXDynamicCastExprClass: case Stmt::CXXReinterpretCastExprClass: diff --git a/tools/CIndex/CXSourceLocation.h b/tools/CIndex/CXSourceLocation.h index 8bfc6f9..66566c1 100644 --- a/tools/CIndex/CXSourceLocation.h +++ b/tools/CIndex/CXSourceLocation.h @@ -16,13 +16,12 @@ #include "clang-c/Index.h" #include "clang/Basic/SourceLocation.h" -#include "clang/Basic/SourceManager.h" #include "clang/Basic/LangOptions.h" #include "clang/AST/ASTContext.h" namespace clang { - -class ASTContext; + +class SourceManager; namespace cxloc { diff --git a/tools/CIndex/Makefile b/tools/CIndex/Makefile index 650bcd3..391746d 100644 --- a/tools/CIndex/Makefile +++ b/tools/CIndex/Makefile @@ -10,6 +10,8 @@ LEVEL = ../../../.. LIBRARYNAME = CIndex +EXPORTED_SYMBOL_FILE = $(PROJ_SRC_DIR)/CIndex.exports + CPP.Flags += -I$(PROJ_SRC_DIR)/../../include -I$(PROJ_OBJ_DIR)/../../include # Include this here so we can get the configuration of the targets @@ -40,7 +42,6 @@ ifeq ($(HOST_OS),Darwin) # extra options to override libtool defaults LLVMLibsOptions := $(LLVMLibsOptions) \ -avoid-version \ - -Wl,-exported_symbols_list -Wl,$(PROJ_SRC_DIR)/CIndex.exports \ -Wl,-dead_strip \ -Wl,-seg1addr -Wl,0xE0000000 diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 64c3a21..ae33b78 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -1,3 +1,3 @@ -add_subdirectory(CIndex) +add_subdirectory(libclang) add_subdirectory(c-index-test) add_subdirectory(driver) diff --git a/tools/Makefile b/tools/Makefile index bfd5ad1..8407dfd 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -8,12 +8,12 @@ ##===----------------------------------------------------------------------===## LEVEL := ../../.. -DIRS := driver CIndex c-index-test +DIRS := driver libclang c-index-test include $(LEVEL)/Makefile.config ifeq ($(OS), $(filter $(OS), Cygwin MingW)) -DIRS := $(filter-out CIndex c-index-test, $(DIRS)) +DIRS := $(filter-out libclang c-index-test, $(DIRS)) endif include $(LEVEL)/Makefile.common diff --git a/tools/c-index-test/CMakeLists.txt b/tools/c-index-test/CMakeLists.txt index f0a34a5..d965fd2 100644 --- a/tools/c-index-test/CMakeLists.txt +++ b/tools/c-index-test/CMakeLists.txt @@ -1,7 +1,7 @@ set(LLVM_NO_RTTI 1) set( LLVM_USED_LIBS - CIndex + libclang clangIndex clangFrontend clangDriver diff --git a/tools/c-index-test/Makefile b/tools/c-index-test/Makefile index e9ba174..24fed16 100644 --- a/tools/c-index-test/Makefile +++ b/tools/c-index-test/Makefile @@ -10,7 +10,6 @@ LEVEL = ../../../.. TOOLNAME = c-index-test CPPFLAGS += -I$(PROJ_SRC_DIR)/../../include -I$(PROJ_OBJ_DIR)/../../include -NO_INSTALL = 1 # No plugins, optimize startup time. TOOL_NO_EXPORTS = 1 @@ -18,7 +17,7 @@ TOOL_NO_EXPORTS = 1 include $(LEVEL)/Makefile.config LINK_COMPONENTS := bitreader mc core -USEDLIBS = CIndex.a clangIndex.a clangFrontend.a clangDriver.a clangSema.a \ +USEDLIBS = clang.a clangIndex.a clangFrontend.a clangDriver.a clangSema.a \ clangAnalysis.a clangAST.a clangParse.a clangLex.a clangBasic.a include $(LLVM_SRC_ROOT)/Makefile.rules diff --git a/tools/c-index-test/c-index-test.c b/tools/c-index-test/c-index-test.c index d7f3483..4941816 100644 --- a/tools/c-index-test/c-index-test.c +++ b/tools/c-index-test/c-index-test.c @@ -372,12 +372,13 @@ enum CXChildVisitResult USRVisitor(CXCursor C, CXCursor parent, VisitorData *Data = (VisitorData *)ClientData; if (!Data->Filter || (C.kind == *(enum CXCursorKind *)Data->Filter)) { CXString USR = clang_getCursorUSR(C); - if (!clang_getCString(USR)) { + const char *cstr = clang_getCString(USR); + if (!cstr || cstr[0] == '\0') { clang_disposeString(USR); - return CXChildVisit_Continue; + return CXChildVisit_Recurse; } - printf("// %s: %s %s", FileCheckPrefix, GetCursorSource(C), - clang_getCString(USR)); + printf("// %s: %s %s", FileCheckPrefix, GetCursorSource(C), cstr); + PrintCursorExtent(C); printf("\n"); clang_disposeString(USR); diff --git a/tools/driver/CMakeLists.txt b/tools/driver/CMakeLists.txt index c4320b0..ca2f408 100644 --- a/tools/driver/CMakeLists.txt +++ b/tools/driver/CMakeLists.txt @@ -30,6 +30,7 @@ add_clang_executable(clang if(UNIX) set(CLANGXX_LINK_OR_COPY create_symlink) + set(CLANGXX_DESTDIR $ENV{DESTDIR}/) else() set(CLANGXX_LINK_OR_COPY copy) endif() @@ -45,4 +46,4 @@ install(TARGETS clang RUNTIME DESTINATION bin) # Create the clang++ symlink at installation time. -install(CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" -E ${CLANGXX_LINK_OR_COPY} \"${CMAKE_INSTALL_PREFIX}/bin/clang${CMAKE_EXECUTABLE_SUFFIX}\" \"\$ENV{DESTDIR}/\${CMAKE_INSTALL_PREFIX}/bin/clang++${CMAKE_EXECUTABLE_SUFFIX}\")") +install(CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" -E ${CLANGXX_LINK_OR_COPY} \"${CMAKE_INSTALL_PREFIX}/bin/clang${CMAKE_EXECUTABLE_SUFFIX}\" \"${CLANGXX_DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/clang++${CMAKE_EXECUTABLE_SUFFIX}\")") diff --git a/tools/driver/cc1_main.cpp b/tools/driver/cc1_main.cpp index dd440f5..144a734 100644 --- a/tools/driver/cc1_main.cpp +++ b/tools/driver/cc1_main.cpp @@ -64,7 +64,6 @@ static FrontendAction *CreateFrontendBaseAction(CompilerInstance &CI) { case ASTPrintXML: return new ASTPrintXMLAction(); case ASTView: return new ASTViewAction(); case DumpRawTokens: return new DumpRawTokensAction(); - case DumpRecordLayouts: return new DumpRecordAction(); case DumpTokens: return new DumpTokensAction(); case EmitAssembly: return new EmitAssemblyAction(); case EmitBC: return new EmitBCAction(); @@ -199,7 +198,7 @@ int cc1_main(const char **ArgBegin, const char **ArgEnd, const char *Argv0, void *MainAddr) { llvm::OwningPtr<CompilerInstance> Clang(new CompilerInstance()); - Clang->setLLVMContext(new llvm::LLVMContext); + Clang->setLLVMContext(new llvm::LLVMContext()); // Run clang -cc1 test. if (ArgBegin != ArgEnd && llvm::StringRef(ArgBegin[0]) == "-cc1test") { @@ -242,6 +241,19 @@ int cc1_main(const char **ArgBegin, const char **ArgEnd, return 0; } + // Honor -mllvm. + // + // FIXME: Remove this, one day. + if (!Clang->getFrontendOpts().LLVMArgs.empty()) { + unsigned NumArgs = Clang->getFrontendOpts().LLVMArgs.size(); + const char **Args = new const char*[NumArgs + 2]; + Args[0] = "clang (LLVM option parsing)"; + for (unsigned i = 0; i != NumArgs; ++i) + Args[i + 1] = Clang->getFrontendOpts().LLVMArgs[i].c_str(); + Args[NumArgs + 1] = 0; + llvm::cl::ParseCommandLineOptions(NumArgs + 1, const_cast<char **>(Args)); + } + // Create the actual diagnostics engine. Clang->createDiagnostics(ArgEnd - ArgBegin, const_cast<char**>(ArgBegin)); if (!Clang->hasDiagnostics()) @@ -249,7 +261,7 @@ int cc1_main(const char **ArgBegin, const char **ArgEnd, // Set an error handler, so that any LLVM backend diagnostics go through our // error handler. - llvm::llvm_install_error_handler(LLVMErrorHandler, + llvm::install_fatal_error_handler(LLVMErrorHandler, static_cast<void*>(&Clang->getDiagnostics())); DiagsBuffer.FlushDiagnostics(Clang->getDiagnostics()); diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp new file mode 100644 index 0000000..f9f7351 --- /dev/null +++ b/tools/libclang/CIndex.cpp @@ -0,0 +1,2598 @@ +//===- 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 "CXCursor.h" +#include "CXSourceLocation.h" +#include "CIndexDiagnostic.h" + +#include "clang/Basic/Version.h" + +#include "clang/AST/DeclVisitor.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/AST/TypeLocVisitor.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Lex/Lexer.h" +#include "clang/Lex/PreprocessingRecord.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/System/Program.h" +#include "llvm/System/Signals.h" + +// Needed to define L_TMPNAM on some systems. +#include <cstdio> + +using namespace clang; +using namespace clang::cxcursor; +using namespace clang::cxstring; + +//===----------------------------------------------------------------------===// +// Crash Reporting. +//===----------------------------------------------------------------------===// + +#ifdef __APPLE__ +#define USE_CRASHTRACER +#include "clang/Analysis/Support/SaveAndRestore.h" +// Integrate with crash reporter. +extern "C" const char *__crashreporter_info__; +#define NUM_CRASH_STRINGS 32 +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<const char*> &Args) + : crashtracerSlot(0) + { + { + llvm::raw_svector_ostream Out(CrashString); + Out << "ClangCIndex [" << getClangFullVersion() << "]" + << "[createTranslationUnitFromSourceFile]: clang"; + for (llvm::SmallVectorImpl<const char*>::iterator I=Args.begin(), + E=Args.end(); I!=E; ++I) + Out << ' ' << *I; + } + crashtracerSlot = SetCrashTracerInfo(CrashString.c_str(), + AggregateString); + } + + ~ArgsCrashTracerInfo() { + ResetCrashTracerInfo(crashtracerSlot); + } +}; +} +#endif + +/// \brief The result of comparing two source ranges. +enum RangeComparisonResult { + /// \brief Either the ranges overlap or one of the ranges is invalid. + RangeOverlap, + + /// \brief The first range ends before the second range starts. + RangeBefore, + + /// \brief The first range starts after the second range ends. + RangeAfter +}; + +/// \brief Compare two source ranges to determine their relative position in +/// the translation unit. +static RangeComparisonResult RangeCompare(SourceManager &SM, + SourceRange R1, + SourceRange R2) { + assert(R1.isValid() && "First range is invalid?"); + assert(R2.isValid() && "Second range is invalid?"); + if (R1.getEnd() == R2.getBegin() || + SM.isBeforeInTranslationUnit(R1.getEnd(), R2.getBegin())) + return RangeBefore; + if (R2.getEnd() == R1.getBegin() || + SM.isBeforeInTranslationUnit(R2.getEnd(), R1.getBegin())) + return RangeAfter; + return RangeOverlap; +} + +/// \brief Translate a Clang source range into a CIndex source range. +/// +/// Clang internally represents ranges where the end location points to the +/// start of the token at the end. However, for external clients it is more +/// useful to have a CXSourceRange be a proper half-open interval. This routine +/// does the appropriate translation. +CXSourceRange cxloc::translateSourceRange(const SourceManager &SM, + const LangOptions &LangOpts, + SourceRange R) { + // We want the last character in this location, so we will adjust the + // location accordingly. + // FIXME: How do do this with a macro instantiation location? + SourceLocation EndLoc = R.getEnd(); + if (!EndLoc.isInvalid() && EndLoc.isFileID()) { + unsigned Length = Lexer::MeasureTokenLength(EndLoc, SM, LangOpts); + EndLoc = EndLoc.getFileLocWithOffset(Length); + } + + CXSourceRange Result = { { (void *)&SM, (void *)&LangOpts }, + R.getBegin().getRawEncoding(), + EndLoc.getRawEncoding() }; + return Result; +} + +//===----------------------------------------------------------------------===// +// Cursor visitor. +//===----------------------------------------------------------------------===// + +namespace { + +// Cursor visitor. +class CursorVisitor : public DeclVisitor<CursorVisitor, bool>, + public TypeLocVisitor<CursorVisitor, bool>, + public StmtVisitor<CursorVisitor, bool> +{ + /// \brief The translation unit we are traversing. + ASTUnit *TU; + + /// \brief The parent cursor whose children we are traversing. + CXCursor Parent; + + /// \brief The declaration that serves at the parent of any statement or + /// expression nodes. + Decl *StmtParent; + + /// \brief The visitor function. + CXCursorVisitor Visitor; + + /// \brief The opaque client data, to be passed along to the visitor. + CXClientData ClientData; + + // 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; + + /// \brief When valid, a source range to which the cursor should restrict + /// its search. + SourceRange RegionOfInterest; + + using DeclVisitor<CursorVisitor, bool>::Visit; + using TypeLocVisitor<CursorVisitor, bool>::Visit; + using StmtVisitor<CursorVisitor, bool>::Visit; + + /// \brief Determine whether this particular source range comes before, comes + /// after, or overlaps the region of interest. + /// + /// \param R a half-open source range retrieved from the abstract syntax tree. + RangeComparisonResult CompareRegionOfInterest(SourceRange R); + +public: + CursorVisitor(ASTUnit *TU, CXCursorVisitor Visitor, CXClientData ClientData, + unsigned MaxPCHLevel, + SourceRange RegionOfInterest = SourceRange()) + : TU(TU), Visitor(Visitor), ClientData(ClientData), + MaxPCHLevel(MaxPCHLevel), RegionOfInterest(RegionOfInterest) + { + Parent.kind = CXCursor_NoDeclFound; + Parent.data[0] = 0; + Parent.data[1] = 0; + Parent.data[2] = 0; + StmtParent = 0; + } + + bool Visit(CXCursor Cursor, bool CheckedRegionOfInterest = false); + + std::pair<PreprocessingRecord::iterator, PreprocessingRecord::iterator> + getPreprocessedEntities(); + + bool VisitChildren(CXCursor Parent); + + // Declaration visitors + bool VisitAttributes(Decl *D); + bool VisitBlockDecl(BlockDecl *B); + bool VisitDeclContext(DeclContext *DC); + bool VisitTranslationUnitDecl(TranslationUnitDecl *D); + bool VisitTypedefDecl(TypedefDecl *D); + bool VisitTagDecl(TagDecl *D); + bool VisitEnumConstantDecl(EnumConstantDecl *D); + bool VisitDeclaratorDecl(DeclaratorDecl *DD); + bool VisitFunctionDecl(FunctionDecl *ND); + bool VisitFieldDecl(FieldDecl *D); + bool VisitVarDecl(VarDecl *); + bool VisitObjCMethodDecl(ObjCMethodDecl *ND); + bool VisitObjCContainerDecl(ObjCContainerDecl *D); + bool VisitObjCCategoryDecl(ObjCCategoryDecl *ND); + bool VisitObjCProtocolDecl(ObjCProtocolDecl *PID); + bool VisitObjCInterfaceDecl(ObjCInterfaceDecl *D); + bool VisitObjCImplDecl(ObjCImplDecl *D); + bool VisitObjCCategoryImplDecl(ObjCCategoryImplDecl *D); + bool VisitObjCImplementationDecl(ObjCImplementationDecl *D); + // FIXME: ObjCPropertyDecl requires TypeSourceInfo, getter/setter locations, + // etc. + // FIXME: ObjCCompatibleAliasDecl requires aliased-class locations. + bool VisitObjCForwardProtocolDecl(ObjCForwardProtocolDecl *D); + bool VisitObjCClassDecl(ObjCClassDecl *D); + + // Type visitors + // FIXME: QualifiedTypeLoc doesn't provide any location information + bool VisitBuiltinTypeLoc(BuiltinTypeLoc TL); + bool VisitTypedefTypeLoc(TypedefTypeLoc TL); + bool VisitUnresolvedUsingTypeLoc(UnresolvedUsingTypeLoc TL); + bool VisitTagTypeLoc(TagTypeLoc TL); + // FIXME: TemplateTypeParmTypeLoc doesn't provide any location information + bool VisitObjCInterfaceTypeLoc(ObjCInterfaceTypeLoc TL); + bool VisitObjCObjectPointerTypeLoc(ObjCObjectPointerTypeLoc TL); + bool VisitPointerTypeLoc(PointerTypeLoc TL); + bool VisitBlockPointerTypeLoc(BlockPointerTypeLoc TL); + bool VisitMemberPointerTypeLoc(MemberPointerTypeLoc TL); + bool VisitLValueReferenceTypeLoc(LValueReferenceTypeLoc TL); + bool VisitRValueReferenceTypeLoc(RValueReferenceTypeLoc TL); + bool VisitFunctionTypeLoc(FunctionTypeLoc TL); + bool VisitArrayTypeLoc(ArrayTypeLoc TL); + // FIXME: Implement for TemplateSpecializationTypeLoc + // FIXME: Implement visitors here when the unimplemented TypeLocs get + // implemented + bool VisitTypeOfExprTypeLoc(TypeOfExprTypeLoc TL); + bool VisitTypeOfTypeLoc(TypeOfTypeLoc TL); + + // Statement visitors + bool VisitStmt(Stmt *S); + bool VisitDeclStmt(DeclStmt *S); + // FIXME: LabelStmt label? + bool VisitIfStmt(IfStmt *S); + bool VisitSwitchStmt(SwitchStmt *S); + bool VisitWhileStmt(WhileStmt *S); + bool VisitForStmt(ForStmt *S); + + // Expression visitors + bool VisitBlockExpr(BlockExpr *B); + bool VisitCompoundLiteralExpr(CompoundLiteralExpr *E); + bool VisitExplicitCastExpr(ExplicitCastExpr *E); + bool VisitObjCMessageExpr(ObjCMessageExpr *E); + bool VisitObjCEncodeExpr(ObjCEncodeExpr *E); + bool VisitOffsetOfExpr(OffsetOfExpr *E); + bool VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *E); +}; + +} // end anonymous namespace + +RangeComparisonResult CursorVisitor::CompareRegionOfInterest(SourceRange R) { + return RangeCompare(TU->getSourceManager(), R, RegionOfInterest); +} + +/// \brief Visit the given cursor and, if requested by the visitor, +/// its children. +/// +/// \param Cursor the cursor to visit. +/// +/// \param CheckRegionOfInterest if true, then the caller already checked that +/// this cursor is within the region of interest. +/// +/// \returns true if the visitation should be aborted, false if it +/// should continue. +bool CursorVisitor::Visit(CXCursor Cursor, bool CheckedRegionOfInterest) { + if (clang_isInvalid(Cursor.kind)) + return false; + + if (clang_isDeclaration(Cursor.kind)) { + Decl *D = getCursorDecl(Cursor); + assert(D && "Invalid declaration cursor"); + if (D->getPCHLevel() > MaxPCHLevel) + return false; + + if (D->isImplicit()) + return false; + } + + // If we have a range of interest, and this cursor doesn't intersect with it, + // we're done. + if (RegionOfInterest.isValid() && !CheckedRegionOfInterest) { + SourceRange Range = + cxloc::translateCXSourceRange(clang_getCursorExtent(Cursor)); + if (Range.isInvalid() || CompareRegionOfInterest(Range)) + return false; + } + + switch (Visitor(Cursor, Parent, ClientData)) { + case CXChildVisit_Break: + return true; + + case CXChildVisit_Continue: + return false; + + case CXChildVisit_Recurse: + return VisitChildren(Cursor); + } + + return false; +} + +std::pair<PreprocessingRecord::iterator, PreprocessingRecord::iterator> +CursorVisitor::getPreprocessedEntities() { + PreprocessingRecord &PPRec + = *TU->getPreprocessor().getPreprocessingRecord(); + + bool OnlyLocalDecls + = !TU->isMainFileAST() && TU->getOnlyLocalDecls(); + + // There is no region of interest; we have to walk everything. + if (RegionOfInterest.isInvalid()) + return std::make_pair(PPRec.begin(OnlyLocalDecls), + PPRec.end(OnlyLocalDecls)); + + // Find the file in which the region of interest lands. + SourceManager &SM = TU->getSourceManager(); + std::pair<FileID, unsigned> Begin + = SM.getDecomposedInstantiationLoc(RegionOfInterest.getBegin()); + std::pair<FileID, unsigned> End + = SM.getDecomposedInstantiationLoc(RegionOfInterest.getEnd()); + + // The region of interest spans files; we have to walk everything. + if (Begin.first != End.first) + return std::make_pair(PPRec.begin(OnlyLocalDecls), + PPRec.end(OnlyLocalDecls)); + + ASTUnit::PreprocessedEntitiesByFileMap &ByFileMap + = TU->getPreprocessedEntitiesByFile(); + if (ByFileMap.empty()) { + // Build the mapping from files to sets of preprocessed entities. + for (PreprocessingRecord::iterator E = PPRec.begin(OnlyLocalDecls), + EEnd = PPRec.end(OnlyLocalDecls); + E != EEnd; ++E) { + std::pair<FileID, unsigned> P + = SM.getDecomposedInstantiationLoc((*E)->getSourceRange().getBegin()); + ByFileMap[P.first].push_back(*E); + } + } + + return std::make_pair(ByFileMap[Begin.first].begin(), + ByFileMap[Begin.first].end()); +} + +/// \brief Visit the children of the given cursor. +/// +/// \returns true if the visitation should be aborted, false if it +/// should continue. +bool CursorVisitor::VisitChildren(CXCursor Cursor) { + if (clang_isReference(Cursor.kind)) { + // By definition, references have no children. + return false; + } + + // Set the Parent field to Cursor, then back to its old value once we're + // done. + class SetParentRAII { + CXCursor &Parent; + Decl *&StmtParent; + CXCursor OldParent; + + public: + SetParentRAII(CXCursor &Parent, Decl *&StmtParent, CXCursor NewParent) + : Parent(Parent), StmtParent(StmtParent), OldParent(Parent) + { + Parent = NewParent; + if (clang_isDeclaration(Parent.kind)) + StmtParent = getCursorDecl(Parent); + } + + ~SetParentRAII() { + Parent = OldParent; + if (clang_isDeclaration(Parent.kind)) + StmtParent = getCursorDecl(Parent); + } + } SetParent(Parent, StmtParent, Cursor); + + if (clang_isDeclaration(Cursor.kind)) { + Decl *D = getCursorDecl(Cursor); + assert(D && "Invalid declaration cursor"); + return VisitAttributes(D) || Visit(D); + } + + if (clang_isStatement(Cursor.kind)) + return Visit(getCursorStmt(Cursor)); + if (clang_isExpression(Cursor.kind)) + return Visit(getCursorExpr(Cursor)); + + if (clang_isTranslationUnit(Cursor.kind)) { + ASTUnit *CXXUnit = getCursorASTUnit(Cursor); + if (!CXXUnit->isMainFileAST() && CXXUnit->getOnlyLocalDecls() && + RegionOfInterest.isInvalid()) { + const std::vector<Decl*> &TLDs = CXXUnit->getTopLevelDecls(); + for (std::vector<Decl*>::const_iterator it = TLDs.begin(), + ie = TLDs.end(); it != ie; ++it) { + if (Visit(MakeCXCursor(*it, CXXUnit), true)) + return true; + } + } else if (VisitDeclContext( + CXXUnit->getASTContext().getTranslationUnitDecl())) + return true; + + // Walk the preprocessing record. + if (CXXUnit->getPreprocessor().getPreprocessingRecord()) { + // FIXME: Once we have the ability to deserialize a preprocessing record, + // do so. + PreprocessingRecord::iterator E, EEnd; + for (llvm::tie(E, EEnd) = getPreprocessedEntities(); E != EEnd; ++E) { + if (MacroInstantiation *MI = dyn_cast<MacroInstantiation>(*E)) { + if (Visit(MakeMacroInstantiationCursor(MI, CXXUnit))) + return true; + + continue; + } + + if (MacroDefinition *MD = dyn_cast<MacroDefinition>(*E)) { + if (Visit(MakeMacroDefinitionCursor(MD, CXXUnit))) + return true; + + continue; + } + } + } + return false; + } + + // Nothing to visit at the moment. + return false; +} + +bool CursorVisitor::VisitBlockDecl(BlockDecl *B) { + for (BlockDecl::param_iterator I=B->param_begin(), E=B->param_end(); I!=E;++I) + if (Decl *D = *I) + if (Visit(D)) + return true; + + return Visit(MakeCXCursor(B->getBody(), StmtParent, TU)); +} + +bool CursorVisitor::VisitDeclContext(DeclContext *DC) { + for (DeclContext::decl_iterator + I = DC->decls_begin(), E = DC->decls_end(); I != E; ++I) { + + CXCursor Cursor = MakeCXCursor(*I, TU); + + if (RegionOfInterest.isValid()) { + SourceRange Range = + cxloc::translateCXSourceRange(clang_getCursorExtent(Cursor)); + if (Range.isInvalid()) + continue; + + switch (CompareRegionOfInterest(Range)) { + case RangeBefore: + // This declaration comes before the region of interest; skip it. + continue; + + case RangeAfter: + // This declaration comes after the region of interest; we're done. + return false; + + case RangeOverlap: + // This declaration overlaps the region of interest; visit it. + break; + } + } + + if (Visit(Cursor, true)) + return true; + } + + return false; +} + +bool CursorVisitor::VisitTranslationUnitDecl(TranslationUnitDecl *D) { + llvm_unreachable("Translation units are visited directly by Visit()"); + return false; +} + +bool CursorVisitor::VisitTypedefDecl(TypedefDecl *D) { + if (TypeSourceInfo *TSInfo = D->getTypeSourceInfo()) + return Visit(TSInfo->getTypeLoc()); + + return false; +} + +bool CursorVisitor::VisitTagDecl(TagDecl *D) { + return VisitDeclContext(D); +} + +bool CursorVisitor::VisitEnumConstantDecl(EnumConstantDecl *D) { + if (Expr *Init = D->getInitExpr()) + return Visit(MakeCXCursor(Init, StmtParent, TU)); + return false; +} + +bool CursorVisitor::VisitDeclaratorDecl(DeclaratorDecl *DD) { + if (TypeSourceInfo *TSInfo = DD->getTypeSourceInfo()) + if (Visit(TSInfo->getTypeLoc())) + return true; + + return false; +} + +bool CursorVisitor::VisitFunctionDecl(FunctionDecl *ND) { + if (VisitDeclaratorDecl(ND)) + return true; + + if (ND->isThisDeclarationADefinition() && + Visit(MakeCXCursor(ND->getBody(), StmtParent, TU))) + return true; + + return false; +} + +bool CursorVisitor::VisitFieldDecl(FieldDecl *D) { + if (VisitDeclaratorDecl(D)) + return true; + + if (Expr *BitWidth = D->getBitWidth()) + return Visit(MakeCXCursor(BitWidth, StmtParent, TU)); + + return false; +} + +bool CursorVisitor::VisitVarDecl(VarDecl *D) { + if (VisitDeclaratorDecl(D)) + return true; + + if (Expr *Init = D->getInit()) + return Visit(MakeCXCursor(Init, StmtParent, TU)); + + return false; +} + +bool CursorVisitor::VisitObjCMethodDecl(ObjCMethodDecl *ND) { + if (TypeSourceInfo *TSInfo = ND->getResultTypeSourceInfo()) + if (Visit(TSInfo->getTypeLoc())) + return true; + + for (ObjCMethodDecl::param_iterator P = ND->param_begin(), + PEnd = ND->param_end(); + P != PEnd; ++P) { + if (Visit(MakeCXCursor(*P, TU))) + return true; + } + + if (ND->isThisDeclarationADefinition() && + Visit(MakeCXCursor(ND->getBody(), StmtParent, TU))) + return true; + + return false; +} + +bool CursorVisitor::VisitObjCContainerDecl(ObjCContainerDecl *D) { + return VisitDeclContext(D); +} + +bool CursorVisitor::VisitObjCCategoryDecl(ObjCCategoryDecl *ND) { + if (Visit(MakeCursorObjCClassRef(ND->getClassInterface(), ND->getLocation(), + TU))) + return true; + + ObjCCategoryDecl::protocol_loc_iterator PL = ND->protocol_loc_begin(); + for (ObjCCategoryDecl::protocol_iterator I = ND->protocol_begin(), + E = ND->protocol_end(); I != E; ++I, ++PL) + if (Visit(MakeCursorObjCProtocolRef(*I, *PL, TU))) + return true; + + return VisitObjCContainerDecl(ND); +} + +bool CursorVisitor::VisitObjCProtocolDecl(ObjCProtocolDecl *PID) { + ObjCProtocolDecl::protocol_loc_iterator PL = PID->protocol_loc_begin(); + for (ObjCProtocolDecl::protocol_iterator I = PID->protocol_begin(), + E = PID->protocol_end(); I != E; ++I, ++PL) + if (Visit(MakeCursorObjCProtocolRef(*I, *PL, TU))) + return true; + + return VisitObjCContainerDecl(PID); +} + +bool CursorVisitor::VisitObjCInterfaceDecl(ObjCInterfaceDecl *D) { + // Issue callbacks for super class. + if (D->getSuperClass() && + Visit(MakeCursorObjCSuperClassRef(D->getSuperClass(), + D->getSuperClassLoc(), + TU))) + return true; + + ObjCInterfaceDecl::protocol_loc_iterator PL = D->protocol_loc_begin(); + for (ObjCInterfaceDecl::protocol_iterator I = D->protocol_begin(), + E = D->protocol_end(); I != E; ++I, ++PL) + if (Visit(MakeCursorObjCProtocolRef(*I, *PL, TU))) + return true; + + return VisitObjCContainerDecl(D); +} + +bool CursorVisitor::VisitObjCImplDecl(ObjCImplDecl *D) { + return VisitObjCContainerDecl(D); +} + +bool CursorVisitor::VisitObjCCategoryImplDecl(ObjCCategoryImplDecl *D) { + // 'ID' could be null when dealing with invalid code. + if (ObjCInterfaceDecl *ID = D->getClassInterface()) + if (Visit(MakeCursorObjCClassRef(ID, D->getLocation(), TU))) + return true; + + return VisitObjCImplDecl(D); +} + +bool CursorVisitor::VisitObjCImplementationDecl(ObjCImplementationDecl *D) { +#if 0 + // Issue callbacks for super class. + // FIXME: No source location information! + if (D->getSuperClass() && + Visit(MakeCursorObjCSuperClassRef(D->getSuperClass(), + D->getSuperClassLoc(), + TU))) + return true; +#endif + + return VisitObjCImplDecl(D); +} + +bool CursorVisitor::VisitObjCForwardProtocolDecl(ObjCForwardProtocolDecl *D) { + ObjCForwardProtocolDecl::protocol_loc_iterator PL = D->protocol_loc_begin(); + for (ObjCForwardProtocolDecl::protocol_iterator I = D->protocol_begin(), + E = D->protocol_end(); + I != E; ++I, ++PL) + if (Visit(MakeCursorObjCProtocolRef(*I, *PL, TU))) + return true; + + return false; +} + +bool CursorVisitor::VisitObjCClassDecl(ObjCClassDecl *D) { + for (ObjCClassDecl::iterator C = D->begin(), CEnd = D->end(); C != CEnd; ++C) + if (Visit(MakeCursorObjCClassRef(C->getInterface(), C->getLocation(), TU))) + return true; + + return false; +} + +bool CursorVisitor::VisitBuiltinTypeLoc(BuiltinTypeLoc TL) { + ASTContext &Context = TU->getASTContext(); + + // Some builtin types (such as Objective-C's "id", "sel", and + // "Class") have associated declarations. Create cursors for those. + QualType VisitType; + switch (TL.getType()->getAs<BuiltinType>()->getKind()) { + case BuiltinType::Void: + case BuiltinType::Bool: + case BuiltinType::Char_U: + case BuiltinType::UChar: + case BuiltinType::Char16: + case BuiltinType::Char32: + case BuiltinType::UShort: + case BuiltinType::UInt: + case BuiltinType::ULong: + case BuiltinType::ULongLong: + case BuiltinType::UInt128: + case BuiltinType::Char_S: + case BuiltinType::SChar: + case BuiltinType::WChar: + case BuiltinType::Short: + case BuiltinType::Int: + case BuiltinType::Long: + case BuiltinType::LongLong: + case BuiltinType::Int128: + case BuiltinType::Float: + case BuiltinType::Double: + case BuiltinType::LongDouble: + case BuiltinType::NullPtr: + case BuiltinType::Overload: + case BuiltinType::Dependent: + break; + + case BuiltinType::UndeducedAuto: // FIXME: Deserves a cursor? + break; + + case BuiltinType::ObjCId: + VisitType = Context.getObjCIdType(); + break; + + case BuiltinType::ObjCClass: + VisitType = Context.getObjCClassType(); + break; + + case BuiltinType::ObjCSel: + VisitType = Context.getObjCSelType(); + break; + } + + if (!VisitType.isNull()) { + if (const TypedefType *Typedef = VisitType->getAs<TypedefType>()) + return Visit(MakeCursorTypeRef(Typedef->getDecl(), TL.getBuiltinLoc(), + TU)); + } + + return false; +} + +bool CursorVisitor::VisitTypedefTypeLoc(TypedefTypeLoc TL) { + return Visit(MakeCursorTypeRef(TL.getTypedefDecl(), TL.getNameLoc(), TU)); +} + +bool CursorVisitor::VisitUnresolvedUsingTypeLoc(UnresolvedUsingTypeLoc TL) { + return Visit(MakeCursorTypeRef(TL.getDecl(), TL.getNameLoc(), TU)); +} + +bool CursorVisitor::VisitTagTypeLoc(TagTypeLoc TL) { + return Visit(MakeCursorTypeRef(TL.getDecl(), TL.getNameLoc(), TU)); +} + +bool CursorVisitor::VisitObjCInterfaceTypeLoc(ObjCInterfaceTypeLoc TL) { + if (Visit(MakeCursorObjCClassRef(TL.getIFaceDecl(), TL.getNameLoc(), TU))) + return true; + + for (unsigned I = 0, N = TL.getNumProtocols(); I != N; ++I) { + if (Visit(MakeCursorObjCProtocolRef(TL.getProtocol(I), TL.getProtocolLoc(I), + TU))) + return true; + } + + return false; +} + +bool CursorVisitor::VisitObjCObjectPointerTypeLoc(ObjCObjectPointerTypeLoc TL) { + if (TL.hasBaseTypeAsWritten() && Visit(TL.getBaseTypeLoc())) + return true; + + if (TL.hasProtocolsAsWritten()) { + for (unsigned I = 0, N = TL.getNumProtocols(); I != N; ++I) { + if (Visit(MakeCursorObjCProtocolRef(TL.getProtocol(I), + TL.getProtocolLoc(I), + TU))) + return true; + } + } + + return false; +} + +bool CursorVisitor::VisitPointerTypeLoc(PointerTypeLoc TL) { + return Visit(TL.getPointeeLoc()); +} + +bool CursorVisitor::VisitBlockPointerTypeLoc(BlockPointerTypeLoc TL) { + return Visit(TL.getPointeeLoc()); +} + +bool CursorVisitor::VisitMemberPointerTypeLoc(MemberPointerTypeLoc TL) { + return Visit(TL.getPointeeLoc()); +} + +bool CursorVisitor::VisitLValueReferenceTypeLoc(LValueReferenceTypeLoc TL) { + return Visit(TL.getPointeeLoc()); +} + +bool CursorVisitor::VisitRValueReferenceTypeLoc(RValueReferenceTypeLoc TL) { + return Visit(TL.getPointeeLoc()); +} + +bool CursorVisitor::VisitFunctionTypeLoc(FunctionTypeLoc TL) { + if (Visit(TL.getResultLoc())) + return true; + + for (unsigned I = 0, N = TL.getNumArgs(); I != N; ++I) + if (Decl *D = TL.getArg(I)) + if (Visit(MakeCXCursor(D, TU))) + return true; + + return false; +} + +bool CursorVisitor::VisitArrayTypeLoc(ArrayTypeLoc TL) { + if (Visit(TL.getElementLoc())) + return true; + + if (Expr *Size = TL.getSizeExpr()) + return Visit(MakeCXCursor(Size, StmtParent, TU)); + + return false; +} + +bool CursorVisitor::VisitTypeOfExprTypeLoc(TypeOfExprTypeLoc TL) { + return Visit(MakeCXCursor(TL.getUnderlyingExpr(), StmtParent, TU)); +} + +bool CursorVisitor::VisitTypeOfTypeLoc(TypeOfTypeLoc TL) { + if (TypeSourceInfo *TSInfo = TL.getUnderlyingTInfo()) + return Visit(TSInfo->getTypeLoc()); + + return false; +} + +bool CursorVisitor::VisitStmt(Stmt *S) { + for (Stmt::child_iterator Child = S->child_begin(), ChildEnd = S->child_end(); + Child != ChildEnd; ++Child) { + if (*Child && Visit(MakeCXCursor(*Child, StmtParent, TU))) + return true; + } + + return false; +} + +bool CursorVisitor::VisitDeclStmt(DeclStmt *S) { + for (DeclStmt::decl_iterator D = S->decl_begin(), DEnd = S->decl_end(); + D != DEnd; ++D) { + if (*D && Visit(MakeCXCursor(*D, TU))) + return true; + } + + return false; +} + +bool CursorVisitor::VisitIfStmt(IfStmt *S) { + if (VarDecl *Var = S->getConditionVariable()) { + if (Visit(MakeCXCursor(Var, TU))) + return true; + } + + if (S->getCond() && Visit(MakeCXCursor(S->getCond(), StmtParent, TU))) + return true; + if (S->getThen() && Visit(MakeCXCursor(S->getThen(), StmtParent, TU))) + return true; + if (S->getElse() && Visit(MakeCXCursor(S->getElse(), StmtParent, TU))) + return true; + + return false; +} + +bool CursorVisitor::VisitSwitchStmt(SwitchStmt *S) { + if (VarDecl *Var = S->getConditionVariable()) { + if (Visit(MakeCXCursor(Var, TU))) + return true; + } + + if (S->getCond() && Visit(MakeCXCursor(S->getCond(), StmtParent, TU))) + return true; + if (S->getBody() && Visit(MakeCXCursor(S->getBody(), StmtParent, TU))) + return true; + + return false; +} + +bool CursorVisitor::VisitWhileStmt(WhileStmt *S) { + if (VarDecl *Var = S->getConditionVariable()) { + if (Visit(MakeCXCursor(Var, TU))) + return true; + } + + if (S->getCond() && Visit(MakeCXCursor(S->getCond(), StmtParent, TU))) + return true; + if (S->getBody() && Visit(MakeCXCursor(S->getBody(), StmtParent, TU))) + return true; + + return false; +} + +bool CursorVisitor::VisitForStmt(ForStmt *S) { + if (S->getInit() && Visit(MakeCXCursor(S->getInit(), StmtParent, TU))) + return true; + if (VarDecl *Var = S->getConditionVariable()) { + if (Visit(MakeCXCursor(Var, TU))) + return true; + } + + if (S->getCond() && Visit(MakeCXCursor(S->getCond(), StmtParent, TU))) + return true; + if (S->getInc() && Visit(MakeCXCursor(S->getInc(), StmtParent, TU))) + return true; + if (S->getBody() && Visit(MakeCXCursor(S->getBody(), StmtParent, TU))) + return true; + + return false; +} + +bool CursorVisitor::VisitBlockExpr(BlockExpr *B) { + return Visit(B->getBlockDecl()); +} + +bool CursorVisitor::VisitOffsetOfExpr(OffsetOfExpr *E) { + // FIXME: Visit fields as well? + if (Visit(E->getTypeSourceInfo()->getTypeLoc())) + return true; + + return VisitExpr(E); +} + +bool CursorVisitor::VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *E) { + if (E->isArgumentType()) { + if (TypeSourceInfo *TSInfo = E->getArgumentTypeInfo()) + return Visit(TSInfo->getTypeLoc()); + + return false; + } + + return VisitExpr(E); +} + +bool CursorVisitor::VisitExplicitCastExpr(ExplicitCastExpr *E) { + if (TypeSourceInfo *TSInfo = E->getTypeInfoAsWritten()) + if (Visit(TSInfo->getTypeLoc())) + return true; + + return VisitCastExpr(E); +} + +bool CursorVisitor::VisitCompoundLiteralExpr(CompoundLiteralExpr *E) { + if (TypeSourceInfo *TSInfo = E->getTypeSourceInfo()) + if (Visit(TSInfo->getTypeLoc())) + return true; + + return VisitExpr(E); +} + +bool CursorVisitor::VisitObjCMessageExpr(ObjCMessageExpr *E) { + if (TypeSourceInfo *TSInfo = E->getClassReceiverTypeInfo()) + if (Visit(TSInfo->getTypeLoc())) + return true; + + return VisitExpr(E); +} + +bool CursorVisitor::VisitObjCEncodeExpr(ObjCEncodeExpr *E) { + return Visit(E->getEncodedTypeSourceInfo()->getTypeLoc()); +} + + +bool CursorVisitor::VisitAttributes(Decl *D) { + for (const Attr *A = D->getAttrs(); A; A = A->getNext()) + if (Visit(MakeCXCursor(A, D, TU))) + return true; + + return false; +} + +extern "C" { +CXIndex clang_createIndex(int excludeDeclarationsFromPCH, + int displayDiagnostics) { + CIndexer *CIdxr = new CIndexer(); + if (excludeDeclarationsFromPCH) + CIdxr->setOnlyLocalDecls(); + if (displayDiagnostics) + CIdxr->setDisplayDiagnostics(); + return CIdxr; +} + +void clang_disposeIndex(CXIndex CIdx) { + if (CIdx) + delete static_cast<CIndexer *>(CIdx); +} + +void clang_setUseExternalASTGeneration(CXIndex CIdx, int value) { + if (CIdx) { + CIndexer *CXXIdx = static_cast<CIndexer *>(CIdx); + CXXIdx->setUseExternalASTGeneration(value); + } +} + +CXTranslationUnit clang_createTranslationUnit(CXIndex CIdx, + const char *ast_filename) { + if (!CIdx) + return 0; + + CIndexer *CXXIdx = static_cast<CIndexer *>(CIdx); + + llvm::IntrusiveRefCntPtr<Diagnostic> Diags; + return ASTUnit::LoadFromPCHFile(ast_filename, Diags, + CXXIdx->getOnlyLocalDecls(), + 0, 0, true); +} + +CXTranslationUnit +clang_createTranslationUnitFromSourceFile(CXIndex CIdx, + const char *source_filename, + int num_command_line_args, + const char **command_line_args, + unsigned num_unsaved_files, + struct CXUnsavedFile *unsaved_files) { + if (!CIdx) + return 0; + + CIndexer *CXXIdx = static_cast<CIndexer *>(CIdx); + + // Configure the diagnostics. + DiagnosticOptions DiagOpts; + llvm::IntrusiveRefCntPtr<Diagnostic> Diags; + Diags = CompilerInstance::createDiagnostics(DiagOpts, 0, 0); + + llvm::SmallVector<ASTUnit::RemappedFile, 4> RemappedFiles; + for (unsigned I = 0; I != num_unsaved_files; ++I) { + llvm::StringRef Data(unsaved_files[I].Contents, unsaved_files[I].Length); + const llvm::MemoryBuffer *Buffer + = llvm::MemoryBuffer::getMemBufferCopy(Data, unsaved_files[I].Filename); + RemappedFiles.push_back(std::make_pair(unsaved_files[I].Filename, + Buffer)); + } + + if (!CXXIdx->getUseExternalASTGeneration()) { + llvm::SmallVector<const char *, 16> 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); + Args.push_back("-Xclang"); + Args.push_back("-detailed-preprocessing-record"); + unsigned NumErrors = Diags->getNumErrors(); + +#ifdef USE_CRASHTRACER + ArgsCrashTracerInfo ACTI(Args); +#endif + + llvm::OwningPtr<ASTUnit> Unit( + ASTUnit::LoadFromCommandLine(Args.data(), Args.data() + Args.size(), + Diags, + CXXIdx->getClangResourcesPath(), + CXXIdx->getOnlyLocalDecls(), + RemappedFiles.data(), + RemappedFiles.size(), + /*CaptureDiagnostics=*/true)); + + // FIXME: Until we have broader testing, just drop the entire AST if we + // encountered an error. + if (NumErrors != Diags->getNumErrors()) { + // Make sure to check that 'Unit' is non-NULL. + if (CXXIdx->getDisplayDiagnostics() && Unit.get()) { + for (ASTUnit::stored_diag_iterator D = Unit->stored_diag_begin(), + DEnd = Unit->stored_diag_end(); + D != DEnd; ++D) { + CXStoredDiagnostic Diag(*D, Unit->getASTContext().getLangOptions()); + CXString Msg = clang_formatDiagnostic(&Diag, + clang_defaultDiagnosticDisplayOptions()); + fprintf(stderr, "%s\n", clang_getCString(Msg)); + clang_disposeString(Msg); + } +#ifdef LLVM_ON_WIN32 + // On Windows, force a flush, since there may be multiple copies of + // stderr and stdout in the file system, all with different buffers + // but writing to the same device. + fflush(stderr); +#endif + } + } + + return Unit.take(); + } + + // Build up the arguments for invoking 'clang'. + std::vector<const char *> argv; + + // First add the complete path to the 'clang' executable. + llvm::sys::Path ClangPath = static_cast<CIndexer *>(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)); + + // Remap any unsaved files to temporary files. + std::vector<llvm::sys::Path> TemporaryFiles; + std::vector<std::string> RemapArgs; + if (RemapFiles(num_unsaved_files, unsaved_files, RemapArgs, TemporaryFiles)) + return 0; + + // The pointers into the elements of RemapArgs are stable because we + // won't be adding anything to RemapArgs after this point. + for (unsigned i = 0, e = RemapArgs.size(); i != e; ++i) + argv.push_back(RemapArgs[i].c_str()); + + // 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); + } + + // Generate a temporary name for the diagnostics file. + char tmpFileResults[L_tmpnam]; + char *tmpResultsFileName = tmpnam(tmpFileResults); + llvm::sys::Path DiagnosticsFile(tmpResultsFileName); + TemporaryFiles.push_back(DiagnosticsFile); + argv.push_back("-fdiagnostics-binary"); + + argv.push_back("-Xclang"); + argv.push_back("-detailed-preprocessing-record"); + + // 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, &DiagnosticsFile, + NULL }; + llvm::sys::Program::ExecuteAndWait(ClangPath, &argv[0], /* env */ NULL, + /* redirects */ &Redirects[0], + /* secondsToWait */ 0, /* memoryLimits */ 0, &ErrMsg); + + if (!ErrMsg.empty()) { + std::string AllArgs; + for (std::vector<const char*>::iterator I = argv.begin(), E = argv.end(); + I != E; ++I) { + AllArgs += ' '; + if (*I) + AllArgs += *I; + } + + Diags->Report(diag::err_fe_invoking) << AllArgs << ErrMsg; + } + + ASTUnit *ATU = ASTUnit::LoadFromPCHFile(astTmpFile, Diags, + CXXIdx->getOnlyLocalDecls(), + RemappedFiles.data(), + RemappedFiles.size(), + /*CaptureDiagnostics=*/true); + if (ATU) { + LoadSerializedDiagnostics(DiagnosticsFile, + num_unsaved_files, unsaved_files, + ATU->getFileManager(), + ATU->getSourceManager(), + ATU->getStoredDiagnostics()); + } else if (CXXIdx->getDisplayDiagnostics()) { + // We failed to load the ASTUnit, but we can still deserialize the + // diagnostics and emit them. + FileManager FileMgr; + Diagnostic Diag; + SourceManager SourceMgr(Diag); + // FIXME: Faked LangOpts! + LangOptions LangOpts; + llvm::SmallVector<StoredDiagnostic, 4> Diags; + LoadSerializedDiagnostics(DiagnosticsFile, + num_unsaved_files, unsaved_files, + FileMgr, SourceMgr, Diags); + for (llvm::SmallVector<StoredDiagnostic, 4>::iterator D = Diags.begin(), + DEnd = Diags.end(); + D != DEnd; ++D) { + CXStoredDiagnostic Diag(*D, LangOpts); + CXString Msg = clang_formatDiagnostic(&Diag, + clang_defaultDiagnosticDisplayOptions()); + fprintf(stderr, "%s\n", clang_getCString(Msg)); + clang_disposeString(Msg); + } + +#ifdef LLVM_ON_WIN32 + // On Windows, force a flush, since there may be multiple copies of + // stderr and stdout in the file system, all with different buffers + // but writing to the same device. + fflush(stderr); +#endif + } + + if (ATU) { + // Make the translation unit responsible for destroying all temporary files. + for (unsigned i = 0, e = TemporaryFiles.size(); i != e; ++i) + ATU->addTemporaryFile(TemporaryFiles[i]); + ATU->addTemporaryFile(llvm::sys::Path(ATU->getPCHFileName())); + } else { + // Destroy all of the temporary files now; they can't be referenced any + // longer. + llvm::sys::Path(astTmpFile).eraseFromDisk(); + for (unsigned i = 0, e = TemporaryFiles.size(); i != e; ++i) + TemporaryFiles[i].eraseFromDisk(); + } + + return ATU; +} + +void clang_disposeTranslationUnit(CXTranslationUnit CTUnit) { + if (CTUnit) + delete static_cast<ASTUnit *>(CTUnit); +} + +CXString clang_getTranslationUnitSpelling(CXTranslationUnit CTUnit) { + if (!CTUnit) + return createCXString(""); + + ASTUnit *CXXUnit = static_cast<ASTUnit *>(CTUnit); + return createCXString(CXXUnit->getOriginalSourceFileName(), true); +} + +CXCursor clang_getTranslationUnitCursor(CXTranslationUnit TU) { + CXCursor Result = { CXCursor_TranslationUnit, { 0, 0, TU } }; + return Result; +} + +} // end: extern "C" + +//===----------------------------------------------------------------------===// +// CXSourceLocation and CXSourceRange Operations. +//===----------------------------------------------------------------------===// + +extern "C" { +CXSourceLocation clang_getNullLocation() { + CXSourceLocation Result = { { 0, 0 }, 0 }; + return Result; +} + +unsigned clang_equalLocations(CXSourceLocation loc1, CXSourceLocation loc2) { + return (loc1.ptr_data[0] == loc2.ptr_data[0] && + loc1.ptr_data[1] == loc2.ptr_data[1] && + loc1.int_data == loc2.int_data); +} + +CXSourceLocation clang_getLocation(CXTranslationUnit tu, + CXFile file, + unsigned line, + unsigned column) { + if (!tu || !file) + return clang_getNullLocation(); + + ASTUnit *CXXUnit = static_cast<ASTUnit *>(tu); + SourceLocation SLoc + = CXXUnit->getSourceManager().getLocation( + static_cast<const FileEntry *>(file), + line, column); + + return cxloc::translateSourceLocation(CXXUnit->getASTContext(), SLoc); +} + +CXSourceRange clang_getNullRange() { + CXSourceRange Result = { { 0, 0 }, 0, 0 }; + return Result; +} + +CXSourceRange clang_getRange(CXSourceLocation begin, CXSourceLocation end) { + if (begin.ptr_data[0] != end.ptr_data[0] || + begin.ptr_data[1] != end.ptr_data[1]) + return clang_getNullRange(); + + CXSourceRange Result = { { begin.ptr_data[0], begin.ptr_data[1] }, + begin.int_data, end.int_data }; + return Result; +} + +void clang_getInstantiationLocation(CXSourceLocation location, + CXFile *file, + unsigned *line, + unsigned *column, + unsigned *offset) { + SourceLocation Loc = SourceLocation::getFromRawEncoding(location.int_data); + + if (!location.ptr_data[0] || Loc.isInvalid()) { + if (file) + *file = 0; + if (line) + *line = 0; + if (column) + *column = 0; + if (offset) + *offset = 0; + return; + } + + const SourceManager &SM = + *static_cast<const SourceManager*>(location.ptr_data[0]); + SourceLocation InstLoc = SM.getInstantiationLoc(Loc); + + if (file) + *file = (void *)SM.getFileEntryForID(SM.getFileID(InstLoc)); + if (line) + *line = SM.getInstantiationLineNumber(InstLoc); + if (column) + *column = SM.getInstantiationColumnNumber(InstLoc); + if (offset) + *offset = SM.getDecomposedLoc(InstLoc).second; +} + +CXSourceLocation clang_getRangeStart(CXSourceRange range) { + CXSourceLocation Result = { { range.ptr_data[0], range.ptr_data[1] }, + range.begin_int_data }; + return Result; +} + +CXSourceLocation clang_getRangeEnd(CXSourceRange range) { + CXSourceLocation Result = { { range.ptr_data[0], range.ptr_data[1] }, + range.end_int_data }; + return Result; +} + +} // end: extern "C" + +//===----------------------------------------------------------------------===// +// CXFile Operations. +//===----------------------------------------------------------------------===// + +extern "C" { +CXString clang_getFileName(CXFile SFile) { + if (!SFile) + return createCXString(NULL); + + FileEntry *FEnt = static_cast<FileEntry *>(SFile); + return createCXString(FEnt->getName()); +} + +time_t clang_getFileTime(CXFile SFile) { + if (!SFile) + return 0; + + FileEntry *FEnt = static_cast<FileEntry *>(SFile); + return FEnt->getModificationTime(); +} + +CXFile clang_getFile(CXTranslationUnit tu, const char *file_name) { + if (!tu) + return 0; + + ASTUnit *CXXUnit = static_cast<ASTUnit *>(tu); + + FileManager &FMgr = CXXUnit->getFileManager(); + const FileEntry *File = FMgr.getFile(file_name, file_name+strlen(file_name)); + return const_cast<FileEntry *>(File); +} + +} // end: extern "C" + +//===----------------------------------------------------------------------===// +// CXCursor Operations. +//===----------------------------------------------------------------------===// + +static Decl *getDeclFromExpr(Stmt *E) { + if (DeclRefExpr *RefExpr = dyn_cast<DeclRefExpr>(E)) + return RefExpr->getDecl(); + if (MemberExpr *ME = dyn_cast<MemberExpr>(E)) + return ME->getMemberDecl(); + if (ObjCIvarRefExpr *RE = dyn_cast<ObjCIvarRefExpr>(E)) + return RE->getDecl(); + + if (CallExpr *CE = dyn_cast<CallExpr>(E)) + return getDeclFromExpr(CE->getCallee()); + if (CastExpr *CE = dyn_cast<CastExpr>(E)) + return getDeclFromExpr(CE->getSubExpr()); + if (ObjCMessageExpr *OME = dyn_cast<ObjCMessageExpr>(E)) + return OME->getMethodDecl(); + + return 0; +} + +static SourceLocation getLocationFromExpr(Expr *E) { + if (ObjCMessageExpr *Msg = dyn_cast<ObjCMessageExpr>(E)) + return /*FIXME:*/Msg->getLeftLoc(); + if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) + return DRE->getLocation(); + if (MemberExpr *Member = dyn_cast<MemberExpr>(E)) + return Member->getMemberLoc(); + if (ObjCIvarRefExpr *Ivar = dyn_cast<ObjCIvarRefExpr>(E)) + return Ivar->getLocation(); + return E->getLocStart(); +} + +extern "C" { + +unsigned clang_visitChildren(CXCursor parent, + CXCursorVisitor visitor, + CXClientData client_data) { + ASTUnit *CXXUnit = getCursorASTUnit(parent); + + 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; + } + + CursorVisitor CursorVis(CXXUnit, visitor, client_data, PCHLevel); + return CursorVis.VisitChildren(parent); +} + +static CXString getDeclSpelling(Decl *D) { + NamedDecl *ND = dyn_cast_or_null<NamedDecl>(D); + if (!ND) + return createCXString(""); + + if (ObjCMethodDecl *OMD = dyn_cast<ObjCMethodDecl>(ND)) + return createCXString(OMD->getSelector().getAsString()); + + if (ObjCCategoryImplDecl *CIMP = dyn_cast<ObjCCategoryImplDecl>(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 createCXString(CIMP->getIdentifier()->getNameStart()); + + if (ND->getIdentifier()) + return createCXString(ND->getIdentifier()->getNameStart()); + + return createCXString(""); +} + +CXString clang_getCursorSpelling(CXCursor C) { + if (clang_isTranslationUnit(C.kind)) + return clang_getTranslationUnitSpelling(C.data[2]); + + if (clang_isReference(C.kind)) { + switch (C.kind) { + case CXCursor_ObjCSuperClassRef: { + ObjCInterfaceDecl *Super = getCursorObjCSuperClassRef(C).first; + return createCXString(Super->getIdentifier()->getNameStart()); + } + case CXCursor_ObjCClassRef: { + ObjCInterfaceDecl *Class = getCursorObjCClassRef(C).first; + return createCXString(Class->getIdentifier()->getNameStart()); + } + case CXCursor_ObjCProtocolRef: { + ObjCProtocolDecl *OID = getCursorObjCProtocolRef(C).first; + assert(OID && "getCursorSpelling(): Missing protocol decl"); + return createCXString(OID->getIdentifier()->getNameStart()); + } + case CXCursor_TypeRef: { + TypeDecl *Type = getCursorTypeRef(C).first; + assert(Type && "Missing type decl"); + + return createCXString(getCursorContext(C).getTypeDeclType(Type). + getAsString()); + } + + default: + return createCXString("<not implemented>"); + } + } + + if (clang_isExpression(C.kind)) { + Decl *D = getDeclFromExpr(getCursorExpr(C)); + if (D) + return getDeclSpelling(D); + return createCXString(""); + } + + if (C.kind == CXCursor_MacroInstantiation) + return createCXString(getCursorMacroInstantiation(C)->getName() + ->getNameStart()); + + if (C.kind == CXCursor_MacroDefinition) + return createCXString(getCursorMacroDefinition(C)->getName() + ->getNameStart()); + + if (clang_isDeclaration(C.kind)) + return getDeclSpelling(getCursorDecl(C)); + + return createCXString(""); +} + +CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) { + switch (Kind) { + case CXCursor_FunctionDecl: + return createCXString("FunctionDecl"); + case CXCursor_TypedefDecl: + return createCXString("TypedefDecl"); + case CXCursor_EnumDecl: + return createCXString("EnumDecl"); + case CXCursor_EnumConstantDecl: + return createCXString("EnumConstantDecl"); + case CXCursor_StructDecl: + return createCXString("StructDecl"); + case CXCursor_UnionDecl: + return createCXString("UnionDecl"); + case CXCursor_ClassDecl: + return createCXString("ClassDecl"); + case CXCursor_FieldDecl: + return createCXString("FieldDecl"); + case CXCursor_VarDecl: + return createCXString("VarDecl"); + case CXCursor_ParmDecl: + return createCXString("ParmDecl"); + case CXCursor_ObjCInterfaceDecl: + return createCXString("ObjCInterfaceDecl"); + case CXCursor_ObjCCategoryDecl: + return createCXString("ObjCCategoryDecl"); + case CXCursor_ObjCProtocolDecl: + return createCXString("ObjCProtocolDecl"); + case CXCursor_ObjCPropertyDecl: + return createCXString("ObjCPropertyDecl"); + case CXCursor_ObjCIvarDecl: + return createCXString("ObjCIvarDecl"); + case CXCursor_ObjCInstanceMethodDecl: + return createCXString("ObjCInstanceMethodDecl"); + case CXCursor_ObjCClassMethodDecl: + return createCXString("ObjCClassMethodDecl"); + case CXCursor_ObjCImplementationDecl: + return createCXString("ObjCImplementationDecl"); + case CXCursor_ObjCCategoryImplDecl: + return createCXString("ObjCCategoryImplDecl"); + case CXCursor_CXXMethod: + return createCXString("CXXMethod"); + case CXCursor_UnexposedDecl: + return createCXString("UnexposedDecl"); + case CXCursor_ObjCSuperClassRef: + return createCXString("ObjCSuperClassRef"); + case CXCursor_ObjCProtocolRef: + return createCXString("ObjCProtocolRef"); + case CXCursor_ObjCClassRef: + return createCXString("ObjCClassRef"); + case CXCursor_TypeRef: + return createCXString("TypeRef"); + case CXCursor_UnexposedExpr: + return createCXString("UnexposedExpr"); + case CXCursor_BlockExpr: + return createCXString("BlockExpr"); + case CXCursor_DeclRefExpr: + return createCXString("DeclRefExpr"); + case CXCursor_MemberRefExpr: + return createCXString("MemberRefExpr"); + case CXCursor_CallExpr: + return createCXString("CallExpr"); + case CXCursor_ObjCMessageExpr: + return createCXString("ObjCMessageExpr"); + case CXCursor_UnexposedStmt: + return createCXString("UnexposedStmt"); + case CXCursor_InvalidFile: + return createCXString("InvalidFile"); + case CXCursor_InvalidCode: + return createCXString("InvalidCode"); + case CXCursor_NoDeclFound: + return createCXString("NoDeclFound"); + case CXCursor_NotImplemented: + return createCXString("NotImplemented"); + case CXCursor_TranslationUnit: + return createCXString("TranslationUnit"); + case CXCursor_UnexposedAttr: + return createCXString("UnexposedAttr"); + case CXCursor_IBActionAttr: + return createCXString("attribute(ibaction)"); + case CXCursor_IBOutletAttr: + return createCXString("attribute(iboutlet)"); + case CXCursor_PreprocessingDirective: + return createCXString("preprocessing directive"); + case CXCursor_MacroDefinition: + return createCXString("macro definition"); + case CXCursor_MacroInstantiation: + return createCXString("macro instantiation"); + } + + llvm_unreachable("Unhandled CXCursorKind"); + return createCXString(NULL); +} + +enum CXChildVisitResult GetCursorVisitor(CXCursor cursor, + CXCursor parent, + CXClientData client_data) { + CXCursor *BestCursor = static_cast<CXCursor *>(client_data); + *BestCursor = cursor; + return CXChildVisit_Recurse; +} + +CXCursor clang_getCursor(CXTranslationUnit TU, CXSourceLocation Loc) { + if (!TU) + return clang_getNullCursor(); + + ASTUnit *CXXUnit = static_cast<ASTUnit *>(TU); + + ASTUnit::ConcurrencyCheck Check(*CXXUnit); + + SourceLocation SLoc = cxloc::translateSourceLocation(Loc); + CXCursor Result = MakeCXCursorInvalid(CXCursor_NoDeclFound); + if (SLoc.isValid()) { + SourceRange RegionOfInterest(SLoc, SLoc.getFileLocWithOffset(1)); + + // FIXME: Would be great to have a "hint" cursor, then walk from that + // hint cursor upward until we find a cursor whose source range encloses + // the region of interest, rather than starting from the translation unit. + CXCursor Parent = clang_getTranslationUnitCursor(CXXUnit); + CursorVisitor CursorVis(CXXUnit, GetCursorVisitor, &Result, + Decl::MaxPCHLevel, RegionOfInterest); + CursorVis.VisitChildren(Parent); + } + return Result; +} + +CXCursor clang_getNullCursor(void) { + return MakeCXCursorInvalid(CXCursor_InvalidFile); +} + +unsigned clang_equalCursors(CXCursor X, CXCursor Y) { + return X == Y; +} + +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_isExpression(enum CXCursorKind K) { + return K >= CXCursor_FirstExpr && K <= CXCursor_LastExpr; +} + +unsigned clang_isStatement(enum CXCursorKind K) { + return K >= CXCursor_FirstStmt && K <= CXCursor_LastStmt; +} + +unsigned clang_isTranslationUnit(enum CXCursorKind K) { + return K == CXCursor_TranslationUnit; +} + +unsigned clang_isPreprocessing(enum CXCursorKind K) { + return K >= CXCursor_FirstPreprocessing && K <= CXCursor_LastPreprocessing; +} + +unsigned clang_isUnexposed(enum CXCursorKind K) { + switch (K) { + case CXCursor_UnexposedDecl: + case CXCursor_UnexposedExpr: + case CXCursor_UnexposedStmt: + case CXCursor_UnexposedAttr: + return true; + default: + return false; + } +} + +CXCursorKind clang_getCursorKind(CXCursor C) { + return C.kind; +} + +CXSourceLocation clang_getCursorLocation(CXCursor C) { + if (clang_isReference(C.kind)) { + switch (C.kind) { + case CXCursor_ObjCSuperClassRef: { + std::pair<ObjCInterfaceDecl *, SourceLocation> P + = getCursorObjCSuperClassRef(C); + return cxloc::translateSourceLocation(P.first->getASTContext(), P.second); + } + + case CXCursor_ObjCProtocolRef: { + std::pair<ObjCProtocolDecl *, SourceLocation> P + = getCursorObjCProtocolRef(C); + return cxloc::translateSourceLocation(P.first->getASTContext(), P.second); + } + + case CXCursor_ObjCClassRef: { + std::pair<ObjCInterfaceDecl *, SourceLocation> P + = getCursorObjCClassRef(C); + return cxloc::translateSourceLocation(P.first->getASTContext(), P.second); + } + + case CXCursor_TypeRef: { + std::pair<TypeDecl *, SourceLocation> P = getCursorTypeRef(C); + return cxloc::translateSourceLocation(P.first->getASTContext(), P.second); + } + + default: + // FIXME: Need a way to enumerate all non-reference cases. + llvm_unreachable("Missed a reference kind"); + } + } + + if (clang_isExpression(C.kind)) + return cxloc::translateSourceLocation(getCursorContext(C), + getLocationFromExpr(getCursorExpr(C))); + + if (C.kind == CXCursor_PreprocessingDirective) { + SourceLocation L = cxcursor::getCursorPreprocessingDirective(C).getBegin(); + return cxloc::translateSourceLocation(getCursorContext(C), L); + } + + if (C.kind == CXCursor_MacroInstantiation) { + SourceLocation L + = cxcursor::getCursorMacroInstantiation(C)->getSourceRange().getBegin(); + return cxloc::translateSourceLocation(getCursorContext(C), L); + } + + if (C.kind == CXCursor_MacroDefinition) { + SourceLocation L = cxcursor::getCursorMacroDefinition(C)->getLocation(); + return cxloc::translateSourceLocation(getCursorContext(C), L); + } + + if (!getCursorDecl(C)) + return clang_getNullLocation(); + + Decl *D = getCursorDecl(C); + SourceLocation Loc = D->getLocation(); + if (ObjCInterfaceDecl *Class = dyn_cast<ObjCInterfaceDecl>(D)) + Loc = Class->getClassLoc(); + return cxloc::translateSourceLocation(getCursorContext(C), Loc); +} + +CXSourceRange clang_getCursorExtent(CXCursor C) { + if (clang_isReference(C.kind)) { + switch (C.kind) { + case CXCursor_ObjCSuperClassRef: { + std::pair<ObjCInterfaceDecl *, SourceLocation> P + = getCursorObjCSuperClassRef(C); + return cxloc::translateSourceRange(P.first->getASTContext(), P.second); + } + + case CXCursor_ObjCProtocolRef: { + std::pair<ObjCProtocolDecl *, SourceLocation> P + = getCursorObjCProtocolRef(C); + return cxloc::translateSourceRange(P.first->getASTContext(), P.second); + } + + case CXCursor_ObjCClassRef: { + std::pair<ObjCInterfaceDecl *, SourceLocation> P + = getCursorObjCClassRef(C); + + return cxloc::translateSourceRange(P.first->getASTContext(), P.second); + } + + case CXCursor_TypeRef: { + std::pair<TypeDecl *, SourceLocation> P = getCursorTypeRef(C); + return cxloc::translateSourceRange(P.first->getASTContext(), P.second); + } + + default: + // FIXME: Need a way to enumerate all non-reference cases. + llvm_unreachable("Missed a reference kind"); + } + } + + if (clang_isExpression(C.kind)) + return cxloc::translateSourceRange(getCursorContext(C), + getCursorExpr(C)->getSourceRange()); + + if (clang_isStatement(C.kind)) + return cxloc::translateSourceRange(getCursorContext(C), + getCursorStmt(C)->getSourceRange()); + + if (C.kind == CXCursor_PreprocessingDirective) { + SourceRange R = cxcursor::getCursorPreprocessingDirective(C); + return cxloc::translateSourceRange(getCursorContext(C), R); + } + + if (C.kind == CXCursor_MacroInstantiation) { + SourceRange R = cxcursor::getCursorMacroInstantiation(C)->getSourceRange(); + return cxloc::translateSourceRange(getCursorContext(C), R); + } + + if (C.kind == CXCursor_MacroDefinition) { + SourceRange R = cxcursor::getCursorMacroDefinition(C)->getSourceRange(); + return cxloc::translateSourceRange(getCursorContext(C), R); + } + + if (!getCursorDecl(C)) + return clang_getNullRange(); + + Decl *D = getCursorDecl(C); + return cxloc::translateSourceRange(getCursorContext(C), D->getSourceRange()); +} + +CXCursor clang_getCursorReferenced(CXCursor C) { + if (clang_isInvalid(C.kind)) + return clang_getNullCursor(); + + ASTUnit *CXXUnit = getCursorASTUnit(C); + if (clang_isDeclaration(C.kind)) + return C; + + if (clang_isExpression(C.kind)) { + Decl *D = getDeclFromExpr(getCursorExpr(C)); + if (D) + return MakeCXCursor(D, CXXUnit); + return clang_getNullCursor(); + } + + if (C.kind == CXCursor_MacroInstantiation) { + if (MacroDefinition *Def = getCursorMacroInstantiation(C)->getDefinition()) + return MakeMacroDefinitionCursor(Def, CXXUnit); + } + + if (!clang_isReference(C.kind)) + return clang_getNullCursor(); + + switch (C.kind) { + case CXCursor_ObjCSuperClassRef: + return MakeCXCursor(getCursorObjCSuperClassRef(C).first, CXXUnit); + + case CXCursor_ObjCProtocolRef: { + return MakeCXCursor(getCursorObjCProtocolRef(C).first, CXXUnit); + + case CXCursor_ObjCClassRef: + return MakeCXCursor(getCursorObjCClassRef(C).first, CXXUnit); + + case CXCursor_TypeRef: + return MakeCXCursor(getCursorTypeRef(C).first, CXXUnit); + + default: + // We would prefer to enumerate all non-reference cursor kinds here. + llvm_unreachable("Unhandled reference cursor kind"); + break; + } + } + + return clang_getNullCursor(); +} + +CXCursor clang_getCursorDefinition(CXCursor C) { + if (clang_isInvalid(C.kind)) + return clang_getNullCursor(); + + ASTUnit *CXXUnit = getCursorASTUnit(C); + + bool WasReference = false; + if (clang_isReference(C.kind) || clang_isExpression(C.kind)) { + C = clang_getCursorReferenced(C); + WasReference = true; + } + + if (C.kind == CXCursor_MacroInstantiation) + return clang_getCursorReferenced(C); + + if (!clang_isDeclaration(C.kind)) + return clang_getNullCursor(); + + Decl *D = getCursorDecl(C); + if (!D) + return clang_getNullCursor(); + + switch (D->getKind()) { + // Declaration kinds that don't really separate the notions of + // declaration and definition. + case Decl::Namespace: + case Decl::Typedef: + case Decl::TemplateTypeParm: + case Decl::EnumConstant: + case Decl::Field: + case Decl::ObjCIvar: + case Decl::ObjCAtDefsField: + case Decl::ImplicitParam: + case Decl::ParmVar: + case Decl::NonTypeTemplateParm: + case Decl::TemplateTemplateParm: + case Decl::ObjCCategoryImpl: + case Decl::ObjCImplementation: + case Decl::LinkageSpec: + case Decl::ObjCPropertyImpl: + case Decl::FileScopeAsm: + case Decl::StaticAssert: + case Decl::Block: + return C; + + // Declaration kinds that don't make any sense here, but are + // nonetheless harmless. + case Decl::TranslationUnit: + break; + + // Declaration kinds for which the definition is not resolvable. + case Decl::UnresolvedUsingTypename: + case Decl::UnresolvedUsingValue: + break; + + case Decl::UsingDirective: + return MakeCXCursor(cast<UsingDirectiveDecl>(D)->getNominatedNamespace(), + CXXUnit); + + case Decl::NamespaceAlias: + return MakeCXCursor(cast<NamespaceAliasDecl>(D)->getNamespace(), CXXUnit); + + case Decl::Enum: + case Decl::Record: + case Decl::CXXRecord: + case Decl::ClassTemplateSpecialization: + case Decl::ClassTemplatePartialSpecialization: + if (TagDecl *Def = cast<TagDecl>(D)->getDefinition()) + return MakeCXCursor(Def, CXXUnit); + return clang_getNullCursor(); + + case Decl::Function: + case Decl::CXXMethod: + case Decl::CXXConstructor: + case Decl::CXXDestructor: + case Decl::CXXConversion: { + const FunctionDecl *Def = 0; + if (cast<FunctionDecl>(D)->getBody(Def)) + return MakeCXCursor(const_cast<FunctionDecl *>(Def), CXXUnit); + return clang_getNullCursor(); + } + + case Decl::Var: { + // Ask the variable if it has a definition. + if (VarDecl *Def = cast<VarDecl>(D)->getDefinition()) + return MakeCXCursor(Def, CXXUnit); + return clang_getNullCursor(); + } + + case Decl::FunctionTemplate: { + const FunctionDecl *Def = 0; + if (cast<FunctionTemplateDecl>(D)->getTemplatedDecl()->getBody(Def)) + return MakeCXCursor(Def->getDescribedFunctionTemplate(), CXXUnit); + return clang_getNullCursor(); + } + + case Decl::ClassTemplate: { + if (RecordDecl *Def = cast<ClassTemplateDecl>(D)->getTemplatedDecl() + ->getDefinition()) + return MakeCXCursor( + cast<CXXRecordDecl>(Def)->getDescribedClassTemplate(), + CXXUnit); + return clang_getNullCursor(); + } + + case Decl::Using: { + UsingDecl *Using = cast<UsingDecl>(D); + CXCursor Def = clang_getNullCursor(); + for (UsingDecl::shadow_iterator S = Using->shadow_begin(), + SEnd = Using->shadow_end(); + S != SEnd; ++S) { + if (Def != clang_getNullCursor()) { + // FIXME: We have no way to return multiple results. + return clang_getNullCursor(); + } + + Def = clang_getCursorDefinition(MakeCXCursor((*S)->getTargetDecl(), + CXXUnit)); + } + + return Def; + } + + case Decl::UsingShadow: + return clang_getCursorDefinition( + MakeCXCursor(cast<UsingShadowDecl>(D)->getTargetDecl(), + CXXUnit)); + + case Decl::ObjCMethod: { + ObjCMethodDecl *Method = cast<ObjCMethodDecl>(D); + if (Method->isThisDeclarationADefinition()) + return C; + + // Dig out the method definition in the associated + // @implementation, if we have it. + // FIXME: The ASTs should make finding the definition easier. + if (ObjCInterfaceDecl *Class + = dyn_cast<ObjCInterfaceDecl>(Method->getDeclContext())) + if (ObjCImplementationDecl *ClassImpl = Class->getImplementation()) + if (ObjCMethodDecl *Def = ClassImpl->getMethod(Method->getSelector(), + Method->isInstanceMethod())) + if (Def->isThisDeclarationADefinition()) + return MakeCXCursor(Def, CXXUnit); + + return clang_getNullCursor(); + } + + case Decl::ObjCCategory: + if (ObjCCategoryImplDecl *Impl + = cast<ObjCCategoryDecl>(D)->getImplementation()) + return MakeCXCursor(Impl, CXXUnit); + return clang_getNullCursor(); + + case Decl::ObjCProtocol: + if (!cast<ObjCProtocolDecl>(D)->isForwardDecl()) + return C; + return clang_getNullCursor(); + + case Decl::ObjCInterface: + // There are two notions of a "definition" for an Objective-C + // class: the interface and its implementation. When we resolved a + // reference to an Objective-C class, produce the @interface as + // the definition; when we were provided with the interface, + // produce the @implementation as the definition. + if (WasReference) { + if (!cast<ObjCInterfaceDecl>(D)->isForwardDecl()) + return C; + } else if (ObjCImplementationDecl *Impl + = cast<ObjCInterfaceDecl>(D)->getImplementation()) + return MakeCXCursor(Impl, CXXUnit); + return clang_getNullCursor(); + + case Decl::ObjCProperty: + // FIXME: We don't really know where to find the + // ObjCPropertyImplDecls that implement this property. + return clang_getNullCursor(); + + case Decl::ObjCCompatibleAlias: + if (ObjCInterfaceDecl *Class + = cast<ObjCCompatibleAliasDecl>(D)->getClassInterface()) + if (!Class->isForwardDecl()) + return MakeCXCursor(Class, CXXUnit); + + return clang_getNullCursor(); + + case Decl::ObjCForwardProtocol: { + ObjCForwardProtocolDecl *Forward = cast<ObjCForwardProtocolDecl>(D); + if (Forward->protocol_size() == 1) + return clang_getCursorDefinition( + MakeCXCursor(*Forward->protocol_begin(), + CXXUnit)); + + // FIXME: Cannot return multiple definitions. + return clang_getNullCursor(); + } + + case Decl::ObjCClass: { + ObjCClassDecl *Class = cast<ObjCClassDecl>(D); + if (Class->size() == 1) { + ObjCInterfaceDecl *IFace = Class->begin()->getInterface(); + if (!IFace->isForwardDecl()) + return MakeCXCursor(IFace, CXXUnit); + return clang_getNullCursor(); + } + + // FIXME: Cannot return multiple definitions. + return clang_getNullCursor(); + } + + case Decl::Friend: + if (NamedDecl *Friend = cast<FriendDecl>(D)->getFriendDecl()) + return clang_getCursorDefinition(MakeCXCursor(Friend, CXXUnit)); + return clang_getNullCursor(); + + case Decl::FriendTemplate: + if (NamedDecl *Friend = cast<FriendTemplateDecl>(D)->getFriendDecl()) + return clang_getCursorDefinition(MakeCXCursor(Friend, CXXUnit)); + return clang_getNullCursor(); + } + + return clang_getNullCursor(); +} + +unsigned clang_isCursorDefinition(CXCursor C) { + if (!clang_isDeclaration(C.kind)) + return 0; + + return clang_getCursorDefinition(C) == C; +} + +void clang_getDefinitionSpellingAndExtent(CXCursor C, + const char **startBuf, + const char **endBuf, + unsigned *startLine, + unsigned *startColumn, + unsigned *endLine, + unsigned *endColumn) { + assert(getCursorDecl(C) && "CXCursor has null decl"); + NamedDecl *ND = static_cast<NamedDecl *>(getCursorDecl(C)); + FunctionDecl *FD = dyn_cast<FunctionDecl>(ND); + CompoundStmt *Body = dyn_cast<CompoundStmt>(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()); +} + +void clang_enableStackTraces(void) { + llvm::sys::PrintStackTraceOnErrorSignal(); +} + +} // end: extern "C" + +//===----------------------------------------------------------------------===// +// Token-based Operations. +//===----------------------------------------------------------------------===// + +/* CXToken layout: + * int_data[0]: a CXTokenKind + * int_data[1]: starting token location + * int_data[2]: token length + * int_data[3]: reserved + * ptr_data: for identifiers and keywords, an IdentifierInfo*. + * otherwise unused. + */ +extern "C" { + +CXTokenKind clang_getTokenKind(CXToken CXTok) { + return static_cast<CXTokenKind>(CXTok.int_data[0]); +} + +CXString clang_getTokenSpelling(CXTranslationUnit TU, CXToken CXTok) { + switch (clang_getTokenKind(CXTok)) { + case CXToken_Identifier: + case CXToken_Keyword: + // We know we have an IdentifierInfo*, so use that. + return createCXString(static_cast<IdentifierInfo *>(CXTok.ptr_data) + ->getNameStart()); + + case CXToken_Literal: { + // We have stashed the starting pointer in the ptr_data field. Use it. + const char *Text = static_cast<const char *>(CXTok.ptr_data); + return createCXString(llvm::StringRef(Text, CXTok.int_data[2])); + } + + case CXToken_Punctuation: + case CXToken_Comment: + break; + } + + // We have to find the starting buffer pointer the hard way, by + // deconstructing the source location. + ASTUnit *CXXUnit = static_cast<ASTUnit *>(TU); + if (!CXXUnit) + return createCXString(""); + + SourceLocation Loc = SourceLocation::getFromRawEncoding(CXTok.int_data[1]); + std::pair<FileID, unsigned> LocInfo + = CXXUnit->getSourceManager().getDecomposedLoc(Loc); + bool Invalid = false; + llvm::StringRef Buffer + = CXXUnit->getSourceManager().getBufferData(LocInfo.first, &Invalid); + if (Invalid) + return createCXString(""); + + return createCXString(Buffer.substr(LocInfo.second, CXTok.int_data[2])); +} + +CXSourceLocation clang_getTokenLocation(CXTranslationUnit TU, CXToken CXTok) { + ASTUnit *CXXUnit = static_cast<ASTUnit *>(TU); + if (!CXXUnit) + return clang_getNullLocation(); + + return cxloc::translateSourceLocation(CXXUnit->getASTContext(), + SourceLocation::getFromRawEncoding(CXTok.int_data[1])); +} + +CXSourceRange clang_getTokenExtent(CXTranslationUnit TU, CXToken CXTok) { + ASTUnit *CXXUnit = static_cast<ASTUnit *>(TU); + if (!CXXUnit) + return clang_getNullRange(); + + return cxloc::translateSourceRange(CXXUnit->getASTContext(), + SourceLocation::getFromRawEncoding(CXTok.int_data[1])); +} + +void clang_tokenize(CXTranslationUnit TU, CXSourceRange Range, + CXToken **Tokens, unsigned *NumTokens) { + if (Tokens) + *Tokens = 0; + if (NumTokens) + *NumTokens = 0; + + ASTUnit *CXXUnit = static_cast<ASTUnit *>(TU); + if (!CXXUnit || !Tokens || !NumTokens) + return; + + ASTUnit::ConcurrencyCheck Check(*CXXUnit); + + SourceRange R = cxloc::translateCXSourceRange(Range); + if (R.isInvalid()) + return; + + SourceManager &SourceMgr = CXXUnit->getSourceManager(); + std::pair<FileID, unsigned> BeginLocInfo + = SourceMgr.getDecomposedLoc(R.getBegin()); + std::pair<FileID, unsigned> EndLocInfo + = SourceMgr.getDecomposedLoc(R.getEnd()); + + // Cannot tokenize across files. + if (BeginLocInfo.first != EndLocInfo.first) + return; + + // Create a lexer + bool Invalid = false; + llvm::StringRef Buffer + = SourceMgr.getBufferData(BeginLocInfo.first, &Invalid); + if (Invalid) + return; + + Lexer Lex(SourceMgr.getLocForStartOfFile(BeginLocInfo.first), + CXXUnit->getASTContext().getLangOptions(), + Buffer.begin(), Buffer.data() + BeginLocInfo.second, Buffer.end()); + Lex.SetCommentRetentionState(true); + + // Lex tokens until we hit the end of the range. + const char *EffectiveBufferEnd = Buffer.data() + EndLocInfo.second; + llvm::SmallVector<CXToken, 32> CXTokens; + Token Tok; + do { + // Lex the next token + Lex.LexFromRawLexer(Tok); + if (Tok.is(tok::eof)) + break; + + // Initialize the CXToken. + CXToken CXTok; + + // - Common fields + CXTok.int_data[1] = Tok.getLocation().getRawEncoding(); + CXTok.int_data[2] = Tok.getLength(); + CXTok.int_data[3] = 0; + + // - Kind-specific fields + if (Tok.isLiteral()) { + CXTok.int_data[0] = CXToken_Literal; + CXTok.ptr_data = (void *)Tok.getLiteralData(); + } else if (Tok.is(tok::identifier)) { + // Lookup the identifier to determine whether we have a keyword. + std::pair<FileID, unsigned> LocInfo + = SourceMgr.getDecomposedLoc(Tok.getLocation()); + bool Invalid = false; + llvm::StringRef Buf + = CXXUnit->getSourceManager().getBufferData(LocInfo.first, &Invalid); + if (Invalid) + return; + + const char *StartPos = Buf.data() + LocInfo.second; + IdentifierInfo *II + = CXXUnit->getPreprocessor().LookUpIdentifierInfo(Tok, StartPos); + CXTok.int_data[0] = II->getTokenID() == tok::identifier? + CXToken_Identifier + : CXToken_Keyword; + CXTok.ptr_data = II; + } else if (Tok.is(tok::comment)) { + CXTok.int_data[0] = CXToken_Comment; + CXTok.ptr_data = 0; + } else { + CXTok.int_data[0] = CXToken_Punctuation; + CXTok.ptr_data = 0; + } + CXTokens.push_back(CXTok); + } while (Lex.getBufferLocation() <= EffectiveBufferEnd); + + if (CXTokens.empty()) + return; + + *Tokens = (CXToken *)malloc(sizeof(CXToken) * CXTokens.size()); + memmove(*Tokens, CXTokens.data(), sizeof(CXToken) * CXTokens.size()); + *NumTokens = CXTokens.size(); +} + +typedef llvm::DenseMap<unsigned, CXCursor> AnnotateTokensData; + +enum CXChildVisitResult AnnotateTokensVisitor(CXCursor cursor, + CXCursor parent, + CXClientData client_data) { + AnnotateTokensData *Data = static_cast<AnnotateTokensData *>(client_data); + + // We only annotate the locations of declarations, simple + // references, and expressions which directly reference something. + CXCursorKind Kind = clang_getCursorKind(cursor); + if (clang_isDeclaration(Kind) || clang_isReference(Kind)) { + // Okay: We can annotate the location of this declaration with the + // declaration or reference + } else if (clang_isExpression(cursor.kind)) { + if (Kind != CXCursor_DeclRefExpr && + Kind != CXCursor_MemberRefExpr && + Kind != CXCursor_ObjCMessageExpr) + return CXChildVisit_Recurse; + + CXCursor Referenced = clang_getCursorReferenced(cursor); + if (Referenced == cursor || Referenced == clang_getNullCursor()) + return CXChildVisit_Recurse; + + // Okay: we can annotate the location of this expression + } else if (clang_isPreprocessing(cursor.kind)) { + // We can always annotate a preprocessing directive/macro instantiation. + } else { + // Nothing to annotate + return CXChildVisit_Recurse; + } + + CXSourceLocation Loc = clang_getCursorLocation(cursor); + (*Data)[Loc.int_data] = cursor; + return CXChildVisit_Recurse; +} + +void clang_annotateTokens(CXTranslationUnit TU, + CXToken *Tokens, unsigned NumTokens, + CXCursor *Cursors) { + if (NumTokens == 0) + return; + + // Any token we don't specifically annotate will have a NULL cursor. + for (unsigned I = 0; I != NumTokens; ++I) + Cursors[I] = clang_getNullCursor(); + + ASTUnit *CXXUnit = static_cast<ASTUnit *>(TU); + if (!CXXUnit || !Tokens) + return; + + ASTUnit::ConcurrencyCheck Check(*CXXUnit); + + // Determine the region of interest, which contains all of the tokens. + SourceRange RegionOfInterest; + RegionOfInterest.setBegin( + cxloc::translateSourceLocation(clang_getTokenLocation(TU, Tokens[0]))); + SourceLocation End + = cxloc::translateSourceLocation(clang_getTokenLocation(TU, + Tokens[NumTokens - 1])); + RegionOfInterest.setEnd(CXXUnit->getPreprocessor().getLocForEndOfToken(End)); + + // A mapping from the source locations found when re-lexing or traversing the + // region of interest to the corresponding cursors. + AnnotateTokensData Annotated; + + // Relex the tokens within the source range to look for preprocessing + // directives. + SourceManager &SourceMgr = CXXUnit->getSourceManager(); + std::pair<FileID, unsigned> BeginLocInfo + = SourceMgr.getDecomposedLoc(RegionOfInterest.getBegin()); + std::pair<FileID, unsigned> EndLocInfo + = SourceMgr.getDecomposedLoc(RegionOfInterest.getEnd()); + + llvm::StringRef Buffer; + bool Invalid = false; + if (BeginLocInfo.first == EndLocInfo.first && + ((Buffer = SourceMgr.getBufferData(BeginLocInfo.first, &Invalid)),true) && + !Invalid) { + Lexer Lex(SourceMgr.getLocForStartOfFile(BeginLocInfo.first), + CXXUnit->getASTContext().getLangOptions(), + Buffer.begin(), Buffer.data() + BeginLocInfo.second, + Buffer.end()); + Lex.SetCommentRetentionState(true); + + // Lex tokens in raw mode until we hit the end of the range, to avoid + // entering #includes or expanding macros. + while (true) { + Token Tok; + Lex.LexFromRawLexer(Tok); + + reprocess: + if (Tok.is(tok::hash) && Tok.isAtStartOfLine()) { + // We have found a preprocessing directive. Gobble it up so that we + // don't see it while preprocessing these tokens later, but keep track of + // all of the token locations inside this preprocessing directive so that + // we can annotate them appropriately. + // + // FIXME: Some simple tests here could identify macro definitions and + // #undefs, to provide specific cursor kinds for those. + std::vector<SourceLocation> Locations; + do { + Locations.push_back(Tok.getLocation()); + Lex.LexFromRawLexer(Tok); + } while (!Tok.isAtStartOfLine() && !Tok.is(tok::eof)); + + using namespace cxcursor; + CXCursor Cursor + = MakePreprocessingDirectiveCursor(SourceRange(Locations.front(), + Locations.back()), + CXXUnit); + for (unsigned I = 0, N = Locations.size(); I != N; ++I) { + Annotated[Locations[I].getRawEncoding()] = Cursor; + } + + if (Tok.isAtStartOfLine()) + goto reprocess; + + continue; + } + + if (Tok.is(tok::eof)) + break; + } + } + + // Annotate all of the source locations in the region of interest that map to + // a specific cursor. + CXCursor Parent = clang_getTranslationUnitCursor(CXXUnit); + CursorVisitor AnnotateVis(CXXUnit, AnnotateTokensVisitor, &Annotated, + Decl::MaxPCHLevel, RegionOfInterest); + AnnotateVis.VisitChildren(Parent); + + for (unsigned I = 0; I != NumTokens; ++I) { + // Determine whether we saw a cursor at this token's location. + AnnotateTokensData::iterator Pos = Annotated.find(Tokens[I].int_data[1]); + if (Pos == Annotated.end()) + continue; + + Cursors[I] = Pos->second; + } +} + +void clang_disposeTokens(CXTranslationUnit TU, + CXToken *Tokens, unsigned NumTokens) { + free(Tokens); +} + +} // end: extern "C" + +//===----------------------------------------------------------------------===// +// Operations for querying linkage of a cursor. +//===----------------------------------------------------------------------===// + +extern "C" { +CXLinkageKind clang_getCursorLinkage(CXCursor cursor) { + if (!clang_isDeclaration(cursor.kind)) + return CXLinkage_Invalid; + + Decl *D = cxcursor::getCursorDecl(cursor); + if (NamedDecl *ND = dyn_cast_or_null<NamedDecl>(D)) + switch (ND->getLinkage()) { + case NoLinkage: return CXLinkage_NoLinkage; + case InternalLinkage: return CXLinkage_Internal; + case UniqueExternalLinkage: return CXLinkage_UniqueExternal; + case ExternalLinkage: return CXLinkage_External; + }; + + return CXLinkage_Invalid; +} +} // end: extern "C" + +//===----------------------------------------------------------------------===// +// Operations for querying language of a cursor. +//===----------------------------------------------------------------------===// + +static CXLanguageKind getDeclLanguage(const Decl *D) { + switch (D->getKind()) { + default: + break; + case Decl::ImplicitParam: + case Decl::ObjCAtDefsField: + case Decl::ObjCCategory: + case Decl::ObjCCategoryImpl: + case Decl::ObjCClass: + case Decl::ObjCCompatibleAlias: + case Decl::ObjCForwardProtocol: + case Decl::ObjCImplementation: + case Decl::ObjCInterface: + case Decl::ObjCIvar: + case Decl::ObjCMethod: + case Decl::ObjCProperty: + case Decl::ObjCPropertyImpl: + case Decl::ObjCProtocol: + return CXLanguage_ObjC; + case Decl::CXXConstructor: + case Decl::CXXConversion: + case Decl::CXXDestructor: + case Decl::CXXMethod: + case Decl::CXXRecord: + case Decl::ClassTemplate: + case Decl::ClassTemplatePartialSpecialization: + case Decl::ClassTemplateSpecialization: + case Decl::Friend: + case Decl::FriendTemplate: + case Decl::FunctionTemplate: + case Decl::LinkageSpec: + case Decl::Namespace: + case Decl::NamespaceAlias: + case Decl::NonTypeTemplateParm: + case Decl::StaticAssert: + case Decl::TemplateTemplateParm: + case Decl::TemplateTypeParm: + case Decl::UnresolvedUsingTypename: + case Decl::UnresolvedUsingValue: + case Decl::Using: + case Decl::UsingDirective: + case Decl::UsingShadow: + return CXLanguage_CPlusPlus; + } + + return CXLanguage_C; +} + +extern "C" { +CXLanguageKind clang_getCursorLanguage(CXCursor cursor) { + if (clang_isDeclaration(cursor.kind)) + return getDeclLanguage(cxcursor::getCursorDecl(cursor)); + + return CXLanguage_Invalid; +} +} // 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" + +namespace clang { namespace cxstring { +CXString 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; +} + +CXString createCXString(llvm::StringRef String, bool DupString) { + CXString Result; + if (DupString || (!String.empty() && String.data()[String.size()] != 0)) { + char *Spelling = (char *)malloc(String.size() + 1); + memmove(Spelling, String.data(), String.size()); + Spelling[String.size()] = 0; + Result.Spelling = Spelling; + Result.MustFreeString = 1; + } else { + Result.Spelling = String.data(); + Result.MustFreeString = 0; + } + return Result; +} +}} + +//===----------------------------------------------------------------------===// +// Misc. utility functions. +//===----------------------------------------------------------------------===// + +extern "C" { + +CXString clang_getClangVersion() { + return createCXString(getClangFullVersion()); +} + +} // end: extern "C" diff --git a/tools/libclang/CIndexCodeCompletion.cpp b/tools/libclang/CIndexCodeCompletion.cpp new file mode 100644 index 0000000..a21614c --- /dev/null +++ b/tools/libclang/CIndexCodeCompletion.cpp @@ -0,0 +1,512 @@ +//===- CIndexCodeCompletion.cpp - Code Completion API hooks ---------------===// +// +// 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 Clang-C Source Indexing library hooks for +// code completion. +// +//===----------------------------------------------------------------------===// + +#include "CIndexer.h" +#include "CIndexDiagnostic.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/FileManager.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Sema/CodeCompleteConsumer.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/System/Program.h" + +#ifdef UDP_CODE_COMPLETION_LOGGER +#include "clang/Basic/Version.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/raw_ostream.h" +#include <arpa/inet.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <unistd.h> +#endif + +using namespace clang; +using namespace clang::cxstring; + +extern "C" { + +enum CXCompletionChunkKind +clang_getCompletionChunkKind(CXCompletionString completion_string, + unsigned chunk_number) { + CodeCompletionString *CCStr = (CodeCompletionString *)completion_string; + if (!CCStr || chunk_number >= CCStr->size()) + return CXCompletionChunk_Text; + + switch ((*CCStr)[chunk_number].Kind) { + case CodeCompletionString::CK_TypedText: + return CXCompletionChunk_TypedText; + case CodeCompletionString::CK_Text: + return CXCompletionChunk_Text; + case CodeCompletionString::CK_Optional: + return CXCompletionChunk_Optional; + case CodeCompletionString::CK_Placeholder: + return CXCompletionChunk_Placeholder; + case CodeCompletionString::CK_Informative: + return CXCompletionChunk_Informative; + case CodeCompletionString::CK_ResultType: + return CXCompletionChunk_ResultType; + case CodeCompletionString::CK_CurrentParameter: + return CXCompletionChunk_CurrentParameter; + case CodeCompletionString::CK_LeftParen: + return CXCompletionChunk_LeftParen; + case CodeCompletionString::CK_RightParen: + return CXCompletionChunk_RightParen; + case CodeCompletionString::CK_LeftBracket: + return CXCompletionChunk_LeftBracket; + case CodeCompletionString::CK_RightBracket: + return CXCompletionChunk_RightBracket; + case CodeCompletionString::CK_LeftBrace: + return CXCompletionChunk_LeftBrace; + case CodeCompletionString::CK_RightBrace: + return CXCompletionChunk_RightBrace; + case CodeCompletionString::CK_LeftAngle: + return CXCompletionChunk_LeftAngle; + case CodeCompletionString::CK_RightAngle: + return CXCompletionChunk_RightAngle; + case CodeCompletionString::CK_Comma: + return CXCompletionChunk_Comma; + case CodeCompletionString::CK_Colon: + return CXCompletionChunk_Colon; + case CodeCompletionString::CK_SemiColon: + return CXCompletionChunk_SemiColon; + case CodeCompletionString::CK_Equal: + return CXCompletionChunk_Equal; + case CodeCompletionString::CK_HorizontalSpace: + return CXCompletionChunk_HorizontalSpace; + case CodeCompletionString::CK_VerticalSpace: + return CXCompletionChunk_VerticalSpace; + } + + // Should be unreachable, but let's be careful. + return CXCompletionChunk_Text; +} + +CXString clang_getCompletionChunkText(CXCompletionString completion_string, + unsigned chunk_number) { + CodeCompletionString *CCStr = (CodeCompletionString *)completion_string; + if (!CCStr || chunk_number >= CCStr->size()) + return createCXString(0); + + switch ((*CCStr)[chunk_number].Kind) { + case CodeCompletionString::CK_TypedText: + case CodeCompletionString::CK_Text: + case CodeCompletionString::CK_Placeholder: + case CodeCompletionString::CK_CurrentParameter: + case CodeCompletionString::CK_Informative: + case CodeCompletionString::CK_LeftParen: + case CodeCompletionString::CK_RightParen: + case CodeCompletionString::CK_LeftBracket: + case CodeCompletionString::CK_RightBracket: + case CodeCompletionString::CK_LeftBrace: + case CodeCompletionString::CK_RightBrace: + case CodeCompletionString::CK_LeftAngle: + case CodeCompletionString::CK_RightAngle: + case CodeCompletionString::CK_Comma: + case CodeCompletionString::CK_ResultType: + case CodeCompletionString::CK_Colon: + case CodeCompletionString::CK_SemiColon: + case CodeCompletionString::CK_Equal: + case CodeCompletionString::CK_HorizontalSpace: + case CodeCompletionString::CK_VerticalSpace: + return createCXString((*CCStr)[chunk_number].Text, false); + + case CodeCompletionString::CK_Optional: + // Note: treated as an empty text block. + return createCXString(""); + } + + // Should be unreachable, but let's be careful. + return createCXString(0); +} + + +CXCompletionString +clang_getCompletionChunkCompletionString(CXCompletionString completion_string, + unsigned chunk_number) { + CodeCompletionString *CCStr = (CodeCompletionString *)completion_string; + if (!CCStr || chunk_number >= CCStr->size()) + return 0; + + switch ((*CCStr)[chunk_number].Kind) { + case CodeCompletionString::CK_TypedText: + case CodeCompletionString::CK_Text: + case CodeCompletionString::CK_Placeholder: + case CodeCompletionString::CK_CurrentParameter: + case CodeCompletionString::CK_Informative: + case CodeCompletionString::CK_LeftParen: + case CodeCompletionString::CK_RightParen: + case CodeCompletionString::CK_LeftBracket: + case CodeCompletionString::CK_RightBracket: + case CodeCompletionString::CK_LeftBrace: + case CodeCompletionString::CK_RightBrace: + case CodeCompletionString::CK_LeftAngle: + case CodeCompletionString::CK_RightAngle: + case CodeCompletionString::CK_Comma: + case CodeCompletionString::CK_ResultType: + case CodeCompletionString::CK_Colon: + case CodeCompletionString::CK_SemiColon: + case CodeCompletionString::CK_Equal: + case CodeCompletionString::CK_HorizontalSpace: + case CodeCompletionString::CK_VerticalSpace: + return 0; + + case CodeCompletionString::CK_Optional: + // Note: treated as an empty text block. + return (*CCStr)[chunk_number].Optional; + } + + // Should be unreachable, but let's be careful. + return 0; +} + +unsigned clang_getNumCompletionChunks(CXCompletionString completion_string) { + CodeCompletionString *CCStr = (CodeCompletionString *)completion_string; + return CCStr? CCStr->size() : 0; +} + +static bool ReadUnsigned(const char *&Memory, const char *MemoryEnd, + unsigned &Value) { + if (Memory + sizeof(unsigned) > MemoryEnd) + return true; + + memmove(&Value, Memory, sizeof(unsigned)); + Memory += sizeof(unsigned); + return false; +} + +/// \brief The CXCodeCompleteResults structure we allocate internally; +/// the client only sees the initial CXCodeCompleteResults structure. +struct AllocatedCXCodeCompleteResults : public CXCodeCompleteResults { + AllocatedCXCodeCompleteResults(); + ~AllocatedCXCodeCompleteResults(); + + /// \brief The memory buffer from which we parsed the results. We + /// retain this buffer because the completion strings point into it. + llvm::MemoryBuffer *Buffer; + + /// \brief Diagnostics produced while performing code completion. + llvm::SmallVector<StoredDiagnostic, 8> Diagnostics; + + /// \brief Diag object + Diagnostic Diag; + + /// \brief Language options used to adjust source locations. + LangOptions LangOpts; + + /// \brief Source manager, used for diagnostics. + SourceManager SourceMgr; + + /// \brief File manager, used for diagnostics. + FileManager FileMgr; + + /// \brief Temporary files that should be removed once we have finished + /// with the code-completion results. + std::vector<llvm::sys::Path> TemporaryFiles; +}; + +AllocatedCXCodeCompleteResults::AllocatedCXCodeCompleteResults() + : CXCodeCompleteResults(), Buffer(0), SourceMgr(Diag) { } + +AllocatedCXCodeCompleteResults::~AllocatedCXCodeCompleteResults() { + for (unsigned I = 0, N = NumResults; I != N; ++I) + delete (CodeCompletionString *)Results[I].CompletionString; + delete [] Results; + delete Buffer; + + for (unsigned I = 0, N = TemporaryFiles.size(); I != N; ++I) + TemporaryFiles[I].eraseFromDisk(); +} + +CXCodeCompleteResults *clang_codeComplete(CXIndex CIdx, + const char *source_filename, + int num_command_line_args, + const char **command_line_args, + unsigned num_unsaved_files, + struct CXUnsavedFile *unsaved_files, + const char *complete_filename, + unsigned complete_line, + unsigned complete_column) { +#ifdef UDP_CODE_COMPLETION_LOGGER +#ifdef UDP_CODE_COMPLETION_LOGGER_PORT + const llvm::TimeRecord &StartTime = llvm::TimeRecord::getCurrentTime(); +#endif +#endif + + // The indexer, which is mainly used to determine where diagnostics go. + CIndexer *CXXIdx = static_cast<CIndexer *>(CIdx); + + // Configure the diagnostics. + DiagnosticOptions DiagOpts; + llvm::IntrusiveRefCntPtr<Diagnostic> Diags; + Diags = CompilerInstance::createDiagnostics(DiagOpts, 0, 0); + + // The set of temporary files that we've built. + std::vector<llvm::sys::Path> TemporaryFiles; + + // Build up the arguments for invoking 'clang'. + std::vector<const char *> argv; + + // First add the complete path to the 'clang' executable. + llvm::sys::Path ClangPath = CXXIdx->getClangPath(); + argv.push_back(ClangPath.c_str()); + + // Add the '-fsyntax-only' argument so that we only perform a basic + // syntax check of the code. + argv.push_back("-fsyntax-only"); + + // Add the appropriate '-code-completion-at=file:line:column' argument + // to perform code completion, with an "-Xclang" preceding it. + std::string code_complete_at; + code_complete_at += complete_filename; + code_complete_at += ":"; + code_complete_at += llvm::utostr(complete_line); + code_complete_at += ":"; + code_complete_at += llvm::utostr(complete_column); + argv.push_back("-Xclang"); + argv.push_back("-code-completion-at"); + argv.push_back("-Xclang"); + argv.push_back(code_complete_at.c_str()); + argv.push_back("-Xclang"); + argv.push_back("-no-code-completion-debug-printer"); + argv.push_back("-Xclang"); + argv.push_back("-code-completion-macros"); + argv.push_back("-fdiagnostics-binary"); + + // Remap any unsaved files to temporary files. + std::vector<std::string> RemapArgs; + if (RemapFiles(num_unsaved_files, unsaved_files, RemapArgs, TemporaryFiles)) + return 0; + + // The pointers into the elements of RemapArgs are stable because we + // won't be adding anything to RemapArgs after this point. + for (unsigned i = 0, e = RemapArgs.size(); i != e; ++i) + argv.push_back(RemapArgs[i].c_str()); + + // Add the source file name (FIXME: later, we'll want to build temporary + // file from the buffer, or just feed the source text via standard input). + if (source_filename) + argv.push_back(source_filename); + + // 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); + + // Generate a temporary name for the code-completion results file. + char tmpFile[L_tmpnam]; + char *tmpFileName = tmpnam(tmpFile); + llvm::sys::Path ResultsFile(tmpFileName); + TemporaryFiles.push_back(ResultsFile); + + // Generate a temporary name for the diagnostics file. + char tmpFileResults[L_tmpnam]; + char *tmpResultsFileName = tmpnam(tmpFileResults); + llvm::sys::Path DiagnosticsFile(tmpResultsFileName); + TemporaryFiles.push_back(DiagnosticsFile); + + // 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, &ResultsFile, + &DiagnosticsFile, 0 }; + llvm::sys::Program::ExecuteAndWait(ClangPath, &argv[0], /* env */ NULL, + /* redirects */ &Redirects[0], + /* secondsToWait */ 0, + /* memoryLimits */ 0, &ErrMsg); + + if (!ErrMsg.empty()) { + std::string AllArgs; + for (std::vector<const char*>::iterator I = argv.begin(), E = argv.end(); + I != E; ++I) { + AllArgs += ' '; + if (*I) + AllArgs += *I; + } + + Diags->Report(diag::err_fe_invoking) << AllArgs << ErrMsg; + } + + // Parse the resulting source file to find code-completion results. + using llvm::MemoryBuffer; + using llvm::StringRef; + AllocatedCXCodeCompleteResults *Results = new AllocatedCXCodeCompleteResults; + Results->Results = 0; + Results->NumResults = 0; + Results->Buffer = 0; + // FIXME: Set Results->LangOpts! + if (MemoryBuffer *F = MemoryBuffer::getFile(ResultsFile.c_str())) { + llvm::SmallVector<CXCompletionResult, 4> CompletionResults; + StringRef Buffer = F->getBuffer(); + for (const char *Str = Buffer.data(), *StrEnd = Str + Buffer.size(); + Str < StrEnd;) { + unsigned KindValue; + if (ReadUnsigned(Str, StrEnd, KindValue)) + break; + + CodeCompletionString *CCStr + = CodeCompletionString::Deserialize(Str, StrEnd); + if (!CCStr) + continue; + + if (!CCStr->empty()) { + // Vend the code-completion result to the caller. + CXCompletionResult Result; + Result.CursorKind = (CXCursorKind)KindValue; + Result.CompletionString = CCStr; + CompletionResults.push_back(Result); + } + }; + + // Allocate the results. + Results->Results = new CXCompletionResult [CompletionResults.size()]; + Results->NumResults = CompletionResults.size(); + memcpy(Results->Results, CompletionResults.data(), + CompletionResults.size() * sizeof(CXCompletionResult)); + Results->Buffer = F; + } + + LoadSerializedDiagnostics(DiagnosticsFile, num_unsaved_files, unsaved_files, + Results->FileMgr, Results->SourceMgr, + Results->Diagnostics); + + // Make sure we delete temporary files when the code-completion results are + // destroyed. + Results->TemporaryFiles.swap(TemporaryFiles); + +#ifdef UDP_CODE_COMPLETION_LOGGER +#ifdef UDP_CODE_COMPLETION_LOGGER_PORT + const llvm::TimeRecord &EndTime = llvm::TimeRecord::getCurrentTime(); + llvm::SmallString<256> LogResult; + llvm::raw_svector_ostream os(LogResult); + + // Figure out the language and whether or not it uses PCH. + const char *lang = 0; + bool usesPCH = false; + + for (std::vector<const char*>::iterator I = argv.begin(), E = argv.end(); + I != E; ++I) { + if (*I == 0) + continue; + if (strcmp(*I, "-x") == 0) { + if (I + 1 != E) { + lang = *(++I); + continue; + } + } + else if (strcmp(*I, "-include") == 0) { + if (I+1 != E) { + const char *arg = *(++I); + llvm::SmallString<512> pchName; + { + llvm::raw_svector_ostream os(pchName); + os << arg << ".pth"; + } + pchName.push_back('\0'); + struct stat stat_results; + if (stat(pchName.data(), &stat_results) == 0) + usesPCH = true; + continue; + } + } + } + + os << "{ "; + os << "\"wall\": " << (EndTime.getWallTime() - StartTime.getWallTime()); + os << ", \"numRes\": " << Results->NumResults; + os << ", \"diags\": " << Results->Diagnostics.size(); + os << ", \"pch\": " << (usesPCH ? "true" : "false"); + os << ", \"lang\": \"" << (lang ? lang : "<unknown>") << '"'; + const char *name = getlogin(); + os << ", \"user\": \"" << (name ? name : "unknown") << '"'; + os << ", \"clangVer\": \"" << getClangFullVersion() << '"'; + os << " }"; + + llvm::StringRef res = os.str(); + if (res.size() > 0) { + do { + // Setup the UDP socket. + struct sockaddr_in servaddr; + bzero(&servaddr, sizeof(servaddr)); + servaddr.sin_family = AF_INET; + servaddr.sin_port = htons(UDP_CODE_COMPLETION_LOGGER_PORT); + if (inet_pton(AF_INET, UDP_CODE_COMPLETION_LOGGER, + &servaddr.sin_addr) <= 0) + break; + + int sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) + break; + + sendto(sockfd, res.data(), res.size(), 0, + (struct sockaddr *)&servaddr, sizeof(servaddr)); + close(sockfd); + } + while (false); + } +#endif +#endif + return Results; +} + +void clang_disposeCodeCompleteResults(CXCodeCompleteResults *ResultsIn) { + if (!ResultsIn) + return; + + AllocatedCXCodeCompleteResults *Results + = static_cast<AllocatedCXCodeCompleteResults*>(ResultsIn); + delete Results; +} + +unsigned +clang_codeCompleteGetNumDiagnostics(CXCodeCompleteResults *ResultsIn) { + AllocatedCXCodeCompleteResults *Results + = static_cast<AllocatedCXCodeCompleteResults*>(ResultsIn); + if (!Results) + return 0; + + return Results->Diagnostics.size(); +} + +CXDiagnostic +clang_codeCompleteGetDiagnostic(CXCodeCompleteResults *ResultsIn, + unsigned Index) { + AllocatedCXCodeCompleteResults *Results + = static_cast<AllocatedCXCodeCompleteResults*>(ResultsIn); + if (!Results || Index >= Results->Diagnostics.size()) + return 0; + + return new CXStoredDiagnostic(Results->Diagnostics[Index], Results->LangOpts); +} + + +} // end extern "C" diff --git a/tools/libclang/CIndexDiagnostic.cpp b/tools/libclang/CIndexDiagnostic.cpp new file mode 100644 index 0000000..3db37b9 --- /dev/null +++ b/tools/libclang/CIndexDiagnostic.cpp @@ -0,0 +1,285 @@ +/*===-- CIndexDiagnostics.cpp - Diagnostics C Interface ---------*- C++ -*-===*\ +|* *| +|* The LLVM Compiler Infrastructure *| +|* *| +|* This file is distributed under the University of Illinois Open Source *| +|* License. See LICENSE.TXT for details. *| +|* *| +|*===----------------------------------------------------------------------===*| +|* *| +|* Implements the diagnostic functions of the Clang C interface. *| +|* *| +\*===----------------------------------------------------------------------===*/ +#include "CIndexDiagnostic.h" +#include "CIndexer.h" +#include "CXSourceLocation.h" + +#include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace clang::cxloc; +using namespace clang::cxstring; +using namespace llvm; + +//----------------------------------------------------------------------------- +// C Interface Routines +//----------------------------------------------------------------------------- +extern "C" { + +unsigned clang_getNumDiagnostics(CXTranslationUnit Unit) { + ASTUnit *CXXUnit = static_cast<ASTUnit *>(Unit); + return CXXUnit? CXXUnit->stored_diag_size() : 0; +} + +CXDiagnostic clang_getDiagnostic(CXTranslationUnit Unit, unsigned Index) { + ASTUnit *CXXUnit = static_cast<ASTUnit *>(Unit); + if (!CXXUnit || Index >= CXXUnit->stored_diag_size()) + return 0; + + return new CXStoredDiagnostic(CXXUnit->stored_diag_begin()[Index], + CXXUnit->getASTContext().getLangOptions()); +} + +void clang_disposeDiagnostic(CXDiagnostic Diagnostic) { + CXStoredDiagnostic *Stored = static_cast<CXStoredDiagnostic *>(Diagnostic); + delete Stored; +} + +CXString clang_formatDiagnostic(CXDiagnostic Diagnostic, unsigned Options) { + if (!Diagnostic) + return createCXString(""); + + CXDiagnosticSeverity Severity = clang_getDiagnosticSeverity(Diagnostic); + + // Ignore diagnostics that should be ignored. + if (Severity == CXDiagnostic_Ignored) + return createCXString(""); + + llvm::SmallString<256> Str; + llvm::raw_svector_ostream Out(Str); + + if (Options & CXDiagnostic_DisplaySourceLocation) { + // Print source location (file:line), along with optional column + // and source ranges. + CXFile File; + unsigned Line, Column; + clang_getInstantiationLocation(clang_getDiagnosticLocation(Diagnostic), + &File, &Line, &Column, 0); + if (File) { + CXString FName = clang_getFileName(File); + Out << clang_getCString(FName) << ":" << Line << ":"; + clang_disposeString(FName); + if (Options & CXDiagnostic_DisplayColumn) + Out << Column << ":"; + + if (Options & CXDiagnostic_DisplaySourceRanges) { + unsigned N = clang_getDiagnosticNumRanges(Diagnostic); + bool PrintedRange = false; + for (unsigned I = 0; I != N; ++I) { + CXFile StartFile, EndFile; + CXSourceRange Range = clang_getDiagnosticRange(Diagnostic, I); + + unsigned StartLine, StartColumn, EndLine, EndColumn; + clang_getInstantiationLocation(clang_getRangeStart(Range), + &StartFile, &StartLine, &StartColumn, + 0); + clang_getInstantiationLocation(clang_getRangeEnd(Range), + &EndFile, &EndLine, &EndColumn, 0); + + if (StartFile != EndFile || StartFile != File) + continue; + + Out << "{" << StartLine << ":" << StartColumn << "-" + << EndLine << ":" << EndColumn << "}"; + PrintedRange = true; + } + if (PrintedRange) + Out << ":"; + } + } + + Out << " "; + } + + /* Print warning/error/etc. */ + switch (Severity) { + case CXDiagnostic_Ignored: assert(0 && "impossible"); break; + case CXDiagnostic_Note: Out << "note: "; break; + case CXDiagnostic_Warning: Out << "warning: "; break; + case CXDiagnostic_Error: Out << "error: "; break; + case CXDiagnostic_Fatal: Out << "fatal error: "; break; + } + + CXString Text = clang_getDiagnosticSpelling(Diagnostic); + if (clang_getCString(Text)) + Out << clang_getCString(Text); + else + Out << "<no diagnostic text>"; + clang_disposeString(Text); + return createCXString(Out.str(), true); +} + +unsigned clang_defaultDiagnosticDisplayOptions() { + return CXDiagnostic_DisplaySourceLocation | CXDiagnostic_DisplayColumn; +} + +enum CXDiagnosticSeverity clang_getDiagnosticSeverity(CXDiagnostic Diag) { + CXStoredDiagnostic *StoredDiag = static_cast<CXStoredDiagnostic *>(Diag); + if (!StoredDiag) + return CXDiagnostic_Ignored; + + switch (StoredDiag->Diag.getLevel()) { + case Diagnostic::Ignored: return CXDiagnostic_Ignored; + case Diagnostic::Note: return CXDiagnostic_Note; + case Diagnostic::Warning: return CXDiagnostic_Warning; + case Diagnostic::Error: return CXDiagnostic_Error; + case Diagnostic::Fatal: return CXDiagnostic_Fatal; + } + + llvm_unreachable("Invalid diagnostic level"); + return CXDiagnostic_Ignored; +} + +CXSourceLocation clang_getDiagnosticLocation(CXDiagnostic Diag) { + CXStoredDiagnostic *StoredDiag = static_cast<CXStoredDiagnostic *>(Diag); + if (!StoredDiag || StoredDiag->Diag.getLocation().isInvalid()) + return clang_getNullLocation(); + + return translateSourceLocation(StoredDiag->Diag.getLocation().getManager(), + StoredDiag->LangOpts, + StoredDiag->Diag.getLocation()); +} + +CXString clang_getDiagnosticSpelling(CXDiagnostic Diag) { + CXStoredDiagnostic *StoredDiag = static_cast<CXStoredDiagnostic *>(Diag); + if (!StoredDiag) + return createCXString(""); + + return createCXString(StoredDiag->Diag.getMessage(), false); +} + +unsigned clang_getDiagnosticNumRanges(CXDiagnostic Diag) { + CXStoredDiagnostic *StoredDiag = static_cast<CXStoredDiagnostic *>(Diag); + if (!StoredDiag || StoredDiag->Diag.getLocation().isInvalid()) + return 0; + + return StoredDiag->Diag.range_size(); +} + +CXSourceRange clang_getDiagnosticRange(CXDiagnostic Diag, unsigned Range) { + CXStoredDiagnostic *StoredDiag = static_cast<CXStoredDiagnostic *>(Diag); + if (!StoredDiag || Range >= StoredDiag->Diag.range_size() || + StoredDiag->Diag.getLocation().isInvalid()) + return clang_getNullRange(); + + return translateSourceRange(StoredDiag->Diag.getLocation().getManager(), + StoredDiag->LangOpts, + StoredDiag->Diag.range_begin()[Range]); +} + +unsigned clang_getDiagnosticNumFixIts(CXDiagnostic Diag) { + CXStoredDiagnostic *StoredDiag = static_cast<CXStoredDiagnostic *>(Diag); + if (!StoredDiag) + return 0; + + return StoredDiag->Diag.fixit_size(); +} + +CXString clang_getDiagnosticFixIt(CXDiagnostic Diagnostic, unsigned FixIt, + CXSourceRange *ReplacementRange) { + CXStoredDiagnostic *StoredDiag + = static_cast<CXStoredDiagnostic *>(Diagnostic); + if (!StoredDiag || FixIt >= StoredDiag->Diag.fixit_size() || + StoredDiag->Diag.getLocation().isInvalid()) { + if (ReplacementRange) + *ReplacementRange = clang_getNullRange(); + + return createCXString(""); + } + + const FixItHint &Hint = StoredDiag->Diag.fixit_begin()[FixIt]; + if (ReplacementRange) { + if (Hint.RemoveRange.isInvalid()) { + // Create an empty range that refers to a single source + // location (which is the insertion point). + CXSourceRange Range = { + { (void *)&StoredDiag->Diag.getLocation().getManager(), + (void *)&StoredDiag->LangOpts }, + Hint.InsertionLoc.getRawEncoding(), + Hint.InsertionLoc.getRawEncoding() + }; + + *ReplacementRange = Range; + } else { + // Create a range that covers the entire replacement (or + // removal) range, adjusting the end of the range to point to + // the end of the token. + *ReplacementRange + = translateSourceRange(StoredDiag->Diag.getLocation().getManager(), + StoredDiag->LangOpts, + Hint.RemoveRange); + } + } + + return createCXString(Hint.CodeToInsert); +} + +} // end extern "C" + +void clang::LoadSerializedDiagnostics(const llvm::sys::Path &DiagnosticsPath, + unsigned num_unsaved_files, + struct CXUnsavedFile *unsaved_files, + FileManager &FileMgr, + SourceManager &SourceMgr, + SmallVectorImpl<StoredDiagnostic> &Diags) { + using llvm::MemoryBuffer; + using llvm::StringRef; + MemoryBuffer *F = MemoryBuffer::getFile(DiagnosticsPath.c_str()); + if (!F) + return; + + // Enter the unsaved files into the file manager. + for (unsigned I = 0; I != num_unsaved_files; ++I) { + const FileEntry *File = FileMgr.getVirtualFile(unsaved_files[I].Filename, + unsaved_files[I].Length, + 0); + if (!File) { + // FIXME: Hard to localize when we have no diagnostics engine! + Diags.push_back(StoredDiagnostic(Diagnostic::Fatal, + (Twine("could not remap from missing file ") + + unsaved_files[I].Filename).str())); + delete F; + return; + } + + MemoryBuffer *Buffer + = MemoryBuffer::getMemBuffer(unsaved_files[I].Contents, + unsaved_files[I].Contents + unsaved_files[I].Length); + if (!Buffer) { + delete F; + return; + } + + SourceMgr.overrideFileContents(File, Buffer); + SourceMgr.createFileID(File, SourceLocation(), SrcMgr::C_User); + } + + // Parse the diagnostics, emitting them one by one until we've + // exhausted the data. + StringRef Buffer = F->getBuffer(); + const char *Memory = Buffer.data(), *MemoryEnd = Memory + Buffer.size(); + while (Memory != MemoryEnd) { + StoredDiagnostic Stored = StoredDiagnostic::Deserialize(FileMgr, SourceMgr, + Memory, MemoryEnd); + if (!Stored) + break; + + Diags.push_back(Stored); + } + delete F; +} diff --git a/tools/libclang/CIndexDiagnostic.h b/tools/libclang/CIndexDiagnostic.h new file mode 100644 index 0000000..919c21c --- /dev/null +++ b/tools/libclang/CIndexDiagnostic.h @@ -0,0 +1,53 @@ +/*===-- CIndexDiagnostic.h - Diagnostics C Interface ------------*- C++ -*-===*\ +|* *| +|* The LLVM Compiler Infrastructure *| +|* *| +|* This file is distributed under the University of Illinois Open Source *| +|* License. See LICENSE.TXT for details. *| +|* *| +|*===----------------------------------------------------------------------===*| +|* *| +|* Implements the diagnostic functions of the Clang C interface. *| +|* *| +\*===----------------------------------------------------------------------===*/ +#ifndef LLVM_CLANG_CINDEX_DIAGNOSTIC_H +#define LLVM_CLANG_CINDEX_DIAGNOSTIC_H + +struct CXUnsavedFile; + +namespace llvm { +template<typename T> class SmallVectorImpl; +namespace sys { class Path; } +} + +namespace clang { + +class Diagnostic; +class FileManager; +class LangOptions; +class Preprocessor; +class StoredDiagnostic; +class SourceManager; + +/// \brief The storage behind a CXDiagnostic +struct CXStoredDiagnostic { + const StoredDiagnostic &Diag; + const LangOptions &LangOpts; + + CXStoredDiagnostic(const StoredDiagnostic &Diag, + const LangOptions &LangOpts) + : Diag(Diag), LangOpts(LangOpts) { } +}; + +/// \brief Given the path to a file that contains binary, serialized +/// diagnostics produced by Clang, load those diagnostics. +void LoadSerializedDiagnostics(const llvm::sys::Path &DiagnosticsPath, + unsigned num_unsaved_files, + struct CXUnsavedFile *unsaved_files, + FileManager &FileMgr, + SourceManager &SourceMgr, + llvm::SmallVectorImpl<StoredDiagnostic> &Diags); + +} // end namespace clang + +#endif // LLVM_CLANG_CINDEX_DIAGNOSTIC_H diff --git a/tools/libclang/CIndexInclusionStack.cpp b/tools/libclang/CIndexInclusionStack.cpp new file mode 100644 index 0000000..e863239 --- /dev/null +++ b/tools/libclang/CIndexInclusionStack.cpp @@ -0,0 +1,67 @@ +//===- CIndexInclusionStack.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 defines a callback mechanism for clients to get the inclusion +// stack from a translation unit. +// +//===----------------------------------------------------------------------===// + +#include "CIndexer.h" +#include "CXSourceLocation.h" +#include "clang/AST/DeclVisitor.h" +#include "clang/Frontend/ASTUnit.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" +using namespace clang; + +extern "C" { +void clang_getInclusions(CXTranslationUnit TU, CXInclusionVisitor CB, + CXClientData clientData) { + + ASTUnit *CXXUnit = static_cast<ASTUnit *>(TU); + SourceManager &SM = CXXUnit->getSourceManager(); + ASTContext &Ctx = CXXUnit->getASTContext(); + + llvm::SmallVector<CXSourceLocation, 10> InclusionStack; + unsigned i = SM.sloc_loaded_entry_size(); + unsigned n = SM.sloc_entry_size(); + + // In the case where all the SLocEntries are in an external source, traverse + // those SLocEntries as well. This is the case where we are looking + // at the inclusion stack of an AST/PCH file. + if (i >= n) + i = 0; + + for ( ; i < n ; ++i) { + + const SrcMgr::SLocEntry &SL = SM.getSLocEntry(i); + + if (!SL.isFile()) + continue; + + const SrcMgr::FileInfo &FI = SL.getFile(); + if (!FI.getContentCache()->Entry) + continue; + + // Build the inclusion stack. + SourceLocation L = FI.getIncludeLoc(); + InclusionStack.clear(); + while (L.isValid()) { + PresumedLoc PLoc = SM.getPresumedLoc(L); + InclusionStack.push_back(cxloc::translateSourceLocation(Ctx, L)); + L = PLoc.getIncludeLoc(); + } + + // Callback to the client. + // FIXME: We should have a function to construct CXFiles. + CB((CXFile) FI.getContentCache()->Entry, + InclusionStack.data(), InclusionStack.size(), clientData); + } +} +} // end extern C diff --git a/tools/libclang/CIndexUSRs.cpp b/tools/libclang/CIndexUSRs.cpp new file mode 100644 index 0000000..f3c74e8 --- /dev/null +++ b/tools/libclang/CIndexUSRs.cpp @@ -0,0 +1,477 @@ +//===- CIndexUSR.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 generation and use of USRs from CXEntities. +// +//===----------------------------------------------------------------------===// + +#include "CIndexer.h" +#include "CXCursor.h" +#include "clang/AST/DeclVisitor.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Lex/PreprocessingRecord.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace clang::cxstring; + +//===----------------------------------------------------------------------===// +// USR generation. +//===----------------------------------------------------------------------===// + +namespace { +class USRGenerator : public DeclVisitor<USRGenerator> { + llvm::raw_ostream &Out; + bool IgnoreResults; + ASTUnit *AU; +public: + USRGenerator(ASTUnit *au, llvm::raw_ostream &out) + : Out(out), IgnoreResults(false), AU(au) {} + + bool ignoreResults() const { return IgnoreResults; } + + // Visitation methods from generating USRs from AST elements. + void VisitDeclContext(DeclContext *D); + void VisitFieldDecl(FieldDecl *D); + void VisitFunctionDecl(FunctionDecl *D); + void VisitNamedDecl(NamedDecl *D); + void VisitNamespaceDecl(NamespaceDecl *D); + void VisitObjCClassDecl(ObjCClassDecl *CD); + void VisitObjCContainerDecl(ObjCContainerDecl *CD); + void VisitObjCForwardProtocolDecl(ObjCForwardProtocolDecl *P); + void VisitObjCMethodDecl(ObjCMethodDecl *MD); + void VisitObjCPropertyDecl(ObjCPropertyDecl *D); + void VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *D); + void VisitTagDecl(TagDecl *D); + void VisitTypedefDecl(TypedefDecl *D); + void VisitVarDecl(VarDecl *D); + + /// Generate the string component containing the location of the + /// declaration. + void GenLoc(const Decl *D); + + /// String generation methods used both by the visitation methods + /// and from other clients that want to directly generate USRs. These + /// methods do not construct complete USRs (which incorporate the parents + /// of an AST element), but only the fragments concerning the AST element + /// itself. + + /// Generate a USR fragment for a named declaration. This does + /// not include the USR component for the parent. + void GenNamedDecl(llvm::StringRef name); + + /// Generate a USR for an Objective-C class. + void GenObjCClass(llvm::StringRef cls); + /// Generate a USR for an Objective-C class category. + void GenObjCCategory(llvm::StringRef cls, llvm::StringRef cat); + /// Generate a USR fragment for an Objective-C instance variable. The + /// complete USR can be created by concatenating the USR for the + /// encompassing class with this USR fragment. + void GenObjCIvar(llvm::StringRef ivar); + /// Generate a USR fragment for an Objective-C method. + void GenObjCMethod(llvm::StringRef sel, bool isInstanceMethod); + /// Generate a USR fragment for an Objective-C property. + void GenObjCProperty(llvm::StringRef prop); + /// Generate a USR for an Objective-C protocol. + void GenObjCProtocol(llvm::StringRef prot); +}; + +class StringUSRGenerator { +private: + llvm::SmallString<1024> StrBuf; + llvm::raw_svector_ostream Out; + USRGenerator UG; +public: + StringUSRGenerator(const CXCursor *C = 0) + : Out(StrBuf), UG(C ? cxcursor::getCursorASTUnit(*C) : 0, Out) { + // Add the USR space prefix. + Out << "c:"; + } + + llvm::StringRef str() { + return Out.str(); + } + + USRGenerator* operator->() { return &UG; } + + template <typename T> + llvm::raw_svector_ostream &operator<<(const T &x) { + Out << x; + return Out; + } +}; + +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Generating USRs from ASTS. +//===----------------------------------------------------------------------===// + +void USRGenerator::VisitDeclContext(DeclContext *DC) { + if (NamedDecl *D = dyn_cast<NamedDecl>(DC)) + Visit(D); +} + +void USRGenerator::VisitFieldDecl(FieldDecl *D) { + const std::string &s = D->getNameAsString(); + if (s.empty()) { + // Bit fields can be anonymous. + IgnoreResults = true; + return; + } + VisitDeclContext(D->getDeclContext()); + Out << (isa<ObjCIvarDecl>(D) ? "@" : "@FI@") << s; +} + +void USRGenerator::VisitFunctionDecl(FunctionDecl *D) { + if (D->getLinkage() != ExternalLinkage) { + GenLoc(D); + if (IgnoreResults) + return; + } + else + VisitDeclContext(D->getDeclContext()); + + Out << "@F@" << D; +} + +void USRGenerator::VisitNamedDecl(NamedDecl *D) { + VisitDeclContext(D->getDeclContext()); + const std::string &s = D->getNameAsString(); + // The string can be empty if the declaration has no name; e.g., it is + // the ParmDecl with no name for declaration of a function pointer type, e.g.: + // void (*f)(void *); + // In this case, don't generate a USR. + if (s.empty()) + IgnoreResults = true; + else + GenNamedDecl(s); +} + +void USRGenerator::VisitVarDecl(VarDecl *D) { + // VarDecls can be declared 'extern' within a function or method body, + // but their enclosing DeclContext is the function, not the TU. We need + // to check the storage class to correctly generate the USR. + if (D->getLinkage() != ExternalLinkage) { + GenLoc(D); + if (IgnoreResults) + return; + } + + // Variables always have simple names. + llvm::StringRef s = D->getName(); + + // The string can be empty if the declaration has no name; e.g., it is + // the ParmDecl with no name for declaration of a function pointer type, e.g.: + // void (*f)(void *); + // In this case, don't generate a USR. + if (s.empty()) + IgnoreResults = true; + else + GenNamedDecl(s); +} + +void USRGenerator::VisitNamespaceDecl(NamespaceDecl *D) { + VisitDeclContext(D->getDeclContext()); + Out << "@N@" << D; +} + +void USRGenerator::VisitObjCMethodDecl(ObjCMethodDecl *D) { + Visit(cast<Decl>(D->getDeclContext())); + GenObjCMethod(DeclarationName(D->getSelector()).getAsString(), + D->isInstanceMethod()); +} + +void USRGenerator::VisitObjCClassDecl(ObjCClassDecl *D) { + // FIXME: @class declarations can refer to multiple classes. We need + // to be able to traverse these. + IgnoreResults = true; +} + +void USRGenerator::VisitObjCForwardProtocolDecl(ObjCForwardProtocolDecl *D) { + // FIXME: @protocol declarations can refer to multiple protocols. We need + // to be able to traverse these. + IgnoreResults = true; +} + +void USRGenerator::VisitObjCContainerDecl(ObjCContainerDecl *D) { + switch (D->getKind()) { + default: + assert(false && "Invalid ObjC container."); + case Decl::ObjCInterface: + case Decl::ObjCImplementation: + GenObjCClass(D->getName()); + break; + case Decl::ObjCCategory: { + ObjCCategoryDecl *CD = cast<ObjCCategoryDecl>(D); + ObjCInterfaceDecl *ID = CD->getClassInterface(); + if (!ID) { + // Handle invalid code where the @interface might not + // have been specified. + // FIXME: We should be able to generate this USR even if the + // @interface isn't available. + IgnoreResults = true; + return; + } + GenObjCCategory(ID->getName(), CD->getName()); + break; + } + case Decl::ObjCCategoryImpl: { + ObjCCategoryImplDecl *CD = cast<ObjCCategoryImplDecl>(D); + ObjCInterfaceDecl *ID = CD->getClassInterface(); + if (!ID) { + // Handle invalid code where the @interface might not + // have been specified. + // FIXME: We should be able to generate this USR even if the + // @interface isn't available. + IgnoreResults = true; + return; + } + GenObjCCategory(ID->getName(), CD->getName()); + break; + } + case Decl::ObjCProtocol: + GenObjCProtocol(cast<ObjCProtocolDecl>(D)->getName()); + break; + } +} + +void USRGenerator::VisitObjCPropertyDecl(ObjCPropertyDecl *D) { + Visit(cast<Decl>(D->getDeclContext())); + GenObjCProperty(D->getName()); +} + +void USRGenerator::VisitObjCPropertyImplDecl(ObjCPropertyImplDecl *D) { + if (ObjCPropertyDecl *PD = D->getPropertyDecl()) { + VisitObjCPropertyDecl(PD); + return; + } + + IgnoreResults = true; +} + +void USRGenerator::VisitTagDecl(TagDecl *D) { + D = D->getCanonicalDecl(); + VisitDeclContext(D->getDeclContext()); + switch (D->getTagKind()) { + case TagDecl::TK_struct: Out << "@S"; break; + case TagDecl::TK_class: Out << "@C"; break; + case TagDecl::TK_union: Out << "@U"; break; + case TagDecl::TK_enum: Out << "@E"; break; + } + + const std::string &s = D->getNameAsString(); + const TypedefDecl *TD = 0; + if (s.empty()) { + TD = D->getTypedefForAnonDecl(); + Out << (TD ? 'A' : 'a'); + } + + // Add the location of the tag decl to handle resolution across + // translation units. + if (D->getLinkage() == NoLinkage) { + Out << '@'; + GenLoc(D); + if (IgnoreResults) + return; + } + + if (s.empty()) { + if (TD) + Out << '@' << TD; + } + else + Out << '@' << s; +} + +void USRGenerator::VisitTypedefDecl(TypedefDecl *D) { + DeclContext *DC = D->getDeclContext(); + if (NamedDecl *DCN = dyn_cast<NamedDecl>(DC)) + Visit(DCN); + Out << "@T@"; + if (D->getLinkage() == NoLinkage) { + GenLoc(D); + if (IgnoreResults) + return; + Out << '@'; + } + Out << D->getName(); +} + +void USRGenerator::GenLoc(const Decl *D) { + const SourceManager &SM = AU->getSourceManager(); + SourceLocation L = D->getLocStart(); + if (L.isInvalid()) { + IgnoreResults = true; + return; + } + L = SM.getInstantiationLoc(L); + const std::pair<FileID, unsigned> &Decomposed = SM.getDecomposedLoc(L); + const FileEntry *FE = SM.getFileEntryForID(Decomposed.first); + if (FE) { + llvm::sys::Path P(FE->getName()); + Out << P.getLast(); + } + else { + // This case really isn't interesting. + IgnoreResults = true; + return; + } + Out << '@' + << SM.getLineNumber(Decomposed.first, Decomposed.second) << ':' + << SM.getColumnNumber(Decomposed.first, Decomposed.second); +} + +//===----------------------------------------------------------------------===// +// General purpose USR generation methods. +//===----------------------------------------------------------------------===// + +void USRGenerator::GenNamedDecl(llvm::StringRef name) { + Out << "@" << name; +} + +void USRGenerator::GenObjCClass(llvm::StringRef cls) { + Out << "objc(cs)" << cls; +} + +void USRGenerator::GenObjCCategory(llvm::StringRef cls, llvm::StringRef cat) { + Out << "objc(cy)" << cls << '@' << cat; +} + +void USRGenerator::GenObjCIvar(llvm::StringRef ivar) { + GenNamedDecl(ivar); +} + +void USRGenerator::GenObjCMethod(llvm::StringRef meth, bool isInstanceMethod) { + Out << (isInstanceMethod ? "(im)" : "(cm)") << meth; +} + +void USRGenerator::GenObjCProperty(llvm::StringRef prop) { + Out << "(py)" << prop; +} + +void USRGenerator::GenObjCProtocol(llvm::StringRef prot) { + Out << "objc(pl)" << prot; +} + +//===----------------------------------------------------------------------===// +// API hooks. +//===----------------------------------------------------------------------===// + +static inline llvm::StringRef extractUSRSuffix(llvm::StringRef s) { + return s.startswith("c:") ? s.substr(2) : ""; +} + +static CXString getDeclCursorUSR(const CXCursor &C) { + Decl *D = cxcursor::getCursorDecl(C); + + // Don't generate USRs for things with invalid locations. + if (!D || D->getLocStart().isInvalid()) + return createCXString(""); + + // Check if the cursor has 'NoLinkage'. + if (const NamedDecl *ND = dyn_cast<NamedDecl>(D)) + switch (ND->getLinkage()) { + case ExternalLinkage: + // Generate USRs for all entities with external linkage. + break; + case NoLinkage: + // We allow enums, typedefs, and structs that have no linkage to + // have USRs that are anchored to the file they were defined in + // (e.g., the header). This is a little gross, but in principal + // enums/anonymous structs/etc. defined in a common header file + // are referred to across multiple translation units. + if (isa<TagDecl>(ND) || isa<TypedefDecl>(ND) || + isa<EnumConstantDecl>(ND) || isa<FieldDecl>(ND) || + isa<VarDecl>(ND)) + break; + // Fall-through. + case InternalLinkage: + if (isa<FunctionDecl>(ND)) + break; + case UniqueExternalLinkage: + return createCXString(""); + } + + StringUSRGenerator SUG(&C); + SUG->Visit(D); + + if (SUG->ignoreResults()) + return createCXString(""); + + // For development testing. + // assert(SUG.str().size() > 2); + + // Return a copy of the string that must be disposed by the caller. + return createCXString(SUG.str(), true); +} + +extern "C" { + +CXString clang_getCursorUSR(CXCursor C) { + const CXCursorKind &K = clang_getCursorKind(C); + + if (clang_isDeclaration(K)) + return getDeclCursorUSR(C); + + if (K == CXCursor_MacroDefinition) { + StringUSRGenerator SUG(&C); + SUG << "macro@" + << cxcursor::getCursorMacroDefinition(C)->getName()->getNameStart(); + return createCXString(SUG.str(), true); + } + + return createCXString(""); +} + +CXString clang_constructUSR_ObjCIvar(const char *name, CXString classUSR) { + StringUSRGenerator SUG; + SUG << extractUSRSuffix(clang_getCString(classUSR)); + SUG->GenObjCIvar(name); + return createCXString(SUG.str(), true); +} + +CXString clang_constructUSR_ObjCMethod(const char *name, + unsigned isInstanceMethod, + CXString classUSR) { + StringUSRGenerator SUG; + SUG << extractUSRSuffix(clang_getCString(classUSR)); + SUG->GenObjCMethod(name, isInstanceMethod); + return createCXString(SUG.str(), true); +} + +CXString clang_constructUSR_ObjCClass(const char *name) { + StringUSRGenerator SUG; + SUG->GenObjCClass(name); + return createCXString(SUG.str(), true); +} + +CXString clang_constructUSR_ObjCProtocol(const char *name) { + StringUSRGenerator SUG; + SUG->GenObjCProtocol(name); + return createCXString(SUG.str(), true); +} + +CXString clang_constructUSR_ObjCCategory(const char *class_name, + const char *category_name) { + StringUSRGenerator SUG; + SUG->GenObjCCategory(class_name, category_name); + return createCXString(SUG.str(), true); +} + +CXString clang_constructUSR_ObjCProperty(const char *property, + CXString classUSR) { + StringUSRGenerator SUG; + SUG << extractUSRSuffix(clang_getCString(classUSR)); + SUG->GenObjCProperty(property); + return createCXString(SUG.str(), true); +} + +} // end extern "C" diff --git a/tools/libclang/CIndexer.cpp b/tools/libclang/CIndexer.cpp new file mode 100644 index 0000000..d5131ff --- /dev/null +++ b/tools/libclang/CIndexer.cpp @@ -0,0 +1,154 @@ +//===- 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 Clang-C Source Indexing library. +// +//===----------------------------------------------------------------------===// + +#include "CIndexer.h" + +#include "clang/AST/Decl.h" +#include "clang/AST/DeclVisitor.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/Version.h" +#include "clang/Sema/CodeCompleteConsumer.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Config/config.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/System/Program.h" + +#include <cstdio> +#include <vector> +#include <sstream> + +#ifdef LLVM_ON_WIN32 +#include <windows.h> +#else +#include <dlfcn.h> +#endif + +using namespace clang; + +const llvm::sys::Path& CIndexer::getClangPath() { + // Did we already compute the path? + if (!ClangPath.empty()) + return ClangPath; + + // Find the location where this library lives (libCIndex.dylib). +#ifdef LLVM_ON_WIN32 + MEMORY_BASIC_INFORMATION mbi; + char path[MAX_PATH]; + VirtualQuery((void *)(uintptr_t)clang_createTranslationUnit, &mbi, + sizeof(mbi)); + GetModuleFileNameA((HINSTANCE)mbi.AllocationBase, path, MAX_PATH); + + llvm::sys::Path CIndexPath(path); + + CIndexPath.eraseComponent(); + CIndexPath.appendComponent("clang"); + CIndexPath.appendSuffix("exe"); + CIndexPath.makeAbsolute(); +#else + // This silly cast below avoids a C++ warning. + Dl_info info; + if (dladdr((void *)(uintptr_t)clang_createTranslationUnit, &info) == 0) + assert(0 && "Call to dladdr() failed"); + + llvm::sys::Path CIndexPath(info.dli_fname); + + // We now have the CIndex directory, locate clang relative to it. + CIndexPath.eraseComponent(); + CIndexPath.appendComponent(".."); + CIndexPath.appendComponent("bin"); + CIndexPath.appendComponent("clang"); +#endif + + // Cache our result. + ClangPath = CIndexPath; + return ClangPath; +} + +std::string CIndexer::getClangResourcesPath() { + llvm::sys::Path P = getClangPath(); + + if (!P.empty()) { + P.eraseComponent(); // Remove /clang from foo/bin/clang + P.eraseComponent(); // Remove /bin from foo/bin + + // Get foo/lib/clang/<version>/include + P.appendComponent("lib"); + P.appendComponent("clang"); + P.appendComponent(CLANG_VERSION_STRING); + } + + return P.str(); +} + +static llvm::sys::Path GetTemporaryPath() { + // FIXME: This is lame; sys::Path should provide this function (in particular, + // it should know how to find the temporary files dir). + std::string Error; + const char *TmpDir = ::getenv("TMPDIR"); + if (!TmpDir) + TmpDir = ::getenv("TEMP"); + if (!TmpDir) + TmpDir = ::getenv("TMP"); + if (!TmpDir) + TmpDir = "/tmp"; + llvm::sys::Path P(TmpDir); + P.appendComponent("remap"); + if (P.makeUnique(false, &Error)) + return llvm::sys::Path(""); + + // FIXME: Grumble, makeUnique sometimes leaves the file around!? PR3837. + P.eraseFromDisk(false, 0); + + return P; +} + +bool clang::RemapFiles(unsigned num_unsaved_files, + struct CXUnsavedFile *unsaved_files, + std::vector<std::string> &RemapArgs, + std::vector<llvm::sys::Path> &TemporaryFiles) { + for (unsigned i = 0; i != num_unsaved_files; ++i) { + // Write the contents of this unsaved file into the temporary file. + llvm::sys::Path SavedFile(GetTemporaryPath()); + if (SavedFile.empty()) + return true; + + std::string ErrorInfo; + llvm::raw_fd_ostream OS(SavedFile.c_str(), ErrorInfo); + if (!ErrorInfo.empty()) + return true; + + OS.write(unsaved_files[i].Contents, unsaved_files[i].Length); + OS.close(); + if (OS.has_error()) { + SavedFile.eraseFromDisk(); + return true; + } + + // Remap the file. + std::string RemapArg = unsaved_files[i].Filename; + RemapArg += ';'; + RemapArg += SavedFile.str(); + RemapArgs.push_back("-Xclang"); + RemapArgs.push_back("-remap-file"); + RemapArgs.push_back("-Xclang"); + RemapArgs.push_back(RemapArg); + TemporaryFiles.push_back(SavedFile); + } + + return false; +} + diff --git a/tools/libclang/CIndexer.h b/tools/libclang/CIndexer.h new file mode 100644 index 0000000..31bf779 --- /dev/null +++ b/tools/libclang/CIndexer.h @@ -0,0 +1,78 @@ +//===- CIndexer.h - 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 defines CIndexer, a subclass of Indexer that provides extra +// functionality needed by the CIndex library. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_CINDEXER_H +#define LLVM_CLANG_CINDEXER_H + +#include "clang-c/Index.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/System/Path.h" +#include <vector> + +namespace clang { +namespace cxstring { + CXString createCXString(const char *String, bool DupString = false); + CXString createCXString(llvm::StringRef String, bool DupString = true); +} +} + +class CIndexer { + bool UseExternalASTGeneration; + bool OnlyLocalDecls; + bool DisplayDiagnostics; + + llvm::sys::Path ClangPath; + +public: + CIndexer() + : UseExternalASTGeneration(false), OnlyLocalDecls(false), + DisplayDiagnostics(false) { } + + /// \brief Whether we only want to see "local" declarations (that did not + /// come from a previous precompiled header). If false, we want to see all + /// declarations. + bool getOnlyLocalDecls() const { return OnlyLocalDecls; } + void setOnlyLocalDecls(bool Local = true) { OnlyLocalDecls = Local; } + + bool getDisplayDiagnostics() const { return DisplayDiagnostics; } + void setDisplayDiagnostics(bool Display = true) { + DisplayDiagnostics = Display; + } + + bool getUseExternalASTGeneration() const { return UseExternalASTGeneration; } + void setUseExternalASTGeneration(bool Value) { + UseExternalASTGeneration = Value; + } + + /// \brief Get the path of the clang binary. + const llvm::sys::Path& getClangPath(); + + /// \brief Get the path of the clang resource files. + std::string getClangResourcesPath(); +}; + +namespace clang { + /** + * \brief Given a set of "unsaved" files, create temporary files and + * construct the clang -cc1 argument list needed to perform the remapping. + * + * \returns true if an error occurred. + */ + bool RemapFiles(unsigned num_unsaved_files, + struct CXUnsavedFile *unsaved_files, + std::vector<std::string> &RemapArgs, + std::vector<llvm::sys::Path> &TemporaryFiles); +} + +#endif diff --git a/tools/libclang/CMakeLists.txt b/tools/libclang/CMakeLists.txt new file mode 100644 index 0000000..d3de94a --- /dev/null +++ b/tools/libclang/CMakeLists.txt @@ -0,0 +1,57 @@ +set(SHARED_LIBRARY TRUE) + +set(LLVM_NO_RTTI 1) + +set(LLVM_USED_LIBS + clangFrontend + clangDriver + clangSema + clangAnalysis + clangAST + clangParse + clangLex + clangBasic) + +set( LLVM_LINK_COMPONENTS + bitreader + mc + core + ) + +add_clang_library(libclang + CIndex.cpp + CIndexCodeCompletion.cpp + CIndexDiagnostic.cpp + CIndexInclusionStack.cpp + CIndexUSRs.cpp + CIndexer.cpp + CXCursor.cpp + ../../include/clang-c/Index.h +) +set_target_properties(libclang PROPERTIES OUTPUT_NAME clang) + +if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + # FIXME: Deal with LLVM_SUBMIT_VERSION? + + # FIXME: This uses a special darwin-specific exports file in order to + # get underscore-prefixed names. It would be better to have build rules + # which know how to produce a darwin-suitable exports file from the + # regular exports file. + set_target_properties(libclang + PROPERTIES + LINK_FLAGS "-avoid-version -Wl,-exported_symbols_list -Wl,${CMAKE_CURRENT_SOURCE_DIR}/libclang.darwin.exports -Wl,-dead_strip -Wl,-seg1addr -Wl,0xE0000000" + INSTALL_NAME_DIR "@executable_path/../lib" + ) +endif() + +if(MSVC) + # windows.h doesn't compile with /Za + get_target_property(NON_ANSI_COMPILE_FLAGS libclang COMPILE_FLAGS) + string(REPLACE /Za "" NON_ANSI_COMPILE_FLAGS ${NON_ANSI_COMPILE_FLAGS}) + set_target_properties(libclang PROPERTIES COMPILE_FLAGS ${NON_ANSI_COMPILE_FLAGS}) +endif(MSVC) + +set_target_properties(libclang + PROPERTIES + LINKER_LANGUAGE CXX + DEFINE_SYMBOL _CINDEX_LIB_) diff --git a/tools/libclang/CXCursor.cpp b/tools/libclang/CXCursor.cpp new file mode 100644 index 0000000..c8eb482 --- /dev/null +++ b/tools/libclang/CXCursor.cpp @@ -0,0 +1,370 @@ +//===- CXCursor.cpp - Routines for manipulating CXCursors -----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines routines for manipulating CXCursors. It should be the +// only file that has internal knowledge of the encoding of the data in +// CXCursor. +// +//===----------------------------------------------------------------------===// + +#include "CXCursor.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Expr.h" +#include "llvm/Support/ErrorHandling.h" + +using namespace clang; + +CXCursor cxcursor::MakeCXCursorInvalid(CXCursorKind K) { + assert(K >= CXCursor_FirstInvalid && K <= CXCursor_LastInvalid); + CXCursor C = { K, { 0, 0, 0 } }; + return C; +} + +static CXCursorKind GetCursorKind(Decl *D) { + assert(D && "Invalid arguments!"); + switch (D->getKind()) { + case Decl::Enum: return CXCursor_EnumDecl; + case Decl::EnumConstant: return CXCursor_EnumConstantDecl; + case Decl::Field: return CXCursor_FieldDecl; + case Decl::Function: + return CXCursor_FunctionDecl; + case Decl::ObjCCategory: return CXCursor_ObjCCategoryDecl; + case Decl::ObjCCategoryImpl: return CXCursor_ObjCCategoryImplDecl; + case Decl::ObjCClass: + // FIXME + return CXCursor_UnexposedDecl; + case Decl::ObjCForwardProtocol: + // FIXME + return CXCursor_UnexposedDecl; + case Decl::ObjCImplementation: return CXCursor_ObjCImplementationDecl; + case Decl::ObjCInterface: return CXCursor_ObjCInterfaceDecl; + case Decl::ObjCIvar: return CXCursor_ObjCIvarDecl; + case Decl::ObjCMethod: + return cast<ObjCMethodDecl>(D)->isInstanceMethod() + ? CXCursor_ObjCInstanceMethodDecl : CXCursor_ObjCClassMethodDecl; + case Decl::CXXMethod: return CXCursor_CXXMethod; + case Decl::ObjCProperty: return CXCursor_ObjCPropertyDecl; + case Decl::ObjCProtocol: return CXCursor_ObjCProtocolDecl; + case Decl::ParmVar: return CXCursor_ParmDecl; + case Decl::Typedef: return CXCursor_TypedefDecl; + case Decl::Var: return CXCursor_VarDecl; + default: + if (TagDecl *TD = dyn_cast<TagDecl>(D)) { + switch (TD->getTagKind()) { + case TagDecl::TK_struct: return CXCursor_StructDecl; + case TagDecl::TK_class: return CXCursor_ClassDecl; + case TagDecl::TK_union: return CXCursor_UnionDecl; + case TagDecl::TK_enum: return CXCursor_EnumDecl; + } + } + + return CXCursor_UnexposedDecl; + } + + llvm_unreachable("Invalid Decl"); + return CXCursor_NotImplemented; +} + +static CXCursorKind GetCursorKind(const Attr *A) { + assert(A && "Invalid arguments!"); + switch (A->getKind()) { + default: break; + case Attr::IBActionKind: return CXCursor_IBActionAttr; + case Attr::IBOutletKind: return CXCursor_IBOutletAttr; + } + + return CXCursor_UnexposedAttr; +} + +CXCursor cxcursor::MakeCXCursor(const Attr *A, Decl *Parent, ASTUnit *TU) { + assert(A && Parent && TU && "Invalid arguments!"); + CXCursor C = { GetCursorKind(A), { Parent, (void*)A, TU } }; + return C; +} + +CXCursor cxcursor::MakeCXCursor(Decl *D, ASTUnit *TU) { + assert(D && TU && "Invalid arguments!"); + CXCursor C = { GetCursorKind(D), { D, 0, TU } }; + return C; +} + +CXCursor cxcursor::MakeCXCursor(Stmt *S, Decl *Parent, ASTUnit *TU) { + assert(S && TU && "Invalid arguments!"); + CXCursorKind K = CXCursor_NotImplemented; + + switch (S->getStmtClass()) { + case Stmt::NoStmtClass: + break; + + case Stmt::NullStmtClass: + case Stmt::CompoundStmtClass: + case Stmt::CaseStmtClass: + case Stmt::DefaultStmtClass: + case Stmt::LabelStmtClass: + case Stmt::IfStmtClass: + case Stmt::SwitchStmtClass: + case Stmt::WhileStmtClass: + case Stmt::DoStmtClass: + case Stmt::ForStmtClass: + case Stmt::GotoStmtClass: + case Stmt::IndirectGotoStmtClass: + case Stmt::ContinueStmtClass: + case Stmt::BreakStmtClass: + case Stmt::ReturnStmtClass: + case Stmt::DeclStmtClass: + case Stmt::SwitchCaseClass: + case Stmt::AsmStmtClass: + case Stmt::ObjCAtTryStmtClass: + case Stmt::ObjCAtCatchStmtClass: + case Stmt::ObjCAtFinallyStmtClass: + case Stmt::ObjCAtThrowStmtClass: + case Stmt::ObjCAtSynchronizedStmtClass: + case Stmt::ObjCForCollectionStmtClass: + case Stmt::CXXCatchStmtClass: + case Stmt::CXXTryStmtClass: + K = CXCursor_UnexposedStmt; + break; + + case Stmt::PredefinedExprClass: + case Stmt::IntegerLiteralClass: + case Stmt::FloatingLiteralClass: + case Stmt::ImaginaryLiteralClass: + case Stmt::StringLiteralClass: + case Stmt::CharacterLiteralClass: + case Stmt::ParenExprClass: + case Stmt::UnaryOperatorClass: + case Stmt::OffsetOfExprClass: + case Stmt::SizeOfAlignOfExprClass: + case Stmt::ArraySubscriptExprClass: + case Stmt::BinaryOperatorClass: + case Stmt::CompoundAssignOperatorClass: + case Stmt::ConditionalOperatorClass: + case Stmt::ImplicitCastExprClass: + case Stmt::CStyleCastExprClass: + case Stmt::CompoundLiteralExprClass: + case Stmt::ExtVectorElementExprClass: + case Stmt::InitListExprClass: + case Stmt::DesignatedInitExprClass: + case Stmt::ImplicitValueInitExprClass: + case Stmt::ParenListExprClass: + case Stmt::VAArgExprClass: + case Stmt::AddrLabelExprClass: + case Stmt::StmtExprClass: + case Stmt::TypesCompatibleExprClass: + case Stmt::ChooseExprClass: + case Stmt::GNUNullExprClass: + case Stmt::CXXStaticCastExprClass: + case Stmt::CXXDynamicCastExprClass: + case Stmt::CXXReinterpretCastExprClass: + case Stmt::CXXConstCastExprClass: + case Stmt::CXXFunctionalCastExprClass: + case Stmt::CXXTypeidExprClass: + case Stmt::CXXBoolLiteralExprClass: + case Stmt::CXXNullPtrLiteralExprClass: + case Stmt::CXXThisExprClass: + case Stmt::CXXThrowExprClass: + case Stmt::CXXDefaultArgExprClass: + case Stmt::CXXZeroInitValueExprClass: + case Stmt::CXXNewExprClass: + case Stmt::CXXDeleteExprClass: + case Stmt::CXXPseudoDestructorExprClass: + case Stmt::UnresolvedLookupExprClass: + case Stmt::UnaryTypeTraitExprClass: + case Stmt::DependentScopeDeclRefExprClass: + case Stmt::CXXBindTemporaryExprClass: + case Stmt::CXXBindReferenceExprClass: + case Stmt::CXXExprWithTemporariesClass: + case Stmt::CXXUnresolvedConstructExprClass: + case Stmt::CXXDependentScopeMemberExprClass: + case Stmt::UnresolvedMemberExprClass: + case Stmt::ObjCStringLiteralClass: + case Stmt::ObjCEncodeExprClass: + case Stmt::ObjCSelectorExprClass: + case Stmt::ObjCProtocolExprClass: + case Stmt::ObjCImplicitSetterGetterRefExprClass: + case Stmt::ObjCSuperExprClass: + case Stmt::ObjCIsaExprClass: + case Stmt::ShuffleVectorExprClass: + case Stmt::BlockExprClass: + K = CXCursor_UnexposedExpr; + break; + case Stmt::DeclRefExprClass: + case Stmt::BlockDeclRefExprClass: + // FIXME: UnresolvedLookupExpr? + // FIXME: DependentScopeDeclRefExpr? + K = CXCursor_DeclRefExpr; + break; + + case Stmt::MemberExprClass: + case Stmt::ObjCIvarRefExprClass: + case Stmt::ObjCPropertyRefExprClass: + // FIXME: UnresolvedMemberExpr? + // FIXME: CXXDependentScopeMemberExpr? + K = CXCursor_MemberRefExpr; + break; + + case Stmt::CallExprClass: + case Stmt::CXXOperatorCallExprClass: + case Stmt::CXXMemberCallExprClass: + case Stmt::CXXConstructExprClass: + case Stmt::CXXTemporaryObjectExprClass: + // FIXME: CXXUnresolvedConstructExpr + // FIXME: ObjCImplicitSetterGetterRefExpr? + K = CXCursor_CallExpr; + break; + + case Stmt::ObjCMessageExprClass: + K = CXCursor_ObjCMessageExpr; + break; + } + + CXCursor C = { K, { Parent, S, TU } }; + return C; +} + +CXCursor cxcursor::MakeCursorObjCSuperClassRef(ObjCInterfaceDecl *Super, + SourceLocation Loc, + ASTUnit *TU) { + assert(Super && TU && "Invalid arguments!"); + void *RawLoc = reinterpret_cast<void *>(Loc.getRawEncoding()); + CXCursor C = { CXCursor_ObjCSuperClassRef, { Super, RawLoc, TU } }; + return C; +} + +std::pair<ObjCInterfaceDecl *, SourceLocation> +cxcursor::getCursorObjCSuperClassRef(CXCursor C) { + assert(C.kind == CXCursor_ObjCSuperClassRef); + return std::make_pair(static_cast<ObjCInterfaceDecl *>(C.data[0]), + SourceLocation::getFromRawEncoding( + reinterpret_cast<uintptr_t>(C.data[1]))); +} + +CXCursor cxcursor::MakeCursorObjCProtocolRef(ObjCProtocolDecl *Super, + SourceLocation Loc, + ASTUnit *TU) { + assert(Super && TU && "Invalid arguments!"); + void *RawLoc = reinterpret_cast<void *>(Loc.getRawEncoding()); + CXCursor C = { CXCursor_ObjCProtocolRef, { Super, RawLoc, TU } }; + return C; +} + +std::pair<ObjCProtocolDecl *, SourceLocation> +cxcursor::getCursorObjCProtocolRef(CXCursor C) { + assert(C.kind == CXCursor_ObjCProtocolRef); + return std::make_pair(static_cast<ObjCProtocolDecl *>(C.data[0]), + SourceLocation::getFromRawEncoding( + reinterpret_cast<uintptr_t>(C.data[1]))); +} + +CXCursor cxcursor::MakeCursorObjCClassRef(ObjCInterfaceDecl *Class, + SourceLocation Loc, + ASTUnit *TU) { + // 'Class' can be null for invalid code. + if (!Class) + return MakeCXCursorInvalid(CXCursor_InvalidCode); + assert(TU && "Invalid arguments!"); + void *RawLoc = reinterpret_cast<void *>(Loc.getRawEncoding()); + CXCursor C = { CXCursor_ObjCClassRef, { Class, RawLoc, TU } }; + return C; +} + +std::pair<ObjCInterfaceDecl *, SourceLocation> +cxcursor::getCursorObjCClassRef(CXCursor C) { + assert(C.kind == CXCursor_ObjCClassRef); + return std::make_pair(static_cast<ObjCInterfaceDecl *>(C.data[0]), + SourceLocation::getFromRawEncoding( + reinterpret_cast<uintptr_t>(C.data[1]))); +} + +CXCursor cxcursor::MakeCursorTypeRef(TypeDecl *Type, SourceLocation Loc, + ASTUnit *TU) { + assert(Type && TU && "Invalid arguments!"); + void *RawLoc = reinterpret_cast<void *>(Loc.getRawEncoding()); + CXCursor C = { CXCursor_TypeRef, { Type, RawLoc, TU } }; + return C; +} + +std::pair<TypeDecl *, SourceLocation> +cxcursor::getCursorTypeRef(CXCursor C) { + assert(C.kind == CXCursor_TypeRef); + return std::make_pair(static_cast<TypeDecl *>(C.data[0]), + SourceLocation::getFromRawEncoding( + reinterpret_cast<uintptr_t>(C.data[1]))); +} + +CXCursor cxcursor::MakePreprocessingDirectiveCursor(SourceRange Range, + ASTUnit *TU) { + CXCursor C = { CXCursor_PreprocessingDirective, + { reinterpret_cast<void *>(Range.getBegin().getRawEncoding()), + reinterpret_cast<void *>(Range.getEnd().getRawEncoding()), + TU } + }; + return C; +} + +SourceRange cxcursor::getCursorPreprocessingDirective(CXCursor C) { + assert(C.kind == CXCursor_PreprocessingDirective); + return SourceRange(SourceLocation::getFromRawEncoding( + reinterpret_cast<uintptr_t> (C.data[0])), + SourceLocation::getFromRawEncoding( + reinterpret_cast<uintptr_t> (C.data[1]))); +} + +CXCursor cxcursor::MakeMacroDefinitionCursor(MacroDefinition *MI, ASTUnit *TU) { + CXCursor C = { CXCursor_MacroDefinition, { MI, 0, TU } }; + return C; +} + +MacroDefinition *cxcursor::getCursorMacroDefinition(CXCursor C) { + assert(C.kind == CXCursor_MacroDefinition); + return static_cast<MacroDefinition *>(C.data[0]); +} + +CXCursor cxcursor::MakeMacroInstantiationCursor(MacroInstantiation *MI, + ASTUnit *TU) { + CXCursor C = { CXCursor_MacroInstantiation, { MI, 0, TU } }; + return C; +} + +MacroInstantiation *cxcursor::getCursorMacroInstantiation(CXCursor C) { + assert(C.kind == CXCursor_MacroInstantiation); + return static_cast<MacroInstantiation *>(C.data[0]); +} + +Decl *cxcursor::getCursorDecl(CXCursor Cursor) { + return (Decl *)Cursor.data[0]; +} + +Expr *cxcursor::getCursorExpr(CXCursor Cursor) { + return dyn_cast_or_null<Expr>(getCursorStmt(Cursor)); +} + +Stmt *cxcursor::getCursorStmt(CXCursor Cursor) { + if (Cursor.kind == CXCursor_ObjCSuperClassRef || + Cursor.kind == CXCursor_ObjCProtocolRef || + Cursor.kind == CXCursor_ObjCClassRef) + return 0; + + return (Stmt *)Cursor.data[1]; +} + +ASTContext &cxcursor::getCursorContext(CXCursor Cursor) { + return getCursorASTUnit(Cursor)->getASTContext(); +} + +ASTUnit *cxcursor::getCursorASTUnit(CXCursor Cursor) { + return static_cast<ASTUnit *>(Cursor.data[2]); +} + +bool cxcursor::operator==(CXCursor X, CXCursor Y) { + return X.kind == Y.kind && X.data[0] == Y.data[0] && X.data[1] == Y.data[1] && + X.data[2] == Y.data[2]; +} diff --git a/tools/libclang/CXCursor.h b/tools/libclang/CXCursor.h new file mode 100644 index 0000000..1664f5a --- /dev/null +++ b/tools/libclang/CXCursor.h @@ -0,0 +1,112 @@ +//===- CXCursor.h - Routines for manipulating CXCursors -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines routines for manipulating CXCursors. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_CXCURSOR_H +#define LLVM_CLANG_CXCURSOR_H + +#include "clang-c/Index.h" +#include "clang/Basic/SourceLocation.h" +#include <utility> + +namespace clang { + +class ASTContext; +class ASTUnit; +class Attr; +class Decl; +class Expr; +class MacroDefinition; +class MacroInstantiation; +class NamedDecl; +class ObjCInterfaceDecl; +class ObjCProtocolDecl; +class Stmt; +class TypeDecl; + +namespace cxcursor { + +CXCursor MakeCXCursor(const clang::Attr *A, clang::Decl *Parent, ASTUnit *TU); +CXCursor MakeCXCursor(clang::Decl *D, ASTUnit *TU); +CXCursor MakeCXCursor(clang::Stmt *S, clang::Decl *Parent, ASTUnit *TU); +CXCursor MakeCXCursorInvalid(CXCursorKind K); + +/// \brief Create an Objective-C superclass reference at the given location. +CXCursor MakeCursorObjCSuperClassRef(ObjCInterfaceDecl *Super, + SourceLocation Loc, + ASTUnit *TU); + +/// \brief Unpack an ObjCSuperClassRef cursor into the interface it references +/// and optionally the location where the reference occurred. +std::pair<ObjCInterfaceDecl *, SourceLocation> + getCursorObjCSuperClassRef(CXCursor C); + +/// \brief Create an Objective-C protocol reference at the given location. +CXCursor MakeCursorObjCProtocolRef(ObjCProtocolDecl *Proto, SourceLocation Loc, + ASTUnit *TU); + +/// \brief Unpack an ObjCProtocolRef cursor into the protocol it references +/// and optionally the location where the reference occurred. +std::pair<ObjCProtocolDecl *, SourceLocation> + getCursorObjCProtocolRef(CXCursor C); + +/// \brief Create an Objective-C class reference at the given location. +CXCursor MakeCursorObjCClassRef(ObjCInterfaceDecl *Class, SourceLocation Loc, + ASTUnit *TU); + +/// \brief Unpack an ObjCClassRef cursor into the class it references +/// and optionally the location where the reference occurred. +std::pair<ObjCInterfaceDecl *, SourceLocation> + getCursorObjCClassRef(CXCursor C); + +/// \brief Create a type reference at the given location. +CXCursor MakeCursorTypeRef(TypeDecl *Type, SourceLocation Loc, ASTUnit *TU); + +/// \brief Unpack a TypeRef cursor into the class it references +/// and optionally the location where the reference occurred. +std::pair<TypeDecl *, SourceLocation> getCursorTypeRef(CXCursor C); + +/// \brief Create a preprocessing directive cursor. +CXCursor MakePreprocessingDirectiveCursor(SourceRange Range, ASTUnit *TU); + +/// \brief Unpack a given preprocessing directive to retrieve its source range. +SourceRange getCursorPreprocessingDirective(CXCursor C); + +/// \brief Create a macro definition cursor. +CXCursor MakeMacroDefinitionCursor(MacroDefinition *, ASTUnit *TU); + +/// \brief Unpack a given macro definition cursor to retrieve its +/// source range. +MacroDefinition *getCursorMacroDefinition(CXCursor C); + +/// \brief Create a macro instantiation cursor. +CXCursor MakeMacroInstantiationCursor(MacroInstantiation *, ASTUnit *TU); + +/// \brief Unpack a given macro instantiation cursor to retrieve its +/// source range. +MacroInstantiation *getCursorMacroInstantiation(CXCursor C); + +Decl *getCursorDecl(CXCursor Cursor); +Expr *getCursorExpr(CXCursor Cursor); +Stmt *getCursorStmt(CXCursor Cursor); +ASTContext &getCursorContext(CXCursor Cursor); +ASTUnit *getCursorASTUnit(CXCursor Cursor); + +bool operator==(CXCursor X, CXCursor Y); + +inline bool operator!=(CXCursor X, CXCursor Y) { + return !(X == Y); +} + +}} // end namespace: clang::cxcursor + +#endif diff --git a/tools/libclang/CXSourceLocation.h b/tools/libclang/CXSourceLocation.h new file mode 100644 index 0000000..66566c1 --- /dev/null +++ b/tools/libclang/CXSourceLocation.h @@ -0,0 +1,75 @@ +//===- CXSourceLocation.h - CXSourceLocations Utilities ---------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines routines for manipulating CXSourceLocations. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_CXSOURCELOCATION_H +#define LLVM_CLANG_CXSOURCELOCATION_H + +#include "clang-c/Index.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/LangOptions.h" +#include "clang/AST/ASTContext.h" + +namespace clang { + +class SourceManager; + +namespace cxloc { + +/// \brief Translate a Clang source location into a CIndex source location. +static inline CXSourceLocation +translateSourceLocation(const SourceManager &SM, const LangOptions &LangOpts, + SourceLocation Loc) { + CXSourceLocation Result = { { (void*) &SM, (void*) &LangOpts, }, + Loc.getRawEncoding() }; + return Result; +} + +/// \brief Translate a Clang source location into a CIndex source location. +static inline CXSourceLocation translateSourceLocation(ASTContext &Context, + SourceLocation Loc) { + return translateSourceLocation(Context.getSourceManager(), + Context.getLangOptions(), + Loc); +} + +/// \brief Translate a Clang source range into a CIndex source range. +/// +/// Clang internally represents ranges where the end location points to the +/// start of the token at the end. However, for external clients it is more +/// useful to have a CXSourceRange be a proper half-open interval. This routine +/// does the appropriate translation. +CXSourceRange translateSourceRange(const SourceManager &SM, + const LangOptions &LangOpts, + SourceRange R); + +/// \brief Translate a Clang source range into a CIndex source range. +static inline CXSourceRange translateSourceRange(ASTContext &Context, + SourceRange R) { + return translateSourceRange(Context.getSourceManager(), + Context.getLangOptions(), + R); +} + +static inline SourceLocation translateSourceLocation(CXSourceLocation L) { + return SourceLocation::getFromRawEncoding(L.int_data); +} + +static inline SourceRange translateCXSourceRange(CXSourceRange R) { + return SourceRange(SourceLocation::getFromRawEncoding(R.begin_int_data), + SourceLocation::getFromRawEncoding(R.end_int_data)); +} + + +}} // end namespace: clang::cxloc + +#endif diff --git a/tools/libclang/Makefile b/tools/libclang/Makefile new file mode 100644 index 0000000..a7877bf --- /dev/null +++ b/tools/libclang/Makefile @@ -0,0 +1,55 @@ +##===- tools/libclang/Makefile -----------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL = ../../../.. +LIBRARYNAME = clang + +EXPORTED_SYMBOL_FILE = $(PROJ_SRC_DIR)/libclang.exports + +CPP.Flags += -I$(PROJ_SRC_DIR)/../../include -I$(PROJ_OBJ_DIR)/../../include + +# Include this here so we can get the configuration of the targets +# that have been configured for construction. We have to do this +# early so we can set up LINK_COMPONENTS before including Makefile.rules +include $(LEVEL)/Makefile.config + +LINK_LIBS_IN_SHARED = 1 +SHARED_LIBRARY = 1 + +LINK_COMPONENTS := bitreader mc core +USEDLIBS = clangFrontend.a clangDriver.a clangSema.a \ + clangAnalysis.a clangAST.a clangParse.a clangLex.a clangBasic.a + +include $(LEVEL)/Makefile.common + +##===----------------------------------------------------------------------===## +# FIXME: This is copied from the 'lto' makefile. Should we share this? +##===----------------------------------------------------------------------===## + +ifeq ($(HOST_OS),Darwin) + # set dylib internal version number to llvmCore submission number + ifdef LLVM_SUBMIT_VERSION + LLVMLibsOptions := $(LLVMLibsOptions) -Wl,-current_version \ + -Wl,$(LLVM_SUBMIT_VERSION).$(LLVM_SUBMIT_SUBVERSION) \ + -Wl,-compatibility_version -Wl,1 + endif + # extra options to override libtool defaults + LLVMLibsOptions := $(LLVMLibsOptions) \ + -avoid-version \ + -Wl,-dead_strip \ + -Wl,-seg1addr -Wl,0xE0000000 + + # Mac OS X 10.4 and earlier tools do not allow a second -install_name on command line + DARWIN_VERS := $(shell echo $(TARGET_TRIPLE) | sed 's/.*darwin\([0-9]*\).*/\1/') + ifneq ($(DARWIN_VERS),8) + LLVMLibsOptions := $(LLVMLibsOptions) \ + -no-undefined -Wl,-install_name \ + -Wl,"@executable_path/../lib/lib$(LIBRARYNAME)$(SHLIBEXT)" + endif +endif diff --git a/tools/libclang/libclang.darwin.exports b/tools/libclang/libclang.darwin.exports new file mode 100644 index 0000000..b361168 --- /dev/null +++ b/tools/libclang/libclang.darwin.exports @@ -0,0 +1,81 @@ +_clang_annotateTokens +_clang_codeComplete +_clang_codeCompleteGetDiagnostic +_clang_codeCompleteGetNumDiagnostics +_clang_constructUSR_ObjCCategory +_clang_constructUSR_ObjCClass +_clang_constructUSR_ObjCIvar +_clang_constructUSR_ObjCMethod +_clang_constructUSR_ObjCProperty +_clang_constructUSR_ObjCProtocol +_clang_createIndex +_clang_createTranslationUnit +_clang_createTranslationUnitFromSourceFile +_clang_defaultDiagnosticDisplayOptions +_clang_disposeCodeCompleteResults +_clang_disposeDiagnostic +_clang_disposeIndex +_clang_disposeString +_clang_disposeTokens +_clang_disposeTranslationUnit +_clang_enableStackTraces +_clang_equalCursors +_clang_equalLocations +_clang_formatDiagnostic +_clang_getCString +_clang_getClangVersion +_clang_getCompletionChunkCompletionString +_clang_getCompletionChunkKind +_clang_getCompletionChunkText +_clang_getCursor +_clang_getCursorDefinition +_clang_getCursorExtent +_clang_getCursorKind +_clang_getCursorKindSpelling +_clang_getCursorLanguage +_clang_getCursorLinkage +_clang_getCursorLocation +_clang_getCursorReferenced +_clang_getCursorSpelling +_clang_getCursorUSR +_clang_getDefinitionSpellingAndExtent +_clang_getDiagnostic +_clang_getDiagnosticFixIt +_clang_getDiagnosticLocation +_clang_getDiagnosticNumFixIts +_clang_getDiagnosticNumRanges +_clang_getDiagnosticRange +_clang_getDiagnosticSeverity +_clang_getDiagnosticSpelling +_clang_getFile +_clang_getFileName +_clang_getFileTime +_clang_getInclusions +_clang_getInstantiationLocation +_clang_getLocation +_clang_getNullCursor +_clang_getNullLocation +_clang_getNullRange +_clang_getNumCompletionChunks +_clang_getNumDiagnostics +_clang_getRange +_clang_getRangeEnd +_clang_getRangeStart +_clang_getTokenExtent +_clang_getTokenKind +_clang_getTokenLocation +_clang_getTokenSpelling +_clang_getTranslationUnitCursor +_clang_getTranslationUnitSpelling +_clang_isCursorDefinition +_clang_isDeclaration +_clang_isExpression +_clang_isInvalid +_clang_isPreprocessing +_clang_isReference +_clang_isStatement +_clang_isTranslationUnit +_clang_isUnexposed +_clang_setUseExternalASTGeneration +_clang_tokenize +_clang_visitChildren diff --git a/tools/libclang/libclang.exports b/tools/libclang/libclang.exports new file mode 100644 index 0000000..991bb06 --- /dev/null +++ b/tools/libclang/libclang.exports @@ -0,0 +1,81 @@ +clang_annotateTokens +clang_codeComplete +clang_codeCompleteGetDiagnostic +clang_codeCompleteGetNumDiagnostics +clang_constructUSR_ObjCCategory +clang_constructUSR_ObjCClass +clang_constructUSR_ObjCIvar +clang_constructUSR_ObjCMethod +clang_constructUSR_ObjCProperty +clang_constructUSR_ObjCProtocol +clang_createIndex +clang_createTranslationUnit +clang_createTranslationUnitFromSourceFile +clang_defaultDiagnosticDisplayOptions +clang_disposeCodeCompleteResults +clang_disposeDiagnostic +clang_disposeIndex +clang_disposeString +clang_disposeTokens +clang_disposeTranslationUnit +clang_enableStackTraces +clang_equalCursors +clang_equalLocations +clang_formatDiagnostic +clang_getCString +clang_getClangVersion +clang_getCompletionChunkCompletionString +clang_getCompletionChunkKind +clang_getCompletionChunkText +clang_getCursor +clang_getCursorDefinition +clang_getCursorExtent +clang_getCursorKind +clang_getCursorKindSpelling +clang_getCursorLanguage +clang_getCursorLinkage +clang_getCursorLocation +clang_getCursorReferenced +clang_getCursorSpelling +clang_getCursorUSR +clang_getDefinitionSpellingAndExtent +clang_getDiagnostic +clang_getDiagnosticFixIt +clang_getDiagnosticLocation +clang_getDiagnosticNumFixIts +clang_getDiagnosticNumRanges +clang_getDiagnosticRange +clang_getDiagnosticSeverity +clang_getDiagnosticSpelling +clang_getFile +clang_getFileName +clang_getFileTime +clang_getInclusions +clang_getInstantiationLocation +clang_getLocation +clang_getNullCursor +clang_getNullLocation +clang_getNullRange +clang_getNumCompletionChunks +clang_getNumDiagnostics +clang_getRange +clang_getRangeEnd +clang_getRangeStart +clang_getTokenExtent +clang_getTokenKind +clang_getTokenLocation +clang_getTokenSpelling +clang_getTranslationUnitCursor +clang_getTranslationUnitSpelling +clang_isCursorDefinition +clang_isDeclaration +clang_isExpression +clang_isInvalid +clang_isPreprocessing +clang_isReference +clang_isStatement +clang_isTranslationUnit +clang_isUnexposed +clang_setUseExternalASTGeneration +clang_tokenize +clang_visitChildren diff --git a/tools/scan-build/ccc-analyzer b/tools/scan-build/ccc-analyzer index daf5f7f..391ea57 100755 --- a/tools/scan-build/ccc-analyzer +++ b/tools/scan-build/ccc-analyzer @@ -139,12 +139,7 @@ sub GetCCArgs { # Strip the newline and initial whitspace chomp $line; $line =~ s/^\s+//; - - my @items = quotewords('\s+', 1, $line); - for (my $i = 0 ; $ i < scalar(@items); ++$i) { - $items[$i] =~ s/^\"//; - $items[$i] =~ s/\"$//; - } + my @items = quotewords('\s+', 0, $line); my $cmd = shift @items; die "cannot find 'clang' in 'clang' command\n" if (!($cmd =~ /clang/)); return \@items; |