diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/CIndex/CIndex.cpp | 777 | ||||
-rw-r--r-- | tools/CMakeLists.txt | 4 | ||||
-rw-r--r-- | tools/c-index-test/CMakeLists.txt | 3 | ||||
-rw-r--r-- | tools/c-index-test/Makefile | 3 | ||||
-rw-r--r-- | tools/c-index-test/c-index-test.c | 134 | ||||
-rw-r--r-- | tools/clang-cc/CMakeLists.txt | 1 | ||||
-rw-r--r-- | tools/clang-cc/Makefile | 2 | ||||
-rw-r--r-- | tools/clang-cc/Options.cpp | 327 | ||||
-rw-r--r-- | tools/clang-cc/Options.h | 7 | ||||
-rw-r--r-- | tools/clang-cc/clang-cc.cpp | 173 | ||||
-rw-r--r-- | tools/driver/CMakeLists.txt | 11 | ||||
-rw-r--r-- | tools/driver/Makefile | 2 | ||||
-rw-r--r-- | tools/driver/cc1_main.cpp | 68 | ||||
-rw-r--r-- | tools/driver/driver.cpp | 17 | ||||
-rw-r--r-- | tools/index-test/CMakeLists.txt | 2 | ||||
-rw-r--r-- | tools/index-test/Makefile | 3 | ||||
-rw-r--r-- | tools/index-test/index-test.cpp | 33 | ||||
-rwxr-xr-x | tools/scan-build/ccc-analyzer | 632 | ||||
-rwxr-xr-x | tools/scan-build/scan-build | 1297 | ||||
-rw-r--r-- | tools/scan-build/scanview.css | 62 | ||||
-rw-r--r-- | tools/scan-build/sorttable.js | 493 | ||||
-rw-r--r-- | tools/wpa/CMakeLists.txt | 20 | ||||
-rw-r--r-- | tools/wpa/Makefile | 16 | ||||
-rw-r--r-- | tools/wpa/clang-wpa.cpp | 62 |
24 files changed, 3243 insertions, 906 deletions
diff --git a/tools/CIndex/CIndex.cpp b/tools/CIndex/CIndex.cpp index 46732de..4681b939 100644 --- a/tools/CIndex/CIndex.cpp +++ b/tools/CIndex/CIndex.cpp @@ -4,7 +4,7 @@ // // 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. @@ -24,7 +24,6 @@ #include "clang/Basic/SourceManager.h" #include "clang/Frontend/ASTUnit.h" #include "llvm/ADT/StringExtras.h" -#include "llvm/ADT/StringSwitch.h" #include "llvm/Config/config.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/MemoryBuffer.h" @@ -47,7 +46,7 @@ using namespace clang; using namespace idx; namespace { -static enum CXCursorKind TranslateDeclRefExpr(DeclRefExpr *DRE) +static enum CXCursorKind TranslateDeclRefExpr(DeclRefExpr *DRE) { NamedDecl *D = DRE->getDecl(); if (isa<VarDecl>(D)) @@ -56,7 +55,7 @@ static enum CXCursorKind TranslateDeclRefExpr(DeclRefExpr *DRE) return CXCursor_FunctionRef; else if (isa<EnumConstantDecl>(D)) return CXCursor_EnumConstantRef; - else + else return CXCursor_NotImplemented; } @@ -66,16 +65,16 @@ class CRefVisitor : public StmtVisitor<CRefVisitor> { CXDecl CDecl; CXDeclIterator Callback; CXClientData CData; - + void Call(enum CXCursorKind CK, Stmt *SRef) { CXCursor C = { CK, CDecl, SRef }; Callback(CDecl, C, CData); } public: - CRefVisitor(CXDecl C, CXDeclIterator cback, CXClientData D) : + CRefVisitor(CXDecl C, CXDeclIterator cback, CXClientData D) : CDecl(C), Callback(cback), CData(D) {} - + void VisitStmt(Stmt *S) { for (Stmt::child_iterator C = S->child_begin(), CEnd = S->child_end(); C != CEnd; ++C) @@ -95,13 +94,13 @@ public: } }; #endif - + /// IgnoreDiagnosticsClient - A DiagnosticsClient that just ignores emitted /// warnings and errors. class VISIBILITY_HIDDEN IgnoreDiagnosticsClient : public DiagnosticClient { public: virtual ~IgnoreDiagnosticsClient() {} - virtual void HandleDiagnostic(Diagnostic::Level, const DiagnosticInfo &) {} + virtual void HandleDiagnostic(Diagnostic::Level, const DiagnosticInfo &) {} }; // Translation Unit Visitor. @@ -109,30 +108,30 @@ class TUVisitor : public DeclVisitor<TUVisitor> { CXTranslationUnit TUnit; CXTranslationUnitIterator Callback; CXClientData CData; - + // MaxPCHLevel - the maximum PCH level of declarations that we will pass on // to the visitor. Declarations with a PCH level greater than this value will // be suppressed. unsigned MaxPCHLevel; - + void Call(enum CXCursorKind CK, NamedDecl *ND) { // Filter any declarations that have a PCH level greater than what we allow. if (ND->getPCHLevel() > MaxPCHLevel) return; - + // Filter any implicit declarations (since the source info will be bogus). if (ND->isImplicit()) return; - + CXCursor C = { CK, ND, 0 }; Callback(TUnit, C, CData); } public: - TUVisitor(CXTranslationUnit CTU, + TUVisitor(CXTranslationUnit CTU, CXTranslationUnitIterator cback, CXClientData D, - unsigned MaxPCHLevel) : + unsigned MaxPCHLevel) : TUnit(CTU), Callback(cback), CData(D), MaxPCHLevel(MaxPCHLevel) {} - + void VisitTranslationUnitDecl(TranslationUnitDecl *D) { VisitDeclContext(dyn_cast<DeclContext>(D)); } @@ -157,63 +156,63 @@ public: } void VisitObjCInterfaceDecl(ObjCInterfaceDecl *ND) { Call(CXCursor_ObjCInterfaceDecl, ND); - } + } void VisitObjCProtocolDecl(ObjCProtocolDecl *ND) { Call(CXCursor_ObjCProtocolDecl, ND); } void VisitTagDecl(TagDecl *ND) { switch (ND->getTagKind()) { - case TagDecl::TK_struct: - Call(CXCursor_StructDecl, ND); - break; - case TagDecl::TK_class: - Call(CXCursor_ClassDecl, ND); - break; - case TagDecl::TK_union: - Call(CXCursor_UnionDecl, ND); - break; - case TagDecl::TK_enum: - Call(CXCursor_EnumDecl, ND); - break; + case TagDecl::TK_struct: + Call(CXCursor_StructDecl, ND); + break; + case TagDecl::TK_class: + Call(CXCursor_ClassDecl, ND); + break; + case TagDecl::TK_union: + Call(CXCursor_UnionDecl, ND); + break; + case TagDecl::TK_enum: + Call(CXCursor_EnumDecl, ND); + break; } } - void VisitTypedefDecl(TypedefDecl *ND) { - Call(CXCursor_TypedefDecl, ND); - } + void VisitTypedefDecl(TypedefDecl *ND) { + Call(CXCursor_TypedefDecl, ND); + } void VisitVarDecl(VarDecl *ND) { Call(CXCursor_VarDecl, ND); - } + } }; - + // Declaration visitor. class CDeclVisitor : public DeclVisitor<CDeclVisitor> { CXDecl CDecl; CXDeclIterator Callback; CXClientData CData; - + // MaxPCHLevel - the maximum PCH level of declarations that we will pass on // to the visitor. Declarations with a PCH level greater than this value will // be suppressed. unsigned MaxPCHLevel; - + void Call(enum CXCursorKind CK, NamedDecl *ND) { // Disable the callback when the context is equal to the visiting decl. if (CDecl == ND && !clang_isReference(CK)) return; - + // Filter any declarations that have a PCH level greater than what we allow. if (ND->getPCHLevel() > MaxPCHLevel) return; - + CXCursor C = { CK, ND, 0 }; Callback(CDecl, C, CData); } public: - CDeclVisitor(CXDecl C, CXDeclIterator cback, CXClientData D, - unsigned MaxPCHLevel) : + CDeclVisitor(CXDecl C, CXDeclIterator cback, CXClientData D, + unsigned MaxPCHLevel) : CDecl(C), Callback(cback), CData(D), MaxPCHLevel(MaxPCHLevel) {} - + void VisitObjCCategoryDecl(ObjCCategoryDecl *ND) { // Issue callbacks for the containing class. Call(CXCursor_ObjCClassRef, ND); @@ -225,16 +224,16 @@ public: if (D->getSuperClass()) Call(CXCursor_ObjCSuperClassRef, D); - for (ObjCProtocolDecl::protocol_iterator I = D->protocol_begin(), - E = D->protocol_end(); I != E; ++I) + for (ObjCProtocolDecl::protocol_iterator I = D->protocol_begin(), + E = D->protocol_end(); I != E; ++I) Call(CXCursor_ObjCProtocolRef, *I); VisitDeclContext(dyn_cast<DeclContext>(D)); } void VisitObjCProtocolDecl(ObjCProtocolDecl *PID) { - for (ObjCProtocolDecl::protocol_iterator I = PID->protocol_begin(), - E = PID->protocol_end(); I != E; ++I) + for (ObjCProtocolDecl::protocol_iterator I = PID->protocol_begin(), + E = PID->protocol_end(); I != E; ++I) Call(CXCursor_ObjCProtocolRef, *I); - + VisitDeclContext(dyn_cast<DeclContext>(PID)); } void VisitTagDecl(TagDecl *D) { @@ -292,9 +291,9 @@ public: }; class CIndexer : public Indexer { -public: - explicit CIndexer(Program *prog) : Indexer(*prog), - OnlyLocalDecls(false), +public: + explicit CIndexer(Program *prog) : Indexer(*prog), + OnlyLocalDecls(false), DisplayDiagnostics(false) {} virtual ~CIndexer() { delete &getProgram(); } @@ -305,17 +304,17 @@ public: bool getOnlyLocalDecls() const { return OnlyLocalDecls; } void setOnlyLocalDecls(bool Local = true) { OnlyLocalDecls = Local; } - void setDisplayDiagnostics(bool Display = true) { + void setDisplayDiagnostics(bool Display = true) { DisplayDiagnostics = Display; } bool getDisplayDiagnostics() const { return DisplayDiagnostics; } - + /// \brief Get the path of the clang binary. const llvm::sys::Path& getClangPath(); private: bool OnlyLocalDecls; bool DisplayDiagnostics; - + llvm::sys::Path ClangPath; }; @@ -360,63 +359,63 @@ const llvm::sys::Path& CIndexer::getClangPath() { } -static SourceLocation getLocationFromCursor(CXCursor C, +static SourceLocation getLocationFromCursor(CXCursor C, SourceManager &SourceMgr, NamedDecl *ND) { if (clang_isReference(C.kind)) { switch (C.kind) { - case CXCursor_ObjCClassRef: { - if (isa<ObjCInterfaceDecl>(ND)) { - // FIXME: This is a hack (storing the parent decl in the stmt slot). - NamedDecl *parentDecl = static_cast<NamedDecl *>(C.stmt); - return parentDecl->getLocation(); - } - ObjCCategoryDecl *OID = dyn_cast<ObjCCategoryDecl>(ND); - assert(OID && "clang_getCursorLine(): Missing category decl"); - return OID->getClassInterface()->getLocation(); - } - case CXCursor_ObjCSuperClassRef: { - ObjCInterfaceDecl *OID = dyn_cast<ObjCInterfaceDecl>(ND); - assert(OID && "clang_getCursorLine(): Missing interface decl"); - return OID->getSuperClassLoc(); - } - case CXCursor_ObjCProtocolRef: { - ObjCProtocolDecl *OID = dyn_cast<ObjCProtocolDecl>(ND); - assert(OID && "clang_getCursorLine(): Missing protocol decl"); - return OID->getLocation(); - } - case CXCursor_ObjCSelectorRef: { - ObjCMessageExpr *OME = dyn_cast<ObjCMessageExpr>( - static_cast<Stmt *>(C.stmt)); - assert(OME && "clang_getCursorLine(): Missing message expr"); - return OME->getLeftLoc(); /* FIXME: should be a range */ + case CXCursor_ObjCClassRef: { + if (isa<ObjCInterfaceDecl>(ND)) { + // FIXME: This is a hack (storing the parent decl in the stmt slot). + NamedDecl *parentDecl = static_cast<NamedDecl *>(C.stmt); + return parentDecl->getLocation(); } - case CXCursor_VarRef: - case CXCursor_FunctionRef: - case CXCursor_EnumConstantRef: { - DeclRefExpr *DRE = dyn_cast<DeclRefExpr>( - static_cast<Stmt *>(C.stmt)); - assert(DRE && "clang_getCursorLine(): Missing decl ref expr"); - return DRE->getLocation(); - } - default: - return SourceLocation(); + ObjCCategoryDecl *OID = dyn_cast<ObjCCategoryDecl>(ND); + assert(OID && "clang_getCursorLine(): Missing category decl"); + return OID->getClassInterface()->getLocation(); + } + case CXCursor_ObjCSuperClassRef: { + ObjCInterfaceDecl *OID = dyn_cast<ObjCInterfaceDecl>(ND); + assert(OID && "clang_getCursorLine(): Missing interface decl"); + return OID->getSuperClassLoc(); + } + case CXCursor_ObjCProtocolRef: { + ObjCProtocolDecl *OID = dyn_cast<ObjCProtocolDecl>(ND); + assert(OID && "clang_getCursorLine(): Missing protocol decl"); + return OID->getLocation(); + } + case CXCursor_ObjCSelectorRef: { + ObjCMessageExpr *OME = dyn_cast<ObjCMessageExpr>( + static_cast<Stmt *>(C.stmt)); + assert(OME && "clang_getCursorLine(): Missing message expr"); + return OME->getLeftLoc(); /* FIXME: should be a range */ + } + case CXCursor_VarRef: + case CXCursor_FunctionRef: + case CXCursor_EnumConstantRef: { + DeclRefExpr *DRE = dyn_cast<DeclRefExpr>( + static_cast<Stmt *>(C.stmt)); + assert(DRE && "clang_getCursorLine(): Missing decl ref expr"); + return DRE->getLocation(); + } + default: + return SourceLocation(); } } else { // We have a declaration or a definition. SourceLocation SLoc; switch (ND->getKind()) { - case Decl::ObjCInterface: { - SLoc = dyn_cast<ObjCInterfaceDecl>(ND)->getClassLoc(); - break; - } - case Decl::ObjCProtocol: { - SLoc = ND->getLocation(); /* FIXME: need to get the name location. */ - break; - } - default: { - SLoc = ND->getLocation(); - break; - } + case Decl::ObjCInterface: { + SLoc = dyn_cast<ObjCInterfaceDecl>(ND)->getClassLoc(); + break; + } + case Decl::ObjCProtocol: { + SLoc = ND->getLocation(); /* FIXME: need to get the name location. */ + break; + } + default: { + SLoc = ND->getLocation(); + break; + } } if (SLoc.isInvalid()) return SourceLocation(); @@ -439,8 +438,7 @@ static CXString createCXString(const char *String, bool DupString = false) { extern "C" { CXIndex clang_createIndex(int excludeDeclarationsFromPCH, - int displayDiagnostics) -{ + int displayDiagnostics) { CIndexer *CIdxr = new CIndexer(new Program()); if (excludeDeclarationsFromPCH) CIdxr->setOnlyLocalDecls(); @@ -449,56 +447,55 @@ CXIndex clang_createIndex(int excludeDeclarationsFromPCH, return CIdxr; } -void clang_disposeIndex(CXIndex CIdx) -{ +void clang_disposeIndex(CXIndex CIdx) { assert(CIdx && "Passed null CXIndex"); delete static_cast<CIndexer *>(CIdx); } // FIXME: need to pass back error info. -CXTranslationUnit clang_createTranslationUnit( - CXIndex CIdx, const char *ast_filename) -{ +CXTranslationUnit clang_createTranslationUnit(CXIndex CIdx, + const char *ast_filename) { assert(CIdx && "Passed null CXIndex"); CIndexer *CXXIdx = static_cast<CIndexer *>(CIdx); std::string astName(ast_filename); std::string ErrMsg; - + CXTranslationUnit TU = - ASTUnit::LoadFromPCHFile(astName, &ErrMsg, - CXXIdx->getDisplayDiagnostics() ? - NULL : new IgnoreDiagnosticsClient(), - CXXIdx->getOnlyLocalDecls(), - /* UseBumpAllocator = */ true); - + ASTUnit::LoadFromPCHFile(astName, &ErrMsg, + CXXIdx->getDisplayDiagnostics() ? + NULL : new IgnoreDiagnosticsClient(), + CXXIdx->getOnlyLocalDecls(), + /* UseBumpAllocator = */ true); + if (CXXIdx->getDisplayDiagnostics() && !ErrMsg.empty()) llvm::errs() << "clang_createTranslationUnit: " << ErrMsg << '\n'; - + return TU; } -CXTranslationUnit clang_createTranslationUnitFromSourceFile( - CXIndex CIdx, - const char *source_filename, - int num_command_line_args, const char **command_line_args) { +CXTranslationUnit +clang_createTranslationUnitFromSourceFile(CXIndex CIdx, + const char *source_filename, + int num_command_line_args, + const char **command_line_args) { assert(CIdx && "Passed null CXIndex"); CIndexer *CXXIdx = static_cast<CIndexer *>(CIdx); // 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); + if (source_filename) + argv.push_back(source_filename); // Generate a temporary name for the AST file. argv.push_back("-o"); @@ -521,7 +518,7 @@ CXTranslationUnit clang_createTranslationUnitFromSourceFile( // Keep the argument. argv.push_back(arg); } - + // Add the null terminator. argv.push_back(NULL); @@ -533,10 +530,10 @@ CXTranslationUnit clang_createTranslationUnitFromSourceFile( llvm::sys::Program::ExecuteAndWait(ClangPath, &argv[0], /* env */ NULL, /* redirects */ !CXXIdx->getDisplayDiagnostics() ? &Redirects[0] : NULL, /* secondsToWait */ 0, /* memoryLimits */ 0, &ErrMsg); - + if (CXXIdx->getDisplayDiagnostics() && !ErrMsg.empty()) { - llvm::errs() << "clang_createTranslationUnitFromSourceFile: " << ErrMsg - << '\n' << "Arguments: \n"; + llvm::errs() << "clang_createTranslationUnitFromSourceFile: " << ErrMsg + << '\n' << "Arguments: \n"; for (std::vector<const char*>::iterator I = argv.begin(), E = argv.end(); I!=E; ++I) { if (*I) @@ -547,45 +544,40 @@ CXTranslationUnit clang_createTranslationUnitFromSourceFile( // Finally, we create the translation unit from the ast file. ASTUnit *ATU = static_cast<ASTUnit *>( - clang_createTranslationUnit(CIdx, astTmpFile)); + clang_createTranslationUnit(CIdx, astTmpFile)); if (ATU) ATU->unlinkTemporaryFile(); return ATU; } -void clang_disposeTranslationUnit( - CXTranslationUnit CTUnit) -{ +void clang_disposeTranslationUnit(CXTranslationUnit CTUnit) { assert(CTUnit && "Passed null CXTranslationUnit"); delete static_cast<ASTUnit *>(CTUnit); } - -CXString clang_getTranslationUnitSpelling(CXTranslationUnit CTUnit) -{ + +CXString clang_getTranslationUnitSpelling(CXTranslationUnit CTUnit) { assert(CTUnit && "Passed null CXTranslationUnit"); ASTUnit *CXXUnit = static_cast<ASTUnit *>(CTUnit); return createCXString(CXXUnit->getOriginalSourceFileName().c_str(), true); } -void clang_loadTranslationUnit(CXTranslationUnit CTUnit, +void clang_loadTranslationUnit(CXTranslationUnit CTUnit, CXTranslationUnitIterator callback, - CXClientData CData) -{ + CXClientData CData) { assert(CTUnit && "Passed null CXTranslationUnit"); ASTUnit *CXXUnit = static_cast<ASTUnit *>(CTUnit); ASTContext &Ctx = CXXUnit->getASTContext(); - - TUVisitor DVisit(CTUnit, callback, CData, + + TUVisitor DVisit(CTUnit, callback, CData, CXXUnit->getOnlyLocalDecls()? 1 : Decl::MaxPCHLevel); DVisit.Visit(Ctx.getTranslationUnitDecl()); } -void clang_loadDeclaration(CXDecl Dcl, - CXDeclIterator callback, - CXClientData CData) -{ +void clang_loadDeclaration(CXDecl Dcl, + CXDeclIterator callback, + CXClientData CData) { assert(Dcl && "Passed null CXDecl"); - + CDeclVisitor DVisit(Dcl, callback, CData, static_cast<Decl *>(Dcl)->getPCHLevel()); DVisit.Visit(static_cast<Decl *>(Dcl)); @@ -593,14 +585,14 @@ void clang_loadDeclaration(CXDecl Dcl, // Some notes on CXEntity: // -// - Since the 'ordinary' namespace includes functions, data, typedefs, -// ObjC interfaces, thecurrent algorithm is a bit naive (resulting in one +// - Since the 'ordinary' namespace includes functions, data, typedefs, +// ObjC interfaces, thecurrent algorithm is a bit naive (resulting in one // entity for 2 different types). For example: // // module1.m: @interface Foo @end Foo *x; // module2.m: void Foo(int); // -// - Since the unique name spans translation units, static data/functions +// - Since the unique name spans translation units, static data/functions // within a CXTranslationUnit are *not* currently represented by entities. // As a result, there will be no entity for the following: // @@ -608,29 +600,27 @@ void clang_loadDeclaration(CXDecl Dcl, // -const char *clang_getDeclarationName(CXEntity) -{ +const char *clang_getDeclarationName(CXEntity) { return ""; } -const char *clang_getURI(CXEntity) -{ + +const char *clang_getURI(CXEntity) { return ""; } -CXEntity clang_getEntity(const char *URI) -{ +CXEntity clang_getEntity(const char *URI) { return 0; } // // CXDecl Operations. // -CXEntity clang_getEntityFromDecl(CXDecl) -{ + +CXEntity clang_getEntityFromDecl(CXDecl) { return 0; } -CXString clang_getDeclSpelling(CXDecl AnonDecl) -{ + +CXString clang_getDeclSpelling(CXDecl AnonDecl) { assert(AnonDecl && "Passed null CXDecl"); NamedDecl *ND = static_cast<NamedDecl *>(AnonDecl); @@ -649,24 +639,21 @@ CXString clang_getDeclSpelling(CXDecl AnonDecl) return createCXString(""); } -unsigned clang_getDeclLine(CXDecl AnonDecl) -{ +unsigned clang_getDeclLine(CXDecl AnonDecl) { assert(AnonDecl && "Passed null CXDecl"); NamedDecl *ND = static_cast<NamedDecl *>(AnonDecl); SourceManager &SourceMgr = ND->getASTContext().getSourceManager(); return SourceMgr.getSpellingLineNumber(ND->getLocation()); } -unsigned clang_getDeclColumn(CXDecl AnonDecl) -{ +unsigned clang_getDeclColumn(CXDecl AnonDecl) { assert(AnonDecl && "Passed null CXDecl"); NamedDecl *ND = static_cast<NamedDecl *>(AnonDecl); SourceManager &SourceMgr = ND->getASTContext().getSourceManager(); return SourceMgr.getSpellingColumnNumber(ND->getLocation()); } -const char *clang_getDeclSource(CXDecl AnonDecl) -{ +const char *clang_getDeclSource(CXDecl AnonDecl) { assert(AnonDecl && "Passed null CXDecl"); FileEntry *FEnt = static_cast<FileEntry *>(clang_getDeclSourceFile(AnonDecl)); assert (FEnt && "Cannot find FileEntry for Decl"); @@ -674,8 +661,7 @@ const char *clang_getDeclSource(CXDecl AnonDecl) } static const FileEntry *getFileEntryFromSourceLocation(SourceManager &SMgr, - SourceLocation SLoc) -{ + SourceLocation SLoc) { FileID FID; if (SLoc.isFileID()) FID = SMgr.getFileID(SLoc); @@ -684,8 +670,7 @@ static const FileEntry *getFileEntryFromSourceLocation(SourceManager &SMgr, return SMgr.getFileEntryForID(FID); } -CXFile clang_getDeclSourceFile(CXDecl AnonDecl) -{ +CXFile clang_getDeclSourceFile(CXDecl AnonDecl) { assert(AnonDecl && "Passed null CXDecl"); NamedDecl *ND = static_cast<NamedDecl *>(AnonDecl); SourceManager &SourceMgr = ND->getASTContext().getSourceManager(); @@ -704,145 +689,143 @@ time_t clang_getFileTime(CXFile SFile) { return FEnt->getModificationTime(); } -CXString clang_getCursorSpelling(CXCursor C) -{ +CXString clang_getCursorSpelling(CXCursor C) { assert(C.decl && "CXCursor has null decl"); NamedDecl *ND = static_cast<NamedDecl *>(C.decl); if (clang_isReference(C.kind)) { switch (C.kind) { - case CXCursor_ObjCSuperClassRef: { - ObjCInterfaceDecl *OID = dyn_cast<ObjCInterfaceDecl>(ND); - assert(OID && "clang_getCursorLine(): Missing interface decl"); - return createCXString(OID->getSuperClass()->getIdentifier() - ->getNameStart()); - } - case CXCursor_ObjCClassRef: { - if (ObjCInterfaceDecl *OID = dyn_cast<ObjCInterfaceDecl>(ND)) - return createCXString(OID->getIdentifier()->getNameStart()); - - ObjCCategoryDecl *OCD = dyn_cast<ObjCCategoryDecl>(ND); - assert(OCD && "clang_getCursorLine(): Missing category decl"); - return createCXString(OCD->getClassInterface()->getIdentifier() - ->getNameStart()); - } - case CXCursor_ObjCProtocolRef: { - ObjCProtocolDecl *OID = dyn_cast<ObjCProtocolDecl>(ND); - assert(OID && "clang_getCursorLine(): Missing protocol decl"); + case CXCursor_ObjCSuperClassRef: { + ObjCInterfaceDecl *OID = dyn_cast<ObjCInterfaceDecl>(ND); + assert(OID && "clang_getCursorLine(): Missing interface decl"); + return createCXString(OID->getSuperClass()->getIdentifier() + ->getNameStart()); + } + case CXCursor_ObjCClassRef: { + if (ObjCInterfaceDecl *OID = dyn_cast<ObjCInterfaceDecl>(ND)) return createCXString(OID->getIdentifier()->getNameStart()); - } - case CXCursor_ObjCSelectorRef: { - ObjCMessageExpr *OME = dyn_cast<ObjCMessageExpr>( - static_cast<Stmt *>(C.stmt)); - assert(OME && "clang_getCursorLine(): Missing message expr"); - return createCXString(OME->getSelector().getAsString().c_str(), true); - } - case CXCursor_VarRef: - case CXCursor_FunctionRef: - case CXCursor_EnumConstantRef: { - DeclRefExpr *DRE = dyn_cast<DeclRefExpr>( - static_cast<Stmt *>(C.stmt)); - assert(DRE && "clang_getCursorLine(): Missing decl ref expr"); - return createCXString(DRE->getDecl()->getIdentifier()->getNameStart()); - } - default: - return createCXString("<not implemented>"); + + ObjCCategoryDecl *OCD = dyn_cast<ObjCCategoryDecl>(ND); + assert(OCD && "clang_getCursorLine(): Missing category decl"); + return createCXString(OCD->getClassInterface()->getIdentifier() + ->getNameStart()); + } + case CXCursor_ObjCProtocolRef: { + ObjCProtocolDecl *OID = dyn_cast<ObjCProtocolDecl>(ND); + assert(OID && "clang_getCursorLine(): Missing protocol decl"); + return createCXString(OID->getIdentifier()->getNameStart()); + } + case CXCursor_ObjCSelectorRef: { + ObjCMessageExpr *OME = dyn_cast<ObjCMessageExpr>( + static_cast<Stmt *>(C.stmt)); + assert(OME && "clang_getCursorLine(): Missing message expr"); + return createCXString(OME->getSelector().getAsString().c_str(), true); + } + case CXCursor_VarRef: + case CXCursor_FunctionRef: + case CXCursor_EnumConstantRef: { + DeclRefExpr *DRE = dyn_cast<DeclRefExpr>( + static_cast<Stmt *>(C.stmt)); + assert(DRE && "clang_getCursorLine(): Missing decl ref expr"); + return createCXString(DRE->getDecl()->getIdentifier()->getNameStart()); + } + default: + return createCXString("<not implemented>"); } } return clang_getDeclSpelling(C.decl); } -const char *clang_getCursorKindSpelling(enum CXCursorKind Kind) -{ +const char *clang_getCursorKindSpelling(enum CXCursorKind Kind) { switch (Kind) { - case CXCursor_FunctionDecl: return "FunctionDecl"; - case CXCursor_TypedefDecl: return "TypedefDecl"; - case CXCursor_EnumDecl: return "EnumDecl"; - case CXCursor_EnumConstantDecl: return "EnumConstantDecl"; - case CXCursor_StructDecl: return "StructDecl"; - case CXCursor_UnionDecl: return "UnionDecl"; - case CXCursor_ClassDecl: return "ClassDecl"; - case CXCursor_FieldDecl: return "FieldDecl"; - case CXCursor_VarDecl: return "VarDecl"; - case CXCursor_ParmDecl: return "ParmDecl"; - case CXCursor_ObjCInterfaceDecl: return "ObjCInterfaceDecl"; - case CXCursor_ObjCCategoryDecl: return "ObjCCategoryDecl"; - case CXCursor_ObjCProtocolDecl: return "ObjCProtocolDecl"; - case CXCursor_ObjCPropertyDecl: return "ObjCPropertyDecl"; - case CXCursor_ObjCIvarDecl: return "ObjCIvarDecl"; - case CXCursor_ObjCInstanceMethodDecl: return "ObjCInstanceMethodDecl"; - case CXCursor_ObjCClassMethodDecl: return "ObjCClassMethodDecl"; - case CXCursor_FunctionDefn: return "FunctionDefn"; - case CXCursor_ObjCInstanceMethodDefn: return "ObjCInstanceMethodDefn"; - case CXCursor_ObjCClassMethodDefn: return "ObjCClassMethodDefn"; - case CXCursor_ObjCClassDefn: return "ObjCClassDefn"; - case CXCursor_ObjCCategoryDefn: return "ObjCCategoryDefn"; - case CXCursor_ObjCSuperClassRef: return "ObjCSuperClassRef"; - case CXCursor_ObjCProtocolRef: return "ObjCProtocolRef"; - case CXCursor_ObjCClassRef: return "ObjCClassRef"; - case CXCursor_ObjCSelectorRef: return "ObjCSelectorRef"; - - case CXCursor_VarRef: return "VarRef"; - case CXCursor_FunctionRef: return "FunctionRef"; - case CXCursor_EnumConstantRef: return "EnumConstantRef"; - case CXCursor_MemberRef: return "MemberRef"; - - case CXCursor_InvalidFile: return "InvalidFile"; - case CXCursor_NoDeclFound: return "NoDeclFound"; - case CXCursor_NotImplemented: return "NotImplemented"; - default: return "<not implemented>"; + case CXCursor_FunctionDecl: return "FunctionDecl"; + case CXCursor_TypedefDecl: return "TypedefDecl"; + case CXCursor_EnumDecl: return "EnumDecl"; + case CXCursor_EnumConstantDecl: return "EnumConstantDecl"; + case CXCursor_StructDecl: return "StructDecl"; + case CXCursor_UnionDecl: return "UnionDecl"; + case CXCursor_ClassDecl: return "ClassDecl"; + case CXCursor_FieldDecl: return "FieldDecl"; + case CXCursor_VarDecl: return "VarDecl"; + case CXCursor_ParmDecl: return "ParmDecl"; + case CXCursor_ObjCInterfaceDecl: return "ObjCInterfaceDecl"; + case CXCursor_ObjCCategoryDecl: return "ObjCCategoryDecl"; + case CXCursor_ObjCProtocolDecl: return "ObjCProtocolDecl"; + case CXCursor_ObjCPropertyDecl: return "ObjCPropertyDecl"; + case CXCursor_ObjCIvarDecl: return "ObjCIvarDecl"; + case CXCursor_ObjCInstanceMethodDecl: return "ObjCInstanceMethodDecl"; + case CXCursor_ObjCClassMethodDecl: return "ObjCClassMethodDecl"; + case CXCursor_FunctionDefn: return "FunctionDefn"; + case CXCursor_ObjCInstanceMethodDefn: return "ObjCInstanceMethodDefn"; + case CXCursor_ObjCClassMethodDefn: return "ObjCClassMethodDefn"; + case CXCursor_ObjCClassDefn: return "ObjCClassDefn"; + case CXCursor_ObjCCategoryDefn: return "ObjCCategoryDefn"; + case CXCursor_ObjCSuperClassRef: return "ObjCSuperClassRef"; + case CXCursor_ObjCProtocolRef: return "ObjCProtocolRef"; + case CXCursor_ObjCClassRef: return "ObjCClassRef"; + case CXCursor_ObjCSelectorRef: return "ObjCSelectorRef"; + + case CXCursor_VarRef: return "VarRef"; + case CXCursor_FunctionRef: return "FunctionRef"; + case CXCursor_EnumConstantRef: return "EnumConstantRef"; + case CXCursor_MemberRef: return "MemberRef"; + + case CXCursor_InvalidFile: return "InvalidFile"; + case CXCursor_NoDeclFound: return "NoDeclFound"; + case CXCursor_NotImplemented: return "NotImplemented"; + default: return "<not implemented>"; } } static enum CXCursorKind TranslateKind(Decl *D) { switch (D->getKind()) { - case Decl::Function: return CXCursor_FunctionDecl; - case Decl::Typedef: return CXCursor_TypedefDecl; - case Decl::Enum: return CXCursor_EnumDecl; - case Decl::EnumConstant: return CXCursor_EnumConstantDecl; - case Decl::Record: return CXCursor_StructDecl; // FIXME: union/class - case Decl::Field: return CXCursor_FieldDecl; - case Decl::Var: return CXCursor_VarDecl; - case Decl::ParmVar: return CXCursor_ParmDecl; - case Decl::ObjCInterface: return CXCursor_ObjCInterfaceDecl; - case Decl::ObjCCategory: return CXCursor_ObjCCategoryDecl; - case Decl::ObjCProtocol: return CXCursor_ObjCProtocolDecl; - case Decl::ObjCMethod: { - ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D); - if (MD->isInstanceMethod()) - return CXCursor_ObjCInstanceMethodDecl; - return CXCursor_ObjCClassMethodDecl; - } - default: break; + case Decl::Function: return CXCursor_FunctionDecl; + case Decl::Typedef: return CXCursor_TypedefDecl; + case Decl::Enum: return CXCursor_EnumDecl; + case Decl::EnumConstant: return CXCursor_EnumConstantDecl; + case Decl::Record: return CXCursor_StructDecl; // FIXME: union/class + case Decl::Field: return CXCursor_FieldDecl; + case Decl::Var: return CXCursor_VarDecl; + case Decl::ParmVar: return CXCursor_ParmDecl; + case Decl::ObjCInterface: return CXCursor_ObjCInterfaceDecl; + case Decl::ObjCCategory: return CXCursor_ObjCCategoryDecl; + case Decl::ObjCProtocol: return CXCursor_ObjCProtocolDecl; + case Decl::ObjCMethod: { + ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D); + if (MD->isInstanceMethod()) + return CXCursor_ObjCInstanceMethodDecl; + return CXCursor_ObjCClassMethodDecl; + } + default: break; } return CXCursor_NotImplemented; } // // CXCursor Operations. // -CXCursor clang_getCursor(CXTranslationUnit CTUnit, const char *source_name, - unsigned line, unsigned column) -{ + +CXCursor clang_getCursor(CXTranslationUnit CTUnit, const char *source_name, + unsigned line, unsigned column) { assert(CTUnit && "Passed null CXTranslationUnit"); ASTUnit *CXXUnit = static_cast<ASTUnit *>(CTUnit); - + FileManager &FMgr = CXXUnit->getFileManager(); - const FileEntry *File = FMgr.getFile(source_name, - source_name+strlen(source_name)); + const FileEntry *File = FMgr.getFile(source_name, + source_name+strlen(source_name)); if (!File) { CXCursor C = { CXCursor_InvalidFile, 0, 0 }; return C; } - SourceLocation SLoc = + SourceLocation SLoc = CXXUnit->getSourceManager().getLocation(File, line, column); - + ASTLocation LastLoc = CXXUnit->getLastASTLocation(); - ASTLocation ALoc = ResolveLocationInAST(CXXUnit->getASTContext(), SLoc, + ASTLocation ALoc = ResolveLocationInAST(CXXUnit->getASTContext(), SLoc, &LastLoc); if (ALoc.isValid()) CXXUnit->setLastASTLocation(ALoc); - + Decl *Dcl = ALoc.getParentDecl(); if (ALoc.isNamedRef()) Dcl = ALoc.AsNamedRef().ND; @@ -855,7 +838,7 @@ CXCursor clang_getCursor(CXTranslationUnit CTUnit, const char *source_name, } else if (ObjCMessageExpr *MExp = dyn_cast<ObjCMessageExpr>(Stm)) { CXCursor C = { CXCursor_ObjCSelectorRef, Dcl, MExp }; return C; - } + } // Fall through...treat as a decl, not a ref. } if (ALoc.isNamedRef()) { @@ -868,7 +851,7 @@ CXCursor clang_getCursor(CXTranslationUnit CTUnit, const char *source_name, return C; } } - CXCursor C = { TranslateKind(Dcl), Dcl, 0 }; + CXCursor C = { TranslateKind(Dcl), Dcl, 0 }; return C; } CXCursor C = { CXCursor_NoDeclFound, 0, 0 }; @@ -886,38 +869,32 @@ CXCursor clang_getNullCursor(void) { unsigned clang_equalCursors(CXCursor X, CXCursor Y) { return X.kind == Y.kind && X.decl == Y.decl && X.stmt == Y.stmt; } - -CXCursor clang_getCursorFromDecl(CXDecl AnonDecl) -{ + +CXCursor clang_getCursorFromDecl(CXDecl AnonDecl) { assert(AnonDecl && "Passed null CXDecl"); NamedDecl *ND = static_cast<NamedDecl *>(AnonDecl); - + CXCursor C = { TranslateKind(ND), ND, 0 }; return C; } -unsigned clang_isInvalid(enum CXCursorKind K) -{ +unsigned clang_isInvalid(enum CXCursorKind K) { return K >= CXCursor_FirstInvalid && K <= CXCursor_LastInvalid; } -unsigned clang_isDeclaration(enum CXCursorKind K) -{ +unsigned clang_isDeclaration(enum CXCursorKind K) { return K >= CXCursor_FirstDecl && K <= CXCursor_LastDecl; } -unsigned clang_isReference(enum CXCursorKind K) -{ +unsigned clang_isReference(enum CXCursorKind K) { return K >= CXCursor_FirstRef && K <= CXCursor_LastRef; } -unsigned clang_isDefinition(enum CXCursorKind K) -{ +unsigned clang_isDefinition(enum CXCursorKind K) { return K >= CXCursor_FirstDefn && K <= CXCursor_LastDefn; } -CXCursorKind clang_getCursorKind(CXCursor C) -{ +CXCursorKind clang_getCursorKind(CXCursor C) { return C.kind; } @@ -939,14 +916,13 @@ static Decl *getDeclFromExpr(Stmt *E) { return 0; } -CXDecl clang_getCursorDecl(CXCursor C) -{ +CXDecl clang_getCursorDecl(CXCursor C) { if (clang_isDeclaration(C.kind)) return C.decl; - + if (clang_isReference(C.kind)) { if (C.stmt) { - if (C.kind == CXCursor_ObjCClassRef || + if (C.kind == CXCursor_ObjCClassRef || C.kind == CXCursor_ObjCProtocolRef) return static_cast<Stmt *>(C.stmt); else @@ -957,44 +933,40 @@ CXDecl clang_getCursorDecl(CXCursor C) return 0; } -unsigned clang_getCursorLine(CXCursor C) -{ +unsigned clang_getCursorLine(CXCursor C) { assert(C.decl && "CXCursor has null decl"); NamedDecl *ND = static_cast<NamedDecl *>(C.decl); SourceManager &SourceMgr = ND->getASTContext().getSourceManager(); - + SourceLocation SLoc = getLocationFromCursor(C, SourceMgr, ND); return SourceMgr.getSpellingLineNumber(SLoc); } -// Access string. const char *clang_getCString(CXString string) { return string.Spelling; } - -// Free CXString. + void clang_disposeString(CXString string) { if (string.MustFreeString) free((void*)string.Spelling); } -unsigned clang_getCursorColumn(CXCursor C) -{ +unsigned clang_getCursorColumn(CXCursor C) { assert(C.decl && "CXCursor has null decl"); NamedDecl *ND = static_cast<NamedDecl *>(C.decl); SourceManager &SourceMgr = ND->getASTContext().getSourceManager(); - + SourceLocation SLoc = getLocationFromCursor(C, SourceMgr, ND); return SourceMgr.getSpellingColumnNumber(SLoc); } -const char *clang_getCursorSource(CXCursor C) -{ + +const char *clang_getCursorSource(CXCursor C) { assert(C.decl && "CXCursor has null decl"); NamedDecl *ND = static_cast<NamedDecl *>(C.decl); SourceManager &SourceMgr = ND->getASTContext().getSourceManager(); - + SourceLocation SLoc = getLocationFromCursor(C, SourceMgr, ND); - + if (SLoc.isFileID()) { const char *bufferName = SourceMgr.getBufferName(SLoc); return bufferName[0] == '<' ? NULL : bufferName; @@ -1003,7 +975,7 @@ const char *clang_getCursorSource(CXCursor C) // Retrieve the file in which the macro was instantiated, then provide that // buffer name. // FIXME: Do we want to give specific macro-instantiation information? - const llvm::MemoryBuffer *Buffer + const llvm::MemoryBuffer *Buffer = SourceMgr.getBuffer(SourceMgr.getDecomposedSpellingLoc(SLoc).first); if (!Buffer) return 0; @@ -1011,29 +983,27 @@ const char *clang_getCursorSource(CXCursor C) return Buffer->getBufferIdentifier(); } -CXFile clang_getCursorSourceFile(CXCursor C) -{ +CXFile clang_getCursorSourceFile(CXCursor C) { assert(C.decl && "CXCursor has null decl"); NamedDecl *ND = static_cast<NamedDecl *>(C.decl); SourceManager &SourceMgr = ND->getASTContext().getSourceManager(); - + return (void *)getFileEntryFromSourceLocation(SourceMgr, - getLocationFromCursor(C,SourceMgr, ND)); + getLocationFromCursor(C,SourceMgr, ND)); } -void clang_getDefinitionSpellingAndExtent(CXCursor C, +void clang_getDefinitionSpellingAndExtent(CXCursor C, const char **startBuf, const char **endBuf, unsigned *startLine, unsigned *startColumn, unsigned *endLine, - unsigned *endColumn) -{ + unsigned *endColumn) { assert(C.decl && "CXCursor has null decl"); NamedDecl *ND = static_cast<NamedDecl *>(C.decl); 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()); @@ -1043,20 +1013,20 @@ void clang_getDefinitionSpellingAndExtent(CXCursor C, *endColumn = SM.getSpellingColumnNumber(Body->getRBracLoc()); } -enum CXCompletionChunkKind +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; + return CXCompletionChunk_Optional; case CodeCompletionString::CK_Placeholder: return CXCompletionChunk_Placeholder; case CodeCompletionString::CK_Informative: @@ -1082,17 +1052,17 @@ clang_getCompletionChunkKind(CXCompletionString completion_string, case CodeCompletionString::CK_Comma: return CXCompletionChunk_Comma; } - + // Should be unreachable, but let's be careful. return CXCompletionChunk_Text; } - + const char *clang_getCompletionChunkText(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: @@ -1109,23 +1079,23 @@ const char *clang_getCompletionChunkText(CXCompletionString completion_string, case CodeCompletionString::CK_RightAngle: case CodeCompletionString::CK_Comma: return (*CCStr)[chunk_number].Text; - + case CodeCompletionString::CK_Optional: // Note: treated as an empty text block. return 0; } - + // Should be unreachable, but let's be careful. return 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: @@ -1142,51 +1112,34 @@ clang_getCompletionChunkCompletionString(CXCompletionString completion_string, case CodeCompletionString::CK_RightAngle: case CodeCompletionString::CK_Comma: 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 CXCursorKind parseResultKind(llvm::StringRef Str) { - return llvm::StringSwitch<CXCursorKind>(Str) - .Case("Typedef", CXCursor_TypedefDecl) - .Case("Struct", CXCursor_StructDecl) - .Case("Union", CXCursor_UnionDecl) - .Case("Class", CXCursor_ClassDecl) - .Case("Enum", CXCursor_EnumDecl) - .Case("Field", CXCursor_FieldDecl) - .Case("EnumConstant", CXCursor_EnumConstantDecl) - .Case("Function", CXCursor_FunctionDecl) - // FIXME: Hacks here to make C++ member functions look like C functions - .Case("CXXMethod", CXCursor_FunctionDecl) - .Case("CXXConstructor", CXCursor_FunctionDecl) - .Case("CXXDestructor", CXCursor_FunctionDecl) - .Case("CXXConversion", CXCursor_FunctionDecl) - .Case("Var", CXCursor_VarDecl) - .Case("ParmVar", CXCursor_ParmDecl) - .Case("ObjCInterface", CXCursor_ObjCInterfaceDecl) - .Case("ObjCCategory", CXCursor_ObjCCategoryDecl) - .Case("ObjCProtocol", CXCursor_ObjCProtocolDecl) - .Case("ObjCProperty", CXCursor_ObjCPropertyDecl) - .Case("ObjCIvar", CXCursor_ObjCIvarDecl) - .Case("ObjCInstanceMethod", CXCursor_ObjCInstanceMethodDecl) - .Case("ObjCClassMethod", CXCursor_ObjCClassMethodDecl) - .Default(CXCursor_NotImplemented); +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; } - -void clang_codeComplete(CXIndex CIdx, + +void clang_codeComplete(CXIndex CIdx, const char *source_filename, - int num_command_line_args, + int num_command_line_args, const char **command_line_args, const char *complete_filename, unsigned complete_line, @@ -1195,15 +1148,15 @@ void clang_codeComplete(CXIndex CIdx, CXClientData client_data) { // The indexer, which is mainly used to determine where diagnostics go. CIndexer *CXXIdx = static_cast<CIndexer *>(CIdx); - + // 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 + + // Add the '-fsyntax-only' argument so that we only perform a basic // syntax check of the code. argv.push_back("-fsyntax-only"); @@ -1220,12 +1173,12 @@ void clang_codeComplete(CXIndex CIdx, argv.push_back(code_complete_at.c_str()); argv.push_back("-Xclang"); argv.push_back("-no-code-completion-debug-printer"); - + // 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); - + 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]) { @@ -1238,19 +1191,19 @@ void clang_codeComplete(CXIndex CIdx, 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 AST file. char tmpFile[L_tmpnam]; char *tmpFileName = tmpnam(tmpFile); llvm::sys::Path ResultsFile(tmpFileName); - + // Invoke 'clang'. llvm::sys::Path DevNull; // leave empty, causes redirection to /dev/null // on Unix or NUL (Windows). @@ -1258,12 +1211,12 @@ void clang_codeComplete(CXIndex CIdx, const llvm::sys::Path *Redirects[] = { &DevNull, &ResultsFile, &DevNull, 0 }; llvm::sys::Program::ExecuteAndWait(ClangPath, &argv[0], /* env */ NULL, /* redirects */ &Redirects[0], - /* secondsToWait */ 0, + /* secondsToWait */ 0, /* memoryLimits */ 0, &ErrMsg); - + if (CXXIdx->getDisplayDiagnostics() && !ErrMsg.empty()) { - llvm::errs() << "clang_codeComplete: " << ErrMsg - << '\n' << "Arguments: \n"; + llvm::errs() << "clang_codeComplete: " << ErrMsg + << '\n' << "Arguments: \n"; for (std::vector<const char*>::iterator I = argv.begin(), E = argv.end(); I!=E; ++I) { if (*I) @@ -1277,84 +1230,32 @@ void clang_codeComplete(CXIndex CIdx, using llvm::StringRef; if (MemoryBuffer *F = MemoryBuffer::getFile(ResultsFile.c_str())) { StringRef Buffer = F->getBuffer(); - do { - StringRef::size_type CompletionIdx = Buffer.find("COMPLETION:"); - StringRef::size_type OverloadIdx = Buffer.find("OVERLOAD:"); - if (CompletionIdx == StringRef::npos && OverloadIdx == StringRef::npos) + for (const char *Str = Buffer.data(), *StrEnd = Str + Buffer.size(); + Str < StrEnd;) { + unsigned KindValue; + if (ReadUnsigned(Str, StrEnd, KindValue)) break; - - if (OverloadIdx < CompletionIdx) { - // Parse an overload result. - Buffer = Buffer.substr(OverloadIdx); - - // Skip past the OVERLOAD: - Buffer = Buffer.substr(Buffer.find(':') + 1); - - // Find the entire completion string. - StringRef::size_type EOL = Buffer.find_first_of("\n\r"); - if (EOL == StringRef::npos) - continue; - - StringRef Line = Buffer.substr(0, EOL); - Buffer = Buffer.substr(EOL + 1); - CodeCompletionString *CCStr = CodeCompletionString::Deserialize(Line); - if (!CCStr || CCStr->empty()) - continue; - + + CodeCompletionString *CCStr + = CodeCompletionString::Deserialize(Str, StrEnd); + if (!CCStr) + continue; + + if (!CCStr->empty()) { // Vend the code-completion result to the caller. CXCompletionResult Result; - Result.CursorKind = CXCursor_NotImplemented; + Result.CursorKind = (CXCursorKind)KindValue; Result.CompletionString = CCStr; if (completion_iterator) completion_iterator(&Result, client_data); - delete CCStr; - - continue; } - - // Parse a completion result. - Buffer = Buffer.substr(CompletionIdx); - - // Skip past the COMPLETION: - Buffer = Buffer.substr(Buffer.find(':') + 1); - - // Get the rank - unsigned Rank = 0; - StringRef::size_type AfterRank = Buffer.find(':'); - Buffer.substr(0, AfterRank).getAsInteger(10, Rank); - Buffer = Buffer.substr(AfterRank + 1); - - // Get the kind of result. - StringRef::size_type AfterKind = Buffer.find(':'); - StringRef Kind = Buffer.substr(0, AfterKind); - Buffer = Buffer.substr(AfterKind + 1); - - // Skip over any whitespace. - Buffer = Buffer.substr(Buffer.find_first_not_of(" \t")); - - // Find the entire completion string. - StringRef::size_type EOL = Buffer.find_first_of("\n\r"); - if (EOL == StringRef::npos) - continue; - - StringRef Line = Buffer.substr(0, EOL); - Buffer = Buffer.substr(EOL + 1); - CodeCompletionString *CCStr = CodeCompletionString::Deserialize(Line); - if (!CCStr || CCStr->empty()) - continue; - - // Vend the code-completion result to the caller. - CXCompletionResult Result; - Result.CursorKind = parseResultKind(Kind); - Result.CompletionString = CCStr; - if (completion_iterator) - completion_iterator(&Result, client_data); + delete CCStr; - } while (true); + }; delete F; - } - + } + ResultsFile.eraseFromDisk(); } - + } // end extern "C" diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 8cded43..8c666dd 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -1,5 +1,5 @@ +add_subdirectory(CIndex) +add_subdirectory(c-index-test) add_subdirectory(clang-cc) add_subdirectory(driver) add_subdirectory(index-test) -add_subdirectory(CIndex) -add_subdirectory(c-index-test) diff --git a/tools/c-index-test/CMakeLists.txt b/tools/c-index-test/CMakeLists.txt index 4c72465..4678461 100644 --- a/tools/c-index-test/CMakeLists.txt +++ b/tools/c-index-test/CMakeLists.txt @@ -4,8 +4,11 @@ set( LLVM_USED_LIBS CIndex clangIndex clangFrontend + clangDriver + clangAnalysis clangSema clangAST + clangParse clangLex clangBasic ) diff --git a/tools/c-index-test/Makefile b/tools/c-index-test/Makefile index 81fee40..ae49bf4 100644 --- a/tools/c-index-test/Makefile +++ b/tools/c-index-test/Makefile @@ -19,6 +19,7 @@ TOOL_NO_EXPORTS = 1 include $(LEVEL)/Makefile.config LINK_COMPONENTS := bitreader mc -USEDLIBS = CIndex.a clangIndex.a clangFrontend.a clangSema.a clangAST.a clangLex.a clangBasic.a +USEDLIBS = CIndex.a clangIndex.a clangFrontend.a clangDriver.a clangAnalysis.a \ + clangSema.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 289dee9..5364d794 100644 --- a/tools/c-index-test/c-index-test.c +++ b/tools/c-index-test/c-index-test.c @@ -38,7 +38,6 @@ static unsigned CreateTranslationUnit(CXIndex Idx, const char *file, return 1; } - /******************************************************************************/ /* Pretty-printing. */ /******************************************************************************/ @@ -71,8 +70,7 @@ static const char* GetCursorSource(CXCursor Cursor) { /* Logic for testing clang_loadTranslationUnit(). */ /******************************************************************************/ -static void DeclVisitor(CXDecl Dcl, CXCursor Cursor, CXClientData Filter) -{ +static void DeclVisitor(CXDecl Dcl, CXCursor Cursor, CXClientData Filter) { if (!Filter || (Cursor.kind == *(enum CXCursorKind *)Filter)) { CXString string; printf("// CHECK: %s:%d:%d: ", GetCursorSource(Cursor), @@ -84,9 +82,9 @@ static void DeclVisitor(CXDecl Dcl, CXCursor Cursor, CXClientData Filter) clang_disposeString(string); } } + static void TranslationUnitVisitor(CXTranslationUnit Unit, CXCursor Cursor, - CXClientData Filter) -{ + CXClientData Filter) { if (!Filter || (Cursor.kind == *(enum CXCursorKind *)Filter)) { CXString string; printf("// CHECK: %s:%d:%d: ", GetCursorSource(Cursor), @@ -99,58 +97,56 @@ static void TranslationUnitVisitor(CXTranslationUnit Unit, CXCursor Cursor, clang_disposeString(string); clang_loadDeclaration(Cursor.decl, DeclVisitor, 0); + } +} - if (Cursor.kind == CXCursor_FunctionDefn) { - const char *startBuf, *endBuf; - unsigned startLine, startColumn, endLine, endColumn; - clang_getDefinitionSpellingAndExtent(Cursor, &startBuf, &endBuf, - &startLine, &startColumn, - &endLine, &endColumn); - { - /* Probe the entire body, looking for both decls and refs. */ - unsigned curLine = startLine, curColumn = startColumn; - CXCursor Ref; - - while (startBuf < endBuf) { - if (*startBuf == '\n') { - startBuf++; - curLine++; - curColumn = 1; - } else if (*startBuf != '\t') - curColumn++; +static void FunctionScanVisitor(CXTranslationUnit Unit, CXCursor Cursor, + CXClientData Filter) { + const char *startBuf, *endBuf; + unsigned startLine, startColumn, endLine, endColumn, curLine, curColumn; + CXCursor Ref; + + if (Cursor.kind != CXCursor_FunctionDefn) + return; + + clang_getDefinitionSpellingAndExtent(Cursor, &startBuf, &endBuf, + &startLine, &startColumn, + &endLine, &endColumn); + /* Probe the entire body, looking for both decls and refs. */ + curLine = startLine; + curColumn = startColumn; + + while (startBuf < endBuf) { + if (*startBuf == '\n') { + startBuf++; + curLine++; + curColumn = 1; + } else if (*startBuf != '\t') + curColumn++; - Ref = clang_getCursor(Unit, clang_getCursorSource(Cursor), - curLine, curColumn); - if (Ref.kind == CXCursor_NoDeclFound) { - /* Nothing found here; that's fine. */ - } else if (Ref.kind != CXCursor_FunctionDecl) { - CXString string; - printf("// CHECK: %s:%d:%d: ", GetCursorSource(Ref), - curLine, curColumn); - PrintCursor(Ref); - string = clang_getDeclSpelling(Ref.decl); - printf(" [Context:%s]\n", clang_getCString(string)); - clang_disposeString(string); - } - startBuf++; - } - } + Ref = clang_getCursor(Unit, clang_getCursorSource(Cursor), + curLine, curColumn); + if (Ref.kind == CXCursor_NoDeclFound) { + /* Nothing found here; that's fine. */ + } else if (Ref.kind != CXCursor_FunctionDecl) { + CXString string; + printf("// CHECK: %s:%d:%d: ", GetCursorSource(Ref), + curLine, curColumn); + PrintCursor(Ref); + string = clang_getDeclSpelling(Ref.decl); + printf(" [Context:%s]\n", clang_getCString(string)); + clang_disposeString(string); } + startBuf++; } } -int perform_test_load_tu(const char *file, const char *filter) { - CXIndex Idx; - CXTranslationUnit TU; +static int perform_test_load(CXIndex Idx, CXTranslationUnit TU, + const char *filter) { enum CXCursorKind K = CXCursor_NotImplemented; + CXTranslationUnitIterator Visitor = TranslationUnitVisitor; enum CXCursorKind *ck = &K; - Idx = clang_createIndex(/* excludeDeclsFromPCH */ - !strcmp(filter, "local") ? 1 : 0, - /* displayDiagnostics */ 1); - - if (!CreateTranslationUnit(Idx, file, &TU)) - return 1; - + /* Perform some simple filtering. */ if (!strcmp(filter, "all") || !strcmp(filter, "local")) ck = NULL; else if (!strcmp(filter, "category")) K = CXCursor_ObjCCategoryDecl; @@ -158,16 +154,46 @@ int perform_test_load_tu(const char *file, const char *filter) { else if (!strcmp(filter, "protocol")) K = CXCursor_ObjCProtocolDecl; else if (!strcmp(filter, "function")) K = CXCursor_FunctionDecl; else if (!strcmp(filter, "typedef")) K = CXCursor_TypedefDecl; + else if (!strcmp(filter, "scan-function")) Visitor = FunctionScanVisitor; else { fprintf(stderr, "Unknown filter for -test-load-tu: %s\n", filter); return 1; } - clang_loadTranslationUnit(TU, TranslationUnitVisitor, ck); + clang_loadTranslationUnit(TU, Visitor, ck); clang_disposeTranslationUnit(TU); return 0; } +int perform_test_load_tu(const char *file, const char *filter) { + CXIndex Idx; + CXTranslationUnit TU; + Idx = clang_createIndex(/* excludeDeclsFromPCH */ + !strcmp(filter, "local") ? 1 : 0, + /* displayDiagnostics */ 1); + + if (!CreateTranslationUnit(Idx, file, &TU)) + return 1; + + return perform_test_load(Idx, TU, filter); +} + +int perform_test_load_source(int argc, const char **argv, const char *filter) { + CXIndex Idx; + CXTranslationUnit TU; + Idx = clang_createIndex(/* excludeDeclsFromPCH */ + !strcmp(filter, "local") ? 1 : 0, + /* displayDiagnostics */ 1); + + TU = clang_createTranslationUnitFromSourceFile(Idx, 0, argc, argv); + if (!TU) { + fprintf(stderr, "Unable to load translation unit!\n"); + return 1; + } + + return perform_test_load(Idx, TU, filter); +} + /******************************************************************************/ /* Logic for testing clang_getCursor(). */ /******************************************************************************/ @@ -394,14 +420,16 @@ static void print_usage(void) { " c-index-test -test-file-scan <AST file> <source file> " "[FileCheck prefix]\n" " c-index-test -test-load-tu <AST file> <symbol filter>\n\n" - " <symbol filter> options for -test-load-tu:\n%s", + " c-index-test -test-load-source <symbol filter> {<args>}*\n\n" + " <symbol filter> options for -test-load-tu and -test-load-source:\n%s", " all - load all symbols, including those from PCH\n" " local - load all symbols except those in PCH\n" " category - only load ObjC categories (non-PCH)\n" " interface - only load ObjC interfaces (non-PCH)\n" " protocol - only load ObjC protocols (non-PCH)\n" " function - only load functions (non-PCH)\n" - " typedef - only load typdefs (non-PCH)\n\n"); + " typedef - only load typdefs (non-PCH)\n" + " scan-function - scan function bodies (non-PCH)\n\n"); } int main(int argc, const char **argv) { @@ -409,6 +437,8 @@ int main(int argc, const char **argv) { return perform_code_completion(argc, argv); if (argc == 4 && strcmp(argv[1], "-test-load-tu") == 0) return perform_test_load_tu(argv[2], argv[3]); + if (argc >= 4 && strcmp(argv[1], "-test-load-source") == 0) + return perform_test_load_source(argc - 3, argv + 3, argv[2]); if (argc >= 4 && strcmp(argv[1], "-test-file-scan") == 0) return perform_file_scan(argv[2], argv[3], argc >= 5 ? argv[4] : 0); diff --git a/tools/clang-cc/CMakeLists.txt b/tools/clang-cc/CMakeLists.txt index e7da908..c96e8b1 100644 --- a/tools/clang-cc/CMakeLists.txt +++ b/tools/clang-cc/CMakeLists.txt @@ -1,6 +1,7 @@ set(LLVM_NO_RTTI 1) set( LLVM_USED_LIBS + clangDriver clangFrontend clangCodeGen clangAnalysis diff --git a/tools/clang-cc/Makefile b/tools/clang-cc/Makefile index 874a42f..ebcc1d5 100644 --- a/tools/clang-cc/Makefile +++ b/tools/clang-cc/Makefile @@ -21,7 +21,7 @@ TOOL_NO_EXPORTS = 1 include $(LEVEL)/Makefile.config LINK_COMPONENTS := $(TARGETS_TO_BUILD) bitreader bitwriter codegen ipo selectiondag -USEDLIBS = clangFrontend.a clangCodeGen.a clangAnalysis.a \ +USEDLIBS = clangDriver.a clangFrontend.a clangCodeGen.a clangAnalysis.a \ clangRewrite.a clangSema.a clangAST.a clangParse.a \ clangLex.a clangBasic.a diff --git a/tools/clang-cc/Options.cpp b/tools/clang-cc/Options.cpp index 584f957..a18598e 100644 --- a/tools/clang-cc/Options.cpp +++ b/tools/clang-cc/Options.cpp @@ -21,6 +21,7 @@ #include "clang/Frontend/DiagnosticOptions.h" #include "clang/Frontend/FrontendOptions.h" #include "clang/Frontend/HeaderSearchOptions.h" +#include "clang/Frontend/LangStandard.h" #include "clang/Frontend/PCHReader.h" #include "clang/Frontend/PreprocessorOptions.h" #include "clang/Frontend/PreprocessorOutputOptions.h" @@ -81,7 +82,7 @@ static llvm::cl::opt<bool> AnalyzeAll("analyzer-opt-analyze-headers", llvm::cl::desc("Force the static analyzer to analyze " "functions defined in header files")); - + static llvm::cl::opt<bool> AnalyzerDisplayProgress("analyzer-display-progress", llvm::cl::desc("Emit verbose output about the analyzer's progress")); @@ -93,7 +94,7 @@ AnalyzerExperimentalChecks("analyzer-experimental-checks", static llvm::cl::opt<bool> AnalyzerExperimentalInternalChecks("analyzer-experimental-internal-checks", llvm::cl::desc("Use new default path-sensitive checks currently in testing")); - + static llvm::cl::opt<std::string> AnalyzeSpecificFunction("analyze-function", llvm::cl::desc("Run analysis on specific function")); @@ -141,6 +142,46 @@ GenerateDebugInfo("g", llvm::cl::desc("Generate source level debug information")); static llvm::cl::opt<bool> +MAsmVerbose("masm-verbose", llvm::cl::desc("Generate verbose assembly output")); + +static llvm::cl::opt<std::string> +MCodeModel("mcode-model", llvm::cl::desc("The code model to use")); + +static llvm::cl::opt<std::string> +MDebugPass("mdebu-pass", llvm::cl::desc("Output additional debug information")); + +static llvm::cl::opt<bool> +MDisableFPElim("mdisable-fp-elim", + llvm::cl::desc("Disable frame pointer elimination optimization")); + +static llvm::cl::opt<std::string> +MFloatABI("mfloat-abi", llvm::cl::desc("The float ABI to use")); + +static llvm::cl::opt<std::string> +MLimitFloatPrecision("mlimit-float-precision", + llvm::cl::desc("Limit float precision to the given value")); + +static llvm::cl::opt<bool> +MNoZeroInitializedInBSS("mno-zero-initialized-in-bss", + llvm::cl::desc("Do not put zero initialized data in the BSS")); + +static llvm::cl::opt<bool> +MSoftFloat("msoft-float", llvm::cl::desc("Use software floating point")); + +static llvm::cl::opt<std::string> +MRelocationModel("mrelocation-model", + llvm::cl::desc("The relocation model to use"), + llvm::cl::init("pic")); + +static llvm::cl::opt<bool> +MUnwindTables("munwind-tables", + llvm::cl::desc("Generate unwinding tables for all functions")); + +static llvm::cl::opt<std::string> +MainFileName("main-file-name", + llvm::cl::desc("Main file name to use for debug info")); + +static llvm::cl::opt<bool> NoCommon("fno-common", llvm::cl::desc("Compile common globals like normal definitions"), llvm::cl::ValueDisallowed); @@ -436,6 +477,8 @@ TimeReport("ftime-report", namespace langoptions { +using namespace clang::frontend; + static llvm::cl::opt<bool> NoBuiltin("fno-builtin", llvm::cl::desc("Disable implicit builtin knowledge of functions")); @@ -448,8 +491,8 @@ AccessControl("faccess-control", llvm::cl::desc("Enable C++ access control")); static llvm::cl::opt<bool> -CharIsSigned("fsigned-char", - llvm::cl::desc("Force char to be a signed/unsigned type")); +NoSignedChar("fno-signed-char", + llvm::cl::desc("Char is unsigned")); static llvm::cl::opt<bool> DollarsInIdents("fdollars-in-identifiers", @@ -481,42 +524,12 @@ GNURuntime("fgnu-runtime", llvm::cl::desc("Generate output compatible with the standard GNU " "Objective-C runtime")); -/// LangStds - Language standards we support. -enum LangStds { - lang_unspecified, - lang_c89, lang_c94, lang_c99, - lang_gnu89, lang_gnu99, - lang_cxx98, lang_gnucxx98, - lang_cxx0x, lang_gnucxx0x -}; -static llvm::cl::opt<LangStds> +static llvm::cl::opt<LangStandard::Kind> LangStd("std", llvm::cl::desc("Language standard to compile for"), - llvm::cl::init(lang_unspecified), - llvm::cl::values(clEnumValN(lang_c89, "c89", "ISO C 1990"), - clEnumValN(lang_c89, "c90", "ISO C 1990"), - clEnumValN(lang_c89, "iso9899:1990", "ISO C 1990"), - clEnumValN(lang_c94, "iso9899:199409", - "ISO C 1990 with amendment 1"), - clEnumValN(lang_c99, "c99", "ISO C 1999"), - clEnumValN(lang_c99, "c9x", "ISO C 1999"), - clEnumValN(lang_c99, "iso9899:1999", "ISO C 1999"), - clEnumValN(lang_c99, "iso9899:199x", "ISO C 1999"), - clEnumValN(lang_gnu89, "gnu89", - "ISO C 1990 with GNU extensions"), - clEnumValN(lang_gnu99, "gnu99", - "ISO C 1999 with GNU extensions (default for C)"), - clEnumValN(lang_gnu99, "gnu9x", - "ISO C 1999 with GNU extensions"), - clEnumValN(lang_cxx98, "c++98", - "ISO C++ 1998 with amendments"), - clEnumValN(lang_gnucxx98, "gnu++98", - "ISO C++ 1998 with amendments and GNU " - "extensions (default for C++)"), - clEnumValN(lang_cxx0x, "c++0x", - "Upcoming ISO C++ 200x with amendments"), - clEnumValN(lang_gnucxx0x, "gnu++0x", - "Upcoming ISO C++ 200x with amendments and GNU " - "extensions"), + llvm::cl::init(LangStandard::lang_unspecified), llvm::cl::values( +#define LANGSTANDARD(id, name, desc, features) \ + clEnumValN(LangStandard::lang_##id, name, desc), +#include "clang/Frontend/LangStandards.def" clEnumValEnd)); static llvm::cl::opt<bool> @@ -524,10 +537,6 @@ MSExtensions("fms-extensions", llvm::cl::desc("Accept some non-standard constructs used in " "Microsoft header files ")); -static llvm::cl::opt<std::string> -MainFileName("main-file-name", - llvm::cl::desc("Main file name to use for debug info")); - static llvm::cl::opt<bool> NoMathErrno("fno-math-errno", llvm::cl::desc("Don't require math functions to respect errno")); @@ -745,6 +754,7 @@ DumpDefines("dD", llvm::cl::desc("Print macro definitions in -E mode in " "addition to normal output")); } + //===----------------------------------------------------------------------===// // Target Options //===----------------------------------------------------------------------===// @@ -757,7 +767,7 @@ TargetABI("target-abi", static llvm::cl::opt<std::string> TargetCPU("mcpu", - llvm::cl::desc("Target a specific cpu type (-mcpu=help for details)")); + llvm::cl::desc("Target a specific cpu type ('-mcpu help' for details)")); static llvm::cl::list<std::string> TargetFeatures("target-feature", llvm::cl::desc("Target specific attributes")); @@ -791,8 +801,7 @@ void clang::InitializeAnalyzerOptions(AnalyzerOptions &Opts) { } void clang::InitializeCodeGenOptions(CodeGenOptions &Opts, - const LangOptions &Lang, - bool TimePasses) { + const LangOptions &Lang) { using namespace codegenoptions; // -Os implies -O2 @@ -810,19 +819,27 @@ void clang::InitializeCodeGenOptions(CodeGenOptions &Opts, Opts.NoCommon = NoCommon; Opts.NoImplicitFloat = NoImplicitFloat; Opts.OptimizeSize = OptSize; - Opts.SimplifyLibCalls = 1; Opts.UnrollLoops = (Opts.OptimizationLevel > 1 && !OptSize); - // FIXME: Eliminate this dependency? - if (Lang.NoBuiltin) - Opts.SimplifyLibCalls = 0; - if (Lang.CPlusPlus) - Opts.NoCommon = 1; - Opts.TimePasses = TimePasses; + // LLVM Code Generator options. + + Opts.AsmVerbose = MAsmVerbose; + Opts.CodeModel = MCodeModel; + Opts.DebugPass = MDebugPass; + Opts.DisableFPElim = MDisableFPElim; + Opts.FloatABI = MFloatABI; + Opts.LimitFloatPrecision = MLimitFloatPrecision; + Opts.NoZeroInitializedInBSS = MNoZeroInitializedInBSS; + Opts.SoftFloat = MSoftFloat; + Opts.RelocationModel = MRelocationModel; + Opts.UnwindTables = MUnwindTables; #ifdef NDEBUG Opts.VerifyModule = 0; #endif + + if (MainFileName.getPosition()) + Opts.MainFileName = MainFileName; } void clang::InitializeDependencyOutputOptions(DependencyOutputOptions &Opts) { @@ -857,13 +874,8 @@ void clang::InitializeDiagnosticOptions(DiagnosticOptions &Opts) { void clang::InitializeFrontendOptions(FrontendOptions &Opts) { using namespace frontendoptions; - // Select program action. Opts.ProgramAction = ProgAction; - if (PluginActionName.getPosition()) { - Opts.ProgramAction = frontend::PluginAction; - Opts.ActionName = PluginActionName; - } - + Opts.ActionName = PluginActionName; Opts.CodeCompletionAt = CodeCompletionAt; Opts.DebugCodeCompletionPrinter = !NoCodeCompletionDebugPrinter; Opts.DisableFree = DisableFree; @@ -876,6 +888,14 @@ void clang::InitializeFrontendOptions(FrontendOptions &Opts) { Opts.ShowTimers = TimeReport; Opts.ViewClassInheritance = InheritanceViewCls; + // Enforce certain program action implications. + if (!Opts.ActionName.empty()) + Opts.ProgramAction = frontend::PluginAction; + if (!Opts.ViewClassInheritance.empty()) + Opts.ProgramAction = frontend::InheritanceView; + if (!Opts.FixItLocations.empty()) + Opts.ProgramAction = frontend::FixIt; + // '-' is the default input if none is given. if (InputFilenames.empty()) { FrontendOptions::InputKind IK = InputType; @@ -1054,145 +1074,87 @@ void clang::InitializePreprocessorOptions(PreprocessorOptions &Opts) { } void clang::InitializeLangOptions(LangOptions &Options, - FrontendOptions::InputKind IK, - TargetInfo &Target) { + FrontendOptions::InputKind IK) { using namespace langoptions; - - switch (IK) { - case FrontendOptions::IK_None: - case FrontendOptions::IK_AST: - assert(0 && "Invalid input kind!"); - case FrontendOptions::IK_Asm: + // Set some properties which depend soley on the input kind; it would be nice + // to move these to the language standard, and have the driver resolve the + // input kind + language standard. + if (IK == FrontendOptions::IK_Asm) { Options.AsmPreprocessor = 1; - // FALLTHROUGH - case FrontendOptions::IK_PreprocessedC: - // FALLTHROUGH - case FrontendOptions::IK_C: - // Do nothing. - break; - case FrontendOptions::IK_PreprocessedCXX: - // FALLTHROUGH - case FrontendOptions::IK_CXX: - Options.CPlusPlus = 1; - break; - case FrontendOptions::IK_PreprocessedObjC: - // FALLTHROUGH - case FrontendOptions::IK_ObjC: + } else if (IK == FrontendOptions::IK_ObjC || + IK == FrontendOptions::IK_ObjCXX || + IK == FrontendOptions::IK_PreprocessedObjC || + IK == FrontendOptions::IK_PreprocessedObjCXX) { Options.ObjC1 = Options.ObjC2 = 1; - break; - case FrontendOptions::IK_PreprocessedObjCXX: - // FALLTHROUGH - case FrontendOptions::IK_ObjCXX: - Options.ObjC1 = Options.ObjC2 = 1; - Options.CPlusPlus = 1; - break; - case FrontendOptions::IK_OpenCL: - Options.OpenCL = 1; - Options.AltiVec = 1; - Options.CXXOperatorNames = 1; - Options.LaxVectorConversions = 1; - break; } - if (ObjCExclusiveGC) - Options.setGCMode(LangOptions::GCOnly); - else if (ObjCEnableGC) - Options.setGCMode(LangOptions::HybridGC); - - if (ObjCEnableGCBitmapPrint) - Options.ObjCGCBitmapPrint = 1; - - if (AltiVec) - Options.AltiVec = 1; - - if (PThread) - Options.POSIXThreads = 1; - - Options.setVisibilityMode(SymbolVisibility); - Options.OverflowChecking = OverflowChecking; - - if (LangStd == lang_unspecified) { + if (LangStd == LangStandard::lang_unspecified) { // Based on the base language, pick one. switch (IK) { case FrontendOptions::IK_None: case FrontendOptions::IK_AST: assert(0 && "Invalid input kind!"); case FrontendOptions::IK_OpenCL: - LangStd = lang_c99; + LangStd = LangStandard::lang_opencl; break; case FrontendOptions::IK_Asm: case FrontendOptions::IK_C: case FrontendOptions::IK_PreprocessedC: case FrontendOptions::IK_ObjC: case FrontendOptions::IK_PreprocessedObjC: - LangStd = lang_gnu99; + LangStd = LangStandard::lang_gnu99; break; case FrontendOptions::IK_CXX: case FrontendOptions::IK_PreprocessedCXX: case FrontendOptions::IK_ObjCXX: case FrontendOptions::IK_PreprocessedObjCXX: - LangStd = lang_gnucxx98; + LangStd = LangStandard::lang_gnucxx98; break; } } - switch (LangStd) { - default: assert(0 && "Unknown language standard!"); - - // Fall through from newer standards to older ones. This isn't really right. - // FIXME: Enable specifically the right features based on the language stds. - case lang_gnucxx0x: - case lang_cxx0x: - Options.CPlusPlus0x = 1; - // FALL THROUGH - case lang_gnucxx98: - case lang_cxx98: - Options.CPlusPlus = 1; - Options.CXXOperatorNames = !NoOperatorNames; - // FALL THROUGH. - case lang_gnu99: - case lang_c99: - Options.C99 = 1; - Options.HexFloats = 1; - // FALL THROUGH. - case lang_gnu89: - Options.BCPLComment = 1; // Only for C99/C++. - // FALL THROUGH. - case lang_c94: - Options.Digraphs = 1; // C94, C99, C++. - // FALL THROUGH. - case lang_c89: - break; + const LangStandard &Std = LangStandard::getLangStandardForKind(LangStd); + Options.BCPLComment = Std.hasBCPLComments(); + Options.C99 = Std.isC99(); + Options.CPlusPlus = Std.isCPlusPlus(); + Options.CPlusPlus0x = Std.isCPlusPlus0x(); + Options.Digraphs = Std.hasDigraphs(); + Options.GNUInline = !Std.isC99(); + Options.GNUMode = Std.isGNUMode(); + Options.HexFloats = Std.hasHexFloats(); + Options.ImplicitInt = Std.hasImplicitInt(); + + // OpenCL has some additional defaults. + if (LangStd == LangStandard::lang_opencl) { + Options.OpenCL = 1; + Options.AltiVec = 1; + Options.CXXOperatorNames = 1; + Options.LaxVectorConversions = 1; } - // GNUMode - Set if we're in gnu99, gnu89, gnucxx98, etc. - switch (LangStd) { - default: assert(0 && "Unknown language standard!"); - case lang_gnucxx0x: - case lang_gnucxx98: - case lang_gnu99: - case lang_gnu89: - Options.GNUMode = 1; - break; - case lang_cxx0x: - case lang_cxx98: - case lang_c99: - case lang_c94: - case lang_c89: - Options.GNUMode = 0; - break; - } + // OpenCL and C++ both have bool, true, false keywords. + Options.Bool = Options.OpenCL || Options.CPlusPlus; - if (Options.CPlusPlus) { - Options.C99 = 0; - Options.HexFloats = 0; - } + if (Options.CPlusPlus) + Options.CXXOperatorNames = !NoOperatorNames; - if (LangStd == lang_c89 || LangStd == lang_c94 || LangStd == lang_gnu89) - Options.ImplicitInt = 1; - else - Options.ImplicitInt = 0; + if (ObjCExclusiveGC) + Options.setGCMode(LangOptions::GCOnly); + else if (ObjCEnableGC) + Options.setGCMode(LangOptions::HybridGC); + + if (ObjCEnableGCBitmapPrint) + Options.ObjCGCBitmapPrint = 1; + + if (AltiVec) + Options.AltiVec = 1; + + if (PThread) + Options.POSIXThreads = 1; + + Options.setVisibilityMode(SymbolVisibility); + Options.OverflowChecking = OverflowChecking; // Mimicing gcc's behavior, trigraphs are only enabled if -trigraphs // is specified, or -std is set to a conforming mode. @@ -1200,17 +1162,8 @@ void clang::InitializeLangOptions(LangOptions &Options, if (Trigraphs.getPosition()) Options.Trigraphs = Trigraphs; // Command line option wins if specified. - // If in a conformant language mode (e.g. -std=c99) Blocks defaults to off - // even if they are normally on for the target. In GNU modes (e.g. - // -std=gnu99) the default for blocks depends on the target settings. - // However, blocks are not turned off when compiling Obj-C or Obj-C++ code. - if (!Options.ObjC1 && !Options.GNUMode) - Options.Blocks = 0; - - // Default to not accepting '$' in identifiers when preprocessing assembler, - // but do accept when preprocessing C. FIXME: these defaults are right for - // darwin, are they right everywhere? - Options.DollarIdents = IK != FrontendOptions::IK_Asm; + // Default to not accepting '$' in identifiers when preprocessing assembler. + Options.DollarIdents = !Options.AsmPreprocessor; if (DollarsInIdents.getPosition()) // Explicit setting overrides default. Options.DollarIdents = DollarsInIdents; @@ -1223,10 +1176,8 @@ void clang::InitializeLangOptions(LangOptions &Options, Options.LaxVectorConversions = 0; Options.Exceptions = Exceptions; Options.Rtti = !NoRtti; - if (EnableBlocks.getPosition()) - Options.Blocks = EnableBlocks; - if (CharIsSigned.getPosition()) - Options.CharIsSigned = CharIsSigned; + Options.Blocks = EnableBlocks; + Options.CharIsSigned = !NoSignedChar; if (ShortWChar.getPosition()) Options.ShortWChar = ShortWChar; @@ -1242,9 +1193,6 @@ void clang::InitializeLangOptions(LangOptions &Options, Options.ElideConstructors = !NoElideConstructors; - // OpenCL and C++ both have bool, true, false keywords. - Options.Bool = Options.OpenCL | Options.CPlusPlus; - Options.MathErrno = !NoMathErrno; if (TemplateDepth.getPosition()) @@ -1255,7 +1203,7 @@ void clang::InitializeLangOptions(LangOptions &Options, Options.NeXTRuntime = 0; if (!ObjCConstantStringClass.empty()) - Options.ObjCConstantStringClass = ObjCConstantStringClass.c_str(); + Options.ObjCConstantStringClass = ObjCConstantStringClass; if (ObjCNonFragileABI) Options.ObjCNonFragileABI = 1; @@ -1272,8 +1220,6 @@ void clang::InitializeLangOptions(LangOptions &Options, assert(PICLevel <= 2 && "Invalid value for -pic-level"); Options.PICLevel = PICLevel; - Options.GNUInline = !Options.C99; - // This is the __NO_INLINE__ define, which just depends on things like the // optimization level and -fno-inline, not actually whether the backend has // inlining enabled. @@ -1292,11 +1238,6 @@ void clang::InitializeLangOptions(LangOptions &Options, case 2: Options.setStackProtectorMode(LangOptions::SSPReq); break; } } - - if (MainFileName.getPosition()) - Options.setMainFileName(MainFileName.c_str()); - - Target.setForcedLangOptions(Options); } void diff --git a/tools/clang-cc/Options.h b/tools/clang-cc/Options.h index 9a2fd9d..91e37f2 100644 --- a/tools/clang-cc/Options.h +++ b/tools/clang-cc/Options.h @@ -30,8 +30,7 @@ class TargetOptions; void InitializeAnalyzerOptions(AnalyzerOptions &Opts); void InitializeCodeGenOptions(CodeGenOptions &Opts, - const LangOptions &Lang, - bool TimePasses); + const LangOptions &Lang); void InitializeDependencyOutputOptions(DependencyOutputOptions &Opts); @@ -42,9 +41,7 @@ void InitializeFrontendOptions(FrontendOptions &Opts); void InitializeHeaderSearchOptions(HeaderSearchOptions &Opts, llvm::StringRef BuiltinIncludePath); -void InitializeLangOptions(LangOptions &Options, - FrontendOptions::InputKind LK, - TargetInfo &Target); +void InitializeLangOptions(LangOptions &Options, FrontendOptions::InputKind LK); void InitializePreprocessorOptions(PreprocessorOptions &Opts); diff --git a/tools/clang-cc/clang-cc.cpp b/tools/clang-cc/clang-cc.cpp index bae8697..2899684 100644 --- a/tools/clang-cc/clang-cc.cpp +++ b/tools/clang-cc/clang-cc.cpp @@ -21,11 +21,18 @@ #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" #include "clang/Basic/Version.h" +#include "clang/Driver/Arg.h" +#include "clang/Driver/ArgList.h" +#include "clang/Driver/CC1Options.h" +#include "clang/Driver/DriverDiagnostic.h" +#include "clang/Driver/OptTable.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/FrontendActions.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Frontend/FrontendPluginRegistry.h" +#include "clang/Frontend/TextDiagnosticBuffer.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" #include "clang/Frontend/VerifyDiagnosticsClient.h" #include "llvm/LLVMContext.h" #include "llvm/ADT/OwningPtr.h" @@ -39,6 +46,7 @@ #include "llvm/System/Path.h" #include "llvm/System/Signals.h" #include "llvm/Target/TargetSelect.h" +#include <cstdio> using namespace clang; //===----------------------------------------------------------------------===// @@ -73,11 +81,6 @@ static void LLVMErrorHandler(void *UserData, const std::string &Message) { exit(1); } -/// ClangFrontendTimer - The front-end activities should charge time to it with -/// TimeRegion. The -ftime-report option controls whether this will do -/// anything. -llvm::Timer *ClangFrontendTimer = 0; - static FrontendAction *CreateFrontendAction(CompilerInstance &CI) { using namespace clang::frontend; @@ -139,18 +142,11 @@ static FrontendAction *CreateFrontendAction(CompilerInstance &CI) { } } -static TargetInfo * -ConstructCompilerInvocation(CompilerInvocation &Opts, Diagnostic &Diags, - const char *Argv0, bool &IsAST) { +static bool ConstructCompilerInvocation(CompilerInvocation &Opts, + Diagnostic &Diags, const char *Argv0) { // Initialize target options. InitializeTargetOptions(Opts.getTargetOpts()); - // Get information about the target being compiled for. - llvm::OwningPtr<TargetInfo> Target( - TargetInfo::CreateTargetInfo(Diags, Opts.getTargetOpts())); - if (!Target) - return 0; - // Initialize frontend options. InitializeFrontendOptions(Opts.getFrontendOpts()); @@ -160,7 +156,7 @@ ConstructCompilerInvocation(CompilerInvocation &Opts, Diagnostic &Diags, if (Opts.getFrontendOpts().Inputs[i].first != IK) { llvm::errs() << "error: cannot have multiple input files of distinct " << "language kinds without -x\n"; - return 0; + return false; } } @@ -168,9 +164,8 @@ ConstructCompilerInvocation(CompilerInvocation &Opts, Diagnostic &Diags, // // FIXME: These aren't used during operations on ASTs. Split onto a separate // code path to make this obvious. - IsAST = (IK == FrontendOptions::IK_AST); - if (!IsAST) - InitializeLangOptions(Opts.getLangOpts(), IK, *Target); + if (IK != FrontendOptions::IK_AST) + InitializeLangOptions(Opts.getLangOpts(), IK); // Initialize the static analyzer options. InitializeAnalyzerOptions(Opts.getAnalyzerOpts()); @@ -188,12 +183,78 @@ ConstructCompilerInvocation(CompilerInvocation &Opts, Diagnostic &Diags, // Initialize the preprocessed output options. InitializePreprocessorOutputOptions(Opts.getPreprocessorOutputOpts()); - // Initialize backend options, which may also be used to key some language - // options. - InitializeCodeGenOptions(Opts.getCodeGenOpts(), Opts.getLangOpts(), - Opts.getFrontendOpts().ShowTimers); + // Initialize backend options. + InitializeCodeGenOptions(Opts.getCodeGenOpts(), Opts.getLangOpts()); + + return true; +} + +static int cc1_main(Diagnostic &Diags, + const char **ArgBegin, const char **ArgEnd, + const char *Argv0, void *MainAddr) { + using namespace clang::driver; + + llvm::errs() << "cc1 argv:"; + for (const char **i = ArgBegin; i != ArgEnd; ++i) + llvm::errs() << " \"" << *i << '"'; + llvm::errs() << "\n"; + + // Parse the arguments. + OptTable *Opts = createCC1OptTable(); + unsigned MissingArgIndex, MissingArgCount; + InputArgList *Args = Opts->ParseArgs(ArgBegin, ArgEnd, + MissingArgIndex, MissingArgCount); + + // Check for missing argument error. + if (MissingArgCount) + Diags.Report(clang::diag::err_drv_missing_argument) + << Args->getArgString(MissingArgIndex) << MissingArgCount; + + // Dump the parsed arguments. + llvm::errs() << "cc1 parsed options:\n"; + for (ArgList::const_iterator it = Args->begin(), ie = Args->end(); + it != ie; ++it) + (*it)->dump(); + + // Create a compiler invocation. + llvm::errs() << "cc1 creating invocation.\n"; + CompilerInvocation Invocation; + CompilerInvocation::CreateFromArgs(Invocation, ArgBegin, ArgEnd, + Argv0, MainAddr, Diags); + + // Convert the invocation back to argument strings. + std::vector<std::string> InvocationArgs; + Invocation.toArgs(InvocationArgs); + + // Dump the converted arguments. + llvm::SmallVector<const char*, 32> Invocation2Args; + llvm::errs() << "invocation argv :"; + for (unsigned i = 0, e = InvocationArgs.size(); i != e; ++i) { + Invocation2Args.push_back(InvocationArgs[i].c_str()); + llvm::errs() << " \"" << InvocationArgs[i] << '"'; + } + llvm::errs() << "\n"; + + // Convert those arguments to another invocation, and check that we got the + // same thing. + CompilerInvocation Invocation2; + CompilerInvocation::CreateFromArgs(Invocation2, Invocation2Args.begin(), + Invocation2Args.end(), Argv0, MainAddr, + Diags); + + // FIXME: Implement CompilerInvocation comparison. + if (true) { + //llvm::errs() << "warning: Invocations differ!\n"; + + std::vector<std::string> Invocation2Args; + Invocation2.toArgs(Invocation2Args); + llvm::errs() << "invocation2 argv:"; + for (unsigned i = 0, e = Invocation2Args.size(); i != e; ++i) + llvm::errs() << " \"" << Invocation2Args[i] << '"'; + llvm::errs() << "\n"; + } - return Target.take(); + return 0; } int main(int argc, char **argv) { @@ -201,10 +262,19 @@ int main(int argc, char **argv) { llvm::PrettyStackTraceProgram X(argc, argv); CompilerInstance Clang(&llvm::getGlobalContext(), false); + // Run clang -cc1 test. + if (argc > 1 && llvm::StringRef(argv[1]) == "-cc1") { + TextDiagnosticPrinter DiagClient(llvm::errs(), DiagnosticOptions()); + Diagnostic Diags(&DiagClient); + return cc1_main(Diags, (const char**) argv + 2, (const char**) argv + argc, + argv[0], (void*) (intptr_t) GetBuiltinIncludePath); + } + // Initialize targets first, so that --version shows registered targets. llvm::InitializeAllTargets(); llvm::InitializeAllAsmPrinters(); +#if 1 llvm::cl::ParseCommandLineOptions(argc, argv, "LLVM 'Clang' Compiler: http://clang.llvm.org\n"); @@ -225,13 +295,49 @@ int main(int argc, char **argv) { // // FIXME: We should move .ast inputs to taking a separate path, they are // really quite different. - bool IsAST = false; - Clang.setTarget( - ConstructCompilerInvocation(Clang.getInvocation(), Clang.getDiagnostics(), - argv[0], IsAST)); + if (!ConstructCompilerInvocation(Clang.getInvocation(), + Clang.getDiagnostics(), argv[0])) + return 1; +#else + // Buffer diagnostics from argument parsing. + TextDiagnosticBuffer DiagsBuffer; + Diagnostic Diags(&DiagsBuffer); + + CompilerInvocation::CreateFromArgs(Clang.getInvocation(), + (const char**) argv + 1, + (const char**) argv + argc, argv[0], + (void*)(intptr_t) GetBuiltinIncludePath, + Diags); + + // Create the actual diagnostics engine. + Clang.createDiagnostics(argc, argv); + if (!Clang.hasDiagnostics()) + return 1; + + // Set an error handler, so that any LLVM backend diagnostics go through our + // error handler. + llvm::llvm_install_error_handler(LLVMErrorHandler, + static_cast<void*>(&Clang.getDiagnostics())); + + DiagsBuffer.FlushDiagnostics(Clang.getDiagnostics()); + + // If there were any errors in processing arguments, exit now. + if (Clang.getDiagnostics().getNumErrors()) + return 1; +#endif + + // Create the target instance. + Clang.setTarget(TargetInfo::CreateTargetInfo(Clang.getDiagnostics(), + Clang.getTargetOpts())); if (!Clang.hasTarget()) return 1; + // Inform the target of the language options + // + // FIXME: We shouldn't need to do this, the target should be immutable once + // created. This complexity should be lifted elsewhere. + Clang.getTarget().setForcedLangOptions(Clang.getLangOpts()); + // Validate/process some options if (Clang.getHeaderSearchOpts().Verbose) llvm::errs() << "clang-cc version " CLANG_VERSION_STRING @@ -239,19 +345,15 @@ int main(int argc, char **argv) { << " hosted on " << llvm::sys::getHostTriple() << "\n"; if (Clang.getFrontendOpts().ShowTimers) - ClangFrontendTimer = new llvm::Timer("Clang front-end time"); - - // Enforce certain implications. - if (!Clang.getFrontendOpts().ViewClassInheritance.empty()) - Clang.getFrontendOpts().ProgramAction = frontend::InheritanceView; - if (!Clang.getFrontendOpts().FixItLocations.empty()) - Clang.getFrontendOpts().ProgramAction = frontend::FixIt; + Clang.createFrontendTimer(); for (unsigned i = 0, e = Clang.getFrontendOpts().Inputs.size(); i != e; ++i) { const std::string &InFile = Clang.getFrontendOpts().Inputs[i].second; // If we aren't using an AST file, setup the file and source managers and // the preprocessor. + bool IsAST = + Clang.getFrontendOpts().Inputs[i].first == FrontendOptions::IK_AST; if (!IsAST) { if (!i) { // Create a file manager object to provide access to and cache the @@ -273,7 +375,6 @@ int main(int argc, char **argv) { if (!Act) break; - Act->setCurrentTimer(ClangFrontendTimer); if (Act->BeginSourceFile(Clang, InFile, IsAST)) { Act->Execute(); Act->EndSourceFile(); @@ -290,8 +391,6 @@ int main(int argc, char **argv) { fprintf(stderr, "\n"); } - delete ClangFrontendTimer; - // Return the appropriate status when verifying diagnostics. // // FIXME: If we could make getNumErrors() do the right thing, we wouldn't need diff --git a/tools/driver/CMakeLists.txt b/tools/driver/CMakeLists.txt index 1a95380..7900211 100644 --- a/tools/driver/CMakeLists.txt +++ b/tools/driver/CMakeLists.txt @@ -3,7 +3,6 @@ set(LLVM_NO_RTTI 1) set( LLVM_USED_LIBS clangDriver clangBasic - clangFrontend ) set(LLVM_LINK_COMPONENTS system support bitreader bitwriter) @@ -15,9 +14,15 @@ add_clang_executable(clang add_dependencies(clang clang-cc) +if(UNIX) + set(CLANGXX_LINK_OR_COPY create_symlink) +else() + set(CLANGXX_LINK_OR_COPY copy) +endif() + # Create the clang++ symlink in the build directory. add_custom_target(clang++ ALL - ${CMAKE_COMMAND} -E create_symlink + ${CMAKE_COMMAND} -E ${CLANGXX_LINK_OR_COPY} "${LLVM_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/clang${CMAKE_EXECUTABLE_SUFFIX}" "${LLVM_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/clang++${CMAKE_EXECUTABLE_SUFFIX}" DEPENDS clang) @@ -26,4 +31,4 @@ install(TARGETS clang RUNTIME DESTINATION bin) # Create the clang++ symlink at installation time. -install(CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" -E create_symlink \"${CMAKE_INSTALL_PREFIX}/bin/clang${CMAKE_EXECUTABLE_SUFFIX}\" \"${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}\" \"${CMAKE_INSTALL_PREFIX}/bin/clang++${CMAKE_EXECUTABLE_SUFFIX}\")") diff --git a/tools/driver/Makefile b/tools/driver/Makefile index 19f93a2..f250651 100644 --- a/tools/driver/Makefile +++ b/tools/driver/Makefile @@ -19,7 +19,7 @@ TOOL_NO_EXPORTS = 1 # FIXME: It is unfortunate we need to pull in the bitcode reader and # writer just to get the serializer stuff used by clangBasic. LINK_COMPONENTS := system support bitreader bitwriter -USEDLIBS = clangDriver.a clangBasic.a clangFrontend.a +USEDLIBS = clangDriver.a clangBasic.a include $(LEVEL)/Makefile.common diff --git a/tools/driver/cc1_main.cpp b/tools/driver/cc1_main.cpp index c516359..a9d27ef 100644 --- a/tools/driver/cc1_main.cpp +++ b/tools/driver/cc1_main.cpp @@ -11,78 +11,14 @@ // //===----------------------------------------------------------------------===// -#include "clang/Driver/Arg.h" -#include "clang/Driver/ArgList.h" -#include "clang/Driver/CC1Options.h" -#include "clang/Driver/DriverDiagnostic.h" -#include "clang/Driver/OptTable.h" -#include "clang/Driver/Option.h" -#include "clang/Frontend/CompilerInvocation.h" #include "llvm/Support/raw_ostream.h" -#include <cstdlib> -#include <vector> -using namespace clang; -using namespace clang::driver; - -int cc1_main(Diagnostic &Diags, const char **ArgBegin, const char **ArgEnd) { +int cc1_main(const char **ArgBegin, const char **ArgEnd, + const char *Argv0, void *MainAddr) { llvm::errs() << "cc1 argv:"; for (const char **i = ArgBegin; i != ArgEnd; ++i) llvm::errs() << " \"" << *i << '"'; llvm::errs() << "\n"; - // Parse the arguments. - OptTable *Opts = createCC1OptTable(); - unsigned MissingArgIndex, MissingArgCount; - InputArgList *Args = Opts->ParseArgs(ArgBegin, ArgEnd, - MissingArgIndex, MissingArgCount); - - // Check for missing argument error. - if (MissingArgCount) - Diags.Report(clang::diag::err_drv_missing_argument) - << Args->getArgString(MissingArgIndex) << MissingArgCount; - - // Dump the parsed arguments. - llvm::errs() << "cc1 parsed options:\n"; - for (ArgList::const_iterator it = Args->begin(), ie = Args->end(); - it != ie; ++it) - (*it)->dump(); - - // Create a compiler invocation. - llvm::errs() << "cc1 creating invocation.\n"; - CompilerInvocation Invocation; - CompilerInvocation::CreateFromArgs(Invocation, - llvm::SmallVector<llvm::StringRef, 32>(ArgBegin, ArgEnd)); - - // Convert the invocation back to argument strings. - std::vector<std::string> InvocationArgs; - Invocation.toArgs(InvocationArgs); - - // Dump the converted arguments. - llvm::SmallVector<llvm::StringRef, 32> Invocation2Args; - llvm::errs() << "invocation argv:"; - for (unsigned i = 0, e = InvocationArgs.size(); i != e; ++i) { - Invocation2Args.push_back(InvocationArgs[i]); - llvm::errs() << " \"" << InvocationArgs[i] << '"'; - } - llvm::errs() << "\n"; - - // Convert those arguments to another invocation, and check that we got the - // same thing. - CompilerInvocation Invocation2; - CompilerInvocation::CreateFromArgs(Invocation2, Invocation2Args); - - // FIXME: Implement CompilerInvocation comparison. - if (memcmp(&Invocation, &Invocation2, sizeof(Invocation)) != 0) { - llvm::errs() << "warning: Invocations differ!\n"; - - std::vector<std::string> Invocation2Args; - Invocation2.toArgs(Invocation2Args); - llvm::errs() << "invocation argv:"; - for (unsigned i = 0, e = Invocation2Args.size(); i != e; ++i) - llvm::errs() << " \"" << Invocation2Args[i] << '"'; - llvm::errs() << "\n"; - } - return 0; } diff --git a/tools/driver/driver.cpp b/tools/driver/driver.cpp index dbfc293..c61ee72 100644 --- a/tools/driver/driver.cpp +++ b/tools/driver/driver.cpp @@ -178,22 +178,23 @@ void ApplyQAOverride(std::vector<const char*> &Args, const char *OverrideStr, } } -extern int cc1_main(Diagnostic &Diags, - const char **ArgBegin, const char **ArgEnd); +extern int cc1_main(const char **ArgBegin, const char **ArgEnd, + const char *Argv0, void *MainAddr); int main(int argc, const char **argv) { llvm::sys::PrintStackTraceOnErrorSignal(); llvm::PrettyStackTraceProgram X(argc, argv); + // Dispatch to cc1_main if appropriate. + if (argc > 1 && llvm::StringRef(argv[1]) == "-cc1") + return cc1_main(argv+2, argv+argc, argv[0], + (void*) (intptr_t) GetExecutablePath); + llvm::sys::Path Path = GetExecutablePath(argv[0]); DriverDiagnosticPrinter DiagClient(Path.getBasename(), llvm::errs()); Diagnostic Diags(&DiagClient); - // Dispatch to cc1_main if appropriate. - if (argc > 1 && llvm::StringRef(argv[1]) == "-cc1") - return cc1_main(Diags, argv+2, argv+argc); - #ifdef CLANG_IS_PRODUCTION bool IsProduction = true; #else @@ -208,7 +209,9 @@ int main(int argc, const char **argv) { // // Note that we intentionally want to use argv[0] here, to support "clang++" // being a symlink. - std::string ProgName(llvm::sys::Path(argv[0]).getBasename()); + // + // We use *argv instead of argv[0] to work around a bogus g++ warning. + std::string ProgName(llvm::sys::Path(*argv).getBasename()); if (llvm::StringRef(ProgName).endswith("++") || llvm::StringRef(ProgName).rsplit('-').first.endswith("++")) TheDriver.CCCIsCXX = true; diff --git a/tools/index-test/CMakeLists.txt b/tools/index-test/CMakeLists.txt index 9c9656a..163afc4 100644 --- a/tools/index-test/CMakeLists.txt +++ b/tools/index-test/CMakeLists.txt @@ -3,8 +3,10 @@ set(LLVM_NO_RTTI 1) set( LLVM_USED_LIBS clangIndex clangFrontend + clangAnalysis clangSema clangAST + clangParse clangLex clangBasic ) diff --git a/tools/index-test/Makefile b/tools/index-test/Makefile index 76602e1..8e7bfe5 100644 --- a/tools/index-test/Makefile +++ b/tools/index-test/Makefile @@ -19,6 +19,7 @@ TOOL_NO_EXPORTS = 1 include $(LEVEL)/Makefile.config LINK_COMPONENTS := bitreader mc -USEDLIBS = clangIndex.a clangFrontend.a clangSema.a clangAST.a clangLex.a clangBasic.a +USEDLIBS = clangIndex.a clangFrontend.a clangDriver.a clangAnalysis.a clangSema.a \ + clangAST.a clangParse.a clangLex.a clangBasic.a include $(LLVM_SRC_ROOT)/Makefile.rules diff --git a/tools/index-test/index-test.cpp b/tools/index-test/index-test.cpp index fce48ed..dd7cbb2 100644 --- a/tools/index-test/index-test.cpp +++ b/tools/index-test/index-test.cpp @@ -43,6 +43,10 @@ #include "clang/Index/Analyzer.h" #include "clang/Index/Utils.h" #include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/CompilerInvocation.h" +#include "clang/Frontend/DiagnosticOptions.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" #include "clang/Frontend/CommandLineSourceLoc.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/ExprObjC.h" @@ -202,9 +206,24 @@ static void ProcessASTLocation(ASTLocation ASTLoc, Indexer &Idxer) { } } +static llvm::cl::opt<bool> +ASTFromSource("ast-from-source", + llvm::cl::desc("Treat the inputs as source files to parse.")); + static llvm::cl::list<std::string> InputFilenames(llvm::cl::Positional, llvm::cl::desc("<input AST files>")); +void CreateCompilerInvocation(const std::string &Filename, + CompilerInvocation &CI, Diagnostic &Diags, + const char *argv0) { + llvm::SmallVector<const char *, 16> Args; + Args.push_back(Filename.c_str()); + + void *MainAddr = (void*) (intptr_t) CreateCompilerInvocation; + CompilerInvocation::CreateFromArgs(CI, Args.data(), Args.data() + Args.size(), + argv0, MainAddr, Diags); +} + int main(int argc, char **argv) { llvm::sys::PrintStackTraceOnErrorSignal(); llvm::PrettyStackTraceProgram X(argc, argv); @@ -215,6 +234,10 @@ int main(int argc, char **argv) { Indexer Idxer(Prog); llvm::SmallVector<TUnit*, 4> TUnits; + TextDiagnosticPrinter DiagClient(llvm::errs(), DiagnosticOptions(), false); + llvm::OwningPtr<Diagnostic> Diags( + CompilerInstance::createDiagnostics(DiagnosticOptions(), argc, argv)); + // If no input was specified, read from stdin. if (InputFilenames.empty()) InputFilenames.push_back("-"); @@ -225,7 +248,15 @@ int main(int argc, char **argv) { std::string ErrMsg; llvm::OwningPtr<ASTUnit> AST; - AST.reset(ASTUnit::LoadFromPCHFile(InFile, &ErrMsg)); + if (ASTFromSource) { + CompilerInvocation CI; + CreateCompilerInvocation(InFile, CI, *Diags, argv[0]); + AST.reset(ASTUnit::LoadFromCompilerInvocation(CI, *Diags)); + if (!AST) + ErrMsg = "unable to create AST"; + } else + AST.reset(ASTUnit::LoadFromPCHFile(InFile, &ErrMsg)); + if (!AST) { llvm::errs() << "[" << InFile << "] Error: " << ErrMsg << '\n'; return 1; diff --git a/tools/scan-build/ccc-analyzer b/tools/scan-build/ccc-analyzer new file mode 100755 index 0000000..25b9800 --- /dev/null +++ b/tools/scan-build/ccc-analyzer @@ -0,0 +1,632 @@ +#!/usr/bin/env perl +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +# +# A script designed to interpose between the build system and gcc. It invokes +# both gcc and the static analyzer. +# +##===----------------------------------------------------------------------===## + +use strict; +use warnings; +use Cwd qw/ getcwd abs_path /; +use File::Temp qw/ tempfile /; +use File::Path qw / mkpath /; +use File::Basename; +use Text::ParseWords; + +my $CC = $ENV{'CCC_CC'}; +if (!defined $CC) { $CC = "gcc"; } + +my $ReportFailures = $ENV{'CCC_REPORT_FAILURES'}; +if (!defined $ReportFailures) { $ReportFailures = 1; } + +my $CleanupFile; +my $ResultFile; + +# Remove any stale files at exit. +END { + if (defined $CleanupFile && -z $CleanupFile) { + `rm -f $CleanupFile`; + } +} + +##----------------------------------------------------------------------------## +# Process Clang Crashes. +##----------------------------------------------------------------------------## + +sub GetPPExt { + my $Lang = shift; + if ($Lang =~ /objective-c/) { return ".mi"; } + return ".i"; +} + +# Set this to 1 if we want to include 'parser rejects' files. +my $IncludeParserRejects = 0; +my $ParserRejects = "Parser Rejects"; + +my $AttributeIgnored = "Attribute Ignored"; + +sub ProcessClangFailure { + my ($ClangCC, $Lang, $file, $Args, $HtmlDir, $ErrorType, $ofile) = @_; + my $Dir = "$HtmlDir/failures"; + mkpath $Dir; + + my $prefix = "clang_crash"; + if ($ErrorType eq $ParserRejects) { + $prefix = "clang_parser_rejects"; + } + elsif ($ErrorType eq $AttributeIgnored) { + $prefix = "clang_attribute_ignored"; + } + + # Generate the preprocessed file with Clang. + my ($PPH, $PPFile) = tempfile( $prefix . "_XXXXXX", + SUFFIX => GetPPExt($Lang), + DIR => $Dir); + system $ClangCC, @$Args, "-E", "-o", $PPFile; + close ($PPH); + + # Create the info file. + open (OUT, ">", "$PPFile.info.txt") or die "Cannot open $PPFile.info.txt\n"; + print OUT abs_path($file), "\n"; + print OUT "$ErrorType\n"; + print OUT "@$Args\n"; + close OUT; + `uname -a >> $PPFile.info.txt 2>&1`; + `$CC -v >> $PPFile.info.txt 2>&1`; + system 'mv',$ofile,"$PPFile.stderr.txt"; + return (basename $PPFile); +} + +##----------------------------------------------------------------------------## +# Running the analyzer. +##----------------------------------------------------------------------------## + +# Determine what clang executable to use. +my $Clang = $ENV{'CLANG'}; +if (!defined $Clang) { $Clang = 'clang'; } + +sub GetCCArgs { + my $Args = shift; + + pipe (FROM_CHILD, TO_PARENT); + my $pid = fork(); + if ($pid == 0) { + close FROM_CHILD; + open(STDOUT,">&", \*TO_PARENT); + open(STDERR,">&", \*TO_PARENT); + exec $Clang, "-###", "-fsyntax-only", @$Args; + } + close(TO_PARENT); + my $line; + while (<FROM_CHILD>) { + next if (!/clang-cc/); + $line = $_; + } + + waitpid($pid,0); + close(FROM_CHILD); + + die "could not find clang-cc line\n" if (!defined $line); + # 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 $cmd = shift @items; + die "cannot find 'clang-cc' in 'clang' command\n" if (!($cmd =~ /clang-cc/)); + return \@items; +} + +sub Analyze { + my ($ClangCC, $Args, $AnalyzeArgs, $Lang, $Output, $Verbose, $HtmlDir, + $file, $Analyses) = @_; + + $Args = GetCCArgs($Args); + + # Skip anything related to C++. + return if ($Lang =~ /c[+][+]/); + + my $RunAnalyzer = 0; + my $Cmd; + my @CmdArgs; + my @CmdArgsSansAnalyses; + + if ($Lang =~ /header/) { + exit 0 if (!defined ($Output)); + $Cmd = 'cp'; + push @CmdArgs,$file; + # Remove the PCH extension. + $Output =~ s/[.]gch$//; + push @CmdArgs,$Output; + @CmdArgsSansAnalyses = @CmdArgs; + } + else { + $Cmd = $ClangCC; + push @CmdArgs,'-DIBOutlet=__attribute__((iboutlet))'; + push @CmdArgs,@$Args; + @CmdArgsSansAnalyses = @CmdArgs; + push @CmdArgs,'-analyze'; + push @CmdArgs,"-analyzer-display-progress"; + push @CmdArgs,"-analyzer-eagerly-assume"; + push @CmdArgs,(split /\s/,$Analyses); + + if (defined $ENV{"CCC_EXPERIMENTAL_CHECKS"}) { + push @CmdArgs,"-analyzer-experimental-internal-checks"; + push @CmdArgs,"-analyzer-experimental-checks"; + } + + $RunAnalyzer = 1; + } + + # Add the analysis arguments passed down from scan-build. + foreach my $Arg (@$AnalyzeArgs) { + push @CmdArgs, $Arg; + } + + my @PrintArgs; + my $dir; + + if ($RunAnalyzer) { + if (defined $ResultFile) { + push @CmdArgs,'-o'; + push @CmdArgs, $ResultFile; + } + elsif (defined $HtmlDir) { + push @CmdArgs,'-o'; + push @CmdArgs, $HtmlDir; + } + } + + if ($Verbose) { + $dir = getcwd(); + print STDERR "\n[LOCATION]: $dir\n"; + push @PrintArgs,"'$Cmd'"; + foreach my $arg (@CmdArgs) { push @PrintArgs,"\'$arg\'"; } + } + + if ($Verbose == 1) { + # We MUST print to stderr. Some clients use the stdout output of + # gcc for various purposes. + print STDERR join(' ',@PrintArgs); + print STDERR "\n"; + } + elsif ($Verbose == 2) { + print STDERR "#SHELL (cd '$dir' && @PrintArgs)\n"; + } + + if (defined $ENV{'CCC_UBI'}) { + push @CmdArgs,"--analyzer-viz-egraph-ubigraph"; + } + + # Capture the STDERR of clang and send it to a temporary file. + # Capture the STDOUT of clang and reroute it to ccc-analyzer's STDERR. + # We save the output file in the 'crashes' directory if clang encounters + # any problems with the file. + pipe (FROM_CHILD, TO_PARENT); + my $pid = fork(); + if ($pid == 0) { + close FROM_CHILD; + open(STDOUT,">&", \*TO_PARENT); + open(STDERR,">&", \*TO_PARENT); + exec $Cmd, @CmdArgs; + } + + close TO_PARENT; + my ($ofh, $ofile) = tempfile("clang_output_XXXXXX", DIR => $HtmlDir); + + while (<FROM_CHILD>) { + print $ofh $_; + print STDERR $_; + } + + waitpid($pid,0); + close(FROM_CHILD); + my $Result = $?; + + # Did the command die because of a signal? + if ($ReportFailures) { + if ($Result & 127 and $Cmd eq $ClangCC and defined $HtmlDir) { + ProcessClangFailure($ClangCC, $Lang, $file, \@CmdArgsSansAnalyses, + $HtmlDir, "Crash", $ofile); + } + elsif ($Result) { + if ($IncludeParserRejects && !($file =~/conftest/)) { + ProcessClangFailure($ClangCC, $Lang, $file, \@CmdArgsSansAnalyses, + $HtmlDir, $ParserRejects, $ofile); + } + } + else { + # Check if there were any unhandled attributes. + if (open(CHILD, $ofile)) { + my %attributes_not_handled; + + # Don't flag warnings about the following attributes that we + # know are currently not supported by Clang. + $attributes_not_handled{"cdecl"} = 1; + + my $ppfile; + while (<CHILD>) { + next if (! /warning: '([^\']+)' attribute ignored/); + + # Have we already spotted this unhandled attribute? + next if (defined $attributes_not_handled{$1}); + $attributes_not_handled{$1} = 1; + + # Get the name of the attribute file. + my $dir = "$HtmlDir/failures"; + my $afile = "$dir/attribute_ignored_$1.txt"; + + # Only create another preprocessed file if the attribute file + # doesn't exist yet. + next if (-e $afile); + + # Add this file to the list of files that contained this attribute. + # Generate a preprocessed file if we haven't already. + if (!(defined $ppfile)) { + $ppfile = ProcessClangFailure($ClangCC, $Lang, $file, + \@CmdArgsSansAnalyses, + $HtmlDir, $AttributeIgnored, $ofile); + } + + mkpath $dir; + open(AFILE, ">$afile"); + print AFILE "$ppfile\n"; + close(AFILE); + } + close CHILD; + } + } + } + + unlink($ofile); +} + +##----------------------------------------------------------------------------## +# Lookup tables. +##----------------------------------------------------------------------------## + +my %CompileOptionMap = ( + '-nostdinc' => 0, + '-fblocks' => 0, + '-fobjc-gc-only' => 0, + '-fobjc-gc' => 0, + '-ffreestanding' => 0, + '-include' => 1, + '-idirafter' => 1, + '-iprefix' => 1, + '-iquote' => 1, + '-isystem' => 1, + '-iwithprefix' => 1, + '-iwithprefixbefore' => 1 +); + +my %LinkerOptionMap = ( + '-framework' => 1 +); + +my %CompilerLinkerOptionMap = ( + '-isysroot' => 1, + '-arch' => 1, + '-v' => 0, + '-fpascal-strings' => 0, + '-mmacosx-version-min' => 0, # This is really a 1 argument, but always has '=' + '-miphoneos-version-min' => 0 # This is really a 1 argument, but always has '=' +); + +my %IgnoredOptionMap = ( + '-MT' => 1, # Ignore these preprocessor options. + '-MF' => 1, + + '-fsyntax-only' => 0, + '-save-temps' => 0, + '-install_name' => 1, + '-exported_symbols_list' => 1, + '-current_version' => 1, + '-compatibility_version' => 1, + '-init' => 1, + '-e' => 1, + '-seg1addr' => 1, + '-bundle_loader' => 1, + '-multiply_defined' => 1, + '-sectorder' => 3, + '--param' => 1, + '-u' => 1 +); + +my %LangMap = ( + 'c' => 'c', + 'cpp' => 'c++', + 'cc' => 'c++', + 'i' => 'c-cpp-output', + 'm' => 'objective-c', + 'mi' => 'objective-c-cpp-output' +); + +my %UniqueOptions = ( + '-isysroot' => 0 +); + +my %LangsAccepted = ( + "objective-c" => 1, + "c" => 1 +); + +##----------------------------------------------------------------------------## +# Main Logic. +##----------------------------------------------------------------------------## + +my $Action = 'link'; +my @CompileOpts; +my @LinkOpts; +my @Files; +my $Lang; +my $Output; +my %Uniqued; + +# Forward arguments to gcc. +my $Status = system($CC,@ARGV); +if ($Status) { exit($Status >> 8); } + +# Get the analysis options. +my $Analyses = $ENV{'CCC_ANALYZER_ANALYSIS'}; +if (!defined($Analyses)) { $Analyses = '-checker-cfref'; } + +# Get the store model. +my $StoreModel = $ENV{'CCC_ANALYZER_STORE_MODEL'}; +if (!defined $StoreModel) { $StoreModel = "region"; } + +# Get the constraints engine. +my $ConstraintsModel = $ENV{'CCC_ANALYZER_CONSTRAINTS_MODEL'}; +if (!defined $ConstraintsModel) { $ConstraintsModel = "range"; } + +# Get the output format. +my $OutputFormat = $ENV{'CCC_ANALYZER_OUTPUT_FORMAT'}; +if (!defined $OutputFormat) { $OutputFormat = "html"; } + +# Determine the level of verbosity. +my $Verbose = 0; +if (defined $ENV{CCC_ANALYZER_VERBOSE}) { $Verbose = 1; } +if (defined $ENV{CCC_ANALYZER_LOG}) { $Verbose = 2; } + +# Determine what clang-cc executable to use. +my $ClangCC = $ENV{'CLANG_CC'}; +if (!defined $ClangCC) { $ClangCC = 'clang-cc'; } + +# Get the HTML output directory. +my $HtmlDir = $ENV{'CCC_ANALYZER_HTML'}; + +my %DisabledArchs = ('ppc' => 1, 'ppc64' => 1); +my %ArchsSeen; +my $HadArch = 0; + +# Process the arguments. +foreach (my $i = 0; $i < scalar(@ARGV); ++$i) { + my $Arg = $ARGV[$i]; + my ($ArgKey) = split /=/,$Arg,2; + + # Modes ccc-analyzer supports + if ($Arg =~ /^-(E|MM?)$/) { $Action = 'preprocess'; } + elsif ($Arg eq '-c') { $Action = 'compile'; } + elsif ($Arg =~ /^-print-prog-name/) { exit 0; } + + # Specially handle duplicate cases of -arch + if ($Arg eq "-arch") { + my $arch = $ARGV[$i+1]; + # We don't want to process 'ppc' because of Clang's lack of support + # for Altivec (also some #defines won't likely be defined correctly, etc.) + if (!(defined $DisabledArchs{$arch})) { $ArchsSeen{$arch} = 1; } + $HadArch = 1; + ++$i; + next; + } + + # Options with possible arguments that should pass through to compiler. + if (defined $CompileOptionMap{$ArgKey}) { + my $Cnt = $CompileOptionMap{$ArgKey}; + push @CompileOpts,$Arg; + while ($Cnt > 0) { ++$i; --$Cnt; push @CompileOpts, $ARGV[$i]; } + next; + } + + # Options with possible arguments that should pass through to linker. + if (defined $LinkerOptionMap{$ArgKey}) { + my $Cnt = $LinkerOptionMap{$ArgKey}; + push @LinkOpts,$Arg; + while ($Cnt > 0) { ++$i; --$Cnt; push @LinkOpts, $ARGV[$i]; } + next; + } + + # Options with possible arguments that should pass through to both compiler + # and the linker. + if (defined $CompilerLinkerOptionMap{$ArgKey}) { + my $Cnt = $CompilerLinkerOptionMap{$ArgKey}; + + # Check if this is an option that should have a unique value, and if so + # determine if the value was checked before. + if ($UniqueOptions{$Arg}) { + if (defined $Uniqued{$Arg}) { + $i += $Cnt; + next; + } + $Uniqued{$Arg} = 1; + } + + push @CompileOpts,$Arg; + push @LinkOpts,$Arg; + + while ($Cnt > 0) { + ++$i; --$Cnt; + push @CompileOpts, $ARGV[$i]; + push @LinkOpts, $ARGV[$i]; + } + next; + } + + # Ignored options. + if (defined $IgnoredOptionMap{$ArgKey}) { + my $Cnt = $IgnoredOptionMap{$ArgKey}; + while ($Cnt > 0) { + ++$i; --$Cnt; + } + next; + } + + # Compile mode flags. + if ($Arg =~ /^-[D,I,U](.*)$/) { + my $Tmp = $Arg; + if ($1 eq '') { + # FIXME: Check if we are going off the end. + ++$i; + $Tmp = $Arg . $ARGV[$i]; + } + push @CompileOpts,$Tmp; + next; + } + + # Language. + if ($Arg eq '-x') { + $Lang = $ARGV[$i+1]; + ++$i; next; + } + + # Output file. + if ($Arg eq '-o') { + ++$i; + $Output = $ARGV[$i]; + next; + } + + # Get the link mode. + if ($Arg =~ /^-[l,L,O]/) { + if ($Arg eq '-O') { push @LinkOpts,'-O1'; } + elsif ($Arg eq '-Os') { push @LinkOpts,'-O2'; } + else { push @LinkOpts,$Arg; } + next; + } + + if ($Arg =~ /^-std=/) { + push @CompileOpts,$Arg; + next; + } + +# if ($Arg =~ /^-f/) { +# # FIXME: Not sure if the remaining -fxxxx options have no arguments. +# push @CompileOpts,$Arg; +# push @LinkOpts,$Arg; # FIXME: Not sure if these are link opts. +# } + + # Get the compiler/link mode. + if ($Arg =~ /^-F(.+)$/) { + my $Tmp = $Arg; + if ($1 eq '') { + # FIXME: Check if we are going off the end. + ++$i; + $Tmp = $Arg . $ARGV[$i]; + } + push @CompileOpts,$Tmp; + push @LinkOpts,$Tmp; + next; + } + + # Input files. + if ($Arg eq '-filelist') { + # FIXME: Make sure we aren't walking off the end. + open(IN, $ARGV[$i+1]); + while (<IN>) { s/\015?\012//; push @Files,$_; } + close(IN); + ++$i; + next; + } + + # Handle -Wno-. We don't care about extra warnings, but + # we should suppress ones that we don't want to see. + if ($Arg =~ /^-Wno-/) { + push @CompileOpts, $Arg; + next; + } + + if (!($Arg =~ /^-/)) { + push @Files, $Arg; + next; + } +} + +if ($Action eq 'compile' or $Action eq 'link') { + my @Archs = keys %ArchsSeen; + # Skip the file if we don't support the architectures specified. + exit 0 if ($HadArch && scalar(@Archs) == 0); + + foreach my $file (@Files) { + # Determine the language for the file. + my $FileLang = $Lang; + + if (!defined($FileLang)) { + # Infer the language from the extension. + if ($file =~ /[.]([^.]+)$/) { + $FileLang = $LangMap{$1}; + } + } + + next if (!defined $FileLang); + next if (!defined $LangsAccepted{$FileLang}); + + my @CmdArgs; + my @AnalyzeArgs; + + if ($FileLang ne 'unknown') { + push @CmdArgs,'-x'; + push @CmdArgs,$FileLang; + } + + if (defined $StoreModel) { + push @AnalyzeArgs, "-analyzer-store=$StoreModel"; + } + + if (defined $ConstraintsModel) { + push @AnalyzeArgs, "-analyzer-constraints=$ConstraintsModel"; + } + + if (defined $OutputFormat) { + push @AnalyzeArgs, "-analyzer-output=" . $OutputFormat; + if ($OutputFormat =~ /plist/) { + # Change "Output" to be a file. + my ($h, $f) = tempfile("report-XXXXXX", SUFFIX => ".plist", + DIR => $HtmlDir); + $ResultFile = $f; + $CleanupFile = $f; + } + } + + push @CmdArgs,@CompileOpts; + push @CmdArgs,$file; + + if (scalar @Archs) { + foreach my $arch (@Archs) { + my @NewArgs; + push @NewArgs, '-arch'; + push @NewArgs, $arch; + push @NewArgs, @CmdArgs; + Analyze($ClangCC, \@NewArgs, \@AnalyzeArgs, $FileLang, $Output, + $Verbose, $HtmlDir, $file, $Analyses); + } + } + else { + Analyze($ClangCC, \@CmdArgs, \@AnalyzeArgs, $FileLang, $Output, + $Verbose, $HtmlDir, $file, $Analyses); + } + } +} + +exit($Status >> 8); + diff --git a/tools/scan-build/scan-build b/tools/scan-build/scan-build new file mode 100755 index 0000000..8d99f07 --- /dev/null +++ b/tools/scan-build/scan-build @@ -0,0 +1,1297 @@ +#!/usr/bin/env perl +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +# +# A script designed to wrap a build so that all calls to gcc are intercepted +# and piped to the static analyzer. +# +##===----------------------------------------------------------------------===## + +use strict; +use warnings; +use FindBin qw($RealBin); +use Digest::MD5; +use File::Basename; +use Term::ANSIColor; +use Term::ANSIColor qw(:constants); +use Cwd qw/ getcwd abs_path /; +use Sys::Hostname; + +my $Verbose = 0; # Verbose output from this script. +my $Prog = "scan-build"; +my $BuildName; +my $BuildDate; +my $CXX; # Leave undefined initially. + +my $TERM = $ENV{'TERM'}; +my $UseColor = (defined $TERM and $TERM eq 'xterm-color' and -t STDOUT + and defined $ENV{'SCAN_BUILD_COLOR'}); + +my $UserName = HtmlEscape(getpwuid($<) || 'unknown'); +my $HostName = HtmlEscape(hostname() || 'unknown'); +my $CurrentDir = HtmlEscape(getcwd()); +my $CurrentDirSuffix = basename($CurrentDir); + +my $CmdArgs; + +my $HtmlTitle; + +my $Date = localtime(); + +##----------------------------------------------------------------------------## +# Diagnostics +##----------------------------------------------------------------------------## + +sub Diag { + if ($UseColor) { + print BOLD, MAGENTA "$Prog: @_"; + print RESET; + } + else { + print "$Prog: @_"; + } +} + +sub DiagCrashes { + my $Dir = shift; + Diag ("The analyzer encountered problems on some source files.\n"); + Diag ("Preprocessed versions of these sources were deposited in '$Dir/failures'.\n"); + Diag ("Please consider submitting a bug report using these files:\n"); + Diag (" http://clang.llvm.org/StaticAnalysisUsage.html#filingbugs\n") +} + +sub DieDiag { + if ($UseColor) { + print BOLD, RED "$Prog: "; + print RESET, RED @_; + print RESET; + } + else { + print "$Prog: ", @_; + } + exit(0); +} + +##----------------------------------------------------------------------------## +# Some initial preprocessing of Clang options. +##----------------------------------------------------------------------------## + +# First, look for 'clang-cc' in libexec. +my $ClangCCSB = Cwd::realpath("$RealBin/libexec/clang-cc"); +# Second, look for 'clang-cc' in the same directory as scan-build. +if (!defined $ClangCCSB || ! -x $ClangCCSB) { + $ClangCCSB = Cwd::realpath("$RealBin/clang-cc"); +} +# Third, look for 'clang-cc' in ../libexec +if (!defined $ClangCCSB || ! -x $ClangCCSB) { + $ClangCCSB = Cwd::realpath("$RealBin/../libexec/clang-cc"); +} +# Finally, default to looking for 'clang-cc' in the path. +if (!defined $ClangCCSB || ! -x $ClangCCSB) { + $ClangCCSB = "clang-cc"; +} +my $ClangCC = $ClangCCSB; + +# Now find 'clang' +my $ClangSB = Cwd::realpath("$RealBin/bin/clang"); +if (!defined $ClangSB || ! -x $ClangSB) { + $ClangSB = Cwd::realpath("$RealBin/clang"); +} +# Third, look for 'clang' in ../bin +if (!defined $ClangSB || ! -x $ClangSB) { + $ClangSB = Cwd::realpath("$RealBin/../bin/clang"); +} +# Finally, default to looking for 'clang-cc' in the path. +if (!defined $ClangSB || ! -x $ClangSB) { + $ClangSB = "clang"; +} +my $Clang = $ClangSB; + + +my %AvailableAnalyses; + +# Query clang for analysis options. +open(PIPE, "-|", $ClangCC, "--help") or + DieDiag("Cannot execute '$ClangCC'\n"); + +my $FoundAnalysis = 0; + +while(<PIPE>) { + if ($FoundAnalysis == 0) { + if (/Checks and Analyses/) { + $FoundAnalysis = 1; + } + next; + } + + if (/^\s\s\s\s([^\s]+)\s(.+)$/) { + next if ($1 =~ /-dump/ or $1 =~ /-view/ + or $1 =~ /-warn-uninit/); + + $AvailableAnalyses{$1} = $2; + next; + } + last; +} + +close (PIPE); + +my %AnalysesDefaultEnabled = ( + '-warn-dead-stores' => 1, + '-checker-cfref' => 1, + '-warn-objc-methodsigs' => 1, + # Do not enable the missing -dealloc check by default. + # '-warn-objc-missing-dealloc' => 1, + '-warn-objc-unused-ivars' => 1, + '-warn-security-syntactic' => 1 +); + +##----------------------------------------------------------------------------## +# GetHTMLRunDir - Construct an HTML directory name for the current sub-run. +##----------------------------------------------------------------------------## + +sub GetHTMLRunDir { + + die "Not enough arguments." if (@_ == 0); + my $Dir = shift @_; + + my $TmpMode = 0; + if (!defined $Dir) { + if (`uname` =~ /Darwin/) { + $Dir = $ENV{'TMPDIR'}; + if (!defined $Dir) { $Dir = "/tmp"; } + } + else { + $Dir = "/tmp"; + } + + $TmpMode = 1; + } + + # Chop off any trailing '/' characters. + while ($Dir =~ /\/$/) { chop $Dir; } + + # Get current date and time. + + my @CurrentTime = localtime(); + + my $year = $CurrentTime[5] + 1900; + my $day = $CurrentTime[3]; + my $month = $CurrentTime[4] + 1; + + my $DateString = sprintf("%d-%02d-%02d", $year, $month, $day); + + # Determine the run number. + + my $RunNumber; + + if (-d $Dir) { + + if (! -r $Dir) { + DieDiag("directory '$Dir' exists but is not readable.\n"); + } + + # Iterate over all files in the specified directory. + + my $max = 0; + + opendir(DIR, $Dir); + my @FILES = grep { -d "$Dir/$_" } readdir(DIR); + closedir(DIR); + + foreach my $f (@FILES) { + + # Strip the prefix '$Prog-' if we are dumping files to /tmp. + if ($TmpMode) { + next if (!($f =~ /^$Prog-(.+)/)); + $f = $1; + } + + + my @x = split/-/, $f; + next if (scalar(@x) != 4); + next if ($x[0] != $year); + next if ($x[1] != $month); + next if ($x[2] != $day); + + if ($x[3] > $max) { + $max = $x[3]; + } + } + + $RunNumber = $max + 1; + } + else { + + if (-x $Dir) { + DieDiag("'$Dir' exists but is not a directory.\n"); + } + + if ($TmpMode) { + DieDiag("The directory '/tmp' does not exist or cannot be accessed.\n"); + } + + # $Dir does not exist. It will be automatically created by the + # clang driver. Set the run number to 1. + + $RunNumber = 1; + } + + die "RunNumber must be defined!" if (!defined $RunNumber); + + # Append the run number. + my $NewDir; + if ($TmpMode) { + $NewDir = "$Dir/$Prog-$DateString-$RunNumber"; + } + else { + $NewDir = "$Dir/$DateString-$RunNumber"; + } + system 'mkdir','-p',$NewDir; + return $NewDir; +} + +sub SetHtmlEnv { + + die "Wrong number of arguments." if (scalar(@_) != 2); + + my $Args = shift; + my $Dir = shift; + + die "No build command." if (scalar(@$Args) == 0); + + my $Cmd = $$Args[0]; + + if ($Cmd =~ /configure/) { + return; + } + + if ($Verbose) { + Diag("Emitting reports for this run to '$Dir'.\n"); + } + + $ENV{'CCC_ANALYZER_HTML'} = $Dir; +} + +##----------------------------------------------------------------------------## +# ComputeDigest - Compute a digest of the specified file. +##----------------------------------------------------------------------------## + +sub ComputeDigest { + my $FName = shift; + DieDiag("Cannot read $FName to compute Digest.\n") if (! -r $FName); + + # Use Digest::MD5. We don't have to be cryptographically secure. We're + # just looking for duplicate files that come from a non-malicious source. + # We use Digest::MD5 because it is a standard Perl module that should + # come bundled on most systems. + open(FILE, $FName) or DieDiag("Cannot open $FName when computing Digest.\n"); + binmode FILE; + my $Result = Digest::MD5->new->addfile(*FILE)->hexdigest; + close(FILE); + + # Return the digest. + return $Result; +} + +##----------------------------------------------------------------------------## +# UpdatePrefix - Compute the common prefix of files. +##----------------------------------------------------------------------------## + +my $Prefix; + +sub UpdatePrefix { + my $x = shift; + my $y = basename($x); + $x =~ s/\Q$y\E$//; + + if (!defined $Prefix) { + $Prefix = $x; + return; + } + + chop $Prefix while (!($x =~ /^\Q$Prefix/)); +} + +sub GetPrefix { + return $Prefix; +} + +##----------------------------------------------------------------------------## +# UpdateInFilePath - Update the path in the report file. +##----------------------------------------------------------------------------## + +sub UpdateInFilePath { + my $fname = shift; + my $regex = shift; + my $newtext = shift; + + open (RIN, $fname) or die "cannot open $fname"; + open (ROUT, ">", "$fname.tmp") or die "cannot open $fname.tmp"; + + while (<RIN>) { + s/$regex/$newtext/; + print ROUT $_; + } + + close (ROUT); + close (RIN); + system("mv", "$fname.tmp", $fname); +} + +##----------------------------------------------------------------------------## +# ScanFile - Scan a report file for various identifying attributes. +##----------------------------------------------------------------------------## + +# Sometimes a source file is scanned more than once, and thus produces +# multiple error reports. We use a cache to solve this problem. + +my %AlreadyScanned; + +sub ScanFile { + + my $Index = shift; + my $Dir = shift; + my $FName = shift; + + # Compute a digest for the report file. Determine if we have already + # scanned a file that looks just like it. + + my $digest = ComputeDigest("$Dir/$FName"); + + if (defined $AlreadyScanned{$digest}) { + # Redundant file. Remove it. + system ("rm", "-f", "$Dir/$FName"); + return; + } + + $AlreadyScanned{$digest} = 1; + + # At this point the report file is not world readable. Make it happen. + system ("chmod", "644", "$Dir/$FName"); + + # Scan the report file for tags. + open(IN, "$Dir/$FName") or DieDiag("Cannot open '$Dir/$FName'\n"); + + my $BugType = ""; + my $BugFile = ""; + my $BugCategory; + my $BugPathLength = 1; + my $BugLine = 0; + + while (<IN>) { + last if (/<!-- BUGMETAEND -->/); + + if (/<!-- BUGTYPE (.*) -->$/) { + $BugType = $1; + } + elsif (/<!-- BUGFILE (.*) -->$/) { + $BugFile = abs_path($1); + UpdatePrefix($BugFile); + } + elsif (/<!-- BUGPATHLENGTH (.*) -->$/) { + $BugPathLength = $1; + } + elsif (/<!-- BUGLINE (.*) -->$/) { + $BugLine = $1; + } + elsif (/<!-- BUGCATEGORY (.*) -->$/) { + $BugCategory = $1; + } + } + + close(IN); + + if (!defined $BugCategory) { + $BugCategory = "Other"; + } + + push @$Index,[ $FName, $BugCategory, $BugType, $BugFile, $BugLine, + $BugPathLength ]; +} + +##----------------------------------------------------------------------------## +# CopyFiles - Copy resource files to target directory. +##----------------------------------------------------------------------------## + +sub CopyFiles { + + my $Dir = shift; + + my $JS = Cwd::realpath("$RealBin/sorttable.js"); + + DieDiag("Cannot find 'sorttable.js'.\n") + if (! -r $JS); + + system ("cp", $JS, "$Dir"); + + DieDiag("Could not copy 'sorttable.js' to '$Dir'.\n") + if (! -r "$Dir/sorttable.js"); + + my $CSS = Cwd::realpath("$RealBin/scanview.css"); + + DieDiag("Cannot find 'scanview.css'.\n") + if (! -r $CSS); + + system ("cp", $CSS, "$Dir"); + + DieDiag("Could not copy 'scanview.css' to '$Dir'.\n") + if (! -r $CSS); +} + +##----------------------------------------------------------------------------## +# Postprocess - Postprocess the results of an analysis scan. +##----------------------------------------------------------------------------## + +sub Postprocess { + + my $Dir = shift; + my $BaseDir = shift; + + die "No directory specified." if (!defined $Dir); + + if (! -d $Dir) { + Diag("No bugs found.\n"); + return 0; + } + + opendir(DIR, $Dir); + my @files = grep { /^report-.*\.html$/ } readdir(DIR); + closedir(DIR); + + if (scalar(@files) == 0 and ! -e "$Dir/failures") { + Diag("Removing directory '$Dir' because it contains no reports.\n"); + system ("rm", "-fR", $Dir); + return 0; + } + + # Scan each report file and build an index. + my @Index; + foreach my $file (@files) { ScanFile(\@Index, $Dir, $file); } + + # Scan the failures directory and use the information in the .info files + # to update the common prefix directory. + my @failures; + my @attributes_ignored; + if (-d "$Dir/failures") { + opendir(DIR, "$Dir/failures"); + @failures = grep { /[.]info.txt$/ && !/attribute_ignored/; } readdir(DIR); + closedir(DIR); + opendir(DIR, "$Dir/failures"); + @attributes_ignored = grep { /^attribute_ignored/; } readdir(DIR); + closedir(DIR); + foreach my $file (@failures) { + open IN, "$Dir/failures/$file" or DieDiag("cannot open $file\n"); + my $Path = <IN>; + if (defined $Path) { UpdatePrefix($Path); } + close IN; + } + } + + # Generate an index.html file. + my $FName = "$Dir/index.html"; + open(OUT, ">", $FName) or DieDiag("Cannot create file '$FName'\n"); + + # Print out the header. + +print OUT <<ENDTEXT; +<html> +<head> +<title>${HtmlTitle}</title> +<link type="text/css" rel="stylesheet" href="scanview.css"/> +<script src="sorttable.js"></script> +<script language='javascript' type="text/javascript"> +function SetDisplay(RowClass, DisplayVal) +{ + var Rows = document.getElementsByTagName("tr"); + for ( var i = 0 ; i < Rows.length; ++i ) { + if (Rows[i].className == RowClass) { + Rows[i].style.display = DisplayVal; + } + } +} + +function CopyCheckedStateToCheckButtons(SummaryCheckButton) { + var Inputs = document.getElementsByTagName("input"); + for ( var i = 0 ; i < Inputs.length; ++i ) { + if (Inputs[i].type == "checkbox") { + if(Inputs[i] != SummaryCheckButton) { + Inputs[i].checked = SummaryCheckButton.checked; + Inputs[i].onclick(); + } + } + } +} + +function returnObjById( id ) { + if (document.getElementById) + var returnVar = document.getElementById(id); + else if (document.all) + var returnVar = document.all[id]; + else if (document.layers) + var returnVar = document.layers[id]; + return returnVar; +} + +var NumUnchecked = 0; + +function ToggleDisplay(CheckButton, ClassName) { + if (CheckButton.checked) { + SetDisplay(ClassName, ""); + if (--NumUnchecked == 0) { + returnObjById("AllBugsCheck").checked = true; + } + } + else { + SetDisplay(ClassName, "none"); + NumUnchecked++; + returnObjById("AllBugsCheck").checked = false; + } +} +</script> +<!-- SUMMARYENDHEAD --> +</head> +<body> +<h1>${HtmlTitle}</h1> + +<table> +<tr><th>User:</th><td>${UserName}\@${HostName}</td></tr> +<tr><th>Working Directory:</th><td>${CurrentDir}</td></tr> +<tr><th>Command Line:</th><td>${CmdArgs}</td></tr> +<tr><th>Date:</th><td>${Date}</td></tr> +ENDTEXT + +print OUT "<tr><th>Version:</th><td>${BuildName} (${BuildDate})</td></tr>\n" + if (defined($BuildName) && defined($BuildDate)); + +print OUT <<ENDTEXT; +</table> +ENDTEXT + + if (scalar(@files)) { + # Print out the summary table. + my %Totals; + + for my $row ( @Index ) { + my $bug_type = ($row->[2]); + my $bug_category = ($row->[1]); + my $key = "$bug_category:$bug_type"; + + if (!defined $Totals{$key}) { $Totals{$key} = [1,$bug_category,$bug_type]; } + else { $Totals{$key}->[0]++; } + } + + print OUT "<h2>Bug Summary</h2>"; + + if (defined $BuildName) { + print OUT "\n<p>Results in this analysis run are based on analyzer build <b>$BuildName</b>.</p>\n" + } + + my $TotalBugs = scalar(@Index); +print OUT <<ENDTEXT; +<table> +<thead><tr><td>Bug Type</td><td>Quantity</td><td class="sorttable_nosort">Display?</td></tr></thead> +<tr style="font-weight:bold"><td class="SUMM_DESC">All Bugs</td><td class="Q">$TotalBugs</td><td><center><input type="checkbox" id="AllBugsCheck" onClick="CopyCheckedStateToCheckButtons(this);" checked/></center></td></tr> +ENDTEXT + + my $last_category; + + for my $key ( + sort { + my $x = $Totals{$a}; + my $y = $Totals{$b}; + my $res = $x->[1] cmp $y->[1]; + $res = $x->[2] cmp $y->[2] if ($res == 0); + $res + } keys %Totals ) + { + my $val = $Totals{$key}; + my $category = $val->[1]; + if (!defined $last_category or $last_category ne $category) { + $last_category = $category; + print OUT "<tr><th>$category</th><th colspan=2></th></tr>\n"; + } + my $x = lc $key; + $x =~ s/[ ,'":\/()]+/_/g; + print OUT "<tr><td class=\"SUMM_DESC\">"; + print OUT $val->[2]; + print OUT "</td><td class=\"Q\">"; + print OUT $val->[0]; + print OUT "</td><td><center><input type=\"checkbox\" onClick=\"ToggleDisplay(this,'bt_$x');\" checked/></center></td></tr>\n"; + } + + # Print out the table of errors. + +print OUT <<ENDTEXT; +</table> +<h2>Reports</h2> + +<table class="sortable" style="table-layout:automatic"> +<thead><tr> + <td>Bug Group</td> + <td class="sorttable_sorted">Bug Type<span id="sorttable_sortfwdind"> ▾</span></td> + <td>File</td> + <td class="Q">Line</td> + <td class="Q">Path Length</td> + <td class="sorttable_nosort"></td> + <!-- REPORTBUGCOL --> +</tr></thead> +<tbody> +ENDTEXT + + my $prefix = GetPrefix(); + my $regex; + my $InFileRegex; + my $InFilePrefix = "File:</td><td>"; + + if (defined $prefix) { + $regex = qr/^\Q$prefix\E/is; + $InFileRegex = qr/\Q$InFilePrefix$prefix\E/is; + } + + for my $row ( sort { $a->[2] cmp $b->[2] } @Index ) { + my $x = "$row->[1]:$row->[2]"; + $x = lc $x; + $x =~ s/[ ,'":\/()]+/_/g; + + my $ReportFile = $row->[0]; + + print OUT "<tr class=\"bt_$x\">"; + print OUT "<td class=\"DESC\">"; + print OUT $row->[1]; + print OUT "</td>"; + print OUT "<td class=\"DESC\">"; + print OUT $row->[2]; + print OUT "</td>"; + + # Update the file prefix. + my $fname = $row->[3]; + + if (defined $regex) { + $fname =~ s/$regex//; + UpdateInFilePath("$Dir/$ReportFile", $InFileRegex, $InFilePrefix) + } + + print OUT "<td>"; + my @fname = split /\//,$fname; + if ($#fname > 0) { + while ($#fname >= 0) { + my $x = shift @fname; + print OUT $x; + if ($#fname >= 0) { + print OUT "<span class=\"W\"> </span>/"; + } + } + } + else { + print OUT $fname; + } + print OUT "</td>"; + + # Print out the quantities. + for my $j ( 4 .. 5 ) { + print OUT "<td class=\"Q\">$row->[$j]</td>"; + } + + # Print the rest of the columns. + for (my $j = 6; $j <= $#{$row}; ++$j) { + print OUT "<td>$row->[$j]</td>" + } + + # Emit the "View" link. + print OUT "<td><a href=\"$ReportFile#EndPath\">View Report</a></td>"; + + # Emit REPORTBUG markers. + print OUT "\n<!-- REPORTBUG id=\"$ReportFile\" -->\n"; + + # End the row. + print OUT "</tr>\n"; + } + + print OUT "</tbody>\n</table>\n\n"; + } + + if (scalar (@failures) || scalar(@attributes_ignored)) { + print OUT "<h2>Analyzer Failures</h2>\n"; + + if (scalar @attributes_ignored) { + print OUT "The analyzer's parser ignored the following attributes:<p>\n"; + print OUT "<table>\n"; + print OUT "<thead><tr><td>Attribute</td><td>Source File</td><td>Preprocessed File</td><td>STDERR Output</td></tr></thead>\n"; + foreach my $file (sort @attributes_ignored) { + die "cannot demangle attribute name\n" if (! ($file =~ /^attribute_ignored_(.+).txt/)); + my $attribute = $1; + # Open the attribute file to get the first file that failed. + next if (!open (ATTR, "$Dir/failures/$file")); + my $ppfile = <ATTR>; + chomp $ppfile; + close ATTR; + next if (! -e "$Dir/failures/$ppfile"); + # Open the info file and get the name of the source file. + open (INFO, "$Dir/failures/$ppfile.info.txt") or + die "Cannot open $Dir/failures/$ppfile.info.txt\n"; + my $srcfile = <INFO>; + chomp $srcfile; + close (INFO); + # Print the information in the table. + my $prefix = GetPrefix(); + if (defined $prefix) { $srcfile =~ s/^\Q$prefix//; } + print OUT "<tr><td>$attribute</td><td>$srcfile</td><td><a href=\"failures/$ppfile\">$ppfile</a></td><td><a href=\"failures/$ppfile.stderr.txt\">$ppfile.stderr.txt</a></td></tr>\n"; + my $ppfile_clang = $ppfile; + $ppfile_clang =~ s/[.](.+)$/.clang.$1/; + print OUT " <!-- REPORTPROBLEM src=\"$srcfile\" file=\"failures/$ppfile\" clangfile=\"failures/$ppfile_clang\" stderr=\"failures/$ppfile.stderr.txt\" info=\"failures/$ppfile.info.txt\" -->\n"; + } + print OUT "</table>\n"; + } + + if (scalar @failures) { + print OUT "<p>The analyzer had problems processing the following files:</p>\n"; + print OUT "<table>\n"; + print OUT "<thead><tr><td>Problem</td><td>Source File</td><td>Preprocessed File</td><td>STDERR Output</td></tr></thead>\n"; + foreach my $file (sort @failures) { + $file =~ /(.+).info.txt$/; + # Get the preprocessed file. + my $ppfile = $1; + # Open the info file and get the name of the source file. + open (INFO, "$Dir/failures/$file") or + die "Cannot open $Dir/failures/$file\n"; + my $srcfile = <INFO>; + chomp $srcfile; + my $problem = <INFO>; + chomp $problem; + close (INFO); + # Print the information in the table. + my $prefix = GetPrefix(); + if (defined $prefix) { $srcfile =~ s/^\Q$prefix//; } + print OUT "<tr><td>$problem</td><td>$srcfile</td><td><a href=\"failures/$ppfile\">$ppfile</a></td><td><a href=\"failures/$ppfile.stderr.txt\">$ppfile.stderr.txt</a></td></tr>\n"; + my $ppfile_clang = $ppfile; + $ppfile_clang =~ s/[.](.+)$/.clang.$1/; + print OUT " <!-- REPORTPROBLEM src=\"$srcfile\" file=\"failures/$ppfile\" clangfile=\"failures/$ppfile_clang\" stderr=\"failures/$ppfile.stderr.txt\" info=\"failures/$ppfile.info.txt\" -->\n"; + } + print OUT "</table>\n"; + } + print OUT "<p>Please consider submitting preprocessed files as <a href=\"http://clang.llvm.org/StaticAnalysisUsage.html#filingbugs\">bug reports</a>. <!-- REPORTCRASHES --> </p>\n"; + } + + print OUT "</body></html>\n"; + close(OUT); + CopyFiles($Dir); + + # Make sure $Dir and $BaseDir are world readable/executable. + system("chmod", "755", $Dir); + if (defined $BaseDir) { system("chmod", "755", $BaseDir); } + + my $Num = scalar(@Index); + Diag("$Num bugs found.\n"); + if ($Num > 0 && -r "$Dir/index.html") { + Diag("Run 'scan-view $Dir' to examine bug reports.\n"); + } + + DiagCrashes($Dir) if (scalar @failures || scalar @attributes_ignored); + + return $Num; +} + +##----------------------------------------------------------------------------## +# RunBuildCommand - Run the build command. +##----------------------------------------------------------------------------## + +sub AddIfNotPresent { + my $Args = shift; + my $Arg = shift; + my $found = 0; + + foreach my $k (@$Args) { + if ($k eq $Arg) { + $found = 1; + last; + } + } + + if ($found == 0) { + push @$Args, $Arg; + } +} + +sub RunBuildCommand { + + my $Args = shift; + my $IgnoreErrors = shift; + my $Cmd = $Args->[0]; + my $CCAnalyzer = shift; + + # Get only the part of the command after the last '/'. + if ($Cmd =~ /\/([^\/]+)$/) { + $Cmd = $1; + } + + if ($Cmd =~ /(.*\/?gcc[^\/]*$)/ or + $Cmd =~ /(.*\/?cc[^\/]*$)/ or + $Cmd =~ /(.*\/?llvm-gcc[^\/]*$)/ or + $Cmd =~ /(.*\/?ccc-analyzer[^\/]*$)/) { + + if (!($Cmd =~ /ccc-analyzer/) and !defined $ENV{"CCC_CC"}) { + $ENV{"CCC_CC"} = $1; + } + + shift @$Args; + unshift @$Args, $CCAnalyzer; + } + elsif ($IgnoreErrors) { + if ($Cmd eq "make" or $Cmd eq "gmake") { + AddIfNotPresent($Args,"-k"); + AddIfNotPresent($Args,"-i"); + } + elsif ($Cmd eq "xcodebuild") { + AddIfNotPresent($Args,"-PBXBuildsContinueAfterErrors=YES"); + } + } + + if ($Cmd eq "xcodebuild") { + # Check if using iPhone SDK 3.0 (simulator). If so the compiler being + # used should be gcc-4.2. + if (!defined $ENV{"CCC_CC"}) { + for (my $i = 0 ; $i < scalar(@$Args); ++$i) { + if ($Args->[$i] eq "-sdk" && $i + 1 < scalar(@$Args)) { + if (@$Args[$i+1] =~ /^iphonesimulator3/) { + $ENV{"CCC_CC"} = "gcc-4.2"; + } + } + } + } + + # Disable distributed builds for xcodebuild. + AddIfNotPresent($Args,"-nodistribute"); + + # Disable PCH files until clang supports them. + AddIfNotPresent($Args,"GCC_PRECOMPILE_PREFIX_HEADER=NO"); + + # When 'CC' is set, xcodebuild uses it to do all linking, even if we are + # linking C++ object files. Set 'LDPLUSPLUS' so that xcodebuild uses 'g++' + # when linking such files. + die if (!defined $CXX); + my $LDPLUSPLUS = `which $CXX`; + $LDPLUSPLUS =~ s/\015?\012//; # strip newlines + $ENV{'LDPLUSPLUS'} = $LDPLUSPLUS; + } + + return (system(@$Args) >> 8); +} + +##----------------------------------------------------------------------------## +# DisplayHelp - Utility function to display all help options. +##----------------------------------------------------------------------------## + +sub DisplayHelp { + +print <<ENDTEXT; +USAGE: $Prog [options] <build command> [build options] + +ENDTEXT + + if (defined $BuildName) { + print "ANALYZER BUILD: $BuildName ($BuildDate)\n\n"; + } + +print <<ENDTEXT; +OPTIONS: + + -analyze-headers - Also analyze functions in #included files. + + --experimental-checks - Enable experimental checks that are currently in heavy testing + + -o - Target directory for HTML report files. Subdirectories + will be created as needed to represent separate "runs" of + the analyzer. If this option is not specified, a directory + is created in /tmp (TMPDIR on Mac OS X) to store the reports. + + -h - Display this message. + --help + + -k - Add a "keep on going" option to the specified build command. + --keep-going This option currently supports make and xcodebuild. + This is a convenience option; one can specify this + behavior directly using build options. + + --html-title [title] - Specify the title used on generated HTML pages. + --html-title=[title] If not specified, a default title will be used. + + -plist - By default the output of scan-build is a set of HTML files. + This option outputs the results as a set of .plist files. + + --status-bugs - By default, the exit status of $Prog is the same as the + executed build command. Specifying this option causes the + exit status of $Prog to be 1 if it found potential bugs + and 0 otherwise. + + --use-cc [compiler path] - By default, $Prog uses 'gcc' to compile and link + --use-cc=[compiler path] your C and Objective-C code. Use this option + to specify an alternate compiler. + + --use-c++ [compiler path] - By default, $Prog uses 'g++' to compile and link + --use-c++=[compiler path] your C++ and Objective-C++ code. Use this option + to specify an alternate compiler. + + -v - Verbose output from $Prog and the analyzer. + A second and third '-v' increases verbosity. + + -V - View analysis results in a web browser when the build + --view completes. + +ADVANCED OPTIONS: + + -constraints [model] - Specify the contraint engine used by the analyzer. + By default the 'range' model is used. Specifying + 'basic' uses a simpler, less powerful constraint model + used by checker-0.160 and earlier. + + -store [model] - Specify the store model used by the analyzer. By default, + the 'region' store model is used. 'region' specifies a field- + sensitive store model. Users can also specify 'basic', which + is far less precise but can more quickly analyze code. + 'basic' was the default store model for checker-0.221 and + earlier. + + -no-failure-reports - Do not create a 'failures' subdirectory that includes + analyzer crash reports and preprocessed source files. + +AVAILABLE ANALYSES (multiple analyses may be specified): + +ENDTEXT + + foreach my $Analysis (sort keys %AvailableAnalyses) { + if (defined $AnalysesDefaultEnabled{$Analysis}) { + print " (+)"; + } + else { + print " "; + } + + print " $Analysis $AvailableAnalyses{$Analysis}\n"; + } + +print <<ENDTEXT + + NOTE: "(+)" indicates that an analysis is enabled by default unless one + or more analysis options are specified + +BUILD OPTIONS + + You can specify any build option acceptable to the build command. + +EXAMPLE + + $Prog -o /tmp/myhtmldir make -j4 + + The above example causes analysis reports to be deposited into + a subdirectory of "/tmp/myhtmldir" and to run "make" with the "-j4" option. + A different subdirectory is created each time $Prog analyzes a project. + The analyzer should support most parallel builds, but not distributed builds. + +ENDTEXT +} + +##----------------------------------------------------------------------------## +# HtmlEscape - HTML entity encode characters that are special in HTML +##----------------------------------------------------------------------------## + +sub HtmlEscape { + # copy argument to new variable so we don't clobber the original + my $arg = shift || ''; + my $tmp = $arg; + $tmp =~ s/&/&/g; + $tmp =~ s/</</g; + $tmp =~ s/>/>/g; + return $tmp; +} + +##----------------------------------------------------------------------------## +# ShellEscape - backslash escape characters that are special to the shell +##----------------------------------------------------------------------------## + +sub ShellEscape { + # copy argument to new variable so we don't clobber the original + my $arg = shift || ''; + if ($arg =~ /["\s]/) { return "'" . $arg . "'"; } + return $arg; +} + +##----------------------------------------------------------------------------## +# Process command-line arguments. +##----------------------------------------------------------------------------## + +my $AnalyzeHeaders = 0; +my $HtmlDir; # Parent directory to store HTML files. +my $IgnoreErrors = 0; # Ignore build errors. +my $ViewResults = 0; # View results when the build terminates. +my $ExitStatusFoundBugs = 0; # Exit status reflects whether bugs were found +my @AnalysesToRun; +my $StoreModel; +my $ConstraintsModel; +my $OutputFormat = "html"; + +if (!@ARGV) { + DisplayHelp(); + exit 1; +} + +while (@ARGV) { + + # Scan for options we recognize. + + my $arg = $ARGV[0]; + + if ($arg eq "-h" or $arg eq "--help") { + DisplayHelp(); + exit 0; + } + + if ($arg eq '-analyze-headers') { + shift @ARGV; + $AnalyzeHeaders = 1; + next; + } + + if (defined $AvailableAnalyses{$arg}) { + shift @ARGV; + push @AnalysesToRun, $arg; + next; + } + + if ($arg eq "-o") { + shift @ARGV; + + if (!@ARGV) { + DieDiag("'-o' option requires a target directory name.\n"); + } + + # Construct an absolute path. Uses the current working directory + # as a base if the original path was not absolute. + $HtmlDir = abs_path(shift @ARGV); + + next; + } + + if ($arg =~ /^--html-title(=(.+))?$/) { + shift @ARGV; + + if (!defined $2 || $2 eq '') { + if (!@ARGV) { + DieDiag("'--html-title' option requires a string.\n"); + } + + $HtmlTitle = shift @ARGV; + } else { + $HtmlTitle = $2; + } + + next; + } + + if ($arg eq "-k" or $arg eq "--keep-going") { + shift @ARGV; + $IgnoreErrors = 1; + next; + } + + if ($arg eq "--experimental-checks") { + shift @ARGV; + $ENV{"CCC_EXPERIMENTAL_CHECKS"} = 1; + next; + } + + if ($arg =~ /^--use-cc(=(.+))?$/) { + shift @ARGV; + my $cc; + + if (!defined $2 || $2 eq "") { + if (!@ARGV) { + DieDiag("'--use-cc' option requires a compiler executable name.\n"); + } + $cc = shift @ARGV; + } + else { + $cc = $2; + } + + $ENV{"CCC_CC"} = $cc; + next; + } + + if ($arg =~ /^--use-c\+\+(=(.+))?$/) { + shift @ARGV; + + if (!defined $2 || $2 eq "") { + if (!@ARGV) { + DieDiag("'--use-c++' option requires a compiler executable name.\n"); + } + $CXX = shift @ARGV; + } + else { + $CXX = $2; + } + next; + } + + if ($arg eq "-v") { + shift @ARGV; + $Verbose++; + next; + } + + if ($arg eq "-V" or $arg eq "--view") { + shift @ARGV; + $ViewResults = 1; + next; + } + + if ($arg eq "--status-bugs") { + shift @ARGV; + $ExitStatusFoundBugs = 1; + next; + } + + if ($arg eq "-store") { + shift @ARGV; + $StoreModel = shift @ARGV; + next; + } + + if ($arg eq "-constraints") { + shift @ARGV; + $ConstraintsModel = shift @ARGV; + next; + } + + if ($arg eq "-plist") { + shift @ARGV; + $OutputFormat = "plist"; + next; + } + if ($arg eq "-plist-html") { + shift @ARGV; + $OutputFormat = "plist-html"; + next; + } + + if ($arg eq "-no-failure-reports") { + $ENV{"CCC_REPORT_FAILURES"} = 0; + next; + } + + DieDiag("unrecognized option '$arg'\n") if ($arg =~ /^-/); + + last; +} + +if (!@ARGV) { + Diag("No build command specified.\n\n"); + DisplayHelp(); + exit 1; +} + +$CmdArgs = HtmlEscape(join(' ', map(ShellEscape($_), @ARGV))); +$HtmlTitle = "${CurrentDirSuffix} - scan-build results" + unless (defined($HtmlTitle)); + +# Determine the output directory for the HTML reports. +my $BaseDir = $HtmlDir; +$HtmlDir = GetHTMLRunDir($HtmlDir); + +# Set the appropriate environment variables. +SetHtmlEnv(\@ARGV, $HtmlDir); + +my $Cmd = Cwd::realpath("$RealBin/libexec/ccc-analyzer"); +if (!defined $Cmd || ! -x $Cmd) { + $Cmd = Cwd::realpath("$RealBin/ccc-analyzer"); + DieDiag("Executable 'ccc-analyzer' does not exist at '$Cmd'\n") if(! -x $Cmd); +} + +if (!defined $ClangCCSB || ! -x $ClangCCSB) { + Diag("'clang-cc' executable not found in '$RealBin/libexec'.\n"); + Diag("Using 'clang-cc' from path.\n"); +} +if (!defined $ClangSB || ! -x $ClangSB) { + Diag("'clang' executable not found in '$RealBin/bin'.\n"); + Diag("Using 'clang' from path.\n"); +} + +if (defined $CXX) { + $ENV{'CXX'} = $CXX; +} +else { + $CXX = 'g++'; # This variable is used by other parts of scan-build + # that need to know a default C++ compiler to fall back to. +} + +$ENV{'CC'} = $Cmd; +$ENV{'CLANG_CC'} = $ClangCC; +$ENV{'CLANG'} = $Clang; + +if ($Verbose >= 2) { + $ENV{'CCC_ANALYZER_VERBOSE'} = 1; +} + +if ($Verbose >= 3) { + $ENV{'CCC_ANALYZER_LOG'} = 1; +} + +if (scalar(@AnalysesToRun) == 0) { + foreach my $key (keys %AnalysesDefaultEnabled) { + push @AnalysesToRun,$key; + } +} + +if ($AnalyzeHeaders) { + push @AnalysesToRun,"-analyzer-opt-analyze-headers"; +} + +$ENV{'CCC_ANALYZER_ANALYSIS'} = join ' ',@AnalysesToRun; + +if (defined $StoreModel) { + $ENV{'CCC_ANALYZER_STORE_MODEL'} = $StoreModel; +} + +if (defined $ConstraintsModel) { + $ENV{'CCC_ANALYZER_CONSTRAINTS_MODEL'} = $ConstraintsModel; +} + +if (defined $OutputFormat) { + $ENV{'CCC_ANALYZER_OUTPUT_FORMAT'} = $OutputFormat; +} + + +# Run the build. +my $ExitStatus = RunBuildCommand(\@ARGV, $IgnoreErrors, $Cmd); + +if (defined $OutputFormat) { + if ($OutputFormat =~ /plist/) { + Diag "Analysis run complete.\n"; + Diag "Analysis results (plist files) deposited in '$HtmlDir'\n"; + } + elsif ($OutputFormat =~ /html/) { + # Postprocess the HTML directory. + my $NumBugs = Postprocess($HtmlDir, $BaseDir); + + if ($ViewResults and -r "$HtmlDir/index.html") { + Diag "Analysis run complete.\n"; + Diag "Viewing analysis results in '$HtmlDir' using scan-view.\n"; + my $ScanView = Cwd::realpath("$RealBin/scan-view"); + if (! -x $ScanView) { $ScanView = "scan-view"; } + exec $ScanView, "$HtmlDir"; + } + + if ($ExitStatusFoundBugs) { + exit 1 if ($NumBugs > 0); + exit 0; + } + } +} + +exit $ExitStatus; + diff --git a/tools/scan-build/scanview.css b/tools/scan-build/scanview.css new file mode 100644 index 0000000..a0406f3 --- /dev/null +++ b/tools/scan-build/scanview.css @@ -0,0 +1,62 @@ +body { color:#000000; background-color:#ffffff } +body { font-family: Helvetica, sans-serif; font-size:9pt } +h1 { font-size: 14pt; } +h2 { font-size: 12pt; } +table { font-size:9pt } +table { border-spacing: 0px; border: 1px solid black } +th, table thead { + background-color:#eee; color:#666666; + font-weight: bold; cursor: default; + text-align:center; + font-weight: bold; font-family: Verdana; + white-space:nowrap; +} +.W { font-size:0px } +th, td { padding:5px; padding-left:8px; text-align:left } +td.SUMM_DESC { padding-left:12px } +td.DESC { white-space:pre } +td.Q { text-align:right } +td { text-align:left } +tbody.scrollContent { overflow:auto } + +table.form_group { + background-color: #ccc; + border: 1px solid #333; + padding: 2px; +} + +table.form_inner_group { + background-color: #ccc; + border: 1px solid #333; + padding: 0px; +} + +table.form { + background-color: #999; + border: 1px solid #333; + padding: 2px; +} + +td.form_label { + text-align: right; + vertical-align: top; +} +/* For one line entires */ +td.form_clabel { + text-align: right; + vertical-align: center; +} +td.form_value { + text-align: left; + vertical-align: top; +} +td.form_submit { + text-align: right; + vertical-align: top; +} + +h1.SubmitFail { + color: #f00; +} +h1.SubmitOk { +} diff --git a/tools/scan-build/sorttable.js b/tools/scan-build/sorttable.js new file mode 100644 index 0000000..4352d3b --- /dev/null +++ b/tools/scan-build/sorttable.js @@ -0,0 +1,493 @@ +/* + SortTable + version 2 + 7th April 2007 + Stuart Langridge, http://www.kryogenix.org/code/browser/sorttable/ + + Instructions: + Download this file + Add <script src="sorttable.js"></script> to your HTML + Add class="sortable" to any table you'd like to make sortable + Click on the headers to sort + + Thanks to many, many people for contributions and suggestions. + Licenced as X11: http://www.kryogenix.org/code/browser/licence.html + This basically means: do what you want with it. +*/ + + +var stIsIE = /*@cc_on!@*/false; + +sorttable = { + init: function() { + // quit if this function has already been called + if (arguments.callee.done) return; + // flag this function so we don't do the same thing twice + arguments.callee.done = true; + // kill the timer + if (_timer) clearInterval(_timer); + + if (!document.createElement || !document.getElementsByTagName) return; + + sorttable.DATE_RE = /^(\d\d?)[\/\.-](\d\d?)[\/\.-]((\d\d)?\d\d)$/; + + forEach(document.getElementsByTagName('table'), function(table) { + if (table.className.search(/\bsortable\b/) != -1) { + sorttable.makeSortable(table); + } + }); + + }, + + makeSortable: function(table) { + if (table.getElementsByTagName('thead').length == 0) { + // table doesn't have a tHead. Since it should have, create one and + // put the first table row in it. + the = document.createElement('thead'); + the.appendChild(table.rows[0]); + table.insertBefore(the,table.firstChild); + } + // Safari doesn't support table.tHead, sigh + if (table.tHead == null) table.tHead = table.getElementsByTagName('thead')[0]; + + if (table.tHead.rows.length != 1) return; // can't cope with two header rows + + // Sorttable v1 put rows with a class of "sortbottom" at the bottom (as + // "total" rows, for example). This is B&R, since what you're supposed + // to do is put them in a tfoot. So, if there are sortbottom rows, + // for backwards compatibility, move them to tfoot (creating it if needed). + sortbottomrows = []; + for (var i=0; i<table.rows.length; i++) { + if (table.rows[i].className.search(/\bsortbottom\b/) != -1) { + sortbottomrows[sortbottomrows.length] = table.rows[i]; + } + } + if (sortbottomrows) { + if (table.tFoot == null) { + // table doesn't have a tfoot. Create one. + tfo = document.createElement('tfoot'); + table.appendChild(tfo); + } + for (var i=0; i<sortbottomrows.length; i++) { + tfo.appendChild(sortbottomrows[i]); + } + delete sortbottomrows; + } + + // work through each column and calculate its type + headrow = table.tHead.rows[0].cells; + for (var i=0; i<headrow.length; i++) { + // manually override the type with a sorttable_type attribute + if (!headrow[i].className.match(/\bsorttable_nosort\b/)) { // skip this col + mtch = headrow[i].className.match(/\bsorttable_([a-z0-9]+)\b/); + if (mtch) { override = mtch[1]; } + if (mtch && typeof sorttable["sort_"+override] == 'function') { + headrow[i].sorttable_sortfunction = sorttable["sort_"+override]; + } else { + headrow[i].sorttable_sortfunction = sorttable.guessType(table,i); + } + // make it clickable to sort + headrow[i].sorttable_columnindex = i; + headrow[i].sorttable_tbody = table.tBodies[0]; + dean_addEvent(headrow[i],"click", function(e) { + + if (this.className.search(/\bsorttable_sorted\b/) != -1) { + // if we're already sorted by this column, just + // reverse the table, which is quicker + sorttable.reverse(this.sorttable_tbody); + this.className = this.className.replace('sorttable_sorted', + 'sorttable_sorted_reverse'); + this.removeChild(document.getElementById('sorttable_sortfwdind')); + sortrevind = document.createElement('span'); + sortrevind.id = "sorttable_sortrevind"; + sortrevind.innerHTML = stIsIE ? ' <font face="webdings">5</font>' : ' ▴'; + this.appendChild(sortrevind); + return; + } + if (this.className.search(/\bsorttable_sorted_reverse\b/) != -1) { + // if we're already sorted by this column in reverse, just + // re-reverse the table, which is quicker + sorttable.reverse(this.sorttable_tbody); + this.className = this.className.replace('sorttable_sorted_reverse', + 'sorttable_sorted'); + this.removeChild(document.getElementById('sorttable_sortrevind')); + sortfwdind = document.createElement('span'); + sortfwdind.id = "sorttable_sortfwdind"; + sortfwdind.innerHTML = stIsIE ? ' <font face="webdings">6</font>' : ' ▾'; + this.appendChild(sortfwdind); + return; + } + + // remove sorttable_sorted classes + theadrow = this.parentNode; + forEach(theadrow.childNodes, function(cell) { + if (cell.nodeType == 1) { // an element + cell.className = cell.className.replace('sorttable_sorted_reverse',''); + cell.className = cell.className.replace('sorttable_sorted',''); + } + }); + sortfwdind = document.getElementById('sorttable_sortfwdind'); + if (sortfwdind) { sortfwdind.parentNode.removeChild(sortfwdind); } + sortrevind = document.getElementById('sorttable_sortrevind'); + if (sortrevind) { sortrevind.parentNode.removeChild(sortrevind); } + + this.className += ' sorttable_sorted'; + sortfwdind = document.createElement('span'); + sortfwdind.id = "sorttable_sortfwdind"; + sortfwdind.innerHTML = stIsIE ? ' <font face="webdings">6</font>' : ' ▾'; + this.appendChild(sortfwdind); + + // build an array to sort. This is a Schwartzian transform thing, + // i.e., we "decorate" each row with the actual sort key, + // sort based on the sort keys, and then put the rows back in order + // which is a lot faster because you only do getInnerText once per row + row_array = []; + col = this.sorttable_columnindex; + rows = this.sorttable_tbody.rows; + for (var j=0; j<rows.length; j++) { + row_array[row_array.length] = [sorttable.getInnerText(rows[j].cells[col]), rows[j]]; + } + /* If you want a stable sort, uncomment the following line */ + sorttable.shaker_sort(row_array, this.sorttable_sortfunction); + /* and comment out this one */ + //row_array.sort(this.sorttable_sortfunction); + + tb = this.sorttable_tbody; + for (var j=0; j<row_array.length; j++) { + tb.appendChild(row_array[j][1]); + } + + delete row_array; + }); + } + } + }, + + guessType: function(table, column) { + // guess the type of a column based on its first non-blank row + sortfn = sorttable.sort_alpha; + for (var i=0; i<table.tBodies[0].rows.length; i++) { + text = sorttable.getInnerText(table.tBodies[0].rows[i].cells[column]); + if (text != '') { + if (text.match(/^-?[£$¤]?[\d,.]+%?$/)) { + return sorttable.sort_numeric; + } + // check for a date: dd/mm/yyyy or dd/mm/yy + // can have / or . or - as separator + // can be mm/dd as well + possdate = text.match(sorttable.DATE_RE) + if (possdate) { + // looks like a date + first = parseInt(possdate[1]); + second = parseInt(possdate[2]); + if (first > 12) { + // definitely dd/mm + return sorttable.sort_ddmm; + } else if (second > 12) { + return sorttable.sort_mmdd; + } else { + // looks like a date, but we can't tell which, so assume + // that it's dd/mm (English imperialism!) and keep looking + sortfn = sorttable.sort_ddmm; + } + } + } + } + return sortfn; + }, + + getInnerText: function(node) { + // gets the text we want to use for sorting for a cell. + // strips leading and trailing whitespace. + // this is *not* a generic getInnerText function; it's special to sorttable. + // for example, you can override the cell text with a customkey attribute. + // it also gets .value for <input> fields. + + hasInputs = (typeof node.getElementsByTagName == 'function') && + node.getElementsByTagName('input').length; + + if (node.getAttribute("sorttable_customkey") != null) { + return node.getAttribute("sorttable_customkey"); + } + else if (typeof node.textContent != 'undefined' && !hasInputs) { + return node.textContent.replace(/^\s+|\s+$/g, ''); + } + else if (typeof node.innerText != 'undefined' && !hasInputs) { + return node.innerText.replace(/^\s+|\s+$/g, ''); + } + else if (typeof node.text != 'undefined' && !hasInputs) { + return node.text.replace(/^\s+|\s+$/g, ''); + } + else { + switch (node.nodeType) { + case 3: + if (node.nodeName.toLowerCase() == 'input') { + return node.value.replace(/^\s+|\s+$/g, ''); + } + case 4: + return node.nodeValue.replace(/^\s+|\s+$/g, ''); + break; + case 1: + case 11: + var innerText = ''; + for (var i = 0; i < node.childNodes.length; i++) { + innerText += sorttable.getInnerText(node.childNodes[i]); + } + return innerText.replace(/^\s+|\s+$/g, ''); + break; + default: + return ''; + } + } + }, + + reverse: function(tbody) { + // reverse the rows in a tbody + newrows = []; + for (var i=0; i<tbody.rows.length; i++) { + newrows[newrows.length] = tbody.rows[i]; + } + for (var i=newrows.length-1; i>=0; i--) { + tbody.appendChild(newrows[i]); + } + delete newrows; + }, + + /* sort functions + each sort function takes two parameters, a and b + you are comparing a[0] and b[0] */ + sort_numeric: function(a,b) { + aa = parseFloat(a[0].replace(/[^0-9.-]/g,'')); + if (isNaN(aa)) aa = 0; + bb = parseFloat(b[0].replace(/[^0-9.-]/g,'')); + if (isNaN(bb)) bb = 0; + return aa-bb; + }, + sort_alpha: function(a,b) { + if (a[0]==b[0]) return 0; + if (a[0]<b[0]) return -1; + return 1; + }, + sort_ddmm: function(a,b) { + mtch = a[0].match(sorttable.DATE_RE); + y = mtch[3]; m = mtch[2]; d = mtch[1]; + if (m.length == 1) m = '0'+m; + if (d.length == 1) d = '0'+d; + dt1 = y+m+d; + mtch = b[0].match(sorttable.DATE_RE); + y = mtch[3]; m = mtch[2]; d = mtch[1]; + if (m.length == 1) m = '0'+m; + if (d.length == 1) d = '0'+d; + dt2 = y+m+d; + if (dt1==dt2) return 0; + if (dt1<dt2) return -1; + return 1; + }, + sort_mmdd: function(a,b) { + mtch = a[0].match(sorttable.DATE_RE); + y = mtch[3]; d = mtch[2]; m = mtch[1]; + if (m.length == 1) m = '0'+m; + if (d.length == 1) d = '0'+d; + dt1 = y+m+d; + mtch = b[0].match(sorttable.DATE_RE); + y = mtch[3]; d = mtch[2]; m = mtch[1]; + if (m.length == 1) m = '0'+m; + if (d.length == 1) d = '0'+d; + dt2 = y+m+d; + if (dt1==dt2) return 0; + if (dt1<dt2) return -1; + return 1; + }, + + shaker_sort: function(list, comp_func) { + // A stable sort function to allow multi-level sorting of data + // see: http://en.wikipedia.org/wiki/Cocktail_sort + // thanks to Joseph Nahmias + var b = 0; + var t = list.length - 1; + var swap = true; + + while(swap) { + swap = false; + for(var i = b; i < t; ++i) { + if ( comp_func(list[i], list[i+1]) > 0 ) { + var q = list[i]; list[i] = list[i+1]; list[i+1] = q; + swap = true; + } + } // for + t--; + + if (!swap) break; + + for(var i = t; i > b; --i) { + if ( comp_func(list[i], list[i-1]) < 0 ) { + var q = list[i]; list[i] = list[i-1]; list[i-1] = q; + swap = true; + } + } // for + b++; + + } // while(swap) + } +} + +/* ****************************************************************** + Supporting functions: bundled here to avoid depending on a library + ****************************************************************** */ + +// Dean Edwards/Matthias Miller/John Resig + +/* for Mozilla/Opera9 */ +if (document.addEventListener) { + document.addEventListener("DOMContentLoaded", sorttable.init, false); +} + +/* for Internet Explorer */ +/*@cc_on @*/ +/*@if (@_win32) + document.write("<script id=__ie_onload defer src=javascript:void(0)><\/script>"); + var script = document.getElementById("__ie_onload"); + script.onreadystatechange = function() { + if (this.readyState == "complete") { + sorttable.init(); // call the onload handler + } + }; +/*@end @*/ + +/* for Safari */ +if (/WebKit/i.test(navigator.userAgent)) { // sniff + var _timer = setInterval(function() { + if (/loaded|complete/.test(document.readyState)) { + sorttable.init(); // call the onload handler + } + }, 10); +} + +/* for other browsers */ +window.onload = sorttable.init; + +// written by Dean Edwards, 2005 +// with input from Tino Zijdel, Matthias Miller, Diego Perini + +// http://dean.edwards.name/weblog/2005/10/add-event/ + +function dean_addEvent(element, type, handler) { + if (element.addEventListener) { + element.addEventListener(type, handler, false); + } else { + // assign each event handler a unique ID + if (!handler.$$guid) handler.$$guid = dean_addEvent.guid++; + // create a hash table of event types for the element + if (!element.events) element.events = {}; + // create a hash table of event handlers for each element/event pair + var handlers = element.events[type]; + if (!handlers) { + handlers = element.events[type] = {}; + // store the existing event handler (if there is one) + if (element["on" + type]) { + handlers[0] = element["on" + type]; + } + } + // store the event handler in the hash table + handlers[handler.$$guid] = handler; + // assign a global event handler to do all the work + element["on" + type] = handleEvent; + } +}; +// a counter used to create unique IDs +dean_addEvent.guid = 1; + +function removeEvent(element, type, handler) { + if (element.removeEventListener) { + element.removeEventListener(type, handler, false); + } else { + // delete the event handler from the hash table + if (element.events && element.events[type]) { + delete element.events[type][handler.$$guid]; + } + } +}; + +function handleEvent(event) { + var returnValue = true; + // grab the event object (IE uses a global event object) + event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event); + // get a reference to the hash table of event handlers + var handlers = this.events[event.type]; + // execute each event handler + for (var i in handlers) { + this.$$handleEvent = handlers[i]; + if (this.$$handleEvent(event) === false) { + returnValue = false; + } + } + return returnValue; +}; + +function fixEvent(event) { + // add W3C standard event methods + event.preventDefault = fixEvent.preventDefault; + event.stopPropagation = fixEvent.stopPropagation; + return event; +}; +fixEvent.preventDefault = function() { + this.returnValue = false; +}; +fixEvent.stopPropagation = function() { + this.cancelBubble = true; +} + +// Dean's forEach: http://dean.edwards.name/base/forEach.js +/* + forEach, version 1.0 + Copyright 2006, Dean Edwards + License: http://www.opensource.org/licenses/mit-license.php +*/ + +// array-like enumeration +if (!Array.forEach) { // mozilla already supports this + Array.forEach = function(array, block, context) { + for (var i = 0; i < array.length; i++) { + block.call(context, array[i], i, array); + } + }; +} + +// generic enumeration +Function.prototype.forEach = function(object, block, context) { + for (var key in object) { + if (typeof this.prototype[key] == "undefined") { + block.call(context, object[key], key, object); + } + } +}; + +// character enumeration +String.forEach = function(string, block, context) { + Array.forEach(string.split(""), function(chr, index) { + block.call(context, chr, index, string); + }); +}; + +// globally resolve forEach enumeration +var forEach = function(object, block, context) { + if (object) { + var resolve = Object; // default + if (object instanceof Function) { + // functions have a "length" property + resolve = Function; + } else if (object.forEach instanceof Function) { + // the object implements a custom forEach method so use that + object.forEach(block, context); + return; + } else if (typeof object == "string") { + // the object is a string + resolve = String; + } else if (typeof object.length == "number") { + // the object is array-like + resolve = Array; + } + resolve.forEach(object, block, context); + } +}; + diff --git a/tools/wpa/CMakeLists.txt b/tools/wpa/CMakeLists.txt deleted file mode 100644 index 5553474..0000000 --- a/tools/wpa/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ -set(LLVM_NO_RTTI 1) - -set( LLVM_USED_LIBS - clangFrontend - clangAnalysis - clangSema - clangAST - clangLex - clangBasic - clangIndex - ) - -set( LLVM_LINK_COMPONENTS - mc - ) - -add_clang_executable(clang-wpa - clang-wpa.cpp - ) -add_dependencies(clang-wpa clang-headers) diff --git a/tools/wpa/Makefile b/tools/wpa/Makefile deleted file mode 100644 index 01dbd11..0000000 --- a/tools/wpa/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -LEVEL = ../../../.. - -TOOLNAME = clang-wpa -CPPFLAGS += -I$(PROJ_SRC_DIR)/../../include -I$(PROJ_OBJ_DIR)/../../include -CXXFLAGS = -fno-rtti -NO_INSTALL = 1 - -# No plugins, optimize startup time. -TOOL_NO_EXPORTS = 1 - -include $(LEVEL)/Makefile.config - -LINK_COMPONENTS := bitreader mc -USEDLIBS = clangFrontend.a clangSema.a clangAST.a clangLex.a clangBasic.a clangAnalysis.a clangIndex.a - -include $(LLVM_SRC_ROOT)/Makefile.rules diff --git a/tools/wpa/clang-wpa.cpp b/tools/wpa/clang-wpa.cpp deleted file mode 100644 index 346634b..0000000 --- a/tools/wpa/clang-wpa.cpp +++ /dev/null @@ -1,62 +0,0 @@ -//===--- clang-wpa.cpp - clang whole program analyzer ---------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This tool reads a sequence of precompiled AST files, and do various -// cross translation unit analyses. -// -//===----------------------------------------------------------------------===// - -#include "clang/Analysis/CallGraph.h" -#include "clang/Frontend/ASTUnit.h" -#include "clang/Basic/FileManager.h" -#include "clang/Basic/SourceManager.h" -#include "clang/Frontend/TextDiagnosticBuffer.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/raw_ostream.h" -using namespace clang; -using namespace idx; - -static llvm::cl::list<std::string> -InputFilenames(llvm::cl::Positional, llvm::cl::desc("<input AST files>")); - -int main(int argc, char **argv) { - llvm::cl::ParseCommandLineOptions(argc, argv, "clang-wpa"); - FileManager FileMgr; - std::vector<ASTUnit*> ASTUnits; - - if (InputFilenames.empty()) - return 0; - - TextDiagnosticBuffer DiagClient; - Diagnostic Diags(&DiagClient); - - for (unsigned i = 0, e = InputFilenames.size(); i != e; ++i) { - const std::string &InFile = InputFilenames[i]; - - std::string ErrMsg; - llvm::OwningPtr<ASTUnit> AST; - - AST.reset(ASTUnit::LoadFromPCHFile(InFile, Diags, FileMgr, &ErrMsg)); - - if (!AST) { - llvm::errs() << "[" << InFile << "] error: " << ErrMsg << '\n'; - return 1; - } - - ASTUnits.push_back(AST.take()); - } - - llvm::OwningPtr<CallGraph> CG; - CG.reset(new CallGraph()); - - for (unsigned i = 0, e = ASTUnits.size(); i != e; ++i) - CG->addTU(ASTUnits[i]->getASTContext()); - - CG->ViewCallGraph(); -} |