diff options
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/CIndex/CIndex.cpp | 745 | ||||
| -rw-r--r-- | tools/CIndex/CIndex.exports | 29 | ||||
| -rw-r--r-- | tools/CIndex/CMakeLists.txt | 27 | ||||
| -rw-r--r-- | tools/CIndex/Makefile | 54 | ||||
| -rw-r--r-- | tools/CMakeLists.txt | 9 | ||||
| -rw-r--r-- | tools/Makefile | 2 | ||||
| -rw-r--r-- | tools/c-index-test/CMakeLists.txt | 25 | ||||
| -rw-r--r-- | tools/c-index-test/Makefile | 24 | ||||
| -rw-r--r-- | tools/c-index-test/c-index-test.c | 106 | ||||
| -rw-r--r-- | tools/clang-cc/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | tools/clang-cc/clang-cc.cpp | 905 | ||||
| -rw-r--r-- | tools/driver/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | tools/driver/Makefile | 6 | ||||
| -rw-r--r-- | tools/driver/driver.cpp | 126 | ||||
| -rw-r--r-- | tools/index-test/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | tools/index-test/Makefile | 5 | ||||
| -rw-r--r-- | tools/index-test/index-test.cpp | 271 | ||||
| -rw-r--r-- | tools/wpa/CMakeLists.txt | 20 | ||||
| -rw-r--r-- | tools/wpa/Makefile | 16 | ||||
| -rw-r--r-- | tools/wpa/clang-wpa.cpp | 62 |
20 files changed, 1960 insertions, 479 deletions
diff --git a/tools/CIndex/CIndex.cpp b/tools/CIndex/CIndex.cpp new file mode 100644 index 000000000000..9204d1863d67 --- /dev/null +++ b/tools/CIndex/CIndex.cpp @@ -0,0 +1,745 @@ +//===- CIndex.cpp - Clang-C Source Indexing Library -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the Clang-C Source Indexing library. +// +//===----------------------------------------------------------------------===// + +#include "clang-c/Index.h" +#include "clang/Index/Program.h" +#include "clang/Index/Indexer.h" +#include "clang/Index/ASTLocation.h" +#include "clang/Index/Utils.h" +#include "clang/AST/DeclVisitor.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/AST/Decl.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Frontend/ASTUnit.h" +#include <cstdio> +using namespace clang; +using namespace idx; + +namespace { + +static enum CXCursorKind TranslateDeclRefExpr(DeclRefExpr *DRE) +{ + NamedDecl *D = DRE->getDecl(); + if (isa<VarDecl>(D)) + return CXCursor_VarRef; + else if (isa<FunctionDecl>(D)) + return CXCursor_FunctionRef; + else if (isa<EnumConstantDecl>(D)) + return CXCursor_EnumConstantRef; + else + return CXCursor_NotImplemented; +} + +#if 0 +// Will be useful one day. +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) : + CDecl(C), Callback(cback), CData(D) {} + + void VisitStmt(Stmt *S) { + for (Stmt::child_iterator C = S->child_begin(), CEnd = S->child_end(); + C != CEnd; ++C) + Visit(*C); + } + void VisitDeclRefExpr(DeclRefExpr *Node) { + Call(TranslateDeclRefExpr(Node), Node); + } + void VisitMemberExpr(MemberExpr *Node) { + Call(CXCursor_MemberRef, Node); + } + void VisitObjCMessageExpr(ObjCMessageExpr *Node) { + Call(CXCursor_ObjCSelectorRef, Node); + } + void VisitObjCIvarRefExpr(ObjCIvarRefExpr *Node) { + Call(CXCursor_ObjCIvarRef, Node); + } +}; +#endif + +// Translation Unit Visitor. +class TUVisitor : public DeclVisitor<TUVisitor> { + CXTranslationUnit TUnit; + CXTranslationUnitIterator Callback; + CXClientData CData; + + void Call(enum CXCursorKind CK, NamedDecl *ND) { + CXCursor C = { CK, ND, 0 }; + Callback(TUnit, C, CData); + } +public: + TUVisitor(CXTranslationUnit CTU, + CXTranslationUnitIterator cback, CXClientData D) : + TUnit(CTU), Callback(cback), CData(D) {} + + void VisitTranslationUnitDecl(TranslationUnitDecl *D) { + VisitDeclContext(dyn_cast<DeclContext>(D)); + } + void VisitDeclContext(DeclContext *DC) { + for (DeclContext::decl_iterator + I = DC->decls_begin(), E = DC->decls_end(); I != E; ++I) + Visit(*I); + } + void VisitTypedefDecl(TypedefDecl *ND) { + Call(CXCursor_TypedefDecl, 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; + } + } + void VisitVarDecl(VarDecl *ND) { + Call(CXCursor_VarDecl, ND); + } + void VisitFunctionDecl(FunctionDecl *ND) { + Call(ND->isThisDeclarationADefinition() ? CXCursor_FunctionDefn + : CXCursor_FunctionDecl, ND); + } + void VisitObjCInterfaceDecl(ObjCInterfaceDecl *ND) { + Call(CXCursor_ObjCInterfaceDecl, ND); + } + void VisitObjCCategoryDecl(ObjCCategoryDecl *ND) { + Call(CXCursor_ObjCCategoryDecl, ND); + } + void VisitObjCProtocolDecl(ObjCProtocolDecl *ND) { + Call(CXCursor_ObjCProtocolDecl, ND); + } + void VisitObjCImplementationDecl(ObjCImplementationDecl *ND) { + Call(CXCursor_ObjCClassDefn, ND); + } + void VisitObjCCategoryImplDecl(ObjCCategoryImplDecl *ND) { + Call(CXCursor_ObjCCategoryDefn, ND); + } +}; + +// Declaration visitor. +class CDeclVisitor : public DeclVisitor<CDeclVisitor> { + CXDecl CDecl; + CXDeclIterator Callback; + CXClientData CData; + + 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; + CXCursor C = { CK, ND, 0 }; + Callback(CDecl, C, CData); + } +public: + CDeclVisitor(CXDecl C, CXDeclIterator cback, CXClientData D) : + CDecl(C), Callback(cback), CData(D) {} + + void VisitObjCCategoryDecl(ObjCCategoryDecl *ND) { + // Issue callbacks for the containing class. + Call(CXCursor_ObjCClassRef, ND); + // FIXME: Issue callbacks for protocol refs. + VisitDeclContext(dyn_cast<DeclContext>(ND)); + } + void VisitObjCInterfaceDecl(ObjCInterfaceDecl *D) { + // Issue callbacks for super class. + if (D->getSuperClass()) + Call(CXCursor_ObjCSuperClassRef, D); + + for (ObjCProtocolDecl::protocol_iterator I = D->protocol_begin(), + E = D->protocol_end(); I != E; ++I) + Call(CXCursor_ObjCProtocolRef, *I); + VisitDeclContext(dyn_cast<DeclContext>(D)); + } + void VisitObjCProtocolDecl(ObjCProtocolDecl *PID) { + for (ObjCProtocolDecl::protocol_iterator I = PID->protocol_begin(), + E = PID->protocol_end(); I != E; ++I) + Call(CXCursor_ObjCProtocolRef, *I); + + VisitDeclContext(dyn_cast<DeclContext>(PID)); + } + void VisitTagDecl(TagDecl *D) { + VisitDeclContext(dyn_cast<DeclContext>(D)); + } + void VisitObjCImplementationDecl(ObjCImplementationDecl *D) { + VisitDeclContext(dyn_cast<DeclContext>(D)); + } + void VisitObjCCategoryImplDecl(ObjCCategoryImplDecl *D) { + VisitDeclContext(dyn_cast<DeclContext>(D)); + } + void VisitDeclContext(DeclContext *DC) { + for (DeclContext::decl_iterator + I = DC->decls_begin(), E = DC->decls_end(); I != E; ++I) + Visit(*I); + } + void VisitEnumConstantDecl(EnumConstantDecl *ND) { + Call(CXCursor_EnumConstantDecl, ND); + } + void VisitFieldDecl(FieldDecl *ND) { + Call(CXCursor_FieldDecl, ND); + } + void VisitVarDecl(VarDecl *ND) { + Call(CXCursor_VarDecl, ND); + } + void VisitParmVarDecl(ParmVarDecl *ND) { + Call(CXCursor_ParmDecl, ND); + } + void VisitObjCPropertyDecl(ObjCPropertyDecl *ND) { + Call(CXCursor_ObjCPropertyDecl, ND); + } + void VisitObjCIvarDecl(ObjCIvarDecl *ND) { + Call(CXCursor_ObjCIvarDecl, ND); + } + void VisitFunctionDecl(FunctionDecl *ND) { + if (ND->isThisDeclarationADefinition()) { + VisitDeclContext(dyn_cast<DeclContext>(ND)); +#if 0 + // Not currently needed. + CompoundStmt *Body = dyn_cast<CompoundStmt>(ND->getBody()); + CRefVisitor RVisit(CDecl, Callback, CData); + RVisit.Visit(Body); +#endif + } + } + void VisitObjCMethodDecl(ObjCMethodDecl *ND) { + if (ND->getBody()) { + Call(ND->isInstanceMethod() ? CXCursor_ObjCInstanceMethodDefn + : CXCursor_ObjCClassMethodDefn, ND); + VisitDeclContext(dyn_cast<DeclContext>(ND)); + } else + Call(ND->isInstanceMethod() ? CXCursor_ObjCInstanceMethodDecl + : CXCursor_ObjCClassMethodDecl, ND); + } +}; + +} + +extern "C" { + +CXIndex clang_createIndex() +{ + // FIXME: Program is leaked. + return new Indexer(*new Program()); +} + +void clang_disposeIndex(CXIndex CIdx) +{ + assert(CIdx && "Passed null CXIndex"); + delete static_cast<Indexer *>(CIdx); +} + +// FIXME: need to pass back error info. +CXTranslationUnit clang_createTranslationUnit( + CXIndex CIdx, const char *ast_filename) +{ + assert(CIdx && "Passed null CXIndex"); + Indexer *CXXIdx = static_cast<Indexer *>(CIdx); + std::string astName(ast_filename); + std::string ErrMsg; + + return ASTUnit::LoadFromPCHFile(astName, CXXIdx->getDiagnostics(), + CXXIdx->getFileManager(), &ErrMsg); +} + +void clang_disposeTranslationUnit( + CXTranslationUnit CTUnit) +{ + assert(CTUnit && "Passed null CXTranslationUnit"); + delete static_cast<ASTUnit *>(CTUnit); +} + +const char *clang_getTranslationUnitSpelling(CXTranslationUnit CTUnit) +{ + assert(CTUnit && "Passed null CXTranslationUnit"); + ASTUnit *CXXUnit = static_cast<ASTUnit *>(CTUnit); + return CXXUnit->getOriginalSourceFileName().c_str(); +} + +void clang_loadTranslationUnit(CXTranslationUnit CTUnit, + CXTranslationUnitIterator callback, + CXClientData CData) +{ + assert(CTUnit && "Passed null CXTranslationUnit"); + ASTUnit *CXXUnit = static_cast<ASTUnit *>(CTUnit); + ASTContext &Ctx = CXXUnit->getASTContext(); + + TUVisitor DVisit(CTUnit, callback, CData); + DVisit.Visit(Ctx.getTranslationUnitDecl()); +} + +void clang_loadDeclaration(CXDecl Dcl, + CXDeclIterator callback, + CXClientData CData) +{ + assert(Dcl && "Passed null CXDecl"); + + CDeclVisitor DVisit(Dcl, callback, CData); + DVisit.Visit(static_cast<Decl *>(Dcl)); +} + +// Some notes on CXEntity: +// +// - 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 +// within a CXTranslationUnit are *not* currently represented by entities. +// As a result, there will be no entity for the following: +// +// module.m: static void Foo() { } +// + + +const char *clang_getDeclarationName(CXEntity) +{ + return ""; +} +const char *clang_getURI(CXEntity) +{ + return ""; +} + +CXEntity clang_getEntity(const char *URI) +{ + return 0; +} + +// +// CXDecl Operations. +// +CXEntity clang_getEntityFromDecl(CXDecl) +{ + return 0; +} +const char *clang_getDeclSpelling(CXDecl AnonDecl) +{ + assert(AnonDecl && "Passed null CXDecl"); + NamedDecl *ND = static_cast<NamedDecl *>(AnonDecl); + + if (ObjCMethodDecl *OMD = dyn_cast<ObjCMethodDecl>(ND)) { + return OMD->getSelector().getAsString().c_str(); + } + if (ND->getIdentifier()) + return ND->getIdentifier()->getName(); + else + return ""; +} + +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) +{ + 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) +{ + assert(AnonDecl && "Passed null CXDecl"); + NamedDecl *ND = static_cast<NamedDecl *>(AnonDecl); + SourceManager &SourceMgr = ND->getASTContext().getSourceManager(); + return SourceMgr.getBufferName(ND->getLocation()); +} + +const char *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 OID->getSuperClass()->getIdentifier()->getName(); + } + case CXCursor_ObjCClassRef: + { + if (ObjCInterfaceDecl *OID = dyn_cast<ObjCInterfaceDecl>(ND)) { + return OID->getIdentifier()->getName(); + } + ObjCCategoryDecl *OID = dyn_cast<ObjCCategoryDecl>(ND); + assert(OID && "clang_getCursorLine(): Missing category decl"); + return OID->getClassInterface()->getIdentifier()->getName(); + } + case CXCursor_ObjCProtocolRef: + { + ObjCProtocolDecl *OID = dyn_cast<ObjCProtocolDecl>(ND); + assert(OID && "clang_getCursorLine(): Missing protocol decl"); + return OID->getIdentifier()->getName(); + } + case CXCursor_ObjCSelectorRef: + { + ObjCMessageExpr *OME = dyn_cast<ObjCMessageExpr>( + static_cast<Stmt *>(C.stmt)); + assert(OME && "clang_getCursorLine(): Missing message expr"); + return OME->getSelector().getAsString().c_str(); + } + 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->getDecl()->getIdentifier()->getName(); + } + default: + return "<not implemented>"; + } + } + return clang_getDeclSpelling(C.decl); +} + +const char *clang_getCursorKindSpelling(enum CXCursorKind Kind) +{ + switch (Kind) { + case CXCursor_FunctionDecl: return "FunctionDecl"; + case CXCursor_TypedefDecl: return "TypedefDecl"; + case CXCursor_EnumDecl: return "EnumDecl"; + case CXCursor_EnumConstantDecl: return "EnumConstantDecl"; + case CXCursor_StructDecl: return "StructDecl"; + case CXCursor_UnionDecl: return "UnionDecl"; + case CXCursor_ClassDecl: return "ClassDecl"; + case CXCursor_FieldDecl: return "FieldDecl"; + case CXCursor_VarDecl: return "VarDecl"; + case CXCursor_ParmDecl: return "ParmDecl"; + case CXCursor_ObjCInterfaceDecl: return "ObjCInterfaceDecl"; + case CXCursor_ObjCCategoryDecl: return "ObjCCategoryDecl"; + case CXCursor_ObjCProtocolDecl: return "ObjCProtocolDecl"; + case CXCursor_ObjCPropertyDecl: return "ObjCPropertyDecl"; + case CXCursor_ObjCIvarDecl: return "ObjCIvarDecl"; + case CXCursor_ObjCInstanceMethodDecl: return "ObjCInstanceMethodDecl"; + case CXCursor_ObjCClassMethodDecl: return "ObjCClassMethodDecl"; + case CXCursor_FunctionDefn: return "FunctionDefn"; + case CXCursor_ObjCInstanceMethodDefn: return "ObjCInstanceMethodDefn"; + case CXCursor_ObjCClassMethodDefn: return "ObjCClassMethodDefn"; + case CXCursor_ObjCClassDefn: return "ObjCClassDefn"; + case CXCursor_ObjCCategoryDefn: return "ObjCCategoryDefn"; + case CXCursor_ObjCSuperClassRef: return "ObjCSuperClassRef"; + case CXCursor_ObjCProtocolRef: return "ObjCProtocolRef"; + case CXCursor_ObjCClassRef: return "ObjCClassRef"; + case CXCursor_ObjCSelectorRef: return "ObjCSelectorRef"; + + case CXCursor_VarRef: return "VarRef"; + case CXCursor_FunctionRef: return "FunctionRef"; + case CXCursor_EnumConstantRef: return "EnumConstantRef"; + case CXCursor_MemberRef: return "MemberRef"; + + case CXCursor_InvalidFile: return "InvalidFile"; + case CXCursor_NoDeclFound: return "NoDeclFound"; + case CXCursor_NotImplemented: return "NotImplemented"; + default: return "<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; + } + return CXCursor_NotImplemented; +} +// +// CXCursor Operations. +// +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)); + if (!File) { + CXCursor C = { CXCursor_InvalidFile, 0, 0 }; + return C; + } + SourceLocation SLoc = + CXXUnit->getSourceManager().getLocation(File, line, column); + + ASTLocation ALoc = ResolveLocationInAST(CXXUnit->getASTContext(), SLoc); + + Decl *Dcl = ALoc.getParentDecl(); + if (ALoc.isNamedRef()) + Dcl = ALoc.AsNamedRef().ND; + Stmt *Stm = ALoc.dyn_AsStmt(); + if (Dcl) { + if (Stm) { + if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Stm)) { + CXCursor C = { TranslateDeclRefExpr(DRE), Dcl, Stm }; + return C; + } 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()) { + if (isa<ObjCInterfaceDecl>(Dcl)) { + CXCursor C = { CXCursor_ObjCClassRef, Dcl, ALoc.getParentDecl() }; + return C; + } + if (isa<ObjCProtocolDecl>(Dcl)) { + CXCursor C = { CXCursor_ObjCProtocolRef, Dcl, ALoc.getParentDecl() }; + return C; + } + } + CXCursor C = { TranslateKind(Dcl), Dcl, 0 }; + return C; + } + CXCursor C = { CXCursor_NoDeclFound, 0, 0 }; + return C; +} + +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) +{ + return K >= CXCursor_FirstInvalid && K <= CXCursor_LastInvalid; +} + +unsigned clang_isDeclaration(enum CXCursorKind K) +{ + return K >= CXCursor_FirstDecl && K <= CXCursor_LastDecl; +} + +unsigned clang_isReference(enum CXCursorKind K) +{ + return K >= CXCursor_FirstRef && K <= CXCursor_LastRef; +} + +unsigned clang_isDefinition(enum CXCursorKind K) +{ + return K >= CXCursor_FirstDefn && K <= CXCursor_LastDefn; +} + +CXCursorKind clang_getCursorKind(CXCursor C) +{ + return C.kind; +} + +static Decl *getDeclFromExpr(Stmt *E) { + if (DeclRefExpr *RefExpr = dyn_cast<DeclRefExpr>(E)) + return RefExpr->getDecl(); + if (MemberExpr *ME = dyn_cast<MemberExpr>(E)) + return ME->getMemberDecl(); + if (ObjCIvarRefExpr *RE = dyn_cast<ObjCIvarRefExpr>(E)) + return RE->getDecl(); + + if (CallExpr *CE = dyn_cast<CallExpr>(E)) + return getDeclFromExpr(CE->getCallee()); + if (CastExpr *CE = dyn_cast<CastExpr>(E)) + return getDeclFromExpr(CE->getSubExpr()); + if (ObjCMessageExpr *OME = dyn_cast<ObjCMessageExpr>(E)) + return OME->getMethodDecl(); + + return 0; +} + +CXDecl clang_getCursorDecl(CXCursor C) +{ + if (clang_isDeclaration(C.kind)) + return C.decl; + + if (clang_isReference(C.kind)) { + if (C.stmt) { + if (C.kind == CXCursor_ObjCClassRef || + C.kind == CXCursor_ObjCProtocolRef) + return static_cast<Stmt *>(C.stmt); + else + return getDeclFromExpr(static_cast<Stmt *>(C.stmt)); + } else + return C.decl; + } + return 0; +} + + +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_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; + } + } + if (SLoc.isInvalid()) + return SourceLocation(); + return SourceMgr.getSpellingLoc(SLoc); // handles macro instantiations. + } +} + +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); +} + +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) +{ + 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.getBufferName(SLoc); +} + +void clang_getDefinitionSpellingAndExtent(CXCursor C, + const char **startBuf, + const char **endBuf, + unsigned *startLine, + unsigned *startColumn, + unsigned *endLine, + unsigned *endColumn) +{ + assert(C.decl && "CXCursor has null decl"); + NamedDecl *ND = static_cast<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()); + *startLine = SM.getSpellingLineNumber(Body->getLBracLoc()); + *startColumn = SM.getSpellingColumnNumber(Body->getLBracLoc()); + *endLine = SM.getSpellingLineNumber(Body->getRBracLoc()); + *endColumn = SM.getSpellingColumnNumber(Body->getRBracLoc()); +} + + +} // end extern "C" diff --git a/tools/CIndex/CIndex.exports b/tools/CIndex/CIndex.exports new file mode 100644 index 000000000000..c1ca0c773519 --- /dev/null +++ b/tools/CIndex/CIndex.exports @@ -0,0 +1,29 @@ +_clang_createIndex +_clang_disposeIndex +_clang_getCursor +_clang_getCursorColumn +_clang_getCursorDecl +_clang_getCursorFromDecl +_clang_getCursorKind +_clang_getCursorLine +_clang_getCursorSource +_clang_getDeclarationName +_clang_getDeclSpelling +_clang_getDeclLine +_clang_getDeclColumn +_clang_getDeclSource +_clang_getEntity +_clang_getEntityFromDecl +_clang_getURI +_clang_loadDeclaration +_clang_loadTranslationUnit +_clang_createTranslationUnit +_clang_disposeTranslationUnit +_clang_isDeclaration +_clang_isReference +_clang_isDefinition +_clang_isInvalid +_clang_getCursorSpelling +_clang_getCursorKindSpelling +_clang_getDefinitionSpellingAndExtent +_clang_getTranslationUnitSpelling diff --git a/tools/CIndex/CMakeLists.txt b/tools/CIndex/CMakeLists.txt new file mode 100644 index 000000000000..71bbde546a6e --- /dev/null +++ b/tools/CIndex/CMakeLists.txt @@ -0,0 +1,27 @@ +set(SHARED_LIBRARY TRUE) + +set(LLVM_NO_RTTI 1) + +set(LLVM_USED_LIBS + clangFrontend clangIndex clangSema clangAST clangLex clangBasic) + +set( LLVM_LINK_COMPONENTS + MC + support + ) + +add_clang_library(CIndex CIndex.cpp) + +if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + # FIXME: Deal with LLVM_SUBMIT_VERSION? + + set_target_properties(CIndex + PROPERTIES + LINK_FLAGS "-avoid-version -Wl,-exported_symbols_list -Wl,${CMAKE_CURRENT_SOURCE_DIR}/CIndex.exports -Wl,-dead_strip -Wl,-seg1addr -Wl,0xE0000000" + INSTALL_NAME_DIR "@executable_path/../lib" + ) +endif() + +set_target_properties(CIndex + PROPERTIES + LINKER_LANGUAGE CXX) diff --git a/tools/CIndex/Makefile b/tools/CIndex/Makefile new file mode 100644 index 000000000000..a9ff394f3399 --- /dev/null +++ b/tools/CIndex/Makefile @@ -0,0 +1,54 @@ +##===- tools/CIndex/Makefile -------------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LEVEL = ../../../.. +LIBRARYNAME = CIndex + +CPPFLAGS += -I$(PROJ_SRC_DIR)/../../include -I$(PROJ_OBJ_DIR)/../../include +CXXFLAGS = -fno-rtti + +# Include this here so we can get the configuration of the targets +# that have been configured for construction. We have to do this +# early so we can set up LINK_COMPONENTS before including Makefile.rules +include $(LEVEL)/Makefile.config + +LINK_LIBS_IN_SHARED = 1 +SHARED_LIBRARY = 1 + +LINK_COMPONENTS := MC support +USEDLIBS = clangFrontend.a clangIndex.a clangSema.a clangAST.a clangLex.a clangBasic.a + +include $(LEVEL)/Makefile.common + +##===----------------------------------------------------------------------===## +# FIXME: This is copied from the 'lto' makefile. Should we share this? +##===----------------------------------------------------------------------===## + +ifeq ($(HOST_OS),Darwin) + # set dylib internal version number to llvmCore submission number + ifdef LLVM_SUBMIT_VERSION + LLVMLibsOptions := $(LLVMLibsOptions) -Wl,-current_version \ + -Wl,$(LLVM_SUBMIT_VERSION).$(LLVM_SUBMIT_SUBVERSION) \ + -Wl,-compatibility_version -Wl,1 + endif + # extra options to override libtool defaults + LLVMLibsOptions := $(LLVMLibsOptions) \ + -avoid-version \ + -Wl,-exported_symbols_list -Wl,$(PROJ_SRC_DIR)/CIndex.exports \ + -Wl,-dead_strip \ + -Wl,-seg1addr -Wl,0xE0000000 + + # Mac OS X 10.4 and earlier tools do not allow a second -install_name on command line + DARWIN_VERS := $(shell echo $(TARGET_TRIPLE) | sed 's/.*darwin\([0-9]*\).*/\1/') + ifneq ($(DARWIN_VERS),8) + LLVMLibsOptions := $(LLVMLibsOptions) \ + -no-undefined -Wl,-install_name \ + -Wl,"@executable_path/../lib/lib$(LIBRARYNAME)$(SHLIBEXT)" + endif +endif diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index fc30fb848e75..cb2aa2004ce7 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -1,3 +1,12 @@ add_subdirectory(clang-cc) add_subdirectory(driver) add_subdirectory(index-test) +option(CLANG_BUILD_EXPERIMENTAL "Build experimenal Clang tools" OFF) +if (CLANG_BUILD_EXPERIMENTAL) + add_subdirectory(wpa) +endif () +add_subdirectory(CIndex) +if (MSVC) +else () + add_subdirectory(c-index-test) +endif () diff --git a/tools/Makefile b/tools/Makefile index 5b49cc8ad0d1..0e98439c3821 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -8,6 +8,6 @@ ##===----------------------------------------------------------------------===## LEVEL := ../../.. -DIRS := clang-cc driver index-test +DIRS := clang-cc driver index-test CIndex c-index-test include $(LEVEL)/Makefile.common diff --git a/tools/c-index-test/CMakeLists.txt b/tools/c-index-test/CMakeLists.txt new file mode 100644 index 000000000000..4c724659f6f6 --- /dev/null +++ b/tools/c-index-test/CMakeLists.txt @@ -0,0 +1,25 @@ +set(LLVM_NO_RTTI 1) + +set( LLVM_USED_LIBS + CIndex + clangIndex + clangFrontend + clangSema + clangAST + clangLex + clangBasic + ) + +set( LLVM_LINK_COMPONENTS + bitreader + mc + ) + +add_clang_executable(c-index-test + c-index-test.c + ) + +set_target_properties(c-index-test + PROPERTIES + LINKER_LANGUAGE CXX) + diff --git a/tools/c-index-test/Makefile b/tools/c-index-test/Makefile new file mode 100644 index 000000000000..81fee40b66e2 --- /dev/null +++ b/tools/c-index-test/Makefile @@ -0,0 +1,24 @@ +##===- tools/index-test/Makefile ---------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +LEVEL = ../../../.. + +TOOLNAME = c-index-test +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 = CIndex.a clangIndex.a clangFrontend.a clangSema.a clangAST.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 new file mode 100644 index 000000000000..c514b63d94be --- /dev/null +++ b/tools/c-index-test/c-index-test.c @@ -0,0 +1,106 @@ +/* c-index-test.c */ + +#include "clang-c/Index.h" +#include <stdio.h> +#include <string.h> + +extern char *basename(const char *); + +static void PrintCursor(CXCursor Cursor) { + if (clang_isInvalid(Cursor.kind)) + printf("Invalid Cursor => %s\n", clang_getCursorKindSpelling(Cursor.kind)); + else { + CXDecl DeclReferenced; + printf("%s=%s", clang_getCursorKindSpelling(Cursor.kind), + clang_getCursorSpelling(Cursor)); + DeclReferenced = clang_getCursorDecl(Cursor); + if (DeclReferenced) + printf(":%d:%d", clang_getDeclLine(DeclReferenced), + clang_getDeclColumn(DeclReferenced)); + } +} + +static void DeclVisitor(CXDecl Dcl, CXCursor Cursor, CXClientData Filter) +{ + if (!Filter || (Cursor.kind == *(enum CXCursorKind *)Filter)) { + printf("// CHECK: %s:%d:%d: ", basename(clang_getCursorSource(Cursor)), + clang_getCursorLine(Cursor), + clang_getCursorColumn(Cursor)); + PrintCursor(Cursor); + printf(" [Context=%s]\n", clang_getDeclSpelling(Dcl)); + } +} +static void TranslationUnitVisitor(CXTranslationUnit Unit, CXCursor Cursor, + CXClientData Filter) +{ + if (!Filter || (Cursor.kind == *(enum CXCursorKind *)Filter)) { + printf("// CHECK: %s:%d:%d: ", basename(clang_getCursorSource(Cursor)), + clang_getCursorLine(Cursor), + clang_getCursorColumn(Cursor)); + PrintCursor(Cursor); + printf(" [Context=%s]\n", basename(clang_getTranslationUnitSpelling(Unit))); + + 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++; + + Ref = clang_getCursor(Unit, clang_getCursorSource(Cursor), + curLine, curColumn); + if (Ref.kind != CXCursor_FunctionDecl) { + printf("// CHECK: %s:%d:%d: ", basename(clang_getCursorSource(Ref)), + curLine, curColumn); + PrintCursor(Ref); + printf(" [Context:%s]\n", clang_getDeclSpelling(Ref.decl)); + } + startBuf++; + } + } + } + } +} + +/* + * First sign of life:-) + */ +int main(int argc, char **argv) { + if (argc != 3) { + printf("Incorrect usage of c-index-test (requires 3 arguments)\n"); + return 0; + } + { + CXIndex Idx = clang_createIndex(); + CXTranslationUnit TU = clang_createTranslationUnit(Idx, argv[1]); + enum CXCursorKind K = CXCursor_NotImplemented; + + if (!strcmp(argv[2], "all")) { + clang_loadTranslationUnit(TU, TranslationUnitVisitor, 0); + return 1; + } + /* Perform some simple filtering. */ + if (!strcmp(argv[2], "category")) K = CXCursor_ObjCCategoryDecl; + else if (!strcmp(argv[2], "interface")) K = CXCursor_ObjCInterfaceDecl; + else if (!strcmp(argv[2], "protocol")) K = CXCursor_ObjCProtocolDecl; + else if (!strcmp(argv[2], "function")) K = CXCursor_FunctionDecl; + else if (!strcmp(argv[2], "typedef")) K = CXCursor_TypedefDecl; + + clang_loadTranslationUnit(TU, TranslationUnitVisitor, &K); + return 1; + } +} diff --git a/tools/clang-cc/CMakeLists.txt b/tools/clang-cc/CMakeLists.txt index e224d40f6c8c..85e4c7c20653 100644 --- a/tools/clang-cc/CMakeLists.txt +++ b/tools/clang-cc/CMakeLists.txt @@ -25,3 +25,6 @@ add_clang_executable(clang-cc clang-cc.cpp ) add_dependencies(clang-cc clang-headers) + +install(TARGETS clang-cc + RUNTIME DESTINATION libexec) diff --git a/tools/clang-cc/clang-cc.cpp b/tools/clang-cc/clang-cc.cpp index 9433c1752e53..0ad7efbc4839 100644 --- a/tools/clang-cc/clang-cc.cpp +++ b/tools/clang-cc/clang-cc.cpp @@ -24,6 +24,7 @@ #include "clang/Frontend/AnalysisConsumer.h" #include "clang/Frontend/ASTConsumers.h" +#include "clang/Frontend/ASTUnit.h" #include "clang/Frontend/CompileOptions.h" #include "clang/Frontend/FixItRewriter.h" #include "clang/Frontend/FrontendDiagnostic.h" @@ -37,6 +38,7 @@ #include "clang/Frontend/Utils.h" #include "clang/Analysis/PathDiagnostic.h" #include "clang/CodeGen/ModuleBuilder.h" +#include "clang/Sema/CodeCompleteConsumer.h" #include "clang/Sema/ParseAST.h" #include "clang/Sema/SemaDiagnostic.h" #include "clang/AST/ASTConsumer.h" @@ -58,12 +60,13 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/Config/config.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/PluginLoader.h" #include "llvm/Support/PrettyStackTrace.h" -#include "llvm/Support/Streams.h" #include "llvm/Support/Timer.h" +#include "llvm/Support/raw_ostream.h" #include "llvm/System/Host.h" #include "llvm/System/Path.h" #include "llvm/System/Process.h" @@ -108,14 +111,14 @@ static bool HadErrors = false; static llvm::cl::opt<bool> Verbose("v", llvm::cl::desc("Enable verbose output")); static llvm::cl::opt<bool> -Stats("print-stats", +Stats("print-stats", llvm::cl::desc("Print performance metrics and statistics")); static llvm::cl::opt<bool> DisableFree("disable-free", llvm::cl::desc("Disable freeing of memory on exit"), llvm::cl::init(false)); static llvm::cl::opt<bool> -EmptyInputOnly("empty-input-only", +EmptyInputOnly("empty-input-only", llvm::cl::desc("Force running on an empty input file")); enum ProgActions { @@ -128,13 +131,14 @@ enum ProgActions { EmitAssembly, // Emit a .s file. EmitLLVM, // Emit a .ll file. EmitBC, // Emit a .bc file. - EmitLLVMOnly, // Generate LLVM IR, but do not + EmitLLVMOnly, // Generate LLVM IR, but do not EmitHTML, // Translate input source into HTML. ASTPrint, // Parse ASTs and print them. ASTPrintXML, // Parse ASTs and print them in XML. ASTDump, // Parse ASTs and dump them. ASTView, // Parse ASTs and view them in Graphviz. PrintDeclContext, // Print DeclContext and their Decls. + DumpRecordLayouts, // Dump record layout information. ParsePrintCallbacks, // Parse and print each callback. ParseSyntaxOnly, // Parse and perform semantic analysis. ParseNoop, // Parse with noop callbacks. @@ -142,13 +146,13 @@ enum ProgActions { PrintPreprocessedInput, // -E mode. DumpTokens, // Dump out preprocessed tokens. DumpRawTokens, // Dump out raw tokens. - RunAnalysis, // Run one or more source code analyses. + RunAnalysis, // Run one or more source code analyses. GeneratePTH, // Generate pre-tokenized header. GeneratePCH, // Generate pre-compiled header. InheritanceView // View C++ inheritance for a specified class. }; -static llvm::cl::opt<ProgActions> +static llvm::cl::opt<ProgActions> ProgAction(llvm::cl::desc("Choose output type:"), llvm::cl::ZeroOrMore, llvm::cl::init(ParseSyntaxOnly), llvm::cl::values( @@ -180,6 +184,8 @@ ProgAction(llvm::cl::desc("Choose output type:"), llvm::cl::ZeroOrMore, "Build ASTs and view them with GraphViz"), clEnumValN(PrintDeclContext, "print-decl-contexts", "Print DeclContexts and their Decls"), + clEnumValN(DumpRecordLayouts, "dump-record-layouts", + "Dump record layout information"), clEnumValN(GeneratePTH, "emit-pth", "Generate pre-tokenized header file"), clEnumValN(GeneratePCH, "emit-pch", @@ -211,6 +217,17 @@ OutputFile("o", llvm::cl::desc("Specify output file")); +static llvm::cl::opt<ParsedSourceLocation> +CodeCompletionAt("code-completion-at", + llvm::cl::value_desc("file:line:column"), + llvm::cl::desc("Dump code-completion information at a location")); + +/// \brief Buld a new code-completion consumer that prints the results of +/// code completion to standard output. +static CodeCompleteConsumer *BuildPrintingCodeCompleter(Sema &S, void *) { + return new PrintingCodeCompleteConsumer(S, llvm::outs()); +} + //===----------------------------------------------------------------------===// // PTH. //===----------------------------------------------------------------------===// @@ -261,13 +278,13 @@ PrintDiagnosticOption("fdiagnostics-show-option", static llvm::cl::opt<unsigned> MessageLength("fmessage-length", - llvm::cl::desc("Format message diagnostics so that they fit " - "within N columns or fewer, when possible."), - llvm::cl::value_desc("N")); + llvm::cl::desc("Format message diagnostics so that they fit " + "within N columns or fewer, when possible."), + llvm::cl::value_desc("N")); static llvm::cl::opt<bool> NoColorDiagnostic("fno-color-diagnostics", - llvm::cl::desc("Don't use colors when showing diagnostics " + llvm::cl::desc("Don't use colors when showing diagnostics " "(automatically turned off if output is not a " "terminal).")); //===----------------------------------------------------------------------===// @@ -317,7 +334,8 @@ enum LangKind { langkind_objc_cpp, langkind_objcxx, langkind_objcxx_cpp, - langkind_ocl + langkind_ocl, + langkind_ast }; static llvm::cl::opt<LangKind> @@ -346,20 +364,21 @@ BaseLang("x", llvm::cl::desc("Base language to compile"), "C++ header"), clEnumValN(langkind_objcxx, "objective-c++-header", "Objective-C++ header"), + clEnumValN(langkind_ast, "ast", + "Clang AST"), clEnumValEnd)); static llvm::cl::opt<bool> -LangObjC("ObjC", llvm::cl::desc("Set base language to Objective-C"), - llvm::cl::Hidden); -static llvm::cl::opt<bool> -LangObjCXX("ObjC++", llvm::cl::desc("Set base language to Objective-C++"), - llvm::cl::Hidden); - -static llvm::cl::opt<bool> ObjCExclusiveGC("fobjc-gc-only", llvm::cl::desc("Use GC exclusively for Objective-C related " "memory management")); +static llvm::cl::opt<std::string> +ObjCConstantStringClass("fconstant-string-class", + llvm::cl::value_desc("class name"), + llvm::cl::desc("Specify the class to use for constant " + "Objective-C string objects.")); + static llvm::cl::opt<bool> ObjCEnableGC("fobjc-gc", llvm::cl::desc("Enable Objective-C garbage collection")); @@ -386,39 +405,23 @@ OverflowChecking("ftrapv", llvm::cl::init(false)); static llvm::cl::opt<bool> -ObjCSenderDispatch("fobjc-sender-dependent-dispatch", - llvm::cl::desc("Enable sender-dependent dispatch for" - "Objective-C messages"), llvm::cl::init(false)); +AltiVec("faltivec", llvm::cl::desc("Enable AltiVec vector initializer syntax"), + llvm::cl::init(false)); -/// InitializeBaseLanguage - Handle the -x foo options. -static void InitializeBaseLanguage() { - if (LangObjC) - BaseLang = langkind_objc; - else if (LangObjCXX) - BaseLang = langkind_objcxx; -} +static llvm::cl::opt<bool> +PThread("pthread", llvm::cl::desc("Support POSIX threads in generated code"), + llvm::cl::init(false)); -static LangKind GetLanguage(const std::string &Filename) { +static LangKind GetLanguage(llvm::StringRef Filename) { if (BaseLang != langkind_unspecified) return BaseLang; - - std::string::size_type DotPos = Filename.rfind('.'); - if (DotPos == std::string::npos) { - BaseLang = langkind_c; // Default to C if no extension. - return langkind_c; - } - - std::string Ext = std::string(Filename.begin()+DotPos+1, Filename.end()); - // C header: .h - // C++ header: .hh or .H; - // assembler no preprocessing: .s - // assembler: .S - if (Ext == "c") + llvm::StringRef Ext = Filename.rsplit('.').second; + if (Ext == "ast") + return langkind_ast; + else if (Ext == "c") return langkind_c; - else if (Ext == "S" || - // If the compiler is run on a .s file, preprocess it as .S - Ext == "s") + else if (Ext == "S" || Ext == "s") return langkind_asm_cpp; else if (Ext == "i") return langkind_c_cpp; @@ -449,12 +452,12 @@ static void InitializeCOptions(LangOptions &Options) { static void InitializeObjCOptions(LangOptions &Options) { Options.ObjC1 = Options.ObjC2 = 1; } - + static void InitializeLangOptions(LangOptions &Options, LangKind LK){ // FIXME: implement -fpreprocessed mode. bool NoPreprocess = false; - + switch (LK) { default: assert(0 && "Unknown language kind!"); case langkind_asm_cpp: @@ -492,25 +495,30 @@ static void InitializeLangOptions(LangOptions &Options, LangKind LK){ 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; } /// LangStds - Language standards we support. enum LangStds { - lang_unspecified, + lang_unspecified, lang_c89, lang_c94, lang_c99, - lang_gnu_START, - lang_gnu89 = lang_gnu_START, lang_gnu99, + lang_gnu89, lang_gnu99, lang_cxx98, lang_gnucxx98, lang_cxx0x, lang_gnucxx0x }; @@ -554,7 +562,7 @@ static llvm::cl::opt<bool> PascalStrings("fpascal-strings", llvm::cl::desc("Recognize and construct Pascal-style " "string literals")); - + static llvm::cl::opt<bool> MSExtensions("fms-extensions", llvm::cl::desc("Accept some non-standard constructs used in " @@ -592,6 +600,10 @@ Exceptions("fexceptions", llvm::cl::desc("Enable support for exception handling")); static llvm::cl::opt<bool> +Rtti("frtti", llvm::cl::init(true), + llvm::cl::desc("Enable generation of rtti information")); + +static llvm::cl::opt<bool> GNURuntime("fgnu-runtime", llvm::cl::desc("Generate output compatible with the standard GNU " "Objective-C runtime")); @@ -622,7 +634,7 @@ static llvm::cl::opt<bool> OptSize("Os", llvm::cl::desc("Optimize for size")); static llvm::cl::opt<bool> -DisableLLVMOptimizations("disable-llvm-optzns", +DisableLLVMOptimizations("disable-llvm-optzns", llvm::cl::desc("Don't run LLVM optimization passes")); static llvm::cl::opt<bool> @@ -636,17 +648,26 @@ MainFileName("main-file-name", // FIXME: Also add an "-fno-access-control" option. static llvm::cl::opt<bool> -AccessControl("faccess-control", +AccessControl("faccess-control", llvm::cl::desc("Enable C++ access control")); +static llvm::cl::opt<bool> +NoElideConstructors("fno-elide-constructors", + llvm::cl::desc("Disable C++ copy constructor elision")); + +static llvm::cl::opt<std::string> +TargetABI("target-abi", + llvm::cl::desc("Target a particular ABI type")); + + // It might be nice to add bounds to the CommandLine library directly. struct OptLevelParser : public llvm::cl::parser<unsigned> { - bool parse(llvm::cl::Option &O, const char *ArgName, - const std::string &Arg, unsigned &Val) { + bool parse(llvm::cl::Option &O, llvm::StringRef ArgName, + llvm::StringRef Arg, unsigned &Val) { if (llvm::cl::parser<unsigned>::parse(O, ArgName, Arg, Val)) return true; if (Val > 3) - return O.error(": '" + Arg + "' invalid optimization level!"); + return O.error("'" + Arg + "' invalid optimization level!"); return false; } }; @@ -675,10 +696,11 @@ static void InitializeLanguageStandard(LangOptions &Options, LangKind LK, // Pass the map of target features to the target for validation and // processing. Target->HandleTargetFeatures(Features); - + if (LangStd == lang_unspecified) { // Based on the base language, pick one. switch (LK) { + case langkind_ast: assert(0 && "Invalid call for AST inputs"); case lang_unspecified: assert(0 && "Unknown base language"); case langkind_ocl: LangStd = lang_c99; @@ -698,7 +720,7 @@ static void InitializeLanguageStandard(LangOptions &Options, LangKind LK, break; } } - + switch (LangStd) { default: assert(0 && "Unknown language standard!"); @@ -729,18 +751,33 @@ static void InitializeLanguageStandard(LangOptions &Options, LangKind LK, } // GNUMode - Set if we're in gnu99, gnu89, gnucxx98, etc. - Options.GNUMode = LangStd >= lang_gnu_START; - + 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; + } + if (Options.CPlusPlus) { Options.C99 = 0; - Options.HexFloats = Options.GNUMode; + Options.HexFloats = 0; } - + if (LangStd == lang_c89 || LangStd == lang_c94 || LangStd == lang_gnu89) Options.ImplicitInt = 1; else Options.ImplicitInt = 0; - + // Mimicing gcc's behavior, trigraphs are only enabled if -trigraphs // is specified, or -std is set to a conforming mode. Options.Trigraphs = !Options.GNUMode; @@ -753,14 +790,14 @@ static void InitializeLanguageStandard(LangOptions &Options, LangKind LK, // 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 = LK != langkind_asm_cpp; if (DollarsInIdents.getPosition()) // Explicit setting overrides default. Options.DollarIdents = DollarsInIdents; - + if (PascalStrings.getPosition()) Options.PascalStrings = PascalStrings; if (MSExtensions.getPosition()) @@ -769,6 +806,7 @@ static void InitializeLanguageStandard(LangOptions &Options, LangKind LK, if (NoLaxVectorConversions.getPosition()) Options.LaxVectorConversions = 0; Options.Exceptions = Exceptions; + Options.Rtti = Rtti; if (EnableBlocks.getPosition()) Options.Blocks = EnableBlocks; if (CharIsSigned.getPosition()) @@ -778,16 +816,18 @@ static void InitializeLanguageStandard(LangOptions &Options, LangKind LK, Options.NoBuiltin = 1; if (Freestanding) Options.Freestanding = Options.NoBuiltin = 1; - + if (EnableHeinousExtensions) Options.HeinousExtensions = 1; if (AccessControl) Options.AccessControl = 1; - + + Options.ElideConstructors = !NoElideConstructors; + // OpenCL and C++ both have bool, true, false keywords. Options.Bool = Options.OpenCL | Options.CPlusPlus; - + Options.MathErrno = MathErrno; Options.InstantiationDepth = TemplateDepth; @@ -798,18 +838,19 @@ static void InitializeLanguageStandard(LangOptions &Options, LangKind LK, else if (GNURuntime) Options.NeXTRuntime = 0; + if (!ObjCConstantStringClass.empty()) + Options.ObjCConstantStringClass = ObjCConstantStringClass.c_str(); + if (ObjCNonFragileABI) Options.ObjCNonFragileABI = 1; - Options.ObjCSenderDispatch = ObjCSenderDispatch; - if (EmitAllDecls) Options.EmitAllDecls = 1; // The __OPTIMIZE_SIZE__ define is tied to -Oz, which we don't // support. Options.OptimizeSize = 0; - + // -Os implies -O2 if (OptSize || OptLevel) Options.Optimize = 1; @@ -818,7 +859,7 @@ static void InitializeLanguageStandard(LangOptions &Options, LangKind LK, Options.PICLevel = PICLevel; Options.GNUInline = !Options.C99; - // FIXME: This is affected by other options (-fno-inline). + // FIXME: This is affected by other options (-fno-inline). Options.NoInline = !OptSize && !OptLevel; Options.Static = StaticDefine; @@ -845,70 +886,72 @@ TargetTriple("triple", llvm::cl::desc("Specify target triple (e.g. i686-apple-darwin9)")); static llvm::cl::opt<std::string> -MacOSVersionMin("mmacosx-version-min", +MacOSVersionMin("mmacosx-version-min", llvm::cl::desc("Specify target Mac OS X version (e.g. 10.5)")); // If -mmacosx-version-min=10.3.9 is specified, change the triple from being // something like powerpc-apple-darwin9 to powerpc-apple-darwin7 // FIXME: We should have the driver do this instead. -static void HandleMacOSVersionMin(std::string &Triple) { - std::string::size_type DarwinDashIdx = Triple.find("-darwin"); - if (DarwinDashIdx == std::string::npos) { - fprintf(stderr, +static void HandleMacOSVersionMin(llvm::Triple &Triple) { + if (Triple.getOS() != llvm::Triple::Darwin) { + fprintf(stderr, "-mmacosx-version-min only valid for darwin (Mac OS X) targets\n"); exit(1); } - unsigned DarwinNumIdx = DarwinDashIdx + strlen("-darwin"); - // Remove the number. - Triple.resize(DarwinNumIdx); - // Validate that MacOSVersionMin is a 'version number', starting with 10.[3-9] - bool MacOSVersionMinIsInvalid = false; - int VersionNum = 0; if (MacOSVersionMin.size() < 4 || MacOSVersionMin.substr(0, 3) != "10." || !isdigit(MacOSVersionMin[3])) { - MacOSVersionMinIsInvalid = true; - } else { - const char *Start = MacOSVersionMin.c_str()+3; - char *End = 0; - VersionNum = (int)strtol(Start, &End, 10); - - // The version number must be in the range 0-9. - MacOSVersionMinIsInvalid = (unsigned)VersionNum > 9; - - // Turn MacOSVersionMin into a darwin number: e.g. 10.3.9 is 3 -> 7. - Triple += llvm::itostr(VersionNum+4); - - if (End[0] == '.' && isdigit(End[1]) && End[2] == '\0') { // 10.4.7 is ok. - // Add the period piece (.7) to the end of the triple. This gives us - // something like ...-darwin8.7 - Triple += End; - } else if (End[0] != '\0') { // "10.4" is ok. 10.4x is not. - MacOSVersionMinIsInvalid = true; - } + fprintf(stderr, + "-mmacosx-version-min=%s is invalid, expected something like '10.4'.\n", + MacOSVersionMin.c_str()); + exit(1); } - if (MacOSVersionMinIsInvalid) { - fprintf(stderr, - "-mmacosx-version-min=%s is invalid, expected something like '10.4'.\n", + unsigned VersionNum = MacOSVersionMin[3]-'0'; + + if (VersionNum <= 4 && Triple.getArch() == llvm::Triple::x86_64) { + fprintf(stderr, + "-mmacosx-version-min=%s is invalid with -arch x86_64.\n", MacOSVersionMin.c_str()); exit(1); } - else if (VersionNum <= 4 && - !strncmp(Triple.c_str(), "x86_64", strlen("x86_64"))) { - fprintf(stderr, - "-mmacosx-version-min=%s is invalid with -arch x86_64.\n", + + + llvm::SmallString<16> NewDarwinString; + NewDarwinString += "darwin"; + + // Turn MacOSVersionMin into a darwin number: e.g. 10.3.9 is 3 -> darwin7. + VersionNum += 4; + if (VersionNum > 9) { + NewDarwinString += '1'; + VersionNum -= 10; + } + NewDarwinString += (VersionNum+'0'); + + if (MacOSVersionMin.size() == 4) { + // "10.4" is ok. + } else if (MacOSVersionMin.size() == 6 && + MacOSVersionMin[4] == '.' && + isdigit(MacOSVersionMin[5])) { // 10.4.7 is ok. + // Add the period piece (.7) to the end of the triple. This gives us + // something like ...-darwin8.7 + NewDarwinString += '.'; + NewDarwinString += MacOSVersionMin[5]; + } else { // "10.4" is ok. 10.4x is not. + fprintf(stderr, + "-mmacosx-version-min=%s is invalid, expected something like '10.4'.\n", MacOSVersionMin.c_str()); exit(1); } + Triple.setOSName(NewDarwinString.str()); } static llvm::cl::opt<std::string> -IPhoneOSVersionMin("miphoneos-version-min", +IPhoneOSVersionMin("miphoneos-version-min", llvm::cl::desc("Specify target iPhone OS version (e.g. 2.0)")); // If -miphoneos-version-min=2.2 is specified, change the triple from being @@ -917,68 +960,46 @@ IPhoneOSVersionMin("miphoneos-version-min", // number in the minor version and revision. // FIXME: We should have the driver do this instead. -static void HandleIPhoneOSVersionMin(std::string &Triple) { - std::string::size_type DarwinDashIdx = Triple.find("-darwin"); - if (DarwinDashIdx == std::string::npos) { - fprintf(stderr, - "-miphoneos-version-min only valid for darwin (Mac OS X) targets\n"); +static void HandleIPhoneOSVersionMin(llvm::Triple &Triple) { + if (Triple.getOS() != llvm::Triple::Darwin) { + fprintf(stderr, + "-miphoneos-version-min only valid for darwin (Mac OS X) targets\n"); exit(1); } - unsigned DarwinNumIdx = DarwinDashIdx + strlen("-darwin"); - - // Remove the number. - Triple.resize(DarwinNumIdx); - - // Validate that IPhoneOSVersionMin is a 'version number', starting with [2-9].[0-9] - bool IPhoneOSVersionMinIsInvalid = false; - int VersionNum = 0; - if (IPhoneOSVersionMin.size() < 3 || - !isdigit(IPhoneOSVersionMin[0])) { - IPhoneOSVersionMinIsInvalid = true; - } else { - const char *Start = IPhoneOSVersionMin.c_str(); - char *End = 0; - VersionNum = (int)strtol(Start, &End, 10); - - // The version number must be in the range 0-9. - IPhoneOSVersionMinIsInvalid = (unsigned)VersionNum > 9; - - // Turn IPhoneOSVersionMin into a darwin number: e.g. 2.0 is 2 -> 9.2. - Triple += "9." + llvm::itostr(VersionNum); - - if (End[0] == '.' && isdigit(End[1]) && End[2] == '\0') { // 2.2 is ok. - // Add the period piece (.2) to the end of the triple. This gives us - // something like ...-darwin9.2.2 - Triple += End; - } else if (End[0] != '\0') { // "2.2" is ok. 2x is not. - IPhoneOSVersionMinIsInvalid = true; - } - } - - if (IPhoneOSVersionMinIsInvalid) { - fprintf(stderr, - "-miphoneos-version-min=%s is invalid, expected something like '2.0'.\n", + + // Validate that IPhoneOSVersionMin is a 'version number', starting with + // [2-9].[0-9] + if (IPhoneOSVersionMin.size() != 3 || !isdigit(IPhoneOSVersionMin[0]) || + IPhoneOSVersionMin[1] != '.' || !isdigit(IPhoneOSVersionMin[2])) { + fprintf(stderr, + "-miphoneos-version-min=%s is invalid, expected something like '2.0'.\n", IPhoneOSVersionMin.c_str()); exit(1); } + + // Turn IPhoneOSVersionMin into a darwin number: e.g. 2.0 is 2 -> 9.2.0 + llvm::SmallString<16> NewDarwinString; + NewDarwinString += "darwin9."; + NewDarwinString += IPhoneOSVersionMin; + Triple.setOSName(NewDarwinString.str()); } /// CreateTargetTriple - Process the various options that affect the target /// triple and build a final aggregate triple that we are compiling for. -static std::string CreateTargetTriple() { +static llvm::Triple CreateTargetTriple() { // Initialize base triple. If a -triple option has been specified, use // that triple. Otherwise, default to the host triple. - std::string Triple = TargetTriple; - if (Triple.empty()) - Triple = llvm::sys::getHostTriple(); + llvm::Triple Triple(TargetTriple); + if (Triple.getTriple().empty()) + Triple = llvm::Triple(llvm::sys::getHostTriple()); // If -mmacosx-version-min=10.3.9 is specified, change the triple from being // something like powerpc-apple-darwin9 to powerpc-apple-darwin7 if (!MacOSVersionMin.empty()) HandleMacOSVersionMin(Triple); else if (!IPhoneOSVersionMin.empty()) - HandleIPhoneOSVersionMin(Triple);; - + HandleIPhoneOSVersionMin(Triple); + return Triple; } @@ -994,14 +1015,14 @@ static bool InitializeSourceManager(Preprocessor &PP, if (EmptyInputOnly) { const char *EmptyStr = ""; - llvm::MemoryBuffer *SB = + llvm::MemoryBuffer *SB = llvm::MemoryBuffer::getMemBuffer(EmptyStr, EmptyStr, "<empty input>"); SourceMgr.createMainFileIDForMemBuffer(SB); } else if (InFile != "-") { const FileEntry *File = FileMgr.getFile(InFile); if (File) SourceMgr.createMainFileID(File, SourceLocation()); if (SourceMgr.getMainFileID().isInvalid()) { - PP.getDiagnostics().Report(FullSourceLoc(), diag::err_fe_error_reading) + PP.getDiagnostics().Report(FullSourceLoc(), diag::err_fe_error_reading) << InFile.c_str(); return true; } @@ -1017,7 +1038,7 @@ static bool InitializeSourceManager(Preprocessor &PP, SourceMgr.createMainFileIDForMemBuffer(SB); if (SourceMgr.getMainFileID().isInvalid()) { - PP.getDiagnostics().Report(FullSourceLoc(), + PP.getDiagnostics().Report(FullSourceLoc(), diag::err_fe_error_reading_stdin); return true; } @@ -1057,6 +1078,10 @@ static llvm::cl::opt<std::string> ImplicitIncludePTH("include-pth", llvm::cl::value_desc("file"), llvm::cl::desc("Include file before parsing")); +static llvm::cl::opt<bool> +RelocatablePCH("relocatable-pch", + llvm::cl::desc("Whether to build a relocatable precompiled " + "header")); //===----------------------------------------------------------------------===// // Preprocessor include path information. @@ -1065,7 +1090,7 @@ ImplicitIncludePTH("include-pth", llvm::cl::value_desc("file"), // This tool exports a large number of command line options to control how the // preprocessor searches for header files. At root, however, the Preprocessor // object takes a very simple interface: a list of directories to search for -// +// // FIXME: -nostdinc++ // FIXME: -imultilib // @@ -1073,6 +1098,10 @@ ImplicitIncludePTH("include-pth", llvm::cl::value_desc("file"), static llvm::cl::opt<bool> nostdinc("nostdinc", llvm::cl::desc("Disable standard #include directories")); +static llvm::cl::opt<bool> +nostdclanginc("nostdclanginc", + llvm::cl::desc("Disable standard clang #include directories")); + // Various command line options. These four add directories to each chain. static llvm::cl::list<std::string> F_dirs("F", llvm::cl::value_desc("directory"), llvm::cl::Prefix, @@ -1108,10 +1137,37 @@ isysroot("isysroot", llvm::cl::value_desc("dir"), llvm::cl::init("/"), // Finally, implement the code that groks the options above. +// Add the clang headers, which are relative to the clang binary. +void AddClangIncludePaths(const char *Argv0, InitHeaderSearch *Init) { + if (nostdclanginc) + return; + + llvm::sys::Path MainExecutablePath = + llvm::sys::Path::GetMainExecutable(Argv0, + (void*)(intptr_t)AddClangIncludePaths); + if (MainExecutablePath.isEmpty()) + return; + + MainExecutablePath.eraseComponent(); // Remove /clang from foo/bin/clang + MainExecutablePath.eraseComponent(); // Remove /bin from foo/bin + + // Get foo/lib/clang/<version>/include + MainExecutablePath.appendComponent("lib"); + MainExecutablePath.appendComponent("clang"); + MainExecutablePath.appendComponent(CLANG_VERSION_STRING); + MainExecutablePath.appendComponent("include"); + + // We pass true to ignore sysroot so that we *always* look for clang headers + // relative to our executable, never relative to -isysroot. + Init->AddPath(MainExecutablePath.c_str(), InitHeaderSearch::System, + false, false, false, true /*ignore sysroot*/); +} + /// InitializeIncludePaths - Process the -I options and set them in the /// HeaderSearch object. void InitializeIncludePaths(const char *Argv0, HeaderSearch &Headers, - FileManager &FM, const LangOptions &Lang) { + FileManager &FM, const LangOptions &Lang, + llvm::Triple &triple) { InitHeaderSearch Init(Headers, Verbose, isysroot); // Handle -I... and -F... options, walking the lists in parallel. @@ -1125,22 +1181,22 @@ void InitializeIncludePaths(const char *Argv0, HeaderSearch &Headers, ++Fidx; } } - + // Consume what's left from whatever list was longer. for (; Iidx != I_dirs.size(); ++Iidx) Init.AddPath(I_dirs[Iidx], InitHeaderSearch::Angled, false, true, false); for (; Fidx != F_dirs.size(); ++Fidx) Init.AddPath(F_dirs[Fidx], InitHeaderSearch::Angled, false, true, true); - + // Handle -idirafter... options. for (unsigned i = 0, e = idirafter_dirs.size(); i != e; ++i) Init.AddPath(idirafter_dirs[i], InitHeaderSearch::After, false, true, false); - + // Handle -iquote... options. for (unsigned i = 0, e = iquote_dirs.size(); i != e; ++i) Init.AddPath(iquote_dirs[i], InitHeaderSearch::Quoted, false, true, false); - + // Handle -isystem... options. for (unsigned i = 0, e = isystem_dirs.size(); i != e; ++i) Init.AddPath(isystem_dirs[i], InitHeaderSearch::System, false, true, false); @@ -1158,28 +1214,28 @@ void InitializeIncludePaths(const char *Argv0, HeaderSearch &Headers, bool iwithprefixbefore_done = iwithprefixbefore_vals.empty(); while (!iprefix_done || !iwithprefix_done || !iwithprefixbefore_done) { if (!iprefix_done && - (iwithprefix_done || - iprefix_vals.getPosition(iprefix_idx) < + (iwithprefix_done || + iprefix_vals.getPosition(iprefix_idx) < iwithprefix_vals.getPosition(iwithprefix_idx)) && - (iwithprefixbefore_done || - iprefix_vals.getPosition(iprefix_idx) < + (iwithprefixbefore_done || + iprefix_vals.getPosition(iprefix_idx) < iwithprefixbefore_vals.getPosition(iwithprefixbefore_idx))) { Prefix = iprefix_vals[iprefix_idx]; ++iprefix_idx; iprefix_done = iprefix_idx == iprefix_vals.size(); } else if (!iwithprefix_done && - (iwithprefixbefore_done || - iwithprefix_vals.getPosition(iwithprefix_idx) < + (iwithprefixbefore_done || + iwithprefix_vals.getPosition(iwithprefix_idx) < iwithprefixbefore_vals.getPosition(iwithprefixbefore_idx))) { - Init.AddPath(Prefix+iwithprefix_vals[iwithprefix_idx], + Init.AddPath(Prefix+iwithprefix_vals[iwithprefix_idx], InitHeaderSearch::System, false, false, false); ++iwithprefix_idx; iwithprefix_done = iwithprefix_idx == iwithprefix_vals.size(); } else { - Init.AddPath(Prefix+iwithprefixbefore_vals[iwithprefixbefore_idx], + Init.AddPath(Prefix+iwithprefixbefore_vals[iwithprefixbefore_idx], InitHeaderSearch::Angled, false, false, false); ++iwithprefixbefore_idx; - iwithprefixbefore_done = + iwithprefixbefore_done = iwithprefixbefore_idx == iwithprefixbefore_vals.size(); } } @@ -1187,37 +1243,18 @@ void InitializeIncludePaths(const char *Argv0, HeaderSearch &Headers, Init.AddDefaultEnvVarPaths(Lang); - // Add the clang headers, which are relative to the clang binary. - llvm::sys::Path MainExecutablePath = - llvm::sys::Path::GetMainExecutable(Argv0, - (void*)(intptr_t)InitializeIncludePaths); - if (!MainExecutablePath.isEmpty()) { - MainExecutablePath.eraseComponent(); // Remove /clang from foo/bin/clang - MainExecutablePath.eraseComponent(); // Remove /bin from foo/bin + AddClangIncludePaths(Argv0, &Init); - // Get foo/lib/clang/<version>/include - MainExecutablePath.appendComponent("lib"); - MainExecutablePath.appendComponent("clang"); - MainExecutablePath.appendComponent(CLANG_VERSION_STRING); - MainExecutablePath.appendComponent("include"); - - // We pass true to ignore sysroot so that we *always* look for clang headers - // relative to our executable, never relative to -isysroot. - Init.AddPath(MainExecutablePath.c_str(), InitHeaderSearch::System, - false, false, false, true /*ignore sysroot*/); - } - - if (!nostdinc) - Init.AddDefaultSystemIncludePaths(Lang); + if (!nostdinc) + Init.AddDefaultSystemIncludePaths(Lang, triple); // Now that we have collected all of the include paths, merge them all // together and tell the preprocessor about them. - + Init.Realize(); } -void InitializePreprocessorInitOptions(PreprocessorInitOptions &InitOpts) -{ +void InitializePreprocessorInitOptions(PreprocessorInitOptions &InitOpts) { // Add macros from the command line. unsigned d = 0, D = D_macros.size(); unsigned u = 0, U = U_macros.size(); @@ -1286,17 +1323,17 @@ class VISIBILITY_HIDDEN DriverPreprocessorFactory : public PreprocessorFactory { TargetInfo &Target; SourceManager &SourceMgr; HeaderSearch &HeaderInfo; - + public: DriverPreprocessorFactory(Diagnostic &diags, const LangOptions &opts, TargetInfo &target, SourceManager &SM, - HeaderSearch &Headers) + HeaderSearch &Headers) : Diags(diags), LangInfo(opts), Target(target), SourceMgr(SM), HeaderInfo(Headers) {} - - + + virtual ~DriverPreprocessorFactory() {} - + virtual Preprocessor* CreatePreprocessor() { llvm::OwningPtr<PTHManager> PTHMgr; @@ -1305,23 +1342,23 @@ public: "options\n"); exit(1); } - + // Use PTH? if (!TokenCache.empty() || !ImplicitIncludePTH.empty()) { const std::string& x = TokenCache.empty() ? ImplicitIncludePTH:TokenCache; - PTHMgr.reset(PTHManager::Create(x, &Diags, + PTHMgr.reset(PTHManager::Create(x, &Diags, TokenCache.empty() ? Diagnostic::Error : Diagnostic::Warning)); } - + if (Diags.hasErrorOccurred()) exit(1); - + // Create the Preprocessor. llvm::OwningPtr<Preprocessor> PP(new Preprocessor(Diags, LangInfo, Target, SourceMgr, HeaderInfo, PTHMgr.get())); - + // Note that this is different then passing PTHMgr to Preprocessor's ctor. // That argument is used as the IdentifierInfoLookup argument to // IdentifierTable's ctor. @@ -1347,7 +1384,7 @@ public: static void ParseFile(Preprocessor &PP, MinimalAction *PA) { Parser P(PP, *PA); PP.EnterMainSourceFile(); - + // Parsing the specified input file. P.ParseTranslationUnit(); delete PA; @@ -1384,24 +1421,24 @@ NoImplicitFloat("no-implicit-float", /// and feature list. static void ComputeFeatureMap(TargetInfo *Target, llvm::StringMap<bool> &Features) { - assert(Features.empty() && "invalid map"); + assert(Features.empty() && "invalid map"); // Initialize the feature map based on the target. Target->getDefaultFeatures(TargetCPU, Features); // Apply the user specified deltas. - for (llvm::cl::list<std::string>::iterator it = TargetFeatures.begin(), + for (llvm::cl::list<std::string>::iterator it = TargetFeatures.begin(), ie = TargetFeatures.end(); it != ie; ++it) { const char *Name = it->c_str(); - + // FIXME: Don't handle errors like this. if (Name[0] != '-' && Name[0] != '+') { - fprintf(stderr, "error: clang-cc: invalid target feature string: %s\n", + fprintf(stderr, "error: clang-cc: invalid target feature string: %s\n", Name); exit(1); } if (!Target->setFeatureEnabled(Features, Name + 1, (Name[0] == '+'))) { - fprintf(stderr, "error: clang-cc: invalid target feature name: %s\n", + fprintf(stderr, "error: clang-cc: invalid target feature name: %s\n", Name + 1); exit(1); } @@ -1442,7 +1479,7 @@ static void InitializeCompileOptions(CompileOptions &Opts, Opts.CPU = TargetCPU; Opts.Features.clear(); - for (llvm::StringMap<bool>::const_iterator it = Features.begin(), + for (llvm::StringMap<bool>::const_iterator it = Features.begin(), ie = Features.end(); it != ie; ++it) { // FIXME: If we are completely confident that we have the right // set, we only need to pass the minuses. @@ -1450,9 +1487,9 @@ static void InitializeCompileOptions(CompileOptions &Opts, Name += it->first(); Opts.Features.push_back(Name); } - + Opts.NoCommon = NoCommon | LangOpts.CPlusPlus; - + // Handle -ftime-report. Opts.TimePasses = TimeReport; @@ -1539,7 +1576,7 @@ clEnumValN(NAME, CMDFLAG, DESC), #include "clang/Frontend/Analyses.def" clEnumValEnd)); -static llvm::cl::opt<AnalysisStores> +static llvm::cl::opt<AnalysisStores> AnalysisStoreOpt("analyzer-store", llvm::cl::desc("Source Code Analysis - Abstract Memory Store Models"), llvm::cl::init(BasicStoreModel), @@ -1549,7 +1586,7 @@ clEnumValN(NAME##Model, CMDFLAG, DESC), #include "clang/Frontend/Analyses.def" clEnumValEnd)); -static llvm::cl::opt<AnalysisConstraints> +static llvm::cl::opt<AnalysisConstraints> AnalysisConstraintsOpt("analyzer-constraints", llvm::cl::desc("Source Code Analysis - Symbolic Constraint Engines"), llvm::cl::init(RangeConstraintsModel), @@ -1641,7 +1678,7 @@ class LoggingDiagnosticClient : public DiagnosticClient { llvm::OwningPtr<DiagnosticClient> Chain1; llvm::OwningPtr<DiagnosticClient> Chain2; public: - + LoggingDiagnosticClient(DiagnosticClient *Normal) { // Output diags both where requested... Chain1.reset(Normal); @@ -1655,12 +1692,12 @@ public: !NoDiagnosticsFixIt, MessageLength)); } - + virtual void setLangOptions(const LangOptions *LO) { Chain1->setLangOptions(LO); Chain2->setLangOptions(LO); } - + virtual bool IncludeInDiagnosticCounts() const { return Chain1->IncludeInDiagnosticCounts(); } @@ -1675,11 +1712,11 @@ public: static void SetUpBuildDumpLog(unsigned argc, char **argv, llvm::OwningPtr<DiagnosticClient> &DiagClient) { - + std::string ErrorInfo; - BuildLogFile = new llvm::raw_fd_ostream(DumpBuildInformation.c_str(), false, + BuildLogFile = new llvm::raw_fd_ostream(DumpBuildInformation.c_str(), ErrorInfo); - + if (!ErrorInfo.empty()) { llvm::errs() << "error opening -dump-build-information file '" << DumpBuildInformation << "', option ignored!\n"; @@ -1693,7 +1730,7 @@ static void SetUpBuildDumpLog(unsigned argc, char **argv, for (unsigned i = 0; i != argc; ++i) (*BuildLogFile) << argv[i] << ' '; (*BuildLogFile) << '\n'; - + // LoggingDiagnosticClient - Insert a new logging diagnostic client in between // the diagnostic producers and the normal receiver. DiagClient.reset(new LoggingDiagnosticClient(DiagClient.take())); @@ -1705,97 +1742,77 @@ static void SetUpBuildDumpLog(unsigned argc, char **argv, // Main driver //===----------------------------------------------------------------------===// -static llvm::raw_ostream* ComputeOutFile(const std::string& InFile, - const char* Extension, +static llvm::raw_ostream *ComputeOutFile(const std::string &InFile, + const char *Extension, bool Binary, llvm::sys::Path& OutPath) { - llvm::raw_ostream* Ret; - bool UseStdout = false; + llvm::raw_ostream *Ret; std::string OutFile; - if (OutputFile == "-" || (OutputFile.empty() && InFile == "-")) { - UseStdout = true; - } else if (!OutputFile.empty()) { + if (!OutputFile.empty()) OutFile = OutputFile; + else if (InFile == "-") { + OutFile = "-"; } else if (Extension) { llvm::sys::Path Path(InFile); Path.eraseSuffix(); Path.appendSuffix(Extension); - OutFile = Path.toString(); + OutFile = Path.str(); } else { - UseStdout = true; + OutFile = "-"; } - if (UseStdout) { - Ret = new llvm::raw_stdout_ostream(); - if (Binary) - llvm::sys::Program::ChangeStdoutToBinary(); - } else { - std::string Error; - Ret = new llvm::raw_fd_ostream(OutFile.c_str(), Binary, Error); - if (!Error.empty()) { - // FIXME: Don't fail this way. - llvm::cerr << "ERROR: " << Error << "\n"; - ::exit(1); - } - OutPath = OutFile; + std::string Error; + Ret = new llvm::raw_fd_ostream(OutFile.c_str(), Error, + (Binary ? llvm::raw_fd_ostream::F_Binary : 0)); + if (!Error.empty()) { + // FIXME: Don't fail this way. + llvm::errs() << "ERROR: " << Error << "\n"; + ::exit(1); } + if (OutFile != "-") + OutPath = OutFile; + return Ret; } -/// ProcessInputFile - Process a single input file with the specified state. -/// -static void ProcessInputFile(Preprocessor &PP, PreprocessorFactory &PPF, - const std::string &InFile, ProgActions PA, - const llvm::StringMap<bool> &Features, - llvm::LLVMContext& Context) { - llvm::OwningPtr<llvm::raw_ostream> OS; - llvm::OwningPtr<ASTConsumer> Consumer; - bool ClearSourceMgr = false; - FixItRewriter *FixItRewrite = 0; - bool CompleteTranslationUnit = true; - llvm::sys::Path OutPath; - +static ASTConsumer *CreateConsumerAction(Preprocessor &PP, + const std::string &InFile, + ProgActions PA, + llvm::OwningPtr<llvm::raw_ostream> &OS, + llvm::sys::Path &OutPath, + const llvm::StringMap<bool> &Features, + llvm::LLVMContext& Context) { switch (PA) { default: - fprintf(stderr, "Unexpected program action!\n"); - HadErrors = true; - return; + return 0; case ASTPrint: OS.reset(ComputeOutFile(InFile, 0, false, OutPath)); - Consumer.reset(CreateASTPrinter(OS.get())); - break; - + return CreateASTPrinter(OS.get()); + case ASTPrintXML: OS.reset(ComputeOutFile(InFile, "xml", false, OutPath)); - Consumer.reset(CreateASTPrinterXML(OS.get())); - break; + return CreateASTPrinterXML(OS.get()); case ASTDump: - Consumer.reset(CreateASTDumper()); - break; + return CreateASTDumper(); case ASTView: - Consumer.reset(CreateASTViewer()); - break; + return CreateASTViewer(); case PrintDeclContext: - Consumer.reset(CreateDeclContextPrinter()); - break; + return CreateDeclContextPrinter(); - case EmitHTML: - OS.reset(ComputeOutFile(InFile, 0, true, OutPath)); - Consumer.reset(CreateHTMLPrinter(OS.get(), PP.getDiagnostics(), &PP, &PPF)); - break; + case DumpRecordLayouts: + return CreateRecordLayoutDumper(); case InheritanceView: - Consumer.reset(CreateInheritanceViewer(InheritanceViewCls)); - break; + return CreateInheritanceViewer(InheritanceViewCls); case EmitAssembly: case EmitLLVM: - case EmitBC: + case EmitBC: case EmitLLVMOnly: { BackendAction Act; if (ProgAction == EmitAssembly) { @@ -1813,36 +1830,71 @@ static void ProcessInputFile(Preprocessor &PP, PreprocessorFactory &PPF, CompileOptions Opts; InitializeCompileOptions(Opts, PP.getLangOptions(), Features); - Consumer.reset(CreateBackendConsumer(Act, PP.getDiagnostics(), - PP.getLangOptions(), Opts, InFile, - OS.get(), Context)); - break; + return CreateBackendConsumer(Act, PP.getDiagnostics(), PP.getLangOptions(), + Opts, InFile, OS.get(), Context); } - case GeneratePCH: - OS.reset(ComputeOutFile(InFile, 0, true, OutPath)); - Consumer.reset(CreatePCHGenerator(PP, OS.get())); - CompleteTranslationUnit = false; - break; - case RewriteObjC: OS.reset(ComputeOutFile(InFile, "cpp", true, OutPath)); - Consumer.reset(CreateObjCRewriter(InFile, OS.get(), PP.getDiagnostics(), - PP.getLangOptions(), - SilenceRewriteMacroWarning)); - break; + return CreateObjCRewriter(InFile, OS.get(), PP.getDiagnostics(), + PP.getLangOptions(), SilenceRewriteMacroWarning); case RewriteBlocks: - Consumer.reset(CreateBlockRewriter(InFile, PP.getDiagnostics(), - PP.getLangOptions())); + return CreateBlockRewriter(InFile, PP.getDiagnostics(), + PP.getLangOptions()); + } +} + +/// ProcessInputFile - Process a single input file with the specified state. +/// +static void ProcessInputFile(Preprocessor &PP, PreprocessorFactory &PPF, + const std::string &InFile, ProgActions PA, + const llvm::StringMap<bool> &Features, + llvm::LLVMContext& Context) { + llvm::OwningPtr<llvm::raw_ostream> OS; + llvm::OwningPtr<ASTConsumer> Consumer; + bool ClearSourceMgr = false; + FixItRewriter *FixItRewrite = 0; + bool CompleteTranslationUnit = true; + llvm::sys::Path OutPath; + + switch (PA) { + default: + Consumer.reset(CreateConsumerAction(PP, InFile, PA, OS, OutPath, + Features, Context)); + + if (!Consumer.get()) { + fprintf(stderr, "Unexpected program action!\n"); + HadErrors = true; + return; + } + + break;; + + case EmitHTML: + OS.reset(ComputeOutFile(InFile, 0, true, OutPath)); + Consumer.reset(CreateHTMLPrinter(OS.get(), PP.getDiagnostics(), &PP, &PPF)); break; - case RunAnalysis: { + case RunAnalysis: Consumer.reset(CreateAnalysisConsumer(PP.getDiagnostics(), &PP, &PPF, PP.getLangOptions(), OutputFile, ReadAnalyzerOptions())); break; - } + + case GeneratePCH: + if (RelocatablePCH.getValue() && !isysroot.getNumOccurrences()) { + PP.Diag(SourceLocation(), diag::err_relocatable_without_without_isysroot); + RelocatablePCH.setValue(false); + } + + OS.reset(ComputeOutFile(InFile, 0, true, OutPath)); + if (RelocatablePCH.getValue()) + Consumer.reset(CreatePCHGenerator(PP, OS.get(), isysroot.c_str())); + else + Consumer.reset(CreatePCHGenerator(PP, OS.get())); + CompleteTranslationUnit = false; + break; case DumpRawTokens: { llvm::TimeRegion Timer(ClangFrontendTimer); @@ -1876,28 +1928,28 @@ static void ProcessInputFile(Preprocessor &PP, PreprocessorFactory &PPF, } case RunPreprocessorOnly: break; - + case GeneratePTH: { llvm::TimeRegion Timer(ClangFrontendTimer); if (OutputFile.empty() || OutputFile == "-") { // FIXME: Don't fail this way. // FIXME: Verify that we can actually seek in the given file. - llvm::cerr << "ERROR: PTH requires an seekable file for output!\n"; + llvm::errs() << "ERROR: PTH requires an seekable file for output!\n"; ::exit(1); } OS.reset(ComputeOutFile(InFile, 0, true, OutPath)); CacheTokens(PP, static_cast<llvm::raw_fd_ostream*>(OS.get())); ClearSourceMgr = true; break; - } + } case PrintPreprocessedInput: OS.reset(ComputeOutFile(InFile, 0, true, OutPath)); break; - + case ParseNoop: break; - + case ParsePrintCallbacks: { llvm::TimeRegion Timer(ClangFrontendTimer); OS.reset(ComputeOutFile(InFile, 0, true, OutPath)); @@ -1911,13 +1963,13 @@ static void ProcessInputFile(Preprocessor &PP, PreprocessorFactory &PPF, Consumer.reset(new ASTConsumer()); break; } - + case RewriteMacros: OS.reset(ComputeOutFile(InFile, 0, true, OutPath)); RewriteMacrosInInput(PP, OS.get()); ClearSourceMgr = true; break; - + case RewriteTest: OS.reset(ComputeOutFile(InFile, 0, true, OutPath)); DoRewriteTest(PP, OS.get()); @@ -1943,7 +1995,7 @@ static void ProcessInputFile(Preprocessor &PP, PreprocessorFactory &PPF, PP.getLangOptions()); bool AddedFixitLocation = false; - for (unsigned Idx = 0, Last = FixItAtLocations.size(); + for (unsigned Idx = 0, Last = FixItAtLocations.size(); Idx != Last; ++Idx) { RequestedSourceLocation Requested; if (ResolveParsedLocation(FixItAtLocations[Idx], @@ -1973,13 +2025,19 @@ static void ProcessInputFile(Preprocessor &PP, PreprocessorFactory &PPF, PP.getBuiltinInfo(), /* FreeMemory = */ !DisableFree, /* size_reserve = */0)); - + llvm::OwningPtr<PCHReader> Reader; llvm::OwningPtr<ExternalASTSource> Source; - + if (!ImplicitIncludePCH.empty()) { - Reader.reset(new PCHReader(PP, ContextOwner.get())); - + // If the user specified -isysroot, it will be used for relocatable PCH + // files. + const char *isysrootPCH = 0; + if (isysroot.getNumOccurrences() != 0) + isysrootPCH = isysroot.c_str(); + + Reader.reset(new PCHReader(PP, ContextOwner.get(), isysrootPCH)); + // The user has asked us to include a precompiled header. Load // the precompiled header into the AST context. switch (Reader->ReadPCH(ImplicitIncludePCH)) { @@ -2026,12 +2084,34 @@ static void ProcessInputFile(Preprocessor &PP, PreprocessorFactory &PPF, if (InitializeSourceManager(PP, InFile)) return; } - - + // If we have an ASTConsumer, run the parser with it. - if (Consumer) - ParseAST(PP, Consumer.get(), *ContextOwner.get(), Stats, - CompleteTranslationUnit); + if (Consumer) { + CodeCompleteConsumer *(*CreateCodeCompleter)(Sema &, void *) = 0; + void *CreateCodeCompleterData = 0; + + if (!CodeCompletionAt.FileName.empty()) { + // Tell the source manager to chop off the given file at a specific + // line and column. + if (const FileEntry *Entry + = PP.getFileManager().getFile(CodeCompletionAt.FileName)) { + // Truncate the named file at the given line/column. + PP.getSourceManager().truncateFileAt(Entry, CodeCompletionAt.Line, + CodeCompletionAt.Column); + + // Set up the creation routine for code-completion. + CreateCodeCompleter = BuildPrintingCodeCompleter; + } else { + PP.getDiagnostics().Report(FullSourceLoc(), + diag::err_fe_invalid_code_complete_file) + << CodeCompletionAt.FileName; + } + } + + ParseAST(PP, Consumer.get(), *ContextOwner.get(), Stats, + CompleteTranslationUnit, + CreateCodeCompleter, CreateCodeCompleterData); + } if (PA == RunPreprocessorOnly) { // Just lex as fast as we can, no output. llvm::TimeRegion Timer(ClangFrontendTimer); @@ -2056,10 +2136,17 @@ static void ProcessInputFile(Preprocessor &PP, PreprocessorFactory &PPF, DisableLineMarkers, DumpDefines); ClearSourceMgr = true; } - + if (FixItRewrite) FixItRewrite->WriteFixedFile(InFile, OutputFile); - + + // Disable the consumer prior to the context, the consumer may perform actions + // in its destructor which require the context. + if (DisableFree) + Consumer.take(); + else + Consumer.reset(); + // If in -disable-free mode, don't deallocate ASTContext. if (DisableFree) ContextOwner.take(); @@ -2079,16 +2166,71 @@ static void ProcessInputFile(Preprocessor &PP, PreprocessorFactory &PPF, fprintf(stderr, "\n"); } - // For a multi-file compilation, some things are ok with nuking the source + // For a multi-file compilation, some things are ok with nuking the source // manager tables, other require stable fileid/macroid's across multiple // files. if (ClearSourceMgr) PP.getSourceManager().clearIDTables(); - if (DisableFree) + // Always delete the output stream because we don't want to leak file + // handles. Also, we don't want to try to erase an open file. + OS.reset(); + + if ((HadErrors || (PP.getDiagnostics().getNumErrors() != 0)) && + !OutPath.isEmpty()) { + // If we had errors, try to erase the output file. + OutPath.eraseFromDisk(); + } +} + +/// ProcessInputFile - Process a single AST input file with the specified state. +/// +static void ProcessASTInputFile(const std::string &InFile, ProgActions PA, + const llvm::StringMap<bool> &Features, + Diagnostic &Diags, FileManager &FileMgr, + llvm::LLVMContext& Context) { + std::string Error; + llvm::OwningPtr<ASTUnit> AST(ASTUnit::LoadFromPCHFile(InFile, Diags, FileMgr, + &Error)); + if (!AST) { + Diags.Report(FullSourceLoc(), diag::err_fe_invalid_ast_file) << Error; + return; + } + + Preprocessor &PP = AST->getPreprocessor(); + + llvm::OwningPtr<llvm::raw_ostream> OS; + llvm::sys::Path OutPath; + llvm::OwningPtr<ASTConsumer> Consumer(CreateConsumerAction(PP, InFile, PA, OS, + OutPath, Features, + Context)); + + if (!Consumer.get()) { + Diags.Report(FullSourceLoc(), diag::err_fe_invalid_ast_action); + return; + } + + // Set the main file ID to an empty file. + // + // FIXME: We probably shouldn't need this, but for now this is the simplest + // way to reuse the logic in ParseAST. + const char *EmptyStr = ""; + llvm::MemoryBuffer *SB = + llvm::MemoryBuffer::getMemBuffer(EmptyStr, EmptyStr, "<dummy input>"); + AST->getSourceManager().createMainFileIDForMemBuffer(SB); + + // Stream the input AST to the consumer. + ParseAST(PP, Consumer.get(), AST->getASTContext(), Stats); + + // Release the consumer and the AST, in that order since the consumer may + // perform actions in its destructor which require the context. + if (DisableFree) { Consumer.take(); - else + AST.take(); + } else { Consumer.reset(); + AST.reset(); + } // Always delete the output stream because we don't want to leak file // handles. Also, we don't want to try to erase an open file. @@ -2104,23 +2246,35 @@ static void ProcessInputFile(Preprocessor &PP, PreprocessorFactory &PPF, static llvm::cl::list<std::string> InputFilenames(llvm::cl::Positional, llvm::cl::desc("<input files>")); +static void LLVMErrorHandler(void *UserData, const std::string &Message) { + Diagnostic &Diags = *static_cast<Diagnostic*>(UserData); + + Diags.Report(FullSourceLoc(), diag::err_fe_error_backend) << Message; + + // We cannot recover from llvm errors. + exit(1); +} + int main(int argc, char **argv) { llvm::sys::PrintStackTraceOnErrorSignal(); llvm::PrettyStackTraceProgram X(argc, argv); - llvm::LLVMContext Context; - llvm::cl::ParseCommandLineOptions(argc, argv, - "LLVM 'Clang' Compiler: http://clang.llvm.org\n"); - + llvm::LLVMContext &Context = llvm::getGlobalContext(); + + // Initialize targets first, so that --version shows registered targets. llvm::InitializeAllTargets(); llvm::InitializeAllAsmPrinters(); - + + llvm::cl::ParseCommandLineOptions(argc, argv, + "LLVM 'Clang' Compiler: http://clang.llvm.org\n"); + if (TimeReport) ClangFrontendTimer = new llvm::Timer("Clang front-end time"); - + if (Verbose) - fprintf(stderr, "clang-cc version 1.0 based upon " PACKAGE_STRING - " hosted on " LLVM_HOSTTRIPLE "\n"); - + llvm::errs() << "clang-cc version " CLANG_VERSION_STRING + << " based upon " << PACKAGE_STRING + << " hosted on " << llvm::sys::getHostTriple() << "\n"; + // If no input was specified, read from stdin. if (InputFilenames.empty()) InputFilenames.push_back("-"); @@ -2164,17 +2318,17 @@ int main(int argc, char **argv) { } else { DiagClient.reset(CreateHTMLDiagnosticClient(HTMLDiag)); } - + if (!DumpBuildInformation.empty()) { if (!HTMLDiag.empty()) { fprintf(stderr, "-dump-build-information and -html-diags don't work together\n"); return 1; } - + SetUpBuildDumpLog(argc, argv, DiagClient); } - + // Configure our handling of diagnostics. Diagnostic Diags(DiagClient.get()); @@ -2182,30 +2336,45 @@ int main(int argc, char **argv) { OptNoWarnings)) 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*>(&Diags)); + // -I- is a deprecated GCC feature, scan for it and reject it. for (unsigned i = 0, e = I_dirs.size(); i != e; ++i) { if (I_dirs[i] == "-") { - Diags.Report(FullSourceLoc(), diag::err_pp_I_dash_not_supported); + Diags.Report(FullSourceLoc(), diag::err_pp_I_dash_not_supported); I_dirs.erase(I_dirs.begin()+i); --i; } } // Get information about the target being compiled for. - std::string Triple = CreateTargetTriple(); - llvm::OwningPtr<TargetInfo> Target(TargetInfo::CreateTargetInfo(Triple)); - + llvm::Triple Triple = CreateTargetTriple(); + llvm::OwningPtr<TargetInfo> + Target(TargetInfo::CreateTargetInfo(Triple.getTriple())); + if (Target == 0) { - Diags.Report(FullSourceLoc(), diag::err_fe_unknown_triple) - << Triple.c_str(); + Diags.Report(FullSourceLoc(), diag::err_fe_unknown_triple) + << Triple.getTriple().c_str(); return 1; } - + + // Set the target ABI if specified. + if (!TargetABI.empty()) { + if (!Target->setABI(TargetABI)) { + Diags.Report(FullSourceLoc(), diag::err_fe_unknown_target_abi) + << TargetABI; + return 1; + } + } + if (!InheritanceViewCls.empty()) // C++ visualization? ProgAction = InheritanceView; - + llvm::OwningPtr<SourceManager> SourceMgr; - + // Create a file manager object to provide access to and cache the filesystem. FileManager FileMgr; @@ -2215,35 +2384,41 @@ int main(int argc, char **argv) { for (unsigned i = 0, e = InputFilenames.size(); i != e; ++i) { const std::string &InFile = InputFilenames[i]; - + + LangKind LK = GetLanguage(InFile); + // AST inputs are handled specially. + if (LK == langkind_ast) { + ProcessASTInputFile(InFile, ProgAction, Features, + Diags, FileMgr, Context); + continue; + } + /// Create a SourceManager object. This tracks and owns all the file /// buffers allocated to a translation unit. if (!SourceMgr) SourceMgr.reset(new SourceManager()); else SourceMgr->clearIDTables(); - + // Initialize language options, inferring file types from input filenames. LangOptions LangInfo; DiagClient->setLangOptions(&LangInfo); - - InitializeBaseLanguage(); - LangKind LK = GetLanguage(InFile); + InitializeLangOptions(LangInfo, LK); InitializeLanguageStandard(LangInfo, LK, Target.get(), Features); - + // Process the -I options and set them in the HeaderInfo. HeaderSearch HeaderInfo(FileMgr); - - - InitializeIncludePaths(argv[0], HeaderInfo, FileMgr, LangInfo); - + + + InitializeIncludePaths(argv[0], HeaderInfo, FileMgr, LangInfo, Triple); + // Set up the preprocessor with these options. DriverPreprocessorFactory PPFactory(Diags, LangInfo, *Target, *SourceMgr.get(), HeaderInfo); - + llvm::OwningPtr<Preprocessor> PP(PPFactory.CreatePreprocessor()); - + if (!PP) continue; @@ -2252,16 +2427,16 @@ int main(int argc, char **argv) { llvm::raw_ostream *DependencyOS; if (DependencyTargets.empty()) { // FIXME: Use a proper diagnostic - llvm::cerr << "-dependency-file requires at least one -MT option\n"; + llvm::errs() << "-dependency-file requires at least one -MT option\n"; HadErrors = true; continue; } std::string ErrStr; DependencyOS = - new llvm::raw_fd_ostream(DependencyFile.c_str(), false, ErrStr); + new llvm::raw_fd_ostream(DependencyFile.c_str(), ErrStr); if (!ErrStr.empty()) { // FIXME: Use a proper diagnostic - llvm::cerr << "unable to open dependency file: " + ErrStr; + llvm::errs() << "unable to open dependency file: " + ErrStr; HadErrors = true; continue; } @@ -2274,7 +2449,7 @@ int main(int argc, char **argv) { if (ImplicitIncludePCH.empty()) { if (InitializeSourceManager(*PP.get(), InFile)) continue; - + // Initialize builtin info. PP->getBuiltinInfo().InitializeBuiltins(PP->getIdentifierTable(), PP->getLangOptions().NoBuiltin); @@ -2285,7 +2460,7 @@ int main(int argc, char **argv) { // Process the source file. ProcessInputFile(*PP, PPFactory, InFile, ProgAction, Features, Context); - + HeaderInfo.ClearFileInfo(); DiagClient->setLangOptions(0); } @@ -2294,7 +2469,7 @@ int main(int argc, char **argv) { if (unsigned NumDiagnostics = Diags.getNumDiagnostics()) fprintf(stderr, "%d diagnostic%s generated.\n", NumDiagnostics, (NumDiagnostics == 1 ? "" : "s")); - + if (Stats) { FileMgr.PrintStats(); fprintf(stderr, "\n"); @@ -2302,11 +2477,11 @@ int main(int argc, char **argv) { delete ClangFrontendTimer; delete BuildLogFile; - + // If verifying diagnostics and we reached here, all is well. if (VerifyDiagnostics) return 0; - + // Managed static deconstruction. Useful for making things like // -time-passes usable. llvm::llvm_shutdown(); diff --git a/tools/driver/CMakeLists.txt b/tools/driver/CMakeLists.txt index f170aa2fa38c..1ad04c8dfde4 100644 --- a/tools/driver/CMakeLists.txt +++ b/tools/driver/CMakeLists.txt @@ -13,3 +13,5 @@ add_clang_executable(clang add_dependencies(clang clang-cc) +install(TARGETS clang + RUNTIME DESTINATION bin) diff --git a/tools/driver/Makefile b/tools/driver/Makefile index 8e9c291cc80c..4b2fb4423445 100644 --- a/tools/driver/Makefile +++ b/tools/driver/Makefile @@ -21,3 +21,9 @@ LINK_COMPONENTS := system support bitreader bitwriter USEDLIBS = clangDriver.a clangBasic.a include $(LEVEL)/Makefile.common + +# Translate make variable to define when building a "production" clang. +ifdef CLANG_IS_PRODUCTION +CPP.Defines += -DCLANG_IS_PRODUCTION +endif + diff --git a/tools/driver/driver.cpp b/tools/driver/driver.cpp index 323283e653b4..9d204ce6c0c2 100644 --- a/tools/driver/driver.cpp +++ b/tools/driver/driver.cpp @@ -34,7 +34,7 @@ class DriverDiagnosticPrinter : public DiagnosticClient { llvm::raw_ostream &OS; public: - DriverDiagnosticPrinter(const std::string _ProgName, + DriverDiagnosticPrinter(const std::string _ProgName, llvm::raw_ostream &_OS) : ProgName(_ProgName), OS(_OS) {} @@ -54,7 +54,7 @@ void DriverDiagnosticPrinter::HandleDiagnostic(Diagnostic::Level Level, case Diagnostic::Error: OS << "error: "; break; case Diagnostic::Fatal: OS << "fatal error: "; break; } - + llvm::SmallString<100> OutStr; Info.FormatDiagnostic(OutStr); OS.write(OutStr.begin(), OutStr.size()); @@ -68,7 +68,7 @@ llvm::sys::Path GetExecutablePath(const char *Argv0) { return llvm::sys::Path::GetMainExecutable(Argv0, P); } -static const char *SaveStringInSet(std::set<std::string> &SavedStrings, +static const char *SaveStringInSet(std::set<std::string> &SavedStrings, const std::string &S) { return SavedStrings.insert(S).first->c_str(); } @@ -79,6 +79,8 @@ static const char *SaveStringInSet(std::set<std::string> &SavedStrings, /// they are applied in order to the input argument lists. Edits /// should be one of the following forms: /// +/// '#': Silence information about the changes to the command line arguments. +/// /// '^': Add FOO as a new argument at the beginning of the command line. /// /// '+': Add FOO as a new argument at the end of the command line. @@ -93,61 +95,74 @@ static const char *SaveStringInSet(std::set<std::string> &SavedStrings, /// /// 'Ox': Removes all flags matching 'O' or 'O[sz0-9]' and adds 'Ox' /// at the end of the command line. -void ApplyOneQAOverride(std::vector<const char*> &Args, +/// +/// \param OS - The stream to write edit information to. +/// \param Args - The vector of command line arguments. +/// \param Edit - The override command to perform. +/// \param SavedStrings - Set to use for storing string representations. +void ApplyOneQAOverride(llvm::raw_ostream &OS, + std::vector<const char*> &Args, const std::string &Edit, std::set<std::string> &SavedStrings) { // This does not need to be efficient. - if (Edit[0] == '^') { - const char *Str = - SaveStringInSet(SavedStrings, Edit.substr(1, std::string::npos)); - llvm::errs() << "### Adding argument " << Str << " at beginning\n"; - Args.insert(Args.begin() + 1, Str); - } else if (Edit[0] == '+') { - const char *Str = - SaveStringInSet(SavedStrings, Edit.substr(1, std::string::npos)); - llvm::errs() << "### Adding argument " << Str << " at end\n"; - Args.push_back(Str); - } else if (Edit[0] == 'x' || Edit[0] == 'X') { - std::string Option = Edit.substr(1, std::string::npos); - for (unsigned i = 1; i < Args.size();) { - if (Option == Args[i]) { - llvm::errs() << "### Deleting argument " << Args[i] << '\n'; - Args.erase(Args.begin() + i); - if (Edit[0] == 'X') { - if (i < Args.size()) { - llvm::errs() << "### Deleting argument " << Args[i] << '\n'; - Args.erase(Args.begin() + i); - } else - llvm::errs() << "### Invalid X edit, end of command line!\n"; - } - } else - ++i; - } - } else if (Edit[0] == 'O') { - for (unsigned i = 1; i < Args.size();) { - const char *A = Args[i]; - if (A[0] == '-' && A[1] == 'O' && - (A[2] == '\0' || - (A[3] == '\0' && (A[2] == 's' || A[2] == 'z' || - ('0' <= A[2] && A[2] <= '9'))))) { - llvm::errs() << "### Deleting argument " << Args[i] << '\n'; - Args.erase(Args.begin() + i); - } else - ++i; - } - llvm::errs() << "### Adding argument " << Edit << " at end\n"; - Args.push_back(SaveStringInSet(SavedStrings, '-' + Edit)); - } else { - llvm::errs() << "### Unrecognized edit: " << Edit << "\n"; - } + if (Edit[0] == '^') { + const char *Str = + SaveStringInSet(SavedStrings, Edit.substr(1, std::string::npos)); + OS << "### Adding argument " << Str << " at beginning\n"; + Args.insert(Args.begin() + 1, Str); + } else if (Edit[0] == '+') { + const char *Str = + SaveStringInSet(SavedStrings, Edit.substr(1, std::string::npos)); + OS << "### Adding argument " << Str << " at end\n"; + Args.push_back(Str); + } else if (Edit[0] == 'x' || Edit[0] == 'X') { + std::string Option = Edit.substr(1, std::string::npos); + for (unsigned i = 1; i < Args.size();) { + if (Option == Args[i]) { + OS << "### Deleting argument " << Args[i] << '\n'; + Args.erase(Args.begin() + i); + if (Edit[0] == 'X') { + if (i < Args.size()) { + OS << "### Deleting argument " << Args[i] << '\n'; + Args.erase(Args.begin() + i); + } else + OS << "### Invalid X edit, end of command line!\n"; + } + } else + ++i; + } + } else if (Edit[0] == 'O') { + for (unsigned i = 1; i < Args.size();) { + const char *A = Args[i]; + if (A[0] == '-' && A[1] == 'O' && + (A[2] == '\0' || + (A[3] == '\0' && (A[2] == 's' || A[2] == 'z' || + ('0' <= A[2] && A[2] <= '9'))))) { + OS << "### Deleting argument " << Args[i] << '\n'; + Args.erase(Args.begin() + i); + } else + ++i; + } + OS << "### Adding argument " << Edit << " at end\n"; + Args.push_back(SaveStringInSet(SavedStrings, '-' + Edit)); + } else { + OS << "### Unrecognized edit: " << Edit << "\n"; + } } /// ApplyQAOverride - Apply a comma separate list of edits to the /// input argument lists. See ApplyOneQAOverride. void ApplyQAOverride(std::vector<const char*> &Args, const char *OverrideStr, std::set<std::string> &SavedStrings) { - llvm::errs() << "### QA_OVERRIDE_GCC3_OPTIONS: " << OverrideStr << "\n"; + llvm::raw_ostream *OS = &llvm::errs(); + + if (OverrideStr[0] == '#') { + ++OverrideStr; + OS = &llvm::nulls(); + } + + *OS << "### QA_OVERRIDE_GCC3_OPTIONS: " << OverrideStr << "\n"; // This does not need to be efficient. @@ -157,7 +172,7 @@ void ApplyQAOverride(std::vector<const char*> &Args, const char *OverrideStr, if (!End) End = S + strlen(S); if (End != S) - ApplyOneQAOverride(Args, std::string(S, End), SavedStrings); + ApplyOneQAOverride(*OS, Args, std::string(S, End), SavedStrings); S = End; if (*S != '\0') ++S; @@ -173,9 +188,14 @@ int main(int argc, const char **argv) { Diagnostic Diags(&DiagClient); +#ifdef CLANG_IS_PRODUCTION + bool IsProduction = true; +#else + bool IsProduction = false; +#endif Driver TheDriver(Path.getBasename().c_str(), Path.getDirname().c_str(), llvm::sys::getHostTriple().c_str(), - "a.out", Diags); + "a.out", IsProduction, Diags); llvm::OwningPtr<Compilation> C; @@ -188,7 +208,7 @@ int main(int argc, const char **argv) { ApplyQAOverride(StringPointers, OverrideStr, SavedStrings); - C.reset(TheDriver.BuildCompilation(StringPointers.size(), + C.reset(TheDriver.BuildCompilation(StringPointers.size(), &StringPointers[0])); } else if (const char *Cur = ::getenv("CCC_ADD_ARGS")) { std::vector<const char*> StringPointers; @@ -198,7 +218,7 @@ int main(int argc, const char **argv) { for (;;) { const char *Next = strchr(Cur, ','); - + if (Next) { StringPointers.push_back(SaveStringInSet(SavedStrings, std::string(Cur, Next))); @@ -212,7 +232,7 @@ int main(int argc, const char **argv) { StringPointers.insert(StringPointers.end(), argv + 1, argv + argc); - C.reset(TheDriver.BuildCompilation(StringPointers.size(), + C.reset(TheDriver.BuildCompilation(StringPointers.size(), &StringPointers[0])); } else C.reset(TheDriver.BuildCompilation(argc, argv)); diff --git a/tools/index-test/CMakeLists.txt b/tools/index-test/CMakeLists.txt index 09f4fc12c1dd..9c9656a1130e 100644 --- a/tools/index-test/CMakeLists.txt +++ b/tools/index-test/CMakeLists.txt @@ -1,6 +1,7 @@ set(LLVM_NO_RTTI 1) set( LLVM_USED_LIBS + clangIndex clangFrontend clangSema clangAST @@ -10,6 +11,7 @@ set( LLVM_USED_LIBS set( LLVM_LINK_COMPONENTS bitreader + mc ) add_clang_executable(index-test diff --git a/tools/index-test/Makefile b/tools/index-test/Makefile index 4fbde291980a..76602e1d278d 100644 --- a/tools/index-test/Makefile +++ b/tools/index-test/Makefile @@ -11,13 +11,14 @@ LEVEL = ../../../.. TOOLNAME = index-test 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 -USEDLIBS = clangFrontend.a clangSema.a clangAST.a clangLex.a clangBasic.a +LINK_COMPONENTS := bitreader mc +USEDLIBS = clangIndex.a clangFrontend.a clangSema.a clangAST.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 552b7b01498a..103874c77d7d 100644 --- a/tools/index-test/index-test.cpp +++ b/tools/index-test/index-test.cpp @@ -17,38 +17,191 @@ // //===----------------------------------------------------------------------===// // -// -point-at [file:column:line] +// -point-at [file:line:column] // Point at a declaration/statement/expression. If no other operation is // specified, prints some info about it. // +// -print-refs +// Print ASTLocations that reference the -point-at node +// +// -print-defs +// Print ASTLocations that define the -point-at node +// +// -print-decls +// Print ASTLocations that declare the -point-at node +// //===----------------------------------------------------------------------===// +#include "clang/Index/Program.h" +#include "clang/Index/Indexer.h" +#include "clang/Index/Entity.h" +#include "clang/Index/TranslationUnit.h" +#include "clang/Index/ASTLocation.h" +#include "clang/Index/DeclReferenceMap.h" +#include "clang/Index/SelectorMap.h" +#include "clang/Index/Handlers.h" +#include "clang/Index/Analyzer.h" +#include "clang/Index/Utils.h" #include "clang/Frontend/ASTUnit.h" -#include "clang/Frontend/Utils.h" #include "clang/Frontend/CommandLineSourceLoc.h" -#include "clang/AST/Decl.h" -#include "clang/AST/Stmt.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/ExprObjC.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/SourceManager.h" -#include "llvm/ADT/STLExtras.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/raw_ostream.h" #include "llvm/System/Signals.h" using namespace clang; +using namespace idx; + +class TUnit : public TranslationUnit { +public: + TUnit(ASTUnit *ast, const std::string &filename) + : AST(ast), Filename(filename), + DeclRefMap(ast->getASTContext()), + SelMap(ast->getASTContext()) { } + virtual ASTContext &getASTContext() { return AST->getASTContext(); } + virtual DeclReferenceMap &getDeclReferenceMap() { return DeclRefMap; } + virtual SelectorMap &getSelectorMap() { return SelMap; } + + llvm::OwningPtr<ASTUnit> AST; + std::string Filename; + DeclReferenceMap DeclRefMap; + SelectorMap SelMap; +}; static llvm::cl::list<ParsedSourceLocation> PointAtLocation("point-at", llvm::cl::Optional, llvm::cl::value_desc("source-location"), llvm::cl::desc("Point at the given source location of the first AST file")); +enum ProgActions { + PrintPoint, // Just print the point-at node + PrintRefs, // Print references of the point-at node + PrintDefs, // Print definitions of the point-at node + PrintDecls // Print declarations of the point-at node +}; + +static llvm::cl::opt<ProgActions> +ProgAction( + llvm::cl::desc("Choose action to perform on the pointed-at AST node:"), + llvm::cl::ZeroOrMore, + llvm::cl::init(PrintPoint), + llvm::cl::values( + clEnumValN(PrintRefs, "print-refs", + "Print references"), + clEnumValN(PrintDefs, "print-defs", + "Print definitions"), + clEnumValN(PrintDecls, "print-decls", + "Print declarations"), + clEnumValEnd)); + static llvm::cl::opt<bool> DisableFree("disable-free", llvm::cl::desc("Disable freeing of memory on exit"), llvm::cl::init(false)); +static bool HadErrors = false; + +static void ProcessObjCMessage(ObjCMessageExpr *Msg, Indexer &Idxer) { + llvm::raw_ostream &OS = llvm::outs(); + typedef Storing<TULocationHandler> ResultsTy; + ResultsTy Results; + + Analyzer Analyz(Idxer.getProgram(), Idxer); + + switch (ProgAction) { + default: assert(0); + case PrintRefs: + llvm::errs() << "Error: Cannot -print-refs on a ObjC message expression\n"; + HadErrors = true; + return; + + case PrintDecls: { + Analyz.FindObjCMethods(Msg, Results); + for (ResultsTy::iterator + I = Results.begin(), E = Results.end(); I != E; ++I) + I->print(OS); + break; + } + + case PrintDefs: { + Analyz.FindObjCMethods(Msg, Results); + for (ResultsTy::iterator + I = Results.begin(), E = Results.end(); I != E; ++I) { + const ObjCMethodDecl *D = cast<ObjCMethodDecl>(I->AsDecl()); + if (D->isThisDeclarationADefinition()) + I->print(OS); + } + break; + } + + } +} + +static void ProcessASTLocation(ASTLocation ASTLoc, Indexer &Idxer) { + assert(ASTLoc.isValid()); + + if (ObjCMessageExpr *Msg = + dyn_cast_or_null<ObjCMessageExpr>(ASTLoc.dyn_AsStmt())) + return ProcessObjCMessage(Msg, Idxer); + + Decl *D = ASTLoc.getReferencedDecl(); + if (D == 0) { + llvm::errs() << "Error: Couldn't get referenced Decl for the ASTLocation\n"; + HadErrors = true; + return; + } + + llvm::raw_ostream &OS = llvm::outs(); + typedef Storing<TULocationHandler> ResultsTy; + ResultsTy Results; + + Analyzer Analyz(Idxer.getProgram(), Idxer); + + switch (ProgAction) { + default: assert(0); + case PrintRefs: { + Analyz.FindReferences(D, Results); + for (ResultsTy::iterator + I = Results.begin(), E = Results.end(); I != E; ++I) + I->print(OS); + break; + } + + case PrintDecls: { + Analyz.FindDeclarations(D, Results); + for (ResultsTy::iterator + I = Results.begin(), E = Results.end(); I != E; ++I) + I->print(OS); + break; + } + + case PrintDefs: { + Analyz.FindDeclarations(D, Results); + for (ResultsTy::iterator + I = Results.begin(), E = Results.end(); I != E; ++I) { + const Decl *D = I->AsDecl(); + bool isDef = false; + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) + isDef = FD->isThisDeclarationADefinition(); + else if (const VarDecl *VD = dyn_cast<VarDecl>(D)) + isDef = VD->getInit() != 0; + else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) + isDef = MD->isThisDeclarationADefinition(); + + if (isDef) + I->print(OS); + } + break; + } + + } +} + static llvm::cl::list<std::string> InputFilenames(llvm::cl::Positional, llvm::cl::desc("<input AST files>")); @@ -57,97 +210,99 @@ int main(int argc, char **argv) { llvm::PrettyStackTraceProgram X(argc, argv); llvm::cl::ParseCommandLineOptions(argc, argv, "LLVM 'Clang' Indexing Test Bed: http://clang.llvm.org\n"); - - FileManager FileMgr; - + + Program Prog; + Indexer Idxer(Prog); + llvm::SmallVector<TUnit*, 4> TUnits; + // If no input was specified, read from stdin. if (InputFilenames.empty()) InputFilenames.push_back("-"); - // FIXME: Only the first AST file is used for now. + for (unsigned i = 0, e = InputFilenames.size(); i != e; ++i) { + const std::string &InFile = InputFilenames[i]; - const std::string &InFile = InputFilenames[0]; - - std::string ErrMsg; - llvm::OwningPtr<ASTUnit> AST; + std::string ErrMsg; + llvm::OwningPtr<ASTUnit> AST; - AST.reset(ASTUnit::LoadFromPCHFile(InFile, FileMgr, &ErrMsg)); - if (!AST) { - llvm::errs() << "[" << InFile << "] Error: " << ErrMsg << '\n'; - return 1; + AST.reset(ASTUnit::LoadFromPCHFile(InFile, Idxer.getDiagnostics(), + Idxer.getFileManager(), &ErrMsg)); + if (!AST) { + llvm::errs() << "[" << InFile << "] Error: " << ErrMsg << '\n'; + return 1; + } + + TUnit *TU = new TUnit(AST.take(), InFile); + TUnits.push_back(TU); + + Idxer.IndexAST(TU); } - struct ASTPoint { - Decl *D; - Stmt *Node; - ASTPoint() : D(0), Node(0) {} - }; - - ASTPoint Point; + ASTLocation ASTLoc; + const std::string &FirstFile = TUnits[0]->Filename; + ASTUnit *FirstAST = TUnits[0]->AST.get(); if (!PointAtLocation.empty()) { const std::string &Filename = PointAtLocation[0].FileName; - const FileEntry *File = FileMgr.getFile(Filename); + const FileEntry *File = Idxer.getFileManager().getFile(Filename); + if (File == 0) { + llvm::errs() << "File '" << Filename << "' does not exist\n"; + return 1; + } // Safety check. Using an out-of-date AST file will only lead to crashes // or incorrect results. // FIXME: Check all the source files that make up the AST file. - const FileEntry *ASTFile = FileMgr.getFile(InFile); + const FileEntry *ASTFile = Idxer.getFileManager().getFile(FirstFile); if (File->getModificationTime() > ASTFile->getModificationTime()) { - llvm::errs() << "[" << InFile << "] Error: " << + llvm::errs() << "[" << FirstFile << "] Error: " << "Pointing at a source file which was modified after creating " "the AST file\n"; return 1; } - if (File == 0) { - llvm::errs() << "File '" << Filename << "' does not exist\n"; - return 1; - } unsigned Line = PointAtLocation[0].Line; unsigned Col = PointAtLocation[0].Column; - SourceLocation Loc = AST->getSourceManager().getLocation(File, Line, Col); + SourceLocation Loc = + FirstAST->getSourceManager().getLocation(File, Line, Col); if (Loc.isInvalid()) { - llvm::errs() << "[" << InFile << "] Error: " << + llvm::errs() << "[" << FirstFile << "] Error: " << "Couldn't resolve source location (invalid location)\n"; return 1; } - - llvm::tie(Point.D, Point.Node) = - ResolveLocationInAST(AST->getASTContext(), Loc); - if (Point.D == 0) { - llvm::errs() << "[" << InFile << "] Error: " << + + ASTLoc = ResolveLocationInAST(FirstAST->getASTContext(), Loc); + if (ASTLoc.isInvalid()) { + llvm::errs() << "[" << FirstFile << "] Error: " << "Couldn't resolve source location (no declaration found)\n"; return 1; } } - - if (Point.D) { - llvm::raw_ostream &OS = llvm::outs(); - OS << "Declaration node at point: " << Point.D->getDeclKindName() << " "; - if (NamedDecl *ND = dyn_cast<NamedDecl>(Point.D)) - OS << ND->getNameAsString(); - OS << "\n"; - - if (const char *Comment = AST->getASTContext().getCommentForDecl(Point.D)) - OS << "Comment associated with this declaration:\n" << Comment << "\n"; - - if (Point.Node) { - OS << "Statement node at point: " << Point.Node->getStmtClassName() - << " "; - Point.Node->printPretty(OS, AST->getASTContext(), 0, - PrintingPolicy(AST->getASTContext().getLangOptions())); - OS << "\n"; + + if (ASTLoc.isValid()) { + if (ProgAction == PrintPoint) { + llvm::raw_ostream &OS = llvm::outs(); + ASTLoc.print(OS); + if (const char *Comment = + FirstAST->getASTContext().getCommentForDecl(ASTLoc.dyn_AsDecl())) + OS << "Comment associated with this declaration:\n" << Comment << "\n"; + } else { + ProcessASTLocation(ASTLoc, Idxer); } } - if (DisableFree) - AST.take(); + if (HadErrors) + return 1; + + if (!DisableFree) { + for (int i=0, e=TUnits.size(); i != e; ++i) + delete TUnits[i]; + } // Managed static deconstruction. Useful for making things like // -time-passes usable. llvm::llvm_shutdown(); - + return 0; } diff --git a/tools/wpa/CMakeLists.txt b/tools/wpa/CMakeLists.txt new file mode 100644 index 000000000000..5553474b4bcd --- /dev/null +++ b/tools/wpa/CMakeLists.txt @@ -0,0 +1,20 @@ +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 new file mode 100644 index 000000000000..01dbd11b8df5 --- /dev/null +++ b/tools/wpa/Makefile @@ -0,0 +1,16 @@ +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 new file mode 100644 index 000000000000..fa2326dc2ba4 --- /dev/null +++ b/tools/wpa/clang-wpa.cpp @@ -0,0 +1,62 @@ +//===--- 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/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]); + + CG->ViewCallGraph(); +} |
