diff options
Diffstat (limited to 'contrib/llvm/tools/clang/lib/Frontend')
26 files changed, 11292 insertions, 0 deletions
diff --git a/contrib/llvm/tools/clang/lib/Frontend/ASTConsumers.cpp b/contrib/llvm/tools/clang/lib/Frontend/ASTConsumers.cpp new file mode 100644 index 0000000..eb7f270 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/ASTConsumers.cpp @@ -0,0 +1,451 @@ +//===--- ASTConsumers.cpp - ASTConsumer implementations -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// AST Consumer Implementations. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/ASTConsumers.h" +#include "clang/Frontend/DocumentXML.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/FileManager.h" +#include "clang/AST/AST.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/RecordLayout.h" +#include "clang/AST/PrettyPrinter.h" +#include "llvm/Module.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/System/Path.h" +using namespace clang; + +//===----------------------------------------------------------------------===// +/// ASTPrinter - Pretty-printer and dumper of ASTs + +namespace { + class ASTPrinter : public ASTConsumer { + llvm::raw_ostream &Out; + bool Dump; + + public: + ASTPrinter(llvm::raw_ostream* o = NULL, bool Dump = false) + : Out(o? *o : llvm::outs()), Dump(Dump) { } + + virtual void HandleTranslationUnit(ASTContext &Context) { + PrintingPolicy Policy = Context.PrintingPolicy; + Policy.Dump = Dump; + Context.getTranslationUnitDecl()->print(Out, Policy); + } + }; +} // end anonymous namespace + +ASTConsumer *clang::CreateASTPrinter(llvm::raw_ostream* out) { + return new ASTPrinter(out); +} + +//===----------------------------------------------------------------------===// +/// ASTPrinterXML - XML-printer of ASTs + +namespace { + class ASTPrinterXML : public ASTConsumer { + DocumentXML Doc; + + public: + ASTPrinterXML(llvm::raw_ostream& o) : Doc("CLANG_XML", o) {} + + void Initialize(ASTContext &Context) { + Doc.initialize(Context); + } + + virtual void HandleTranslationUnit(ASTContext &Ctx) { + Doc.addSubNode("TranslationUnit"); + for (DeclContext::decl_iterator + D = Ctx.getTranslationUnitDecl()->decls_begin(), + DEnd = Ctx.getTranslationUnitDecl()->decls_end(); + D != DEnd; + ++D) + Doc.PrintDecl(*D); + Doc.toParent(); + Doc.finalize(); + } + }; +} // end anonymous namespace + + +ASTConsumer *clang::CreateASTPrinterXML(llvm::raw_ostream* out) { + return new ASTPrinterXML(out ? *out : llvm::outs()); +} + +ASTConsumer *clang::CreateASTDumper() { + return new ASTPrinter(0, true); +} + +//===----------------------------------------------------------------------===// +/// ASTViewer - AST Visualization + +namespace { + class ASTViewer : public ASTConsumer { + ASTContext *Context; + public: + void Initialize(ASTContext &Context) { + this->Context = &Context; + } + + virtual void HandleTopLevelDecl(DeclGroupRef D) { + for (DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; ++I) + HandleTopLevelSingleDecl(*I); + } + + void HandleTopLevelSingleDecl(Decl *D); + }; +} + +void ASTViewer::HandleTopLevelSingleDecl(Decl *D) { + if (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)) { + D->print(llvm::errs()); + + if (Stmt *Body = D->getBody()) { + llvm::errs() << '\n'; + Body->viewAST(); + llvm::errs() << '\n'; + } + } +} + + +ASTConsumer *clang::CreateASTViewer() { return new ASTViewer(); } + +//===----------------------------------------------------------------------===// +/// DeclContextPrinter - Decl and DeclContext Visualization + +namespace { + +class DeclContextPrinter : public ASTConsumer { + llvm::raw_ostream& Out; +public: + DeclContextPrinter() : Out(llvm::errs()) {} + + void HandleTranslationUnit(ASTContext &C) { + PrintDeclContext(C.getTranslationUnitDecl(), 4); + } + + void PrintDeclContext(const DeclContext* DC, unsigned Indentation); +}; +} // end anonymous namespace + +void DeclContextPrinter::PrintDeclContext(const DeclContext* DC, + unsigned Indentation) { + // Print DeclContext name. + switch (DC->getDeclKind()) { + case Decl::TranslationUnit: + Out << "[translation unit] " << DC; + break; + case Decl::Namespace: { + Out << "[namespace] "; + const NamespaceDecl* ND = cast<NamespaceDecl>(DC); + Out << ND; + break; + } + case Decl::Enum: { + const EnumDecl* ED = cast<EnumDecl>(DC); + if (ED->isDefinition()) + Out << "[enum] "; + else + Out << "<enum> "; + Out << ED; + break; + } + case Decl::Record: { + const RecordDecl* RD = cast<RecordDecl>(DC); + if (RD->isDefinition()) + Out << "[struct] "; + else + Out << "<struct> "; + Out << RD; + break; + } + case Decl::CXXRecord: { + const CXXRecordDecl* RD = cast<CXXRecordDecl>(DC); + if (RD->isDefinition()) + Out << "[class] "; + else + Out << "<class> "; + Out << RD << ' ' << DC; + break; + } + case Decl::ObjCMethod: + Out << "[objc method]"; + break; + case Decl::ObjCInterface: + Out << "[objc interface]"; + break; + case Decl::ObjCCategory: + Out << "[objc category]"; + break; + case Decl::ObjCProtocol: + Out << "[objc protocol]"; + break; + case Decl::ObjCImplementation: + Out << "[objc implementation]"; + break; + case Decl::ObjCCategoryImpl: + Out << "[objc categoryimpl]"; + break; + case Decl::LinkageSpec: + Out << "[linkage spec]"; + break; + case Decl::Block: + Out << "[block]"; + break; + case Decl::Function: { + const FunctionDecl* FD = cast<FunctionDecl>(DC); + if (FD->isThisDeclarationADefinition()) + Out << "[function] "; + else + Out << "<function> "; + Out << FD; + // Print the parameters. + Out << "("; + bool PrintComma = false; + for (FunctionDecl::param_const_iterator I = FD->param_begin(), + E = FD->param_end(); I != E; ++I) { + if (PrintComma) + Out << ", "; + else + PrintComma = true; + Out << *I; + } + Out << ")"; + break; + } + case Decl::CXXMethod: { + const CXXMethodDecl* D = cast<CXXMethodDecl>(DC); + if (D->isOutOfLine()) + Out << "[c++ method] "; + else if (D->isImplicit()) + Out << "(c++ method) "; + else + Out << "<c++ method> "; + Out << D; + // Print the parameters. + Out << "("; + bool PrintComma = false; + for (FunctionDecl::param_const_iterator I = D->param_begin(), + E = D->param_end(); I != E; ++I) { + if (PrintComma) + Out << ", "; + else + PrintComma = true; + Out << *I; + } + Out << ")"; + + // Check the semantic DeclContext. + const DeclContext* SemaDC = D->getDeclContext(); + const DeclContext* LexicalDC = D->getLexicalDeclContext(); + if (SemaDC != LexicalDC) + Out << " [[" << SemaDC << "]]"; + + break; + } + case Decl::CXXConstructor: { + const CXXConstructorDecl* D = cast<CXXConstructorDecl>(DC); + if (D->isOutOfLine()) + Out << "[c++ ctor] "; + else if (D->isImplicit()) + Out << "(c++ ctor) "; + else + Out << "<c++ ctor> "; + Out << D; + // Print the parameters. + Out << "("; + bool PrintComma = false; + for (FunctionDecl::param_const_iterator I = D->param_begin(), + E = D->param_end(); I != E; ++I) { + if (PrintComma) + Out << ", "; + else + PrintComma = true; + Out << *I; + } + Out << ")"; + + // Check the semantic DC. + const DeclContext* SemaDC = D->getDeclContext(); + const DeclContext* LexicalDC = D->getLexicalDeclContext(); + if (SemaDC != LexicalDC) + Out << " [[" << SemaDC << "]]"; + break; + } + case Decl::CXXDestructor: { + const CXXDestructorDecl* D = cast<CXXDestructorDecl>(DC); + if (D->isOutOfLine()) + Out << "[c++ dtor] "; + else if (D->isImplicit()) + Out << "(c++ dtor) "; + else + Out << "<c++ dtor> "; + Out << D; + // Check the semantic DC. + const DeclContext* SemaDC = D->getDeclContext(); + const DeclContext* LexicalDC = D->getLexicalDeclContext(); + if (SemaDC != LexicalDC) + Out << " [[" << SemaDC << "]]"; + break; + } + case Decl::CXXConversion: { + const CXXConversionDecl* D = cast<CXXConversionDecl>(DC); + if (D->isOutOfLine()) + Out << "[c++ conversion] "; + else if (D->isImplicit()) + Out << "(c++ conversion) "; + else + Out << "<c++ conversion> "; + Out << D; + // Check the semantic DC. + const DeclContext* SemaDC = D->getDeclContext(); + const DeclContext* LexicalDC = D->getLexicalDeclContext(); + if (SemaDC != LexicalDC) + Out << " [[" << SemaDC << "]]"; + break; + } + + default: + assert(0 && "a decl that inherits DeclContext isn't handled"); + } + + Out << "\n"; + + // Print decls in the DeclContext. + for (DeclContext::decl_iterator I = DC->decls_begin(), E = DC->decls_end(); + I != E; ++I) { + for (unsigned i = 0; i < Indentation; ++i) + Out << " "; + + Decl::Kind DK = I->getKind(); + switch (DK) { + case Decl::Namespace: + case Decl::Enum: + case Decl::Record: + case Decl::CXXRecord: + case Decl::ObjCMethod: + case Decl::ObjCInterface: + case Decl::ObjCCategory: + case Decl::ObjCProtocol: + case Decl::ObjCImplementation: + case Decl::ObjCCategoryImpl: + case Decl::LinkageSpec: + case Decl::Block: + case Decl::Function: + case Decl::CXXMethod: + case Decl::CXXConstructor: + case Decl::CXXDestructor: + case Decl::CXXConversion: + { + DeclContext* DC = cast<DeclContext>(*I); + PrintDeclContext(DC, Indentation+2); + break; + } + case Decl::Field: { + FieldDecl* FD = cast<FieldDecl>(*I); + Out << "<field> " << FD << '\n'; + break; + } + case Decl::Typedef: { + TypedefDecl* TD = cast<TypedefDecl>(*I); + Out << "<typedef> " << TD << '\n'; + break; + } + case Decl::EnumConstant: { + EnumConstantDecl* ECD = cast<EnumConstantDecl>(*I); + Out << "<enum constant> " << ECD << '\n'; + break; + } + case Decl::Var: { + VarDecl* VD = cast<VarDecl>(*I); + Out << "<var> " << VD << '\n'; + break; + } + case Decl::ImplicitParam: { + ImplicitParamDecl* IPD = cast<ImplicitParamDecl>(*I); + Out << "<implicit parameter> " << IPD << '\n'; + break; + } + case Decl::ParmVar: { + ParmVarDecl* PVD = cast<ParmVarDecl>(*I); + Out << "<parameter> " << PVD << '\n'; + break; + } + case Decl::ObjCProperty: { + ObjCPropertyDecl* OPD = cast<ObjCPropertyDecl>(*I); + Out << "<objc property> " << OPD << '\n'; + break; + } + case Decl::FunctionTemplate: { + FunctionTemplateDecl* FTD = cast<FunctionTemplateDecl>(*I); + Out << "<function template> " << FTD << '\n'; + break; + } + case Decl::FileScopeAsm: { + Out << "<file-scope asm>\n"; + break; + } + case Decl::UsingDirective: { + Out << "<using directive>\n"; + break; + } + case Decl::NamespaceAlias: { + NamespaceAliasDecl* NAD = cast<NamespaceAliasDecl>(*I); + Out << "<namespace alias> " << NAD << '\n'; + break; + } + case Decl::ClassTemplate: { + ClassTemplateDecl *CTD = cast<ClassTemplateDecl>(*I); + Out << "<class template> " << CTD << '\n'; + break; + } + default: + Out << "DeclKind: " << DK << '"' << *I << "\"\n"; + assert(0 && "decl unhandled"); + } + } +} +ASTConsumer *clang::CreateDeclContextPrinter() { + return new DeclContextPrinter(); +} + +//===----------------------------------------------------------------------===// +/// InheritanceViewer - C++ Inheritance Visualization + +namespace { +class InheritanceViewer : public ASTConsumer { + const std::string clsname; +public: + InheritanceViewer(const std::string& cname) : clsname(cname) {} + + void HandleTranslationUnit(ASTContext &C) { + for (ASTContext::type_iterator I=C.types_begin(),E=C.types_end(); I!=E; ++I) + if (RecordType *T = dyn_cast<RecordType>(*I)) { + if (CXXRecordDecl *D = dyn_cast<CXXRecordDecl>(T->getDecl())) { + // FIXME: This lookup needs to be generalized to handle namespaces and + // (when we support them) templates. + if (D->getNameAsString() == clsname) { + D->viewInheritance(C); + } + } + } + } +}; +} + +ASTConsumer *clang::CreateInheritanceViewer(const std::string& clsname) { + return new InheritanceViewer(clsname); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/ASTMerge.cpp b/contrib/llvm/tools/clang/lib/Frontend/ASTMerge.cpp new file mode 100644 index 0000000..b46212f --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/ASTMerge.cpp @@ -0,0 +1,111 @@ +//===-- ASTMerge.cpp - AST Merging Frontent Action --------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/ASTDiagnostic.h" +#include "clang/AST/ASTImporter.h" +#include "clang/Basic/Diagnostic.h" + +using namespace clang; + +ASTConsumer *ASTMergeAction::CreateASTConsumer(CompilerInstance &CI, + llvm::StringRef InFile) { + return AdaptedAction->CreateASTConsumer(CI, InFile); +} + +bool ASTMergeAction::BeginSourceFileAction(CompilerInstance &CI, + llvm::StringRef Filename) { + // FIXME: This is a hack. We need a better way to communicate the + // AST file, compiler instance, and file name than member variables + // of FrontendAction. + AdaptedAction->setCurrentFile(getCurrentFile(), getCurrentFileKind(), + takeCurrentASTUnit()); + AdaptedAction->setCompilerInstance(&CI); + return AdaptedAction->BeginSourceFileAction(CI, Filename); +} + +void ASTMergeAction::ExecuteAction() { + CompilerInstance &CI = getCompilerInstance(); + CI.getDiagnostics().getClient()->BeginSourceFile( + CI.getASTContext().getLangOptions()); + CI.getDiagnostics().SetArgToStringFn(&FormatASTNodeDiagnosticArgument, + &CI.getASTContext()); + llvm::IntrusiveRefCntPtr<Diagnostic> Diags(&CI.getDiagnostics()); + for (unsigned I = 0, N = ASTFiles.size(); I != N; ++I) { + ASTUnit *Unit = ASTUnit::LoadFromASTFile(ASTFiles[I], Diags, false); + if (!Unit) + continue; + + // Reset the argument -> string function so that it has the AST + // context we want, since the Sema object created by + // LoadFromASTFile will override it. + CI.getDiagnostics().SetArgToStringFn(&FormatASTNodeDiagnosticArgument, + &CI.getASTContext()); + + ASTImporter Importer(CI.getDiagnostics(), + CI.getASTContext(), + CI.getFileManager(), + Unit->getASTContext(), + Unit->getFileManager()); + + TranslationUnitDecl *TU = Unit->getASTContext().getTranslationUnitDecl(); + for (DeclContext::decl_iterator D = TU->decls_begin(), + DEnd = TU->decls_end(); + D != DEnd; ++D) { + // Don't re-import __va_list_tag, __builtin_va_list. + if (NamedDecl *ND = dyn_cast<NamedDecl>(*D)) + if (IdentifierInfo *II = ND->getIdentifier()) + if (II->isStr("__va_list_tag") || II->isStr("__builtin_va_list")) + continue; + + Importer.Import(*D); + } + + delete Unit; + } + + AdaptedAction->ExecuteAction(); + CI.getDiagnostics().getClient()->EndSourceFile(); +} + +void ASTMergeAction::EndSourceFileAction() { + return AdaptedAction->EndSourceFileAction(); +} + +ASTMergeAction::ASTMergeAction(FrontendAction *AdaptedAction, + std::string *ASTFiles, unsigned NumASTFiles) + : AdaptedAction(AdaptedAction), ASTFiles(ASTFiles, ASTFiles + NumASTFiles) { + assert(AdaptedAction && "ASTMergeAction needs an action to adapt"); +} + +ASTMergeAction::~ASTMergeAction() { + delete AdaptedAction; +} + +bool ASTMergeAction::usesPreprocessorOnly() const { + return AdaptedAction->usesPreprocessorOnly(); +} + +bool ASTMergeAction::usesCompleteTranslationUnit() { + return AdaptedAction->usesCompleteTranslationUnit(); +} + +bool ASTMergeAction::hasPCHSupport() const { + return AdaptedAction->hasPCHSupport(); +} + +bool ASTMergeAction::hasASTFileSupport() const { + return AdaptedAction->hasASTFileSupport(); +} + +bool ASTMergeAction::hasCodeCompletionSupport() const { + return AdaptedAction->hasCodeCompletionSupport(); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/ASTUnit.cpp b/contrib/llvm/tools/clang/lib/Frontend/ASTUnit.cpp new file mode 100644 index 0000000..c76488b --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/ASTUnit.cpp @@ -0,0 +1,1875 @@ +//===--- ASTUnit.cpp - ASTUnit utility ------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// ASTUnit Implementation. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/ASTUnit.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/DeclVisitor.h" +#include "clang/AST/TypeOrdering.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/Driver/Compilation.h" +#include "clang/Driver/Driver.h" +#include "clang/Driver/Job.h" +#include "clang/Driver/Tool.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/FrontendOptions.h" +#include "clang/Serialization/ASTReader.h" +#include "clang/Serialization/ASTWriter.h" +#include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Basic/TargetOptions.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Basic/Diagnostic.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/System/Host.h" +#include "llvm/System/Path.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/Timer.h" +#include <cstdlib> +#include <cstdio> +#include <sys/stat.h> +using namespace clang; + +/// \brief After failing to build a precompiled preamble (due to +/// errors in the source that occurs in the preamble), the number of +/// reparses during which we'll skip even trying to precompile the +/// preamble. +const unsigned DefaultPreambleRebuildInterval = 5; + +ASTUnit::ASTUnit(bool _MainFileIsAST) + : CaptureDiagnostics(false), MainFileIsAST(_MainFileIsAST), + CompleteTranslationUnit(true), ConcurrencyCheckValue(CheckUnlocked), + PreambleRebuildCounter(0), SavedMainFileBuffer(0), PreambleBuffer(0), + ShouldCacheCodeCompletionResults(false), + NumTopLevelDeclsAtLastCompletionCache(0), + CacheCodeCompletionCoolDown(0), + UnsafeToFree(false) { +} + +ASTUnit::~ASTUnit() { + ConcurrencyCheckValue = CheckLocked; + CleanTemporaryFiles(); + if (!PreambleFile.empty()) + llvm::sys::Path(PreambleFile).eraseFromDisk(); + + // Free the buffers associated with remapped files. We are required to + // perform this operation here because we explicitly request that the + // compiler instance *not* free these buffers for each invocation of the + // parser. + if (Invocation.get()) { + PreprocessorOptions &PPOpts = Invocation->getPreprocessorOpts(); + for (PreprocessorOptions::remapped_file_buffer_iterator + FB = PPOpts.remapped_file_buffer_begin(), + FBEnd = PPOpts.remapped_file_buffer_end(); + FB != FBEnd; + ++FB) + delete FB->second; + } + + delete SavedMainFileBuffer; + delete PreambleBuffer; + + ClearCachedCompletionResults(); + + for (unsigned I = 0, N = Timers.size(); I != N; ++I) + delete Timers[I]; +} + +void ASTUnit::CleanTemporaryFiles() { + for (unsigned I = 0, N = TemporaryFiles.size(); I != N; ++I) + TemporaryFiles[I].eraseFromDisk(); + TemporaryFiles.clear(); +} + +/// \brief Determine the set of code-completion contexts in which this +/// declaration should be shown. +static unsigned getDeclShowContexts(NamedDecl *ND, + const LangOptions &LangOpts, + bool &IsNestedNameSpecifier) { + IsNestedNameSpecifier = false; + + if (isa<UsingShadowDecl>(ND)) + ND = dyn_cast<NamedDecl>(ND->getUnderlyingDecl()); + if (!ND) + return 0; + + unsigned Contexts = 0; + if (isa<TypeDecl>(ND) || isa<ObjCInterfaceDecl>(ND) || + isa<ClassTemplateDecl>(ND) || isa<TemplateTemplateParmDecl>(ND)) { + // Types can appear in these contexts. + if (LangOpts.CPlusPlus || !isa<TagDecl>(ND)) + Contexts |= (1 << (CodeCompletionContext::CCC_TopLevel - 1)) + | (1 << (CodeCompletionContext::CCC_ObjCIvarList - 1)) + | (1 << (CodeCompletionContext::CCC_ClassStructUnion - 1)) + | (1 << (CodeCompletionContext::CCC_Statement - 1)) + | (1 << (CodeCompletionContext::CCC_Type - 1)); + + // In C++, types can appear in expressions contexts (for functional casts). + if (LangOpts.CPlusPlus) + Contexts |= (1 << (CodeCompletionContext::CCC_Expression - 1)); + + // In Objective-C, message sends can send interfaces. In Objective-C++, + // all types are available due to functional casts. + if (LangOpts.CPlusPlus || isa<ObjCInterfaceDecl>(ND)) + Contexts |= (1 << (CodeCompletionContext::CCC_ObjCMessageReceiver - 1)); + + // Deal with tag names. + if (isa<EnumDecl>(ND)) { + Contexts |= (1 << (CodeCompletionContext::CCC_EnumTag - 1)); + + // Part of the nested-name-specifier in C++0x. + if (LangOpts.CPlusPlus0x) + IsNestedNameSpecifier = true; + } else if (RecordDecl *Record = dyn_cast<RecordDecl>(ND)) { + if (Record->isUnion()) + Contexts |= (1 << (CodeCompletionContext::CCC_UnionTag - 1)); + else + Contexts |= (1 << (CodeCompletionContext::CCC_ClassOrStructTag - 1)); + + if (LangOpts.CPlusPlus) + IsNestedNameSpecifier = true; + } else if (isa<ClassTemplateDecl>(ND) || isa<TemplateTemplateParmDecl>(ND)) + IsNestedNameSpecifier = true; + } else if (isa<ValueDecl>(ND) || isa<FunctionTemplateDecl>(ND)) { + // Values can appear in these contexts. + Contexts = (1 << (CodeCompletionContext::CCC_Statement - 1)) + | (1 << (CodeCompletionContext::CCC_Expression - 1)) + | (1 << (CodeCompletionContext::CCC_ObjCMessageReceiver - 1)); + } else if (isa<ObjCProtocolDecl>(ND)) { + Contexts = (1 << (CodeCompletionContext::CCC_ObjCProtocolName - 1)); + } else if (isa<NamespaceDecl>(ND) || isa<NamespaceAliasDecl>(ND)) { + Contexts = (1 << (CodeCompletionContext::CCC_Namespace - 1)); + + // Part of the nested-name-specifier. + IsNestedNameSpecifier = true; + } + + return Contexts; +} + +void ASTUnit::CacheCodeCompletionResults() { + if (!TheSema) + return; + + llvm::Timer *CachingTimer = 0; + if (TimerGroup.get()) { + CachingTimer = new llvm::Timer("Cache global code completions", + *TimerGroup); + CachingTimer->startTimer(); + Timers.push_back(CachingTimer); + } + + // Clear out the previous results. + ClearCachedCompletionResults(); + + // Gather the set of global code completions. + typedef CodeCompletionResult Result; + llvm::SmallVector<Result, 8> Results; + TheSema->GatherGlobalCodeCompletions(Results); + + // Translate global code completions into cached completions. + llvm::DenseMap<CanQualType, unsigned> CompletionTypes; + + for (unsigned I = 0, N = Results.size(); I != N; ++I) { + switch (Results[I].Kind) { + case Result::RK_Declaration: { + bool IsNestedNameSpecifier = false; + CachedCodeCompletionResult CachedResult; + CachedResult.Completion = Results[I].CreateCodeCompletionString(*TheSema); + CachedResult.ShowInContexts = getDeclShowContexts(Results[I].Declaration, + Ctx->getLangOptions(), + IsNestedNameSpecifier); + CachedResult.Priority = Results[I].Priority; + CachedResult.Kind = Results[I].CursorKind; + CachedResult.Availability = Results[I].Availability; + + // Keep track of the type of this completion in an ASTContext-agnostic + // way. + QualType UsageType = getDeclUsageType(*Ctx, Results[I].Declaration); + if (UsageType.isNull()) { + CachedResult.TypeClass = STC_Void; + CachedResult.Type = 0; + } else { + CanQualType CanUsageType + = Ctx->getCanonicalType(UsageType.getUnqualifiedType()); + CachedResult.TypeClass = getSimplifiedTypeClass(CanUsageType); + + // Determine whether we have already seen this type. If so, we save + // ourselves the work of formatting the type string by using the + // temporary, CanQualType-based hash table to find the associated value. + unsigned &TypeValue = CompletionTypes[CanUsageType]; + if (TypeValue == 0) { + TypeValue = CompletionTypes.size(); + CachedCompletionTypes[QualType(CanUsageType).getAsString()] + = TypeValue; + } + + CachedResult.Type = TypeValue; + } + + CachedCompletionResults.push_back(CachedResult); + + /// Handle nested-name-specifiers in C++. + if (TheSema->Context.getLangOptions().CPlusPlus && + IsNestedNameSpecifier && !Results[I].StartsNestedNameSpecifier) { + // The contexts in which a nested-name-specifier can appear in C++. + unsigned NNSContexts + = (1 << (CodeCompletionContext::CCC_TopLevel - 1)) + | (1 << (CodeCompletionContext::CCC_ObjCIvarList - 1)) + | (1 << (CodeCompletionContext::CCC_ClassStructUnion - 1)) + | (1 << (CodeCompletionContext::CCC_Statement - 1)) + | (1 << (CodeCompletionContext::CCC_Expression - 1)) + | (1 << (CodeCompletionContext::CCC_ObjCMessageReceiver - 1)) + | (1 << (CodeCompletionContext::CCC_EnumTag - 1)) + | (1 << (CodeCompletionContext::CCC_UnionTag - 1)) + | (1 << (CodeCompletionContext::CCC_ClassOrStructTag - 1)) + | (1 << (CodeCompletionContext::CCC_Type - 1)) + | (1 << (CodeCompletionContext::CCC_PotentiallyQualifiedName - 1)); + + if (isa<NamespaceDecl>(Results[I].Declaration) || + isa<NamespaceAliasDecl>(Results[I].Declaration)) + NNSContexts |= (1 << (CodeCompletionContext::CCC_Namespace - 1)); + + if (unsigned RemainingContexts + = NNSContexts & ~CachedResult.ShowInContexts) { + // If there any contexts where this completion can be a + // nested-name-specifier but isn't already an option, create a + // nested-name-specifier completion. + Results[I].StartsNestedNameSpecifier = true; + CachedResult.Completion = Results[I].CreateCodeCompletionString(*TheSema); + CachedResult.ShowInContexts = RemainingContexts; + CachedResult.Priority = CCP_NestedNameSpecifier; + CachedResult.TypeClass = STC_Void; + CachedResult.Type = 0; + CachedCompletionResults.push_back(CachedResult); + } + } + break; + } + + case Result::RK_Keyword: + case Result::RK_Pattern: + // Ignore keywords and patterns; we don't care, since they are so + // easily regenerated. + break; + + case Result::RK_Macro: { + CachedCodeCompletionResult CachedResult; + CachedResult.Completion = Results[I].CreateCodeCompletionString(*TheSema); + CachedResult.ShowInContexts + = (1 << (CodeCompletionContext::CCC_TopLevel - 1)) + | (1 << (CodeCompletionContext::CCC_ObjCInterface - 1)) + | (1 << (CodeCompletionContext::CCC_ObjCImplementation - 1)) + | (1 << (CodeCompletionContext::CCC_ObjCIvarList - 1)) + | (1 << (CodeCompletionContext::CCC_ClassStructUnion - 1)) + | (1 << (CodeCompletionContext::CCC_Statement - 1)) + | (1 << (CodeCompletionContext::CCC_Expression - 1)) + | (1 << (CodeCompletionContext::CCC_ObjCMessageReceiver - 1)) + | (1 << (CodeCompletionContext::CCC_MacroNameUse - 1)) + | (1 << (CodeCompletionContext::CCC_PreprocessorExpression - 1)); + + CachedResult.Priority = Results[I].Priority; + CachedResult.Kind = Results[I].CursorKind; + CachedResult.Availability = Results[I].Availability; + CachedResult.TypeClass = STC_Void; + CachedResult.Type = 0; + CachedCompletionResults.push_back(CachedResult); + break; + } + } + Results[I].Destroy(); + } + + if (CachingTimer) + CachingTimer->stopTimer(); + + // Make a note of the state when we performed this caching. + NumTopLevelDeclsAtLastCompletionCache = top_level_size(); + CacheCodeCompletionCoolDown = 15; +} + +void ASTUnit::ClearCachedCompletionResults() { + for (unsigned I = 0, N = CachedCompletionResults.size(); I != N; ++I) + delete CachedCompletionResults[I].Completion; + CachedCompletionResults.clear(); + CachedCompletionTypes.clear(); +} + +namespace { + +/// \brief Gathers information from ASTReader that will be used to initialize +/// a Preprocessor. +class ASTInfoCollector : public ASTReaderListener { + LangOptions &LangOpt; + HeaderSearch &HSI; + std::string &TargetTriple; + std::string &Predefines; + unsigned &Counter; + + unsigned NumHeaderInfos; + +public: + ASTInfoCollector(LangOptions &LangOpt, HeaderSearch &HSI, + std::string &TargetTriple, std::string &Predefines, + unsigned &Counter) + : LangOpt(LangOpt), HSI(HSI), TargetTriple(TargetTriple), + Predefines(Predefines), Counter(Counter), NumHeaderInfos(0) {} + + virtual bool ReadLanguageOptions(const LangOptions &LangOpts) { + LangOpt = LangOpts; + return false; + } + + virtual bool ReadTargetTriple(llvm::StringRef Triple) { + TargetTriple = Triple; + return false; + } + + virtual bool ReadPredefinesBuffer(const PCHPredefinesBlocks &Buffers, + llvm::StringRef OriginalFileName, + std::string &SuggestedPredefines) { + Predefines = Buffers[0].Data; + for (unsigned I = 1, N = Buffers.size(); I != N; ++I) { + Predefines += Buffers[I].Data; + } + return false; + } + + virtual void ReadHeaderFileInfo(const HeaderFileInfo &HFI, unsigned ID) { + HSI.setHeaderFileInfoForUID(HFI, NumHeaderInfos++); + } + + virtual void ReadCounter(unsigned Value) { + Counter = Value; + } +}; + +class StoredDiagnosticClient : public DiagnosticClient { + llvm::SmallVectorImpl<StoredDiagnostic> &StoredDiags; + +public: + explicit StoredDiagnosticClient( + llvm::SmallVectorImpl<StoredDiagnostic> &StoredDiags) + : StoredDiags(StoredDiags) { } + + virtual void HandleDiagnostic(Diagnostic::Level Level, + const DiagnosticInfo &Info); +}; + +/// \brief RAII object that optionally captures diagnostics, if +/// there is no diagnostic client to capture them already. +class CaptureDroppedDiagnostics { + Diagnostic &Diags; + StoredDiagnosticClient Client; + DiagnosticClient *PreviousClient; + +public: + CaptureDroppedDiagnostics(bool RequestCapture, Diagnostic &Diags, + llvm::SmallVectorImpl<StoredDiagnostic> &StoredDiags) + : Diags(Diags), Client(StoredDiags), PreviousClient(0) + { + if (RequestCapture || Diags.getClient() == 0) { + PreviousClient = Diags.takeClient(); + Diags.setClient(&Client); + } + } + + ~CaptureDroppedDiagnostics() { + if (Diags.getClient() == &Client) { + Diags.takeClient(); + Diags.setClient(PreviousClient); + } + } +}; + +} // anonymous namespace + +void StoredDiagnosticClient::HandleDiagnostic(Diagnostic::Level Level, + const DiagnosticInfo &Info) { + StoredDiags.push_back(StoredDiagnostic(Level, Info)); +} + +const std::string &ASTUnit::getOriginalSourceFileName() { + return OriginalSourceFile; +} + +const std::string &ASTUnit::getASTFileName() { + assert(isMainFileAST() && "Not an ASTUnit from an AST file!"); + return static_cast<ASTReader *>(Ctx->getExternalSource())->getFileName(); +} + +ASTUnit *ASTUnit::LoadFromASTFile(const std::string &Filename, + llvm::IntrusiveRefCntPtr<Diagnostic> Diags, + bool OnlyLocalDecls, + RemappedFile *RemappedFiles, + unsigned NumRemappedFiles, + bool CaptureDiagnostics) { + llvm::OwningPtr<ASTUnit> AST(new ASTUnit(true)); + + if (!Diags.getPtr()) { + // No diagnostics engine was provided, so create our own diagnostics object + // with the default options. + DiagnosticOptions DiagOpts; + Diags = CompilerInstance::createDiagnostics(DiagOpts, 0, 0); + } + + AST->CaptureDiagnostics = CaptureDiagnostics; + AST->OnlyLocalDecls = OnlyLocalDecls; + AST->Diagnostics = Diags; + AST->FileMgr.reset(new FileManager); + AST->SourceMgr.reset(new SourceManager(AST->getDiagnostics())); + AST->HeaderInfo.reset(new HeaderSearch(AST->getFileManager())); + + // If requested, capture diagnostics in the ASTUnit. + CaptureDroppedDiagnostics Capture(CaptureDiagnostics, AST->getDiagnostics(), + AST->StoredDiagnostics); + + for (unsigned I = 0; I != NumRemappedFiles; ++I) { + // Create the file entry for the file that we're mapping from. + const FileEntry *FromFile + = AST->getFileManager().getVirtualFile(RemappedFiles[I].first, + RemappedFiles[I].second->getBufferSize(), + 0); + if (!FromFile) { + AST->getDiagnostics().Report(diag::err_fe_remap_missing_from_file) + << RemappedFiles[I].first; + delete RemappedFiles[I].second; + continue; + } + + // Override the contents of the "from" file with the contents of + // the "to" file. + AST->getSourceManager().overrideFileContents(FromFile, + RemappedFiles[I].second); + } + + // Gather Info for preprocessor construction later on. + + LangOptions LangInfo; + HeaderSearch &HeaderInfo = *AST->HeaderInfo.get(); + std::string TargetTriple; + std::string Predefines; + unsigned Counter; + + llvm::OwningPtr<ASTReader> Reader; + + Reader.reset(new ASTReader(AST->getSourceManager(), AST->getFileManager(), + AST->getDiagnostics())); + Reader->setListener(new ASTInfoCollector(LangInfo, HeaderInfo, TargetTriple, + Predefines, Counter)); + + switch (Reader->ReadAST(Filename)) { + case ASTReader::Success: + break; + + case ASTReader::Failure: + case ASTReader::IgnorePCH: + AST->getDiagnostics().Report(diag::err_fe_unable_to_load_pch); + return NULL; + } + + AST->OriginalSourceFile = Reader->getOriginalSourceFile(); + + // AST file loaded successfully. Now create the preprocessor. + + // Get information about the target being compiled for. + // + // FIXME: This is broken, we should store the TargetOptions in the AST file. + TargetOptions TargetOpts; + TargetOpts.ABI = ""; + TargetOpts.CXXABI = ""; + TargetOpts.CPU = ""; + TargetOpts.Features.clear(); + TargetOpts.Triple = TargetTriple; + AST->Target.reset(TargetInfo::CreateTargetInfo(AST->getDiagnostics(), + TargetOpts)); + AST->PP.reset(new Preprocessor(AST->getDiagnostics(), LangInfo, + *AST->Target.get(), + AST->getSourceManager(), HeaderInfo)); + Preprocessor &PP = *AST->PP.get(); + + PP.setPredefines(Reader->getSuggestedPredefines()); + PP.setCounterValue(Counter); + Reader->setPreprocessor(PP); + + // Create and initialize the ASTContext. + + AST->Ctx.reset(new ASTContext(LangInfo, + AST->getSourceManager(), + *AST->Target.get(), + PP.getIdentifierTable(), + PP.getSelectorTable(), + PP.getBuiltinInfo(), + /* size_reserve = */0)); + ASTContext &Context = *AST->Ctx.get(); + + Reader->InitializeContext(Context); + + // Attach the AST reader to the AST context as an external AST + // source, so that declarations will be deserialized from the + // AST file as needed. + ASTReader *ReaderPtr = Reader.get(); + llvm::OwningPtr<ExternalASTSource> Source(Reader.take()); + Context.setExternalSource(Source); + + // Create an AST consumer, even though it isn't used. + AST->Consumer.reset(new ASTConsumer); + + // Create a semantic analysis object and tell the AST reader about it. + AST->TheSema.reset(new Sema(PP, Context, *AST->Consumer)); + AST->TheSema->Initialize(); + ReaderPtr->InitializeSema(*AST->TheSema); + + return AST.take(); +} + +namespace { + +class TopLevelDeclTrackerConsumer : public ASTConsumer { + ASTUnit &Unit; + +public: + TopLevelDeclTrackerConsumer(ASTUnit &_Unit) : Unit(_Unit) {} + + void HandleTopLevelDecl(DeclGroupRef D) { + for (DeclGroupRef::iterator it = D.begin(), ie = D.end(); it != ie; ++it) { + Decl *D = *it; + // FIXME: Currently ObjC method declarations are incorrectly being + // reported as top-level declarations, even though their DeclContext + // is the containing ObjC @interface/@implementation. This is a + // fundamental problem in the parser right now. + if (isa<ObjCMethodDecl>(D)) + continue; + Unit.addTopLevelDecl(D); + } + } + + // We're not interested in "interesting" decls. + void HandleInterestingDecl(DeclGroupRef) {} +}; + +class TopLevelDeclTrackerAction : public ASTFrontendAction { +public: + ASTUnit &Unit; + + virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI, + llvm::StringRef InFile) { + return new TopLevelDeclTrackerConsumer(Unit); + } + +public: + TopLevelDeclTrackerAction(ASTUnit &_Unit) : Unit(_Unit) {} + + virtual bool hasCodeCompletionSupport() const { return false; } + virtual bool usesCompleteTranslationUnit() { + return Unit.isCompleteTranslationUnit(); + } +}; + +class PrecompilePreambleConsumer : public PCHGenerator { + ASTUnit &Unit; + std::vector<Decl *> TopLevelDecls; + +public: + PrecompilePreambleConsumer(ASTUnit &Unit, + const Preprocessor &PP, bool Chaining, + const char *isysroot, llvm::raw_ostream *Out) + : PCHGenerator(PP, Chaining, isysroot, Out), Unit(Unit) { } + + virtual void HandleTopLevelDecl(DeclGroupRef D) { + for (DeclGroupRef::iterator it = D.begin(), ie = D.end(); it != ie; ++it) { + Decl *D = *it; + // FIXME: Currently ObjC method declarations are incorrectly being + // reported as top-level declarations, even though their DeclContext + // is the containing ObjC @interface/@implementation. This is a + // fundamental problem in the parser right now. + if (isa<ObjCMethodDecl>(D)) + continue; + TopLevelDecls.push_back(D); + } + } + + virtual void HandleTranslationUnit(ASTContext &Ctx) { + PCHGenerator::HandleTranslationUnit(Ctx); + if (!Unit.getDiagnostics().hasErrorOccurred()) { + // Translate the top-level declarations we captured during + // parsing into declaration IDs in the precompiled + // preamble. This will allow us to deserialize those top-level + // declarations when requested. + for (unsigned I = 0, N = TopLevelDecls.size(); I != N; ++I) + Unit.addTopLevelDeclFromPreamble( + getWriter().getDeclID(TopLevelDecls[I])); + } + } +}; + +class PrecompilePreambleAction : public ASTFrontendAction { + ASTUnit &Unit; + +public: + explicit PrecompilePreambleAction(ASTUnit &Unit) : Unit(Unit) {} + + virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI, + llvm::StringRef InFile) { + std::string Sysroot; + llvm::raw_ostream *OS = 0; + bool Chaining; + if (GeneratePCHAction::ComputeASTConsumerArguments(CI, InFile, Sysroot, + OS, Chaining)) + return 0; + + const char *isysroot = CI.getFrontendOpts().RelocatablePCH ? + Sysroot.c_str() : 0; + return new PrecompilePreambleConsumer(Unit, CI.getPreprocessor(), Chaining, + isysroot, OS); + } + + virtual bool hasCodeCompletionSupport() const { return false; } + virtual bool hasASTFileSupport() const { return false; } + virtual bool usesCompleteTranslationUnit() { return false; } +}; + +} + +/// Parse the source file into a translation unit using the given compiler +/// invocation, replacing the current translation unit. +/// +/// \returns True if a failure occurred that causes the ASTUnit not to +/// contain any translation-unit information, false otherwise. +bool ASTUnit::Parse(llvm::MemoryBuffer *OverrideMainBuffer) { + delete SavedMainFileBuffer; + SavedMainFileBuffer = 0; + + if (!Invocation.get()) { + delete OverrideMainBuffer; + return true; + } + + // Create the compiler instance to use for building the AST. + CompilerInstance Clang; + Clang.setInvocation(Invocation.take()); + OriginalSourceFile = Clang.getFrontendOpts().Inputs[0].second; + + // Set up diagnostics, capturing any diagnostics that would + // otherwise be dropped. + Clang.setDiagnostics(&getDiagnostics()); + CaptureDroppedDiagnostics Capture(CaptureDiagnostics, + getDiagnostics(), + StoredDiagnostics); + + // Create the target instance. + Clang.setTarget(TargetInfo::CreateTargetInfo(Clang.getDiagnostics(), + Clang.getTargetOpts())); + if (!Clang.hasTarget()) { + delete OverrideMainBuffer; + return true; + } + + // Inform the target of the language options. + // + // FIXME: We shouldn't need to do this, the target should be immutable once + // created. This complexity should be lifted elsewhere. + Clang.getTarget().setForcedLangOptions(Clang.getLangOpts()); + + assert(Clang.getFrontendOpts().Inputs.size() == 1 && + "Invocation must have exactly one source file!"); + assert(Clang.getFrontendOpts().Inputs[0].first != IK_AST && + "FIXME: AST inputs not yet supported here!"); + assert(Clang.getFrontendOpts().Inputs[0].first != IK_LLVM_IR && + "IR inputs not support here!"); + + // Configure the various subsystems. + // FIXME: Should we retain the previous file manager? + FileMgr.reset(new FileManager); + SourceMgr.reset(new SourceManager(getDiagnostics())); + TheSema.reset(); + Ctx.reset(); + PP.reset(); + + // Clear out old caches and data. + TopLevelDecls.clear(); + CleanTemporaryFiles(); + PreprocessedEntitiesByFile.clear(); + + if (!OverrideMainBuffer) { + StoredDiagnostics.clear(); + TopLevelDeclsInPreamble.clear(); + } + + // Create a file manager object to provide access to and cache the filesystem. + Clang.setFileManager(&getFileManager()); + + // Create the source manager. + Clang.setSourceManager(&getSourceManager()); + + // If the main file has been overridden due to the use of a preamble, + // make that override happen and introduce the preamble. + PreprocessorOptions &PreprocessorOpts = Clang.getPreprocessorOpts(); + std::string PriorImplicitPCHInclude; + if (OverrideMainBuffer) { + PreprocessorOpts.addRemappedFile(OriginalSourceFile, OverrideMainBuffer); + PreprocessorOpts.PrecompiledPreambleBytes.first = Preamble.size(); + PreprocessorOpts.PrecompiledPreambleBytes.second + = PreambleEndsAtStartOfLine; + PriorImplicitPCHInclude = PreprocessorOpts.ImplicitPCHInclude; + PreprocessorOpts.ImplicitPCHInclude = PreambleFile; + PreprocessorOpts.DisablePCHValidation = true; + + // Keep track of the override buffer; + SavedMainFileBuffer = OverrideMainBuffer; + + // The stored diagnostic has the old source manager in it; update + // the locations to refer into the new source manager. Since we've + // been careful to make sure that the source manager's state + // before and after are identical, so that we can reuse the source + // location itself. + for (unsigned I = 0, N = StoredDiagnostics.size(); I != N; ++I) { + FullSourceLoc Loc(StoredDiagnostics[I].getLocation(), + getSourceManager()); + StoredDiagnostics[I].setLocation(Loc); + } + } else { + PreprocessorOpts.PrecompiledPreambleBytes.first = 0; + PreprocessorOpts.PrecompiledPreambleBytes.second = false; + } + + llvm::OwningPtr<TopLevelDeclTrackerAction> Act; + Act.reset(new TopLevelDeclTrackerAction(*this)); + if (!Act->BeginSourceFile(Clang, Clang.getFrontendOpts().Inputs[0].second, + Clang.getFrontendOpts().Inputs[0].first)) + goto error; + + Act->Execute(); + + // Steal the created target, context, and preprocessor, and take back the + // source and file managers. + TheSema.reset(Clang.takeSema()); + Consumer.reset(Clang.takeASTConsumer()); + Ctx.reset(Clang.takeASTContext()); + PP.reset(Clang.takePreprocessor()); + Clang.takeSourceManager(); + Clang.takeFileManager(); + Target.reset(Clang.takeTarget()); + + Act->EndSourceFile(); + + // Remove the overridden buffer we used for the preamble. + if (OverrideMainBuffer) { + PreprocessorOpts.eraseRemappedFile( + PreprocessorOpts.remapped_file_buffer_end() - 1); + PreprocessorOpts.ImplicitPCHInclude = PriorImplicitPCHInclude; + } + + Invocation.reset(Clang.takeInvocation()); + + // If we were asked to cache code-completion results and don't have any + // results yet, do so now. + if (ShouldCacheCodeCompletionResults && CachedCompletionResults.empty()) + CacheCodeCompletionResults(); + + return false; + +error: + // Remove the overridden buffer we used for the preamble. + if (OverrideMainBuffer) { + PreprocessorOpts.eraseRemappedFile( + PreprocessorOpts.remapped_file_buffer_end() - 1); + PreprocessorOpts.DisablePCHValidation = true; + PreprocessorOpts.ImplicitPCHInclude = PriorImplicitPCHInclude; + delete OverrideMainBuffer; + } + + Clang.takeSourceManager(); + Clang.takeFileManager(); + Invocation.reset(Clang.takeInvocation()); + return true; +} + +/// \brief Simple function to retrieve a path for a preamble precompiled header. +static std::string GetPreamblePCHPath() { + // FIXME: This is lame; sys::Path should provide this function (in particular, + // it should know how to find the temporary files dir). + // FIXME: This is really lame. I copied this code from the Driver! + std::string Error; + const char *TmpDir = ::getenv("TMPDIR"); + if (!TmpDir) + TmpDir = ::getenv("TEMP"); + if (!TmpDir) + TmpDir = ::getenv("TMP"); + if (!TmpDir) + TmpDir = "/tmp"; + llvm::sys::Path P(TmpDir); + P.appendComponent("preamble"); + P.appendSuffix("pch"); + if (P.createTemporaryFileOnDisk()) + return std::string(); + + return P.str(); +} + +/// \brief Compute the preamble for the main file, providing the source buffer +/// that corresponds to the main file along with a pair (bytes, start-of-line) +/// that describes the preamble. +std::pair<llvm::MemoryBuffer *, std::pair<unsigned, bool> > +ASTUnit::ComputePreamble(CompilerInvocation &Invocation, + unsigned MaxLines, bool &CreatedBuffer) { + FrontendOptions &FrontendOpts = Invocation.getFrontendOpts(); + PreprocessorOptions &PreprocessorOpts + = Invocation.getPreprocessorOpts(); + CreatedBuffer = false; + + // Try to determine if the main file has been remapped, either from the + // command line (to another file) or directly through the compiler invocation + // (to a memory buffer). + llvm::MemoryBuffer *Buffer = 0; + llvm::sys::PathWithStatus MainFilePath(FrontendOpts.Inputs[0].second); + if (const llvm::sys::FileStatus *MainFileStatus = MainFilePath.getFileStatus()) { + // Check whether there is a file-file remapping of the main file + for (PreprocessorOptions::remapped_file_iterator + M = PreprocessorOpts.remapped_file_begin(), + E = PreprocessorOpts.remapped_file_end(); + M != E; + ++M) { + llvm::sys::PathWithStatus MPath(M->first); + if (const llvm::sys::FileStatus *MStatus = MPath.getFileStatus()) { + if (MainFileStatus->uniqueID == MStatus->uniqueID) { + // We found a remapping. Try to load the resulting, remapped source. + if (CreatedBuffer) { + delete Buffer; + CreatedBuffer = false; + } + + Buffer = llvm::MemoryBuffer::getFile(M->second); + if (!Buffer) + return std::make_pair((llvm::MemoryBuffer*)0, + std::make_pair(0, true)); + CreatedBuffer = true; + + // Remove this remapping. We've captured the buffer already. + M = PreprocessorOpts.eraseRemappedFile(M); + E = PreprocessorOpts.remapped_file_end(); + if (M == E) + break; + } + } + } + + // Check whether there is a file-buffer remapping. It supercedes the + // file-file remapping. + for (PreprocessorOptions::remapped_file_buffer_iterator + M = PreprocessorOpts.remapped_file_buffer_begin(), + E = PreprocessorOpts.remapped_file_buffer_end(); + M != E; + ++M) { + llvm::sys::PathWithStatus MPath(M->first); + if (const llvm::sys::FileStatus *MStatus = MPath.getFileStatus()) { + if (MainFileStatus->uniqueID == MStatus->uniqueID) { + // We found a remapping. + if (CreatedBuffer) { + delete Buffer; + CreatedBuffer = false; + } + + Buffer = const_cast<llvm::MemoryBuffer *>(M->second); + + // Remove this remapping. We've captured the buffer already. + M = PreprocessorOpts.eraseRemappedFile(M); + E = PreprocessorOpts.remapped_file_buffer_end(); + if (M == E) + break; + } + } + } + } + + // If the main source file was not remapped, load it now. + if (!Buffer) { + Buffer = llvm::MemoryBuffer::getFile(FrontendOpts.Inputs[0].second); + if (!Buffer) + return std::make_pair((llvm::MemoryBuffer*)0, std::make_pair(0, true)); + + CreatedBuffer = true; + } + + return std::make_pair(Buffer, Lexer::ComputePreamble(Buffer, MaxLines)); +} + +static llvm::MemoryBuffer *CreatePaddedMainFileBuffer(llvm::MemoryBuffer *Old, + bool DeleteOld, + unsigned NewSize, + llvm::StringRef NewName) { + llvm::MemoryBuffer *Result + = llvm::MemoryBuffer::getNewUninitMemBuffer(NewSize, NewName); + memcpy(const_cast<char*>(Result->getBufferStart()), + Old->getBufferStart(), Old->getBufferSize()); + memset(const_cast<char*>(Result->getBufferStart()) + Old->getBufferSize(), + ' ', NewSize - Old->getBufferSize() - 1); + const_cast<char*>(Result->getBufferEnd())[-1] = '\n'; + + if (DeleteOld) + delete Old; + + return Result; +} + +/// \brief Attempt to build or re-use a precompiled preamble when (re-)parsing +/// the source file. +/// +/// This routine will compute the preamble of the main source file. If a +/// non-trivial preamble is found, it will precompile that preamble into a +/// precompiled header so that the precompiled preamble can be used to reduce +/// reparsing time. If a precompiled preamble has already been constructed, +/// this routine will determine if it is still valid and, if so, avoid +/// rebuilding the precompiled preamble. +/// +/// \param AllowRebuild When true (the default), this routine is +/// allowed to rebuild the precompiled preamble if it is found to be +/// out-of-date. +/// +/// \param MaxLines When non-zero, the maximum number of lines that +/// can occur within the preamble. +/// +/// \returns If the precompiled preamble can be used, returns a newly-allocated +/// buffer that should be used in place of the main file when doing so. +/// Otherwise, returns a NULL pointer. +llvm::MemoryBuffer *ASTUnit::getMainBufferWithPrecompiledPreamble( + CompilerInvocation PreambleInvocation, + bool AllowRebuild, + unsigned MaxLines) { + FrontendOptions &FrontendOpts = PreambleInvocation.getFrontendOpts(); + PreprocessorOptions &PreprocessorOpts + = PreambleInvocation.getPreprocessorOpts(); + + bool CreatedPreambleBuffer = false; + std::pair<llvm::MemoryBuffer *, std::pair<unsigned, bool> > NewPreamble + = ComputePreamble(PreambleInvocation, MaxLines, CreatedPreambleBuffer); + + if (!NewPreamble.second.first) { + // We couldn't find a preamble in the main source. Clear out the current + // preamble, if we have one. It's obviously no good any more. + Preamble.clear(); + if (!PreambleFile.empty()) { + llvm::sys::Path(PreambleFile).eraseFromDisk(); + PreambleFile.clear(); + } + if (CreatedPreambleBuffer) + delete NewPreamble.first; + + // The next time we actually see a preamble, precompile it. + PreambleRebuildCounter = 1; + return 0; + } + + if (!Preamble.empty()) { + // We've previously computed a preamble. Check whether we have the same + // preamble now that we did before, and that there's enough space in + // the main-file buffer within the precompiled preamble to fit the + // new main file. + if (Preamble.size() == NewPreamble.second.first && + PreambleEndsAtStartOfLine == NewPreamble.second.second && + NewPreamble.first->getBufferSize() < PreambleReservedSize-2 && + memcmp(&Preamble[0], NewPreamble.first->getBufferStart(), + NewPreamble.second.first) == 0) { + // The preamble has not changed. We may be able to re-use the precompiled + // preamble. + + // Check that none of the files used by the preamble have changed. + bool AnyFileChanged = false; + + // First, make a record of those files that have been overridden via + // remapping or unsaved_files. + llvm::StringMap<std::pair<off_t, time_t> > OverriddenFiles; + for (PreprocessorOptions::remapped_file_iterator + R = PreprocessorOpts.remapped_file_begin(), + REnd = PreprocessorOpts.remapped_file_end(); + !AnyFileChanged && R != REnd; + ++R) { + struct stat StatBuf; + if (stat(R->second.c_str(), &StatBuf)) { + // If we can't stat the file we're remapping to, assume that something + // horrible happened. + AnyFileChanged = true; + break; + } + + OverriddenFiles[R->first] = std::make_pair(StatBuf.st_size, + StatBuf.st_mtime); + } + for (PreprocessorOptions::remapped_file_buffer_iterator + R = PreprocessorOpts.remapped_file_buffer_begin(), + REnd = PreprocessorOpts.remapped_file_buffer_end(); + !AnyFileChanged && R != REnd; + ++R) { + // FIXME: Should we actually compare the contents of file->buffer + // remappings? + OverriddenFiles[R->first] = std::make_pair(R->second->getBufferSize(), + 0); + } + + // Check whether anything has changed. + for (llvm::StringMap<std::pair<off_t, time_t> >::iterator + F = FilesInPreamble.begin(), FEnd = FilesInPreamble.end(); + !AnyFileChanged && F != FEnd; + ++F) { + llvm::StringMap<std::pair<off_t, time_t> >::iterator Overridden + = OverriddenFiles.find(F->first()); + if (Overridden != OverriddenFiles.end()) { + // This file was remapped; check whether the newly-mapped file + // matches up with the previous mapping. + if (Overridden->second != F->second) + AnyFileChanged = true; + continue; + } + + // The file was not remapped; check whether it has changed on disk. + struct stat StatBuf; + if (stat(F->first(), &StatBuf)) { + // If we can't stat the file, assume that something horrible happened. + AnyFileChanged = true; + } else if (StatBuf.st_size != F->second.first || + StatBuf.st_mtime != F->second.second) + AnyFileChanged = true; + } + + if (!AnyFileChanged) { + // Okay! We can re-use the precompiled preamble. + + // Set the state of the diagnostic object to mimic its state + // after parsing the preamble. + getDiagnostics().Reset(); + getDiagnostics().setNumWarnings(NumWarningsInPreamble); + if (StoredDiagnostics.size() > NumStoredDiagnosticsInPreamble) + StoredDiagnostics.erase( + StoredDiagnostics.begin() + NumStoredDiagnosticsInPreamble, + StoredDiagnostics.end()); + + // Create a version of the main file buffer that is padded to + // buffer size we reserved when creating the preamble. + return CreatePaddedMainFileBuffer(NewPreamble.first, + CreatedPreambleBuffer, + PreambleReservedSize, + FrontendOpts.Inputs[0].second); + } + } + + // If we aren't allowed to rebuild the precompiled preamble, just + // return now. + if (!AllowRebuild) + return 0; + + // We can't reuse the previously-computed preamble. Build a new one. + Preamble.clear(); + llvm::sys::Path(PreambleFile).eraseFromDisk(); + PreambleRebuildCounter = 1; + } else if (!AllowRebuild) { + // We aren't allowed to rebuild the precompiled preamble; just + // return now. + return 0; + } + + // If the preamble rebuild counter > 1, it's because we previously + // failed to build a preamble and we're not yet ready to try + // again. Decrement the counter and return a failure. + if (PreambleRebuildCounter > 1) { + --PreambleRebuildCounter; + return 0; + } + + // We did not previously compute a preamble, or it can't be reused anyway. + llvm::Timer *PreambleTimer = 0; + if (TimerGroup.get()) { + PreambleTimer = new llvm::Timer("Precompiling preamble", *TimerGroup); + PreambleTimer->startTimer(); + Timers.push_back(PreambleTimer); + } + + // Create a new buffer that stores the preamble. The buffer also contains + // extra space for the original contents of the file (which will be present + // when we actually parse the file) along with more room in case the file + // grows. + PreambleReservedSize = NewPreamble.first->getBufferSize(); + if (PreambleReservedSize < 4096) + PreambleReservedSize = 8191; + else + PreambleReservedSize *= 2; + + // Save the preamble text for later; we'll need to compare against it for + // subsequent reparses. + Preamble.assign(NewPreamble.first->getBufferStart(), + NewPreamble.first->getBufferStart() + + NewPreamble.second.first); + PreambleEndsAtStartOfLine = NewPreamble.second.second; + + delete PreambleBuffer; + PreambleBuffer + = llvm::MemoryBuffer::getNewUninitMemBuffer(PreambleReservedSize, + FrontendOpts.Inputs[0].second); + memcpy(const_cast<char*>(PreambleBuffer->getBufferStart()), + NewPreamble.first->getBufferStart(), Preamble.size()); + memset(const_cast<char*>(PreambleBuffer->getBufferStart()) + Preamble.size(), + ' ', PreambleReservedSize - Preamble.size() - 1); + const_cast<char*>(PreambleBuffer->getBufferEnd())[-1] = '\n'; + + // Remap the main source file to the preamble buffer. + llvm::sys::PathWithStatus MainFilePath(FrontendOpts.Inputs[0].second); + PreprocessorOpts.addRemappedFile(MainFilePath.str(), PreambleBuffer); + + // Tell the compiler invocation to generate a temporary precompiled header. + FrontendOpts.ProgramAction = frontend::GeneratePCH; + // FIXME: Set ChainedPCH unconditionally, once it is ready. + if (::getenv("LIBCLANG_CHAINING")) + FrontendOpts.ChainedPCH = true; + // FIXME: Generate the precompiled header into memory? + FrontendOpts.OutputFile = GetPreamblePCHPath(); + + // Create the compiler instance to use for building the precompiled preamble. + CompilerInstance Clang; + Clang.setInvocation(&PreambleInvocation); + OriginalSourceFile = Clang.getFrontendOpts().Inputs[0].second; + + // Set up diagnostics, capturing all of the diagnostics produced. + Clang.setDiagnostics(&getDiagnostics()); + CaptureDroppedDiagnostics Capture(CaptureDiagnostics, + getDiagnostics(), + StoredDiagnostics); + + // Create the target instance. + Clang.setTarget(TargetInfo::CreateTargetInfo(Clang.getDiagnostics(), + Clang.getTargetOpts())); + if (!Clang.hasTarget()) { + llvm::sys::Path(FrontendOpts.OutputFile).eraseFromDisk(); + Preamble.clear(); + if (CreatedPreambleBuffer) + delete NewPreamble.first; + if (PreambleTimer) + PreambleTimer->stopTimer(); + PreambleRebuildCounter = DefaultPreambleRebuildInterval; + PreprocessorOpts.eraseRemappedFile( + PreprocessorOpts.remapped_file_buffer_end() - 1); + return 0; + } + + // Inform the target of the language options. + // + // FIXME: We shouldn't need to do this, the target should be immutable once + // created. This complexity should be lifted elsewhere. + Clang.getTarget().setForcedLangOptions(Clang.getLangOpts()); + + assert(Clang.getFrontendOpts().Inputs.size() == 1 && + "Invocation must have exactly one source file!"); + assert(Clang.getFrontendOpts().Inputs[0].first != IK_AST && + "FIXME: AST inputs not yet supported here!"); + assert(Clang.getFrontendOpts().Inputs[0].first != IK_LLVM_IR && + "IR inputs not support here!"); + + // Clear out old caches and data. + StoredDiagnostics.clear(); + TopLevelDecls.clear(); + TopLevelDeclsInPreamble.clear(); + + // Create a file manager object to provide access to and cache the filesystem. + Clang.setFileManager(new FileManager); + + // Create the source manager. + Clang.setSourceManager(new SourceManager(getDiagnostics())); + + llvm::OwningPtr<PrecompilePreambleAction> Act; + Act.reset(new PrecompilePreambleAction(*this)); + if (!Act->BeginSourceFile(Clang, Clang.getFrontendOpts().Inputs[0].second, + Clang.getFrontendOpts().Inputs[0].first)) { + Clang.takeInvocation(); + llvm::sys::Path(FrontendOpts.OutputFile).eraseFromDisk(); + Preamble.clear(); + if (CreatedPreambleBuffer) + delete NewPreamble.first; + if (PreambleTimer) + PreambleTimer->stopTimer(); + PreambleRebuildCounter = DefaultPreambleRebuildInterval; + PreprocessorOpts.eraseRemappedFile( + PreprocessorOpts.remapped_file_buffer_end() - 1); + return 0; + } + + Act->Execute(); + Act->EndSourceFile(); + Clang.takeInvocation(); + + if (Diagnostics->hasErrorOccurred()) { + // There were errors parsing the preamble, so no precompiled header was + // generated. Forget that we even tried. + // FIXME: Should we leave a note for ourselves to try again? + llvm::sys::Path(FrontendOpts.OutputFile).eraseFromDisk(); + Preamble.clear(); + if (CreatedPreambleBuffer) + delete NewPreamble.first; + if (PreambleTimer) + PreambleTimer->stopTimer(); + TopLevelDeclsInPreamble.clear(); + PreambleRebuildCounter = DefaultPreambleRebuildInterval; + PreprocessorOpts.eraseRemappedFile( + PreprocessorOpts.remapped_file_buffer_end() - 1); + return 0; + } + + // Keep track of the preamble we precompiled. + PreambleFile = FrontendOpts.OutputFile; + NumStoredDiagnosticsInPreamble = StoredDiagnostics.size(); + NumWarningsInPreamble = getDiagnostics().getNumWarnings(); + + // Keep track of all of the files that the source manager knows about, + // so we can verify whether they have changed or not. + FilesInPreamble.clear(); + SourceManager &SourceMgr = Clang.getSourceManager(); + const llvm::MemoryBuffer *MainFileBuffer + = SourceMgr.getBuffer(SourceMgr.getMainFileID()); + for (SourceManager::fileinfo_iterator F = SourceMgr.fileinfo_begin(), + FEnd = SourceMgr.fileinfo_end(); + F != FEnd; + ++F) { + const FileEntry *File = F->second->Entry; + if (!File || F->second->getRawBuffer() == MainFileBuffer) + continue; + + FilesInPreamble[File->getName()] + = std::make_pair(F->second->getSize(), File->getModificationTime()); + } + + if (PreambleTimer) + PreambleTimer->stopTimer(); + + PreambleRebuildCounter = 1; + PreprocessorOpts.eraseRemappedFile( + PreprocessorOpts.remapped_file_buffer_end() - 1); + return CreatePaddedMainFileBuffer(NewPreamble.first, + CreatedPreambleBuffer, + PreambleReservedSize, + FrontendOpts.Inputs[0].second); +} + +void ASTUnit::RealizeTopLevelDeclsFromPreamble() { + std::vector<Decl *> Resolved; + Resolved.reserve(TopLevelDeclsInPreamble.size()); + ExternalASTSource &Source = *getASTContext().getExternalSource(); + for (unsigned I = 0, N = TopLevelDeclsInPreamble.size(); I != N; ++I) { + // Resolve the declaration ID to an actual declaration, possibly + // deserializing the declaration in the process. + Decl *D = Source.GetExternalDecl(TopLevelDeclsInPreamble[I]); + if (D) + Resolved.push_back(D); + } + TopLevelDeclsInPreamble.clear(); + TopLevelDecls.insert(TopLevelDecls.begin(), Resolved.begin(), Resolved.end()); +} + +unsigned ASTUnit::getMaxPCHLevel() const { + if (!getOnlyLocalDecls()) + return Decl::MaxPCHLevel; + + unsigned Result = 0; + if (isMainFileAST() || SavedMainFileBuffer) + ++Result; + return Result; +} + +ASTUnit *ASTUnit::LoadFromCompilerInvocation(CompilerInvocation *CI, + llvm::IntrusiveRefCntPtr<Diagnostic> Diags, + bool OnlyLocalDecls, + bool CaptureDiagnostics, + bool PrecompilePreamble, + bool CompleteTranslationUnit, + bool CacheCodeCompletionResults) { + if (!Diags.getPtr()) { + // No diagnostics engine was provided, so create our own diagnostics object + // with the default options. + DiagnosticOptions DiagOpts; + Diags = CompilerInstance::createDiagnostics(DiagOpts, 0, 0); + } + + // Create the AST unit. + llvm::OwningPtr<ASTUnit> AST; + AST.reset(new ASTUnit(false)); + AST->Diagnostics = Diags; + AST->CaptureDiagnostics = CaptureDiagnostics; + AST->OnlyLocalDecls = OnlyLocalDecls; + AST->CompleteTranslationUnit = CompleteTranslationUnit; + AST->ShouldCacheCodeCompletionResults = CacheCodeCompletionResults; + AST->Invocation.reset(CI); + CI->getPreprocessorOpts().RetainRemappedFileBuffers = true; + + if (getenv("LIBCLANG_TIMING")) + AST->TimerGroup.reset( + new llvm::TimerGroup(CI->getFrontendOpts().Inputs[0].second)); + + + llvm::MemoryBuffer *OverrideMainBuffer = 0; + // FIXME: When C++ PCH is ready, allow use of it for a precompiled preamble. + if (PrecompilePreamble && !CI->getLangOpts().CPlusPlus) { + AST->PreambleRebuildCounter = 1; + OverrideMainBuffer + = AST->getMainBufferWithPrecompiledPreamble(*AST->Invocation); + } + + llvm::Timer *ParsingTimer = 0; + if (AST->TimerGroup.get()) { + ParsingTimer = new llvm::Timer("Initial parse", *AST->TimerGroup); + ParsingTimer->startTimer(); + AST->Timers.push_back(ParsingTimer); + } + + bool Failed = AST->Parse(OverrideMainBuffer); + if (ParsingTimer) + ParsingTimer->stopTimer(); + + return Failed? 0 : AST.take(); +} + +ASTUnit *ASTUnit::LoadFromCommandLine(const char **ArgBegin, + const char **ArgEnd, + llvm::IntrusiveRefCntPtr<Diagnostic> Diags, + llvm::StringRef ResourceFilesPath, + bool OnlyLocalDecls, + RemappedFile *RemappedFiles, + unsigned NumRemappedFiles, + bool CaptureDiagnostics, + bool PrecompilePreamble, + bool CompleteTranslationUnit, + bool CacheCodeCompletionResults) { + bool CreatedDiagnosticsObject = false; + + if (!Diags.getPtr()) { + // No diagnostics engine was provided, so create our own diagnostics object + // with the default options. + DiagnosticOptions DiagOpts; + Diags = CompilerInstance::createDiagnostics(DiagOpts, 0, 0); + CreatedDiagnosticsObject = true; + } + + llvm::SmallVector<const char *, 16> Args; + Args.push_back("<clang>"); // FIXME: Remove dummy argument. + Args.insert(Args.end(), ArgBegin, ArgEnd); + + // FIXME: Find a cleaner way to force the driver into restricted modes. We + // also want to force it to use clang. + Args.push_back("-fsyntax-only"); + + // FIXME: We shouldn't have to pass in the path info. + driver::Driver TheDriver("clang", llvm::sys::getHostTriple(), + "a.out", false, false, *Diags); + + // Don't check that inputs exist, they have been remapped. + TheDriver.setCheckInputsExist(false); + + llvm::OwningPtr<driver::Compilation> C( + TheDriver.BuildCompilation(Args.size(), Args.data())); + + // We expect to get back exactly one command job, if we didn't something + // failed. + const driver::JobList &Jobs = C->getJobs(); + if (Jobs.size() != 1 || !isa<driver::Command>(Jobs.begin())) { + llvm::SmallString<256> Msg; + llvm::raw_svector_ostream OS(Msg); + C->PrintJob(OS, C->getJobs(), "; ", true); + Diags->Report(diag::err_fe_expected_compiler_job) << OS.str(); + return 0; + } + + const driver::Command *Cmd = cast<driver::Command>(*Jobs.begin()); + if (llvm::StringRef(Cmd->getCreator().getName()) != "clang") { + Diags->Report(diag::err_fe_expected_clang_command); + return 0; + } + + const driver::ArgStringList &CCArgs = Cmd->getArguments(); + llvm::OwningPtr<CompilerInvocation> CI(new CompilerInvocation); + CompilerInvocation::CreateFromArgs(*CI, + const_cast<const char **>(CCArgs.data()), + const_cast<const char **>(CCArgs.data()) + + CCArgs.size(), + *Diags); + + // Override any files that need remapping + for (unsigned I = 0; I != NumRemappedFiles; ++I) + CI->getPreprocessorOpts().addRemappedFile(RemappedFiles[I].first, + RemappedFiles[I].second); + + // Override the resources path. + CI->getHeaderSearchOpts().ResourceDir = ResourceFilesPath; + + CI->getFrontendOpts().DisableFree = false; + return LoadFromCompilerInvocation(CI.take(), Diags, OnlyLocalDecls, + CaptureDiagnostics, PrecompilePreamble, + CompleteTranslationUnit, + CacheCodeCompletionResults); +} + +bool ASTUnit::Reparse(RemappedFile *RemappedFiles, unsigned NumRemappedFiles) { + if (!Invocation.get()) + return true; + + llvm::Timer *ReparsingTimer = 0; + if (TimerGroup.get()) { + ReparsingTimer = new llvm::Timer("Reparse", *TimerGroup); + ReparsingTimer->startTimer(); + Timers.push_back(ReparsingTimer); + } + + // Remap files. + PreprocessorOptions &PPOpts = Invocation->getPreprocessorOpts(); + for (PreprocessorOptions::remapped_file_buffer_iterator + R = PPOpts.remapped_file_buffer_begin(), + REnd = PPOpts.remapped_file_buffer_end(); + R != REnd; + ++R) { + delete R->second; + } + Invocation->getPreprocessorOpts().clearRemappedFiles(); + for (unsigned I = 0; I != NumRemappedFiles; ++I) + Invocation->getPreprocessorOpts().addRemappedFile(RemappedFiles[I].first, + RemappedFiles[I].second); + + // If we have a preamble file lying around, or if we might try to + // build a precompiled preamble, do so now. + llvm::MemoryBuffer *OverrideMainBuffer = 0; + if (!PreambleFile.empty() || PreambleRebuildCounter > 0) + OverrideMainBuffer = getMainBufferWithPrecompiledPreamble(*Invocation); + + // Clear out the diagnostics state. + if (!OverrideMainBuffer) + getDiagnostics().Reset(); + + // Parse the sources + bool Result = Parse(OverrideMainBuffer); + if (ReparsingTimer) + ReparsingTimer->stopTimer(); + + if (ShouldCacheCodeCompletionResults) { + if (CacheCodeCompletionCoolDown > 0) + --CacheCodeCompletionCoolDown; + else if (top_level_size() != NumTopLevelDeclsAtLastCompletionCache) + CacheCodeCompletionResults(); + } + + return Result; +} + +//----------------------------------------------------------------------------// +// Code completion +//----------------------------------------------------------------------------// + +namespace { + /// \brief Code completion consumer that combines the cached code-completion + /// results from an ASTUnit with the code-completion results provided to it, + /// then passes the result on to + class AugmentedCodeCompleteConsumer : public CodeCompleteConsumer { + unsigned NormalContexts; + ASTUnit &AST; + CodeCompleteConsumer &Next; + + public: + AugmentedCodeCompleteConsumer(ASTUnit &AST, CodeCompleteConsumer &Next, + bool IncludeMacros, bool IncludeCodePatterns, + bool IncludeGlobals) + : CodeCompleteConsumer(IncludeMacros, IncludeCodePatterns, IncludeGlobals, + Next.isOutputBinary()), AST(AST), Next(Next) + { + // Compute the set of contexts in which we will look when we don't have + // any information about the specific context. + NormalContexts + = (1 << (CodeCompletionContext::CCC_TopLevel - 1)) + | (1 << (CodeCompletionContext::CCC_ObjCInterface - 1)) + | (1 << (CodeCompletionContext::CCC_ObjCImplementation - 1)) + | (1 << (CodeCompletionContext::CCC_ObjCIvarList - 1)) + | (1 << (CodeCompletionContext::CCC_Statement - 1)) + | (1 << (CodeCompletionContext::CCC_Expression - 1)) + | (1 << (CodeCompletionContext::CCC_ObjCMessageReceiver - 1)) + | (1 << (CodeCompletionContext::CCC_MemberAccess - 1)) + | (1 << (CodeCompletionContext::CCC_ObjCProtocolName - 1)); + + if (AST.getASTContext().getLangOptions().CPlusPlus) + NormalContexts |= (1 << (CodeCompletionContext::CCC_EnumTag - 1)) + | (1 << (CodeCompletionContext::CCC_UnionTag - 1)) + | (1 << (CodeCompletionContext::CCC_ClassOrStructTag - 1)); + } + + virtual void ProcessCodeCompleteResults(Sema &S, + CodeCompletionContext Context, + CodeCompletionResult *Results, + unsigned NumResults); + + virtual void ProcessOverloadCandidates(Sema &S, unsigned CurrentArg, + OverloadCandidate *Candidates, + unsigned NumCandidates) { + Next.ProcessOverloadCandidates(S, CurrentArg, Candidates, NumCandidates); + } + }; +} + +/// \brief Helper function that computes which global names are hidden by the +/// local code-completion results. +void CalculateHiddenNames(const CodeCompletionContext &Context, + CodeCompletionResult *Results, + unsigned NumResults, + ASTContext &Ctx, + llvm::StringSet<> &HiddenNames) { + bool OnlyTagNames = false; + switch (Context.getKind()) { + case CodeCompletionContext::CCC_Other: + case CodeCompletionContext::CCC_TopLevel: + case CodeCompletionContext::CCC_ObjCInterface: + case CodeCompletionContext::CCC_ObjCImplementation: + case CodeCompletionContext::CCC_ObjCIvarList: + case CodeCompletionContext::CCC_ClassStructUnion: + case CodeCompletionContext::CCC_Statement: + case CodeCompletionContext::CCC_Expression: + case CodeCompletionContext::CCC_ObjCMessageReceiver: + case CodeCompletionContext::CCC_MemberAccess: + case CodeCompletionContext::CCC_Namespace: + case CodeCompletionContext::CCC_Type: + case CodeCompletionContext::CCC_Name: + case CodeCompletionContext::CCC_PotentiallyQualifiedName: + break; + + case CodeCompletionContext::CCC_EnumTag: + case CodeCompletionContext::CCC_UnionTag: + case CodeCompletionContext::CCC_ClassOrStructTag: + OnlyTagNames = true; + break; + + case CodeCompletionContext::CCC_ObjCProtocolName: + case CodeCompletionContext::CCC_MacroName: + case CodeCompletionContext::CCC_MacroNameUse: + case CodeCompletionContext::CCC_PreprocessorExpression: + case CodeCompletionContext::CCC_PreprocessorDirective: + case CodeCompletionContext::CCC_NaturalLanguage: + case CodeCompletionContext::CCC_SelectorName: + case CodeCompletionContext::CCC_TypeQualifiers: + // We're looking for nothing, or we're looking for names that cannot + // be hidden. + return; + } + + typedef CodeCompletionResult Result; + for (unsigned I = 0; I != NumResults; ++I) { + if (Results[I].Kind != Result::RK_Declaration) + continue; + + unsigned IDNS + = Results[I].Declaration->getUnderlyingDecl()->getIdentifierNamespace(); + + bool Hiding = false; + if (OnlyTagNames) + Hiding = (IDNS & Decl::IDNS_Tag); + else { + unsigned HiddenIDNS = (Decl::IDNS_Type | Decl::IDNS_Member | + Decl::IDNS_Namespace | Decl::IDNS_Ordinary | + Decl::IDNS_NonMemberOperator); + if (Ctx.getLangOptions().CPlusPlus) + HiddenIDNS |= Decl::IDNS_Tag; + Hiding = (IDNS & HiddenIDNS); + } + + if (!Hiding) + continue; + + DeclarationName Name = Results[I].Declaration->getDeclName(); + if (IdentifierInfo *Identifier = Name.getAsIdentifierInfo()) + HiddenNames.insert(Identifier->getName()); + else + HiddenNames.insert(Name.getAsString()); + } +} + + +void AugmentedCodeCompleteConsumer::ProcessCodeCompleteResults(Sema &S, + CodeCompletionContext Context, + CodeCompletionResult *Results, + unsigned NumResults) { + // Merge the results we were given with the results we cached. + bool AddedResult = false; + unsigned InContexts + = (Context.getKind() == CodeCompletionContext::CCC_Other? NormalContexts + : (1 << (Context.getKind() - 1))); + + // Contains the set of names that are hidden by "local" completion results. + llvm::StringSet<> HiddenNames; + llvm::SmallVector<CodeCompletionString *, 4> StringsToDestroy; + typedef CodeCompletionResult Result; + llvm::SmallVector<Result, 8> AllResults; + for (ASTUnit::cached_completion_iterator + C = AST.cached_completion_begin(), + CEnd = AST.cached_completion_end(); + C != CEnd; ++C) { + // If the context we are in matches any of the contexts we are + // interested in, we'll add this result. + if ((C->ShowInContexts & InContexts) == 0) + continue; + + // If we haven't added any results previously, do so now. + if (!AddedResult) { + CalculateHiddenNames(Context, Results, NumResults, S.Context, + HiddenNames); + AllResults.insert(AllResults.end(), Results, Results + NumResults); + AddedResult = true; + } + + // Determine whether this global completion result is hidden by a local + // completion result. If so, skip it. + if (C->Kind != CXCursor_MacroDefinition && + HiddenNames.count(C->Completion->getTypedText())) + continue; + + // Adjust priority based on similar type classes. + unsigned Priority = C->Priority; + CXCursorKind CursorKind = C->Kind; + CodeCompletionString *Completion = C->Completion; + if (!Context.getPreferredType().isNull()) { + if (C->Kind == CXCursor_MacroDefinition) { + Priority = getMacroUsagePriority(C->Completion->getTypedText(), + Context.getPreferredType()->isAnyPointerType()); + } else if (C->Type) { + CanQualType Expected + = S.Context.getCanonicalType( + Context.getPreferredType().getUnqualifiedType()); + SimplifiedTypeClass ExpectedSTC = getSimplifiedTypeClass(Expected); + if (ExpectedSTC == C->TypeClass) { + // We know this type is similar; check for an exact match. + llvm::StringMap<unsigned> &CachedCompletionTypes + = AST.getCachedCompletionTypes(); + llvm::StringMap<unsigned>::iterator Pos + = CachedCompletionTypes.find(QualType(Expected).getAsString()); + if (Pos != CachedCompletionTypes.end() && Pos->second == C->Type) + Priority /= CCF_ExactTypeMatch; + else + Priority /= CCF_SimilarTypeMatch; + } + } + } + + // Adjust the completion string, if required. + if (C->Kind == CXCursor_MacroDefinition && + Context.getKind() == CodeCompletionContext::CCC_MacroNameUse) { + // Create a new code-completion string that just contains the + // macro name, without its arguments. + Completion = new CodeCompletionString; + Completion->AddTypedTextChunk(C->Completion->getTypedText()); + StringsToDestroy.push_back(Completion); + CursorKind = CXCursor_NotImplemented; + Priority = CCP_CodePattern; + } + + AllResults.push_back(Result(Completion, Priority, CursorKind, + C->Availability)); + } + + // If we did not add any cached completion results, just forward the + // results we were given to the next consumer. + if (!AddedResult) { + Next.ProcessCodeCompleteResults(S, Context, Results, NumResults); + return; + } + + Next.ProcessCodeCompleteResults(S, Context, AllResults.data(), + AllResults.size()); + + for (unsigned I = 0, N = StringsToDestroy.size(); I != N; ++I) + delete StringsToDestroy[I]; +} + + + +void ASTUnit::CodeComplete(llvm::StringRef File, unsigned Line, unsigned Column, + RemappedFile *RemappedFiles, + unsigned NumRemappedFiles, + bool IncludeMacros, + bool IncludeCodePatterns, + CodeCompleteConsumer &Consumer, + Diagnostic &Diag, LangOptions &LangOpts, + SourceManager &SourceMgr, FileManager &FileMgr, + llvm::SmallVectorImpl<StoredDiagnostic> &StoredDiagnostics, + llvm::SmallVectorImpl<const llvm::MemoryBuffer *> &OwnedBuffers) { + if (!Invocation.get()) + return; + + llvm::Timer *CompletionTimer = 0; + if (TimerGroup.get()) { + llvm::SmallString<128> TimerName; + llvm::raw_svector_ostream TimerNameOut(TimerName); + TimerNameOut << "Code completion @ " << File << ":" << Line << ":" + << Column; + CompletionTimer = new llvm::Timer(TimerNameOut.str(), *TimerGroup); + CompletionTimer->startTimer(); + Timers.push_back(CompletionTimer); + } + + CompilerInvocation CCInvocation(*Invocation); + FrontendOptions &FrontendOpts = CCInvocation.getFrontendOpts(); + PreprocessorOptions &PreprocessorOpts = CCInvocation.getPreprocessorOpts(); + + FrontendOpts.ShowMacrosInCodeCompletion + = IncludeMacros && CachedCompletionResults.empty(); + FrontendOpts.ShowCodePatternsInCodeCompletion = IncludeCodePatterns; + FrontendOpts.ShowGlobalSymbolsInCodeCompletion + = CachedCompletionResults.empty(); + FrontendOpts.CodeCompletionAt.FileName = File; + FrontendOpts.CodeCompletionAt.Line = Line; + FrontendOpts.CodeCompletionAt.Column = Column; + + // Turn on spell-checking when performing code completion. It leads + // to better results. + unsigned SpellChecking = CCInvocation.getLangOpts().SpellChecking; + CCInvocation.getLangOpts().SpellChecking = 1; + + // Set the language options appropriately. + LangOpts = CCInvocation.getLangOpts(); + + CompilerInstance Clang; + Clang.setInvocation(&CCInvocation); + OriginalSourceFile = Clang.getFrontendOpts().Inputs[0].second; + + // Set up diagnostics, capturing any diagnostics produced. + Clang.setDiagnostics(&Diag); + CaptureDroppedDiagnostics Capture(true, + Clang.getDiagnostics(), + StoredDiagnostics); + + // Create the target instance. + Clang.setTarget(TargetInfo::CreateTargetInfo(Clang.getDiagnostics(), + Clang.getTargetOpts())); + if (!Clang.hasTarget()) { + Clang.takeInvocation(); + CCInvocation.getLangOpts().SpellChecking = SpellChecking; + return; + } + + // Inform the target of the language options. + // + // FIXME: We shouldn't need to do this, the target should be immutable once + // created. This complexity should be lifted elsewhere. + Clang.getTarget().setForcedLangOptions(Clang.getLangOpts()); + + assert(Clang.getFrontendOpts().Inputs.size() == 1 && + "Invocation must have exactly one source file!"); + assert(Clang.getFrontendOpts().Inputs[0].first != IK_AST && + "FIXME: AST inputs not yet supported here!"); + assert(Clang.getFrontendOpts().Inputs[0].first != IK_LLVM_IR && + "IR inputs not support here!"); + + + // Use the source and file managers that we were given. + Clang.setFileManager(&FileMgr); + Clang.setSourceManager(&SourceMgr); + + // Remap files. + PreprocessorOpts.clearRemappedFiles(); + PreprocessorOpts.RetainRemappedFileBuffers = true; + for (unsigned I = 0; I != NumRemappedFiles; ++I) { + PreprocessorOpts.addRemappedFile(RemappedFiles[I].first, + RemappedFiles[I].second); + OwnedBuffers.push_back(RemappedFiles[I].second); + } + + // Use the code completion consumer we were given, but adding any cached + // code-completion results. + AugmentedCodeCompleteConsumer + AugmentedConsumer(*this, Consumer, FrontendOpts.ShowMacrosInCodeCompletion, + FrontendOpts.ShowCodePatternsInCodeCompletion, + FrontendOpts.ShowGlobalSymbolsInCodeCompletion); + Clang.setCodeCompletionConsumer(&AugmentedConsumer); + + // If we have a precompiled preamble, try to use it. We only allow + // the use of the precompiled preamble if we're if the completion + // point is within the main file, after the end of the precompiled + // preamble. + llvm::MemoryBuffer *OverrideMainBuffer = 0; + if (!PreambleFile.empty()) { + using llvm::sys::FileStatus; + llvm::sys::PathWithStatus CompleteFilePath(File); + llvm::sys::PathWithStatus MainPath(OriginalSourceFile); + if (const FileStatus *CompleteFileStatus = CompleteFilePath.getFileStatus()) + if (const FileStatus *MainStatus = MainPath.getFileStatus()) + if (CompleteFileStatus->getUniqueID() == MainStatus->getUniqueID()) + OverrideMainBuffer + = getMainBufferWithPrecompiledPreamble(CCInvocation, false, + Line - 1); + } + + // If the main file has been overridden due to the use of a preamble, + // make that override happen and introduce the preamble. + if (OverrideMainBuffer) { + PreprocessorOpts.addRemappedFile(OriginalSourceFile, OverrideMainBuffer); + PreprocessorOpts.PrecompiledPreambleBytes.first = Preamble.size(); + PreprocessorOpts.PrecompiledPreambleBytes.second + = PreambleEndsAtStartOfLine; + PreprocessorOpts.ImplicitPCHInclude = PreambleFile; + PreprocessorOpts.DisablePCHValidation = true; + + // The stored diagnostics have the old source manager. Copy them + // to our output set of stored diagnostics, updating the source + // manager to the one we were given. + for (unsigned I = 0, N = this->StoredDiagnostics.size(); I != N; ++I) { + StoredDiagnostics.push_back(this->StoredDiagnostics[I]); + FullSourceLoc Loc(StoredDiagnostics[I].getLocation(), SourceMgr); + StoredDiagnostics[I].setLocation(Loc); + } + + OwnedBuffers.push_back(OverrideMainBuffer); + } else { + PreprocessorOpts.PrecompiledPreambleBytes.first = 0; + PreprocessorOpts.PrecompiledPreambleBytes.second = false; + } + + llvm::OwningPtr<SyntaxOnlyAction> Act; + Act.reset(new SyntaxOnlyAction); + if (Act->BeginSourceFile(Clang, Clang.getFrontendOpts().Inputs[0].second, + Clang.getFrontendOpts().Inputs[0].first)) { + Act->Execute(); + Act->EndSourceFile(); + } + + if (CompletionTimer) + CompletionTimer->stopTimer(); + + // Steal back our resources. + Clang.takeFileManager(); + Clang.takeSourceManager(); + Clang.takeInvocation(); + Clang.takeCodeCompletionConsumer(); + CCInvocation.getLangOpts().SpellChecking = SpellChecking; +} + +bool ASTUnit::Save(llvm::StringRef File) { + if (getDiagnostics().hasErrorOccurred()) + return true; + + // FIXME: Can we somehow regenerate the stat cache here, or do we need to + // unconditionally create a stat cache when we parse the file? + std::string ErrorInfo; + llvm::raw_fd_ostream Out(File.str().c_str(), ErrorInfo, + llvm::raw_fd_ostream::F_Binary); + if (!ErrorInfo.empty() || Out.has_error()) + return true; + + std::vector<unsigned char> Buffer; + llvm::BitstreamWriter Stream(Buffer); + ASTWriter Writer(Stream); + Writer.WriteAST(getSema(), 0, 0); + + // Write the generated bitstream to "Out". + if (!Buffer.empty()) + Out.write((char *)&Buffer.front(), Buffer.size()); + Out.close(); + return Out.has_error(); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/BoostConAction.cpp b/contrib/llvm/tools/clang/lib/Frontend/BoostConAction.cpp new file mode 100644 index 0000000..4a12ff2 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/BoostConAction.cpp @@ -0,0 +1,39 @@ +//===-- BoostConAction.cpp - BoostCon Workshop Action -----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "clang/Frontend/FrontendActions.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include <cstdio> +#include <iostream> +using namespace clang; + +namespace { + class BoostConASTConsumer : public ASTConsumer, + public RecursiveASTVisitor<BoostConASTConsumer> { + public: + /// HandleTranslationUnit - This method is called when the ASTs for entire + /// translation unit have been parsed. + virtual void HandleTranslationUnit(ASTContext &Ctx); + + bool VisitCXXRecordDecl(CXXRecordDecl *D) { + std::cout << D->getNameAsString() << std::endl; + return true; + } + }; +} + +ASTConsumer *BoostConAction::CreateASTConsumer(CompilerInstance &CI, + llvm::StringRef InFile) { + return new BoostConASTConsumer(); +} + +void BoostConASTConsumer::HandleTranslationUnit(ASTContext &Ctx) { + fprintf(stderr, "Welcome to BoostCon!\n"); + TraverseDecl(Ctx.getTranslationUnitDecl()); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/CMakeLists.txt b/contrib/llvm/tools/clang/lib/Frontend/CMakeLists.txt new file mode 100644 index 0000000..5a31495 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/CMakeLists.txt @@ -0,0 +1,45 @@ +set(LLVM_NO_RTTI 1) + +add_clang_library(clangFrontend + ASTConsumers.cpp + ASTMerge.cpp + ASTUnit.cpp + BoostConAction.cpp + CacheTokens.cpp + CompilerInstance.cpp + CompilerInvocation.cpp + DeclXML.cpp + DependencyFile.cpp + DiagChecker.cpp + DocumentXML.cpp + FrontendAction.cpp + FrontendActions.cpp + FrontendOptions.cpp + InitHeaderSearch.cpp + InitPreprocessor.cpp + LangStandards.cpp + PrintPreprocessedOutput.cpp + StmtXML.cpp + TextDiagnosticBuffer.cpp + TextDiagnosticPrinter.cpp + TypeXML.cpp + VerifyDiagnosticsClient.cpp + Warnings.cpp + ) + +IF(MSVC) + get_target_property(NON_ANSI_COMPILE_FLAGS clangFrontend COMPILE_FLAGS) + string(REPLACE /Za + "" NON_ANSI_COMPILE_FLAGS + ${NON_ANSI_COMPILE_FLAGS}) + set_target_properties(clangFrontend PROPERTIES COMPILE_FLAGS ${NON_ANSI_COMPILE_FLAGS}) +ENDIF(MSVC) + +add_dependencies(clangFrontend + ClangAttrClasses + ClangAttrList + ClangDiagnosticFrontend + ClangDiagnosticLex + ClangDiagnosticSema + ClangDeclNodes + ClangStmtNodes) diff --git a/contrib/llvm/tools/clang/lib/Frontend/CacheTokens.cpp b/contrib/llvm/tools/clang/lib/Frontend/CacheTokens.cpp new file mode 100644 index 0000000..53f7362 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/CacheTokens.cpp @@ -0,0 +1,654 @@ +//===--- CacheTokens.cpp - Caching of lexer tokens for PTH support --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This provides a possible implementation of PTH support for Clang that is +// based on caching lexed tokens and identifiers. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/Utils.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/OnDiskHashTable.h" +#include "clang/Lex/Lexer.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/System/Path.h" + +// FIXME: put this somewhere else? +#ifndef S_ISDIR +#define S_ISDIR(x) (((x)&_S_IFDIR)!=0) +#endif + +using namespace clang; +using namespace clang::io; + +//===----------------------------------------------------------------------===// +// PTH-specific stuff. +//===----------------------------------------------------------------------===// + +namespace { +class PTHEntry { + Offset TokenData, PPCondData; + +public: + PTHEntry() {} + + PTHEntry(Offset td, Offset ppcd) + : TokenData(td), PPCondData(ppcd) {} + + Offset getTokenOffset() const { return TokenData; } + Offset getPPCondTableOffset() const { return PPCondData; } +}; + + +class PTHEntryKeyVariant { + union { const FileEntry* FE; const char* Path; }; + enum { IsFE = 0x1, IsDE = 0x2, IsNoExist = 0x0 } Kind; + struct stat *StatBuf; +public: + PTHEntryKeyVariant(const FileEntry *fe) + : FE(fe), Kind(IsFE), StatBuf(0) {} + + PTHEntryKeyVariant(struct stat* statbuf, const char* path) + : Path(path), Kind(IsDE), StatBuf(new struct stat(*statbuf)) {} + + explicit PTHEntryKeyVariant(const char* path) + : Path(path), Kind(IsNoExist), StatBuf(0) {} + + bool isFile() const { return Kind == IsFE; } + + llvm::StringRef getString() const { + return Kind == IsFE ? FE->getName() : Path; + } + + unsigned getKind() const { return (unsigned) Kind; } + + void EmitData(llvm::raw_ostream& Out) { + switch (Kind) { + case IsFE: + // Emit stat information. + ::Emit32(Out, FE->getInode()); + ::Emit32(Out, FE->getDevice()); + ::Emit16(Out, FE->getFileMode()); + ::Emit64(Out, FE->getModificationTime()); + ::Emit64(Out, FE->getSize()); + break; + case IsDE: + // Emit stat information. + ::Emit32(Out, (uint32_t) StatBuf->st_ino); + ::Emit32(Out, (uint32_t) StatBuf->st_dev); + ::Emit16(Out, (uint16_t) StatBuf->st_mode); + ::Emit64(Out, (uint64_t) StatBuf->st_mtime); + ::Emit64(Out, (uint64_t) StatBuf->st_size); + delete StatBuf; + break; + default: + break; + } + } + + unsigned getRepresentationLength() const { + return Kind == IsNoExist ? 0 : 4 + 4 + 2 + 8 + 8; + } +}; + +class FileEntryPTHEntryInfo { +public: + typedef PTHEntryKeyVariant key_type; + typedef key_type key_type_ref; + + typedef PTHEntry data_type; + typedef const PTHEntry& data_type_ref; + + static unsigned ComputeHash(PTHEntryKeyVariant V) { + return llvm::HashString(V.getString()); + } + + static std::pair<unsigned,unsigned> + EmitKeyDataLength(llvm::raw_ostream& Out, PTHEntryKeyVariant V, + const PTHEntry& E) { + + unsigned n = V.getString().size() + 1 + 1; + ::Emit16(Out, n); + + unsigned m = V.getRepresentationLength() + (V.isFile() ? 4 + 4 : 0); + ::Emit8(Out, m); + + return std::make_pair(n, m); + } + + static void EmitKey(llvm::raw_ostream& Out, PTHEntryKeyVariant V, unsigned n){ + // Emit the entry kind. + ::Emit8(Out, (unsigned) V.getKind()); + // Emit the string. + Out.write(V.getString().data(), n - 1); + } + + static void EmitData(llvm::raw_ostream& Out, PTHEntryKeyVariant V, + const PTHEntry& E, unsigned) { + + + // For file entries emit the offsets into the PTH file for token data + // and the preprocessor blocks table. + if (V.isFile()) { + ::Emit32(Out, E.getTokenOffset()); + ::Emit32(Out, E.getPPCondTableOffset()); + } + + // Emit any other data associated with the key (i.e., stat information). + V.EmitData(Out); + } +}; + +class OffsetOpt { + bool valid; + Offset off; +public: + OffsetOpt() : valid(false) {} + bool hasOffset() const { return valid; } + Offset getOffset() const { assert(valid); return off; } + void setOffset(Offset o) { off = o; valid = true; } +}; +} // end anonymous namespace + +typedef OnDiskChainedHashTableGenerator<FileEntryPTHEntryInfo> PTHMap; + +namespace { +class PTHWriter { + typedef llvm::DenseMap<const IdentifierInfo*,uint32_t> IDMap; + typedef llvm::StringMap<OffsetOpt, llvm::BumpPtrAllocator> CachedStrsTy; + + IDMap IM; + llvm::raw_fd_ostream& Out; + Preprocessor& PP; + uint32_t idcount; + PTHMap PM; + CachedStrsTy CachedStrs; + Offset CurStrOffset; + std::vector<llvm::StringMapEntry<OffsetOpt>*> StrEntries; + + //// Get the persistent id for the given IdentifierInfo*. + uint32_t ResolveID(const IdentifierInfo* II); + + /// Emit a token to the PTH file. + void EmitToken(const Token& T); + + void Emit8(uint32_t V) { ::Emit8(Out, V); } + + void Emit16(uint32_t V) { ::Emit16(Out, V); } + + void Emit24(uint32_t V) { ::Emit24(Out, V); } + + void Emit32(uint32_t V) { ::Emit32(Out, V); } + + void EmitBuf(const char *Ptr, unsigned NumBytes) { + Out.write(Ptr, NumBytes); + } + + void EmitString(llvm::StringRef V) { + ::Emit16(Out, V.size()); + EmitBuf(V.data(), V.size()); + } + + /// EmitIdentifierTable - Emits two tables to the PTH file. The first is + /// a hashtable mapping from identifier strings to persistent IDs. + /// The second is a straight table mapping from persistent IDs to string data + /// (the keys of the first table). + std::pair<Offset, Offset> EmitIdentifierTable(); + + /// EmitFileTable - Emit a table mapping from file name strings to PTH + /// token data. + Offset EmitFileTable() { return PM.Emit(Out); } + + PTHEntry LexTokens(Lexer& L); + Offset EmitCachedSpellings(); + +public: + PTHWriter(llvm::raw_fd_ostream& out, Preprocessor& pp) + : Out(out), PP(pp), idcount(0), CurStrOffset(0) {} + + PTHMap &getPM() { return PM; } + void GeneratePTH(const std::string &MainFile); +}; +} // end anonymous namespace + +uint32_t PTHWriter::ResolveID(const IdentifierInfo* II) { + // Null IdentifierInfo's map to the persistent ID 0. + if (!II) + return 0; + + IDMap::iterator I = IM.find(II); + if (I != IM.end()) + return I->second; // We've already added 1. + + IM[II] = ++idcount; // Pre-increment since '0' is reserved for NULL. + return idcount; +} + +void PTHWriter::EmitToken(const Token& T) { + // Emit the token kind, flags, and length. + Emit32(((uint32_t) T.getKind()) | ((((uint32_t) T.getFlags())) << 8)| + (((uint32_t) T.getLength()) << 16)); + + if (!T.isLiteral()) { + Emit32(ResolveID(T.getIdentifierInfo())); + } else { + // We cache *un-cleaned* spellings. This gives us 100% fidelity with the + // source code. + const char* s = T.getLiteralData(); + unsigned len = T.getLength(); + + // Get the string entry. + llvm::StringMapEntry<OffsetOpt> *E = &CachedStrs.GetOrCreateValue(s, s+len); + + // If this is a new string entry, bump the PTH offset. + if (!E->getValue().hasOffset()) { + E->getValue().setOffset(CurStrOffset); + StrEntries.push_back(E); + CurStrOffset += len + 1; + } + + // Emit the relative offset into the PTH file for the spelling string. + Emit32(E->getValue().getOffset()); + } + + // Emit the offset into the original source file of this token so that we + // can reconstruct its SourceLocation. + Emit32(PP.getSourceManager().getFileOffset(T.getLocation())); +} + +PTHEntry PTHWriter::LexTokens(Lexer& L) { + // Pad 0's so that we emit tokens to a 4-byte alignment. + // This speed up reading them back in. + Pad(Out, 4); + Offset TokenOff = (Offset) Out.tell(); + + // Keep track of matching '#if' ... '#endif'. + typedef std::vector<std::pair<Offset, unsigned> > PPCondTable; + PPCondTable PPCond; + std::vector<unsigned> PPStartCond; + bool ParsingPreprocessorDirective = false; + Token Tok; + + do { + L.LexFromRawLexer(Tok); + NextToken: + + if ((Tok.isAtStartOfLine() || Tok.is(tok::eof)) && + ParsingPreprocessorDirective) { + // Insert an eom token into the token cache. It has the same + // position as the next token that is not on the same line as the + // preprocessor directive. Observe that we continue processing + // 'Tok' when we exit this branch. + Token Tmp = Tok; + Tmp.setKind(tok::eom); + Tmp.clearFlag(Token::StartOfLine); + Tmp.setIdentifierInfo(0); + EmitToken(Tmp); + ParsingPreprocessorDirective = false; + } + + if (Tok.is(tok::identifier)) { + PP.LookUpIdentifierInfo(Tok); + EmitToken(Tok); + continue; + } + + if (Tok.is(tok::hash) && Tok.isAtStartOfLine()) { + // Special processing for #include. Store the '#' token and lex + // the next token. + assert(!ParsingPreprocessorDirective); + Offset HashOff = (Offset) Out.tell(); + + // Get the next token. + Token NextTok; + L.LexFromRawLexer(NextTok); + + // If we see the start of line, then we had a null directive "#". In + // this case, discard both tokens. + if (NextTok.isAtStartOfLine()) + goto NextToken; + + // The token is the start of a directive. Emit it. + EmitToken(Tok); + Tok = NextTok; + + // Did we see 'include'/'import'/'include_next'? + if (Tok.isNot(tok::identifier)) { + EmitToken(Tok); + continue; + } + + IdentifierInfo* II = PP.LookUpIdentifierInfo(Tok); + tok::PPKeywordKind K = II->getPPKeywordID(); + + ParsingPreprocessorDirective = true; + + switch (K) { + case tok::pp_not_keyword: + // Invalid directives "#foo" can occur in #if 0 blocks etc, just pass + // them through. + default: + break; + + case tok::pp_include: + case tok::pp_import: + case tok::pp_include_next: { + // Save the 'include' token. + EmitToken(Tok); + // Lex the next token as an include string. + L.setParsingPreprocessorDirective(true); + L.LexIncludeFilename(Tok); + L.setParsingPreprocessorDirective(false); + assert(!Tok.isAtStartOfLine()); + if (Tok.is(tok::identifier)) + PP.LookUpIdentifierInfo(Tok); + + break; + } + case tok::pp_if: + case tok::pp_ifdef: + case tok::pp_ifndef: { + // Add an entry for '#if' and friends. We initially set the target + // index to 0. This will get backpatched when we hit #endif. + PPStartCond.push_back(PPCond.size()); + PPCond.push_back(std::make_pair(HashOff, 0U)); + break; + } + case tok::pp_endif: { + // Add an entry for '#endif'. We set the target table index to itself. + // This will later be set to zero when emitting to the PTH file. We + // use 0 for uninitialized indices because that is easier to debug. + unsigned index = PPCond.size(); + // Backpatch the opening '#if' entry. + assert(!PPStartCond.empty()); + assert(PPCond.size() > PPStartCond.back()); + assert(PPCond[PPStartCond.back()].second == 0); + PPCond[PPStartCond.back()].second = index; + PPStartCond.pop_back(); + // Add the new entry to PPCond. + PPCond.push_back(std::make_pair(HashOff, index)); + EmitToken(Tok); + + // Some files have gibberish on the same line as '#endif'. + // Discard these tokens. + do + L.LexFromRawLexer(Tok); + while (Tok.isNot(tok::eof) && !Tok.isAtStartOfLine()); + // We have the next token in hand. + // Don't immediately lex the next one. + goto NextToken; + } + case tok::pp_elif: + case tok::pp_else: { + // Add an entry for #elif or #else. + // This serves as both a closing and opening of a conditional block. + // This means that its entry will get backpatched later. + unsigned index = PPCond.size(); + // Backpatch the previous '#if' entry. + assert(!PPStartCond.empty()); + assert(PPCond.size() > PPStartCond.back()); + assert(PPCond[PPStartCond.back()].second == 0); + PPCond[PPStartCond.back()].second = index; + PPStartCond.pop_back(); + // Now add '#elif' as a new block opening. + PPCond.push_back(std::make_pair(HashOff, 0U)); + PPStartCond.push_back(index); + break; + } + } + } + + EmitToken(Tok); + } + while (Tok.isNot(tok::eof)); + + assert(PPStartCond.empty() && "Error: imblanced preprocessor conditionals."); + + // Next write out PPCond. + Offset PPCondOff = (Offset) Out.tell(); + + // Write out the size of PPCond so that clients can identifer empty tables. + Emit32(PPCond.size()); + + for (unsigned i = 0, e = PPCond.size(); i!=e; ++i) { + Emit32(PPCond[i].first - TokenOff); + uint32_t x = PPCond[i].second; + assert(x != 0 && "PPCond entry not backpatched."); + // Emit zero for #endifs. This allows us to do checking when + // we read the PTH file back in. + Emit32(x == i ? 0 : x); + } + + return PTHEntry(TokenOff, PPCondOff); +} + +Offset PTHWriter::EmitCachedSpellings() { + // Write each cached strings to the PTH file. + Offset SpellingsOff = Out.tell(); + + for (std::vector<llvm::StringMapEntry<OffsetOpt>*>::iterator + I = StrEntries.begin(), E = StrEntries.end(); I!=E; ++I) + EmitBuf((*I)->getKeyData(), (*I)->getKeyLength()+1 /*nul included*/); + + return SpellingsOff; +} + +void PTHWriter::GeneratePTH(const std::string &MainFile) { + // Generate the prologue. + Out << "cfe-pth"; + Emit32(PTHManager::Version); + + // Leave 4 words for the prologue. + Offset PrologueOffset = Out.tell(); + for (unsigned i = 0; i < 4; ++i) + Emit32(0); + + // Write the name of the MainFile. + if (!MainFile.empty()) { + EmitString(MainFile); + } else { + // String with 0 bytes. + Emit16(0); + } + Emit8(0); + + // Iterate over all the files in SourceManager. Create a lexer + // for each file and cache the tokens. + SourceManager &SM = PP.getSourceManager(); + const LangOptions &LOpts = PP.getLangOptions(); + + for (SourceManager::fileinfo_iterator I = SM.fileinfo_begin(), + E = SM.fileinfo_end(); I != E; ++I) { + const SrcMgr::ContentCache &C = *I->second; + const FileEntry *FE = C.Entry; + + // FIXME: Handle files with non-absolute paths. + llvm::sys::Path P(FE->getName()); + if (!P.isAbsolute()) + continue; + + const llvm::MemoryBuffer *B = C.getBuffer(PP.getDiagnostics(), SM); + if (!B) continue; + + FileID FID = SM.createFileID(FE, SourceLocation(), SrcMgr::C_User); + const llvm::MemoryBuffer *FromFile = SM.getBuffer(FID); + Lexer L(FID, FromFile, SM, LOpts); + PM.insert(FE, LexTokens(L)); + } + + // Write out the identifier table. + const std::pair<Offset,Offset> &IdTableOff = EmitIdentifierTable(); + + // Write out the cached strings table. + Offset SpellingOff = EmitCachedSpellings(); + + // Write out the file table. + Offset FileTableOff = EmitFileTable(); + + // Finally, write the prologue. + Out.seek(PrologueOffset); + Emit32(IdTableOff.first); + Emit32(IdTableOff.second); + Emit32(FileTableOff); + Emit32(SpellingOff); +} + +namespace { +/// StatListener - A simple "interpose" object used to monitor stat calls +/// invoked by FileManager while processing the original sources used +/// as input to PTH generation. StatListener populates the PTHWriter's +/// file map with stat information for directories as well as negative stats. +/// Stat information for files are populated elsewhere. +class StatListener : public StatSysCallCache { + PTHMap &PM; +public: + StatListener(PTHMap &pm) : PM(pm) {} + ~StatListener() {} + + int stat(const char *path, struct stat *buf) { + int result = StatSysCallCache::stat(path, buf); + + if (result != 0) // Failed 'stat'. + PM.insert(PTHEntryKeyVariant(path), PTHEntry()); + else if (S_ISDIR(buf->st_mode)) { + // Only cache directories with absolute paths. + if (!llvm::sys::Path(path).isAbsolute()) + return result; + + PM.insert(PTHEntryKeyVariant(buf, path), PTHEntry()); + } + + return result; + } +}; +} // end anonymous namespace + + +void clang::CacheTokens(Preprocessor &PP, llvm::raw_fd_ostream* OS) { + // Get the name of the main file. + const SourceManager &SrcMgr = PP.getSourceManager(); + const FileEntry *MainFile = SrcMgr.getFileEntryForID(SrcMgr.getMainFileID()); + llvm::sys::Path MainFilePath(MainFile->getName()); + + MainFilePath.makeAbsolute(); + + // Create the PTHWriter. + PTHWriter PW(*OS, PP); + + // Install the 'stat' system call listener in the FileManager. + StatListener *StatCache = new StatListener(PW.getPM()); + PP.getFileManager().addStatCache(StatCache, /*AtBeginning=*/true); + + // Lex through the entire file. This will populate SourceManager with + // all of the header information. + Token Tok; + PP.EnterMainSourceFile(); + do { PP.Lex(Tok); } while (Tok.isNot(tok::eof)); + + // Generate the PTH file. + PP.getFileManager().removeStatCache(StatCache); + PW.GeneratePTH(MainFilePath.str()); +} + +//===----------------------------------------------------------------------===// + +namespace { +class PTHIdKey { +public: + const IdentifierInfo* II; + uint32_t FileOffset; +}; + +class PTHIdentifierTableTrait { +public: + typedef PTHIdKey* key_type; + typedef key_type key_type_ref; + + typedef uint32_t data_type; + typedef data_type data_type_ref; + + static unsigned ComputeHash(PTHIdKey* key) { + return llvm::HashString(key->II->getName()); + } + + static std::pair<unsigned,unsigned> + EmitKeyDataLength(llvm::raw_ostream& Out, const PTHIdKey* key, uint32_t) { + unsigned n = key->II->getLength() + 1; + ::Emit16(Out, n); + return std::make_pair(n, sizeof(uint32_t)); + } + + static void EmitKey(llvm::raw_ostream& Out, PTHIdKey* key, unsigned n) { + // Record the location of the key data. This is used when generating + // the mapping from persistent IDs to strings. + key->FileOffset = Out.tell(); + Out.write(key->II->getNameStart(), n); + } + + static void EmitData(llvm::raw_ostream& Out, PTHIdKey*, uint32_t pID, + unsigned) { + ::Emit32(Out, pID); + } +}; +} // end anonymous namespace + +/// EmitIdentifierTable - Emits two tables to the PTH file. The first is +/// a hashtable mapping from identifier strings to persistent IDs. The second +/// is a straight table mapping from persistent IDs to string data (the +/// keys of the first table). +/// +std::pair<Offset,Offset> PTHWriter::EmitIdentifierTable() { + // Build two maps: + // (1) an inverse map from persistent IDs -> (IdentifierInfo*,Offset) + // (2) a map from (IdentifierInfo*, Offset)* -> persistent IDs + + // Note that we use 'calloc', so all the bytes are 0. + PTHIdKey *IIDMap = (PTHIdKey*)calloc(idcount, sizeof(PTHIdKey)); + + // Create the hashtable. + OnDiskChainedHashTableGenerator<PTHIdentifierTableTrait> IIOffMap; + + // Generate mapping from persistent IDs -> IdentifierInfo*. + for (IDMap::iterator I = IM.begin(), E = IM.end(); I != E; ++I) { + // Decrement by 1 because we are using a vector for the lookup and + // 0 is reserved for NULL. + assert(I->second > 0); + assert(I->second-1 < idcount); + unsigned idx = I->second-1; + + // Store the mapping from persistent ID to IdentifierInfo* + IIDMap[idx].II = I->first; + + // Store the reverse mapping in a hashtable. + IIOffMap.insert(&IIDMap[idx], I->second); + } + + // Write out the inverse map first. This causes the PCIDKey entries to + // record PTH file offsets for the string data. This is used to write + // the second table. + Offset StringTableOffset = IIOffMap.Emit(Out); + + // Now emit the table mapping from persistent IDs to PTH file offsets. + Offset IDOff = Out.tell(); + Emit32(idcount); // Emit the number of identifiers. + for (unsigned i = 0 ; i < idcount; ++i) + Emit32(IIDMap[i].FileOffset); + + // Finally, release the inverse map. + free(IIDMap); + + return std::make_pair(IDOff, StringTableOffset); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/CompilerInstance.cpp b/contrib/llvm/tools/clang/lib/Frontend/CompilerInstance.cpp new file mode 100644 index 0000000..ce0b072 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/CompilerInstance.cpp @@ -0,0 +1,562 @@ +//===--- CompilerInstance.cpp ---------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Sema/Sema.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Basic/Version.h" +#include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Lex/PTHManager.h" +#include "clang/Frontend/ChainedDiagnosticClient.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Frontend/VerifyDiagnosticsClient.h" +#include "clang/Frontend/Utils.h" +#include "clang/Serialization/ASTReader.h" +#include "clang/Sema/CodeCompleteConsumer.h" +#include "llvm/LLVMContext.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/Support/Timer.h" +#include "llvm/System/Host.h" +#include "llvm/System/Path.h" +#include "llvm/System/Program.h" +using namespace clang; + +CompilerInstance::CompilerInstance() + : Invocation(new CompilerInvocation()) { +} + +CompilerInstance::~CompilerInstance() { +} + +void CompilerInstance::setLLVMContext(llvm::LLVMContext *Value) { + LLVMContext.reset(Value); +} + +void CompilerInstance::setInvocation(CompilerInvocation *Value) { + Invocation.reset(Value); +} + +void CompilerInstance::setDiagnostics(Diagnostic *Value) { + Diagnostics = Value; +} + +void CompilerInstance::setTarget(TargetInfo *Value) { + Target.reset(Value); +} + +void CompilerInstance::setFileManager(FileManager *Value) { + FileMgr.reset(Value); +} + +void CompilerInstance::setSourceManager(SourceManager *Value) { + SourceMgr.reset(Value); +} + +void CompilerInstance::setPreprocessor(Preprocessor *Value) { + PP.reset(Value); +} + +void CompilerInstance::setASTContext(ASTContext *Value) { + Context.reset(Value); +} + +void CompilerInstance::setSema(Sema *S) { + TheSema.reset(S); +} + +void CompilerInstance::setASTConsumer(ASTConsumer *Value) { + Consumer.reset(Value); +} + +void CompilerInstance::setCodeCompletionConsumer(CodeCompleteConsumer *Value) { + CompletionConsumer.reset(Value); +} + +// Diagnostics +namespace { + class BinaryDiagnosticSerializer : public DiagnosticClient { + llvm::raw_ostream &OS; + SourceManager *SourceMgr; + public: + explicit BinaryDiagnosticSerializer(llvm::raw_ostream &OS) + : OS(OS), SourceMgr(0) { } + + virtual void HandleDiagnostic(Diagnostic::Level DiagLevel, + const DiagnosticInfo &Info); + }; +} + +void BinaryDiagnosticSerializer::HandleDiagnostic(Diagnostic::Level DiagLevel, + const DiagnosticInfo &Info) { + StoredDiagnostic(DiagLevel, Info).Serialize(OS); +} + +static void SetUpBuildDumpLog(const DiagnosticOptions &DiagOpts, + unsigned argc, char **argv, + Diagnostic &Diags) { + std::string ErrorInfo; + llvm::OwningPtr<llvm::raw_ostream> OS( + new llvm::raw_fd_ostream(DiagOpts.DumpBuildInformation.c_str(), ErrorInfo)); + if (!ErrorInfo.empty()) { + Diags.Report(diag::err_fe_unable_to_open_logfile) + << DiagOpts.DumpBuildInformation << ErrorInfo; + return; + } + + (*OS) << "clang -cc1 command line arguments: "; + for (unsigned i = 0; i != argc; ++i) + (*OS) << argv[i] << ' '; + (*OS) << '\n'; + + // Chain in a diagnostic client which will log the diagnostics. + DiagnosticClient *Logger = + new TextDiagnosticPrinter(*OS.take(), DiagOpts, /*OwnsOutputStream=*/true); + Diags.setClient(new ChainedDiagnosticClient(Diags.takeClient(), Logger)); +} + +void CompilerInstance::createDiagnostics(int Argc, char **Argv) { + Diagnostics = createDiagnostics(getDiagnosticOpts(), Argc, Argv); +} + +llvm::IntrusiveRefCntPtr<Diagnostic> +CompilerInstance::createDiagnostics(const DiagnosticOptions &Opts, + int Argc, char **Argv) { + llvm::IntrusiveRefCntPtr<Diagnostic> Diags(new Diagnostic()); + + // Create the diagnostic client for reporting errors or for + // implementing -verify. + llvm::OwningPtr<DiagnosticClient> DiagClient; + if (Opts.BinaryOutput) { + if (llvm::sys::Program::ChangeStderrToBinary()) { + // We weren't able to set standard error to binary, which is a + // bit of a problem. So, just create a text diagnostic printer + // to complain about this problem, and pretend that the user + // didn't try to use binary output. + Diags->setClient(new TextDiagnosticPrinter(llvm::errs(), Opts)); + Diags->Report(diag::err_fe_stderr_binary); + return Diags; + } else { + Diags->setClient(new BinaryDiagnosticSerializer(llvm::errs())); + } + } else { + Diags->setClient(new TextDiagnosticPrinter(llvm::errs(), Opts)); + } + + // Chain in -verify checker, if requested. + if (Opts.VerifyDiagnostics) + Diags->setClient(new VerifyDiagnosticsClient(*Diags, Diags->takeClient())); + + if (!Opts.DumpBuildInformation.empty()) + SetUpBuildDumpLog(Opts, Argc, Argv, *Diags); + + // Configure our handling of diagnostics. + ProcessWarningOptions(*Diags, Opts); + + return Diags; +} + +// File Manager + +void CompilerInstance::createFileManager() { + FileMgr.reset(new FileManager()); +} + +// Source Manager + +void CompilerInstance::createSourceManager() { + SourceMgr.reset(new SourceManager(getDiagnostics())); +} + +// Preprocessor + +void CompilerInstance::createPreprocessor() { + PP.reset(createPreprocessor(getDiagnostics(), getLangOpts(), + getPreprocessorOpts(), getHeaderSearchOpts(), + getDependencyOutputOpts(), getTarget(), + getFrontendOpts(), getSourceManager(), + getFileManager())); +} + +Preprocessor * +CompilerInstance::createPreprocessor(Diagnostic &Diags, + const LangOptions &LangInfo, + const PreprocessorOptions &PPOpts, + const HeaderSearchOptions &HSOpts, + const DependencyOutputOptions &DepOpts, + const TargetInfo &Target, + const FrontendOptions &FEOpts, + SourceManager &SourceMgr, + FileManager &FileMgr) { + // Create a PTH manager if we are using some form of a token cache. + PTHManager *PTHMgr = 0; + if (!PPOpts.TokenCache.empty()) + PTHMgr = PTHManager::Create(PPOpts.TokenCache, Diags); + + // Create the Preprocessor. + HeaderSearch *HeaderInfo = new HeaderSearch(FileMgr); + Preprocessor *PP = new Preprocessor(Diags, LangInfo, Target, + SourceMgr, *HeaderInfo, PTHMgr, + /*OwnsHeaderSearch=*/true); + + // Note that this is different then passing PTHMgr to Preprocessor's ctor. + // That argument is used as the IdentifierInfoLookup argument to + // IdentifierTable's ctor. + if (PTHMgr) { + PTHMgr->setPreprocessor(PP); + PP->setPTHManager(PTHMgr); + } + + if (PPOpts.DetailedRecord) + PP->createPreprocessingRecord(); + + InitializePreprocessor(*PP, PPOpts, HSOpts, FEOpts); + + // Handle generating dependencies, if requested. + if (!DepOpts.OutputFile.empty()) + AttachDependencyFileGen(*PP, DepOpts); + + return PP; +} + +// ASTContext + +void CompilerInstance::createASTContext() { + Preprocessor &PP = getPreprocessor(); + Context.reset(new ASTContext(getLangOpts(), PP.getSourceManager(), + getTarget(), PP.getIdentifierTable(), + PP.getSelectorTable(), PP.getBuiltinInfo(), + /*size_reserve=*/ 0)); +} + +// ExternalASTSource + +void CompilerInstance::createPCHExternalASTSource(llvm::StringRef Path, + bool DisablePCHValidation, + void *DeserializationListener){ + llvm::OwningPtr<ExternalASTSource> Source; + Source.reset(createPCHExternalASTSource(Path, getHeaderSearchOpts().Sysroot, + DisablePCHValidation, + getPreprocessor(), getASTContext(), + DeserializationListener)); + getASTContext().setExternalSource(Source); +} + +ExternalASTSource * +CompilerInstance::createPCHExternalASTSource(llvm::StringRef Path, + const std::string &Sysroot, + bool DisablePCHValidation, + Preprocessor &PP, + ASTContext &Context, + void *DeserializationListener) { + llvm::OwningPtr<ASTReader> Reader; + Reader.reset(new ASTReader(PP, &Context, + Sysroot.empty() ? 0 : Sysroot.c_str(), + DisablePCHValidation)); + + Reader->setDeserializationListener( + static_cast<ASTDeserializationListener *>(DeserializationListener)); + switch (Reader->ReadAST(Path)) { + case ASTReader::Success: + // Set the predefines buffer as suggested by the PCH reader. Typically, the + // predefines buffer will be empty. + PP.setPredefines(Reader->getSuggestedPredefines()); + return Reader.take(); + + case ASTReader::Failure: + // Unrecoverable failure: don't even try to process the input file. + break; + + case ASTReader::IgnorePCH: + // No suitable PCH file could be found. Return an error. + break; + } + + return 0; +} + +// Code Completion + +static bool EnableCodeCompletion(Preprocessor &PP, + const std::string &Filename, + unsigned Line, + unsigned Column) { + // Tell the source manager to chop off the given file at a specific + // line and column. + const FileEntry *Entry = PP.getFileManager().getFile(Filename); + if (!Entry) { + PP.getDiagnostics().Report(diag::err_fe_invalid_code_complete_file) + << Filename; + return true; + } + + // Truncate the named file at the given line/column. + PP.SetCodeCompletionPoint(Entry, Line, Column); + return false; +} + +void CompilerInstance::createCodeCompletionConsumer() { + const ParsedSourceLocation &Loc = getFrontendOpts().CodeCompletionAt; + if (!CompletionConsumer) { + CompletionConsumer.reset( + createCodeCompletionConsumer(getPreprocessor(), + Loc.FileName, Loc.Line, Loc.Column, + getFrontendOpts().DebugCodeCompletionPrinter, + getFrontendOpts().ShowMacrosInCodeCompletion, + getFrontendOpts().ShowCodePatternsInCodeCompletion, + getFrontendOpts().ShowGlobalSymbolsInCodeCompletion, + llvm::outs())); + if (!CompletionConsumer) + return; + } else if (EnableCodeCompletion(getPreprocessor(), Loc.FileName, + Loc.Line, Loc.Column)) { + CompletionConsumer.reset(); + return; + } + + if (CompletionConsumer->isOutputBinary() && + llvm::sys::Program::ChangeStdoutToBinary()) { + getPreprocessor().getDiagnostics().Report(diag::err_fe_stdout_binary); + CompletionConsumer.reset(); + } +} + +void CompilerInstance::createFrontendTimer() { + FrontendTimer.reset(new llvm::Timer("Clang front-end timer")); +} + +CodeCompleteConsumer * +CompilerInstance::createCodeCompletionConsumer(Preprocessor &PP, + const std::string &Filename, + unsigned Line, + unsigned Column, + bool UseDebugPrinter, + bool ShowMacros, + bool ShowCodePatterns, + bool ShowGlobals, + llvm::raw_ostream &OS) { + if (EnableCodeCompletion(PP, Filename, Line, Column)) + return 0; + + // Set up the creation routine for code-completion. + if (UseDebugPrinter) + return new PrintingCodeCompleteConsumer(ShowMacros, ShowCodePatterns, + ShowGlobals, OS); + else + return new CIndexCodeCompleteConsumer(ShowMacros, ShowCodePatterns, + ShowGlobals, OS); +} + +void CompilerInstance::createSema(bool CompleteTranslationUnit, + CodeCompleteConsumer *CompletionConsumer) { + TheSema.reset(new Sema(getPreprocessor(), getASTContext(), getASTConsumer(), + CompleteTranslationUnit, CompletionConsumer)); +} + +// Output Files + +void CompilerInstance::addOutputFile(llvm::StringRef Path, + llvm::raw_ostream *OS) { + assert(OS && "Attempt to add empty stream to output list!"); + OutputFiles.push_back(std::make_pair(Path, OS)); +} + +void CompilerInstance::clearOutputFiles(bool EraseFiles) { + for (std::list< std::pair<std::string, llvm::raw_ostream*> >::iterator + it = OutputFiles.begin(), ie = OutputFiles.end(); it != ie; ++it) { + delete it->second; + if (EraseFiles && !it->first.empty()) + llvm::sys::Path(it->first).eraseFromDisk(); + } + OutputFiles.clear(); +} + +llvm::raw_fd_ostream * +CompilerInstance::createDefaultOutputFile(bool Binary, + llvm::StringRef InFile, + llvm::StringRef Extension) { + return createOutputFile(getFrontendOpts().OutputFile, Binary, + InFile, Extension); +} + +llvm::raw_fd_ostream * +CompilerInstance::createOutputFile(llvm::StringRef OutputPath, + bool Binary, + llvm::StringRef InFile, + llvm::StringRef Extension) { + std::string Error, OutputPathName; + llvm::raw_fd_ostream *OS = createOutputFile(OutputPath, Error, Binary, + InFile, Extension, + &OutputPathName); + if (!OS) { + getDiagnostics().Report(diag::err_fe_unable_to_open_output) + << OutputPath << Error; + return 0; + } + + // Add the output file -- but don't try to remove "-", since this means we are + // using stdin. + addOutputFile((OutputPathName != "-") ? OutputPathName : "", OS); + + return OS; +} + +llvm::raw_fd_ostream * +CompilerInstance::createOutputFile(llvm::StringRef OutputPath, + std::string &Error, + bool Binary, + llvm::StringRef InFile, + llvm::StringRef Extension, + std::string *ResultPathName) { + std::string OutFile; + if (!OutputPath.empty()) { + OutFile = OutputPath; + } else if (InFile == "-") { + OutFile = "-"; + } else if (!Extension.empty()) { + llvm::sys::Path Path(InFile); + Path.eraseSuffix(); + Path.appendSuffix(Extension); + OutFile = Path.str(); + } else { + OutFile = "-"; + } + + llvm::OwningPtr<llvm::raw_fd_ostream> OS( + new llvm::raw_fd_ostream(OutFile.c_str(), Error, + (Binary ? llvm::raw_fd_ostream::F_Binary : 0))); + if (!Error.empty()) + return 0; + + if (ResultPathName) + *ResultPathName = OutFile; + + return OS.take(); +} + +// Initialization Utilities + +bool CompilerInstance::InitializeSourceManager(llvm::StringRef InputFile) { + return InitializeSourceManager(InputFile, getDiagnostics(), getFileManager(), + getSourceManager(), getFrontendOpts()); +} + +bool CompilerInstance::InitializeSourceManager(llvm::StringRef InputFile, + Diagnostic &Diags, + FileManager &FileMgr, + SourceManager &SourceMgr, + const FrontendOptions &Opts) { + // Figure out where to get and map in the main file. + if (InputFile != "-") { + const FileEntry *File = FileMgr.getFile(InputFile); + if (File) SourceMgr.createMainFileID(File); + if (SourceMgr.getMainFileID().isInvalid()) { + Diags.Report(diag::err_fe_error_reading) << InputFile; + return false; + } + } else { + llvm::MemoryBuffer *SB = llvm::MemoryBuffer::getSTDIN(); + if (SB) SourceMgr.createMainFileIDForMemBuffer(SB); + if (SourceMgr.getMainFileID().isInvalid()) { + Diags.Report(diag::err_fe_error_reading_stdin); + return false; + } + } + + return true; +} + +// High-Level Operations + +bool CompilerInstance::ExecuteAction(FrontendAction &Act) { + assert(hasDiagnostics() && "Diagnostics engine is not initialized!"); + assert(!getFrontendOpts().ShowHelp && "Client must handle '-help'!"); + assert(!getFrontendOpts().ShowVersion && "Client must handle '-version'!"); + + // FIXME: Take this as an argument, once all the APIs we used have moved to + // taking it as an input instead of hard-coding llvm::errs. + llvm::raw_ostream &OS = llvm::errs(); + + // Create the target instance. + setTarget(TargetInfo::CreateTargetInfo(getDiagnostics(), getTargetOpts())); + if (!hasTarget()) + return false; + + // Inform the target of the language options. + // + // FIXME: We shouldn't need to do this, the target should be immutable once + // created. This complexity should be lifted elsewhere. + getTarget().setForcedLangOptions(getLangOpts()); + + // Validate/process some options. + if (getHeaderSearchOpts().Verbose) + OS << "clang -cc1 version " CLANG_VERSION_STRING + << " based upon " << PACKAGE_STRING + << " hosted on " << llvm::sys::getHostTriple() << "\n"; + + if (getFrontendOpts().ShowTimers) + createFrontendTimer(); + + if (getFrontendOpts().ShowStats) + llvm::EnableStatistics(); + + for (unsigned i = 0, e = getFrontendOpts().Inputs.size(); i != e; ++i) { + const std::string &InFile = getFrontendOpts().Inputs[i].second; + + // Reset the ID tables if we are reusing the SourceManager. + if (hasSourceManager()) + getSourceManager().clearIDTables(); + + if (Act.BeginSourceFile(*this, InFile, getFrontendOpts().Inputs[i].first)) { + Act.Execute(); + Act.EndSourceFile(); + } + } + + if (getDiagnosticOpts().ShowCarets) { + unsigned NumWarnings = getDiagnostics().getNumWarnings(); + unsigned NumErrors = getDiagnostics().getNumErrors() - + getDiagnostics().getNumErrorsSuppressed(); + + if (NumWarnings) + OS << NumWarnings << " warning" << (NumWarnings == 1 ? "" : "s"); + if (NumWarnings && NumErrors) + OS << " and "; + if (NumErrors) + OS << NumErrors << " error" << (NumErrors == 1 ? "" : "s"); + if (NumWarnings || NumErrors) + OS << " generated.\n"; + } + + if (getFrontendOpts().ShowStats && hasFileManager()) { + getFileManager().PrintStats(); + OS << "\n"; + } + + // Return the appropriate status when verifying diagnostics. + // + // FIXME: If we could make getNumErrors() do the right thing, we wouldn't need + // this. + if (getDiagnosticOpts().VerifyDiagnostics) + return !static_cast<VerifyDiagnosticsClient&>( + getDiagnosticClient()).HadErrors(); + + return !getDiagnostics().getNumErrors(); +} + + diff --git a/contrib/llvm/tools/clang/lib/Frontend/CompilerInvocation.cpp b/contrib/llvm/tools/clang/lib/Frontend/CompilerInvocation.cpp new file mode 100644 index 0000000..bf8b867 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/CompilerInvocation.cpp @@ -0,0 +1,1522 @@ +//===--- CompilerInvocation.cpp -------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/CompilerInvocation.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/Version.h" +#include "clang/Driver/Arg.h" +#include "clang/Driver/ArgList.h" +#include "clang/Driver/CC1Options.h" +#include "clang/Driver/DriverDiagnostic.h" +#include "clang/Driver/OptTable.h" +#include "clang/Driver/Option.h" +#include "clang/Frontend/CompilerInvocation.h" +#include "clang/Frontend/LangStandard.h" +#include "clang/Serialization/ASTReader.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/System/Host.h" +#include "llvm/System/Path.h" +using namespace clang; + +static const char *getAnalysisName(Analyses Kind) { + switch (Kind) { + default: + llvm_unreachable("Unknown analysis kind!"); +#define ANALYSIS(NAME, CMDFLAG, DESC, SCOPE)\ + case NAME: return "-" CMDFLAG; +#include "clang/Frontend/Analyses.def" + } +} + +static const char *getAnalysisStoreName(AnalysisStores Kind) { + switch (Kind) { + default: + llvm_unreachable("Unknown analysis store!"); +#define ANALYSIS_STORE(NAME, CMDFLAG, DESC, CREATFN) \ + case NAME##Model: return CMDFLAG; +#include "clang/Frontend/Analyses.def" + } +} + +static const char *getAnalysisConstraintName(AnalysisConstraints Kind) { + switch (Kind) { + default: + llvm_unreachable("Unknown analysis constraints!"); +#define ANALYSIS_CONSTRAINTS(NAME, CMDFLAG, DESC, CREATFN) \ + case NAME##Model: return CMDFLAG; +#include "clang/Frontend/Analyses.def" + } +} + +static const char *getAnalysisDiagClientName(AnalysisDiagClients Kind) { + switch (Kind) { + default: + llvm_unreachable("Unknown analysis client!"); +#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATFN, AUTOCREATE) \ + case PD_##NAME: return CMDFLAG; +#include "clang/Frontend/Analyses.def" + } +} + +//===----------------------------------------------------------------------===// +// Serialization (to args) +//===----------------------------------------------------------------------===// + +static void AnalyzerOptsToArgs(const AnalyzerOptions &Opts, + std::vector<std::string> &Res) { + for (unsigned i = 0, e = Opts.AnalysisList.size(); i != e; ++i) + Res.push_back(getAnalysisName(Opts.AnalysisList[i])); + if (Opts.AnalysisStoreOpt != BasicStoreModel) { + Res.push_back("-analyzer-store"); + Res.push_back(getAnalysisStoreName(Opts.AnalysisStoreOpt)); + } + if (Opts.AnalysisConstraintsOpt != RangeConstraintsModel) { + Res.push_back("-analyzer-constraints"); + Res.push_back(getAnalysisConstraintName(Opts.AnalysisConstraintsOpt)); + } + if (Opts.AnalysisDiagOpt != PD_HTML) { + Res.push_back("-analyzer-output"); + Res.push_back(getAnalysisDiagClientName(Opts.AnalysisDiagOpt)); + } + if (!Opts.AnalyzeSpecificFunction.empty()) { + Res.push_back("-analyze-function"); + Res.push_back(Opts.AnalyzeSpecificFunction); + } + if (Opts.AnalyzeAll) + Res.push_back("-analyzer-opt-analyze-headers"); + if (Opts.AnalyzerDisplayProgress) + Res.push_back("-analyzer-display-progress"); + if (Opts.AnalyzeNestedBlocks) + Res.push_back("-analyzer-opt-analyze-nested-blocks"); + if (Opts.EagerlyAssume) + Res.push_back("-analyzer-eagerly-assume"); + if (!Opts.PurgeDead) + Res.push_back("-analyzer-no-purge-dead"); + if (Opts.TrimGraph) + Res.push_back("-trim-egraph"); + if (Opts.VisualizeEGDot) + Res.push_back("-analyzer-viz-egraph-graphviz"); + if (Opts.VisualizeEGDot) + Res.push_back("-analyzer-viz-egraph-ubigraph"); + if (Opts.EnableExperimentalChecks) + Res.push_back("-analyzer-experimental-checks"); + if (Opts.EnableExperimentalInternalChecks) + Res.push_back("-analyzer-experimental-internal-checks"); + if (Opts.IdempotentOps) + Res.push_back("-analyzer-check-idempotent-operations"); +} + +static void CodeGenOptsToArgs(const CodeGenOptions &Opts, + std::vector<std::string> &Res) { + if (Opts.DebugInfo) + Res.push_back("-g"); + if (Opts.DisableLLVMOpts) + Res.push_back("-disable-llvm-optzns"); + if (Opts.DisableRedZone) + Res.push_back("-disable-red-zone"); + if (!Opts.DwarfDebugFlags.empty()) { + Res.push_back("-dwarf-debug-flags"); + Res.push_back(Opts.DwarfDebugFlags); + } + if (!Opts.MergeAllConstants) + Res.push_back("-fno-merge-all-constants"); + if (Opts.NoCommon) + Res.push_back("-fno-common"); + if (Opts.NoImplicitFloat) + Res.push_back("-no-implicit-float"); + if (Opts.OmitLeafFramePointer) + Res.push_back("-momit-leaf-frame-pointer"); + if (Opts.OptimizeSize) { + assert(Opts.OptimizationLevel == 2 && "Invalid options!"); + Res.push_back("-Os"); + } else if (Opts.OptimizationLevel != 0) + Res.push_back("-O" + llvm::utostr(Opts.OptimizationLevel)); + if (!Opts.MainFileName.empty()) { + Res.push_back("-main-file-name"); + Res.push_back(Opts.MainFileName); + } + // SimplifyLibCalls is only derived. + // TimePasses is only derived. + // UnitAtATime is unused. + // Inlining is only derived. + + // UnrollLoops is derived, but also accepts an option, no + // harm in pushing it back here. + if (Opts.UnrollLoops) + Res.push_back("-funroll-loops"); + if (Opts.DataSections) + Res.push_back("-fdata-sections"); + if (Opts.FunctionSections) + Res.push_back("-ffunction-sections"); + if (Opts.AsmVerbose) + Res.push_back("-masm-verbose"); + if (!Opts.CodeModel.empty()) { + Res.push_back("-mcode-model"); + Res.push_back(Opts.CodeModel); + } + if (!Opts.CXAAtExit) + Res.push_back("-fno-use-cxa-atexit"); + if (Opts.CXXCtorDtorAliases) + Res.push_back("-mconstructor-aliases"); + if (!Opts.DebugPass.empty()) { + Res.push_back("-mdebug-pass"); + Res.push_back(Opts.DebugPass); + } + if (Opts.DisableFPElim) + Res.push_back("-mdisable-fp-elim"); + if (!Opts.FloatABI.empty()) { + Res.push_back("-mfloat-abi"); + Res.push_back(Opts.FloatABI); + } + if (!Opts.LimitFloatPrecision.empty()) { + Res.push_back("-mlimit-float-precision"); + Res.push_back(Opts.LimitFloatPrecision); + } + if (Opts.NoZeroInitializedInBSS) + Res.push_back("-mno-zero-initialized-bss"); + switch (Opts.getObjCDispatchMethod()) { + case CodeGenOptions::Legacy: + break; + case CodeGenOptions::Mixed: + Res.push_back("-fobjc-dispatch-method=mixed"); + break; + case CodeGenOptions::NonLegacy: + Res.push_back("-fobjc-dispatch-method=non-legacy"); + break; + } + if (Opts.RelaxAll) + Res.push_back("-mrelax-all"); + if (Opts.SoftFloat) + Res.push_back("-msoft-float"); + if (Opts.UnwindTables) + Res.push_back("-munwind-tables"); + if (Opts.RelocationModel != "pic") { + Res.push_back("-mrelocation-model"); + Res.push_back(Opts.RelocationModel); + } + if (!Opts.VerifyModule) + Res.push_back("-disable-llvm-verifier"); +} + +static void DependencyOutputOptsToArgs(const DependencyOutputOptions &Opts, + std::vector<std::string> &Res) { + if (Opts.IncludeSystemHeaders) + Res.push_back("-sys-header-deps"); + if (Opts.UsePhonyTargets) + Res.push_back("-MP"); + if (!Opts.OutputFile.empty()) { + Res.push_back("-dependency-file"); + Res.push_back(Opts.OutputFile); + } + for (unsigned i = 0, e = Opts.Targets.size(); i != e; ++i) { + Res.push_back("-MT"); + Res.push_back(Opts.Targets[i]); + } +} + +static void DiagnosticOptsToArgs(const DiagnosticOptions &Opts, + std::vector<std::string> &Res) { + if (Opts.IgnoreWarnings) + Res.push_back("-w"); + if (Opts.NoRewriteMacros) + Res.push_back("-Wno-rewrite-macros"); + if (Opts.Pedantic) + Res.push_back("-pedantic"); + if (Opts.PedanticErrors) + Res.push_back("-pedantic-errors"); + if (!Opts.ShowColumn) + Res.push_back("-fno-show-column"); + if (!Opts.ShowLocation) + Res.push_back("-fno-show-source-location"); + if (!Opts.ShowCarets) + Res.push_back("-fno-caret-diagnostics"); + if (!Opts.ShowFixits) + Res.push_back("-fno-diagnostics-fixit-info"); + if (Opts.ShowSourceRanges) + Res.push_back("-fdiagnostics-print-source-range-info"); + if (Opts.ShowParseableFixits) + Res.push_back("-fdiagnostics-parseable-fixits"); + if (Opts.ShowColors) + Res.push_back("-fcolor-diagnostics"); + if (Opts.VerifyDiagnostics) + Res.push_back("-verify"); + if (Opts.BinaryOutput) + Res.push_back("-fdiagnostics-binary"); + if (Opts.ShowOptionNames) + Res.push_back("-fdiagnostics-show-option"); + if (Opts.ShowCategories == 1) + Res.push_back("-fdiagnostics-show-category=id"); + else if (Opts.ShowCategories == 2) + Res.push_back("-fdiagnostics-show-category=name"); + if (Opts.ErrorLimit) { + Res.push_back("-ferror-limit"); + Res.push_back(llvm::utostr(Opts.ErrorLimit)); + } + if (Opts.MacroBacktraceLimit + != DiagnosticOptions::DefaultMacroBacktraceLimit) { + Res.push_back("-fmacro-backtrace-limit"); + Res.push_back(llvm::utostr(Opts.MacroBacktraceLimit)); + } + if (Opts.TemplateBacktraceLimit + != DiagnosticOptions::DefaultTemplateBacktraceLimit) { + Res.push_back("-ftemplate-backtrace-limit"); + Res.push_back(llvm::utostr(Opts.TemplateBacktraceLimit)); + } + + if (Opts.TabStop != DiagnosticOptions::DefaultTabStop) { + Res.push_back("-ftabstop"); + Res.push_back(llvm::utostr(Opts.TabStop)); + } + if (Opts.MessageLength) { + Res.push_back("-fmessage-length"); + Res.push_back(llvm::utostr(Opts.MessageLength)); + } + if (!Opts.DumpBuildInformation.empty()) { + Res.push_back("-dump-build-information"); + Res.push_back(Opts.DumpBuildInformation); + } + for (unsigned i = 0, e = Opts.Warnings.size(); i != e; ++i) + Res.push_back("-W" + Opts.Warnings[i]); +} + +static const char *getInputKindName(InputKind Kind) { + switch (Kind) { + case IK_None: break; + case IK_AST: return "ast"; + case IK_Asm: return "assembler-with-cpp"; + case IK_C: return "c"; + case IK_CXX: return "c++"; + case IK_LLVM_IR: return "ir"; + case IK_ObjC: return "objective-c"; + case IK_ObjCXX: return "objective-c++"; + case IK_OpenCL: return "cl"; + case IK_PreprocessedC: return "cpp-output"; + case IK_PreprocessedCXX: return "c++-cpp-output"; + case IK_PreprocessedObjC: return "objective-c-cpp-output"; + case IK_PreprocessedObjCXX:return "objective-c++-cpp-output"; + } + + llvm_unreachable("Unexpected language kind!"); + return 0; +} + +static const char *getActionName(frontend::ActionKind Kind) { + switch (Kind) { + case frontend::PluginAction: + case frontend::InheritanceView: + llvm_unreachable("Invalid kind!"); + + case frontend::ASTDump: return "-ast-dump"; + case frontend::ASTPrint: return "-ast-print"; + case frontend::ASTPrintXML: return "-ast-print-xml"; + case frontend::ASTView: return "-ast-view"; + case frontend::BoostCon: return "-boostcon"; + case frontend::CreateModule: return "-create-module"; + case frontend::DumpRawTokens: return "-dump-raw-tokens"; + case frontend::DumpTokens: return "-dump-tokens"; + case frontend::EmitAssembly: return "-S"; + case frontend::EmitBC: return "-emit-llvm-bc"; + case frontend::EmitHTML: return "-emit-html"; + case frontend::EmitLLVM: return "-emit-llvm"; + case frontend::EmitLLVMOnly: return "-emit-llvm-only"; + case frontend::EmitCodeGenOnly: return "-emit-codegen-only"; + case frontend::EmitObj: return "-emit-obj"; + case frontend::FixIt: return "-fixit"; + case frontend::GeneratePCH: return "-emit-pch"; + case frontend::GeneratePTH: return "-emit-pth"; + case frontend::InitOnly: return "-init-only"; + case frontend::ParseSyntaxOnly: return "-fsyntax-only"; + case frontend::PrintDeclContext: return "-print-decl-contexts"; + case frontend::PrintPreamble: return "-print-preamble"; + case frontend::PrintPreprocessedInput: return "-E"; + case frontend::RewriteMacros: return "-rewrite-macros"; + case frontend::RewriteObjC: return "-rewrite-objc"; + case frontend::RewriteTest: return "-rewrite-test"; + case frontend::RunAnalysis: return "-analyze"; + case frontend::RunPreprocessorOnly: return "-Eonly"; + } + + llvm_unreachable("Unexpected language kind!"); + return 0; +} + +static void FrontendOptsToArgs(const FrontendOptions &Opts, + std::vector<std::string> &Res) { + if (!Opts.DebugCodeCompletionPrinter) + Res.push_back("-no-code-completion-debug-printer"); + if (Opts.DisableFree) + Res.push_back("-disable-free"); + if (Opts.RelocatablePCH) + Res.push_back("-relocatable-pch"); + if (Opts.ChainedPCH) + Res.push_back("-chained-pch"); + if (Opts.ShowHelp) + Res.push_back("-help"); + if (Opts.ShowMacrosInCodeCompletion) + Res.push_back("-code-completion-macros"); + if (Opts.ShowCodePatternsInCodeCompletion) + Res.push_back("-code-completion-patterns"); + if (!Opts.ShowGlobalSymbolsInCodeCompletion) + Res.push_back("-no-code-completion-globals"); + if (Opts.ShowStats) + Res.push_back("-print-stats"); + if (Opts.ShowTimers) + Res.push_back("-ftime-report"); + if (Opts.ShowVersion) + Res.push_back("-version"); + if (Opts.FixWhatYouCan) + Res.push_back("-fix-what-you-can"); + + bool NeedLang = false; + for (unsigned i = 0, e = Opts.Inputs.size(); i != e; ++i) + if (FrontendOptions::getInputKindForExtension(Opts.Inputs[i].second) != + Opts.Inputs[i].first) + NeedLang = true; + if (NeedLang) { + Res.push_back("-x"); + Res.push_back(getInputKindName(Opts.Inputs[0].first)); + } + for (unsigned i = 0, e = Opts.Inputs.size(); i != e; ++i) { + assert((!NeedLang || Opts.Inputs[i].first == Opts.Inputs[0].first) && + "Unable to represent this input vector!"); + Res.push_back(Opts.Inputs[i].second); + } + + if (!Opts.OutputFile.empty()) { + Res.push_back("-o"); + Res.push_back(Opts.OutputFile); + } + if (!Opts.ViewClassInheritance.empty()) { + Res.push_back("-cxx-inheritance-view"); + Res.push_back(Opts.ViewClassInheritance); + } + if (!Opts.CodeCompletionAt.FileName.empty()) { + Res.push_back("-code-completion-at"); + Res.push_back(Opts.CodeCompletionAt.FileName + ":" + + llvm::utostr(Opts.CodeCompletionAt.Line) + ":" + + llvm::utostr(Opts.CodeCompletionAt.Column)); + } + if (Opts.ProgramAction != frontend::InheritanceView && + Opts.ProgramAction != frontend::PluginAction) + Res.push_back(getActionName(Opts.ProgramAction)); + if (!Opts.ActionName.empty()) { + Res.push_back("-plugin"); + Res.push_back(Opts.ActionName); + for(unsigned i = 0, e = Opts.PluginArgs.size(); i != e; ++i) { + Res.push_back("-plugin-arg-" + Opts.ActionName); + Res.push_back(Opts.PluginArgs[i]); + } + } + for (unsigned i = 0, e = Opts.Plugins.size(); i != e; ++i) { + Res.push_back("-load"); + Res.push_back(Opts.Plugins[i]); + } + for (unsigned i = 0, e = Opts.ASTMergeFiles.size(); i != e; ++i) { + Res.push_back("-ast-merge"); + Res.push_back(Opts.ASTMergeFiles[i]); + } + for (unsigned i = 0, e = Opts.Modules.size(); i != e; ++i) { + Res.push_back("-import-module"); + Res.push_back(Opts.Modules[i]); + } + for (unsigned i = 0, e = Opts.LLVMArgs.size(); i != e; ++i) { + Res.push_back("-mllvm"); + Res.push_back(Opts.LLVMArgs[i]); + } +} + +static void HeaderSearchOptsToArgs(const HeaderSearchOptions &Opts, + std::vector<std::string> &Res) { + if (Opts.Sysroot != "/") { + Res.push_back("-isysroot"); + Res.push_back(Opts.Sysroot); + } + + /// User specified include entries. + for (unsigned i = 0, e = Opts.UserEntries.size(); i != e; ++i) { + const HeaderSearchOptions::Entry &E = Opts.UserEntries[i]; + if (E.IsFramework && (E.Group != frontend::Angled || !E.IsUserSupplied)) + llvm::report_fatal_error("Invalid option set!"); + if (E.IsUserSupplied) { + if (E.Group == frontend::After) { + Res.push_back("-idirafter"); + } else if (E.Group == frontend::Quoted) { + Res.push_back("-iquote"); + } else if (E.Group == frontend::System) { + Res.push_back("-isystem"); + } else { + assert(E.Group == frontend::Angled && "Invalid group!"); + Res.push_back(E.IsFramework ? "-F" : "-I"); + } + } else { + if (E.Group != frontend::Angled && E.Group != frontend::System) + llvm::report_fatal_error("Invalid option set!"); + Res.push_back(E.Group == frontend::Angled ? "-iwithprefixbefore" : + "-iwithprefix"); + } + Res.push_back(E.Path); + } + + if (!Opts.EnvIncPath.empty()) { + // FIXME: Provide an option for this, and move env detection to driver. + llvm::report_fatal_error("Not yet implemented!"); + } + if (!Opts.CEnvIncPath.empty()) { + // FIXME: Provide an option for this, and move env detection to driver. + llvm::report_fatal_error("Not yet implemented!"); + } + if (!Opts.ObjCEnvIncPath.empty()) { + // FIXME: Provide an option for this, and move env detection to driver. + llvm::report_fatal_error("Not yet implemented!"); + } + if (!Opts.CXXEnvIncPath.empty()) { + // FIXME: Provide an option for this, and move env detection to driver. + llvm::report_fatal_error("Not yet implemented!"); + } + if (!Opts.ObjCXXEnvIncPath.empty()) { + // FIXME: Provide an option for this, and move env detection to driver. + llvm::report_fatal_error("Not yet implemented!"); + } + if (!Opts.ResourceDir.empty()) { + Res.push_back("-resource-dir"); + Res.push_back(Opts.ResourceDir); + } + if (!Opts.UseStandardIncludes) + Res.push_back("-nostdinc"); + if (!Opts.UseStandardCXXIncludes) + Res.push_back("-nostdinc++"); + if (Opts.Verbose) + Res.push_back("-v"); +} + +static void LangOptsToArgs(const LangOptions &Opts, + std::vector<std::string> &Res) { + LangOptions DefaultLangOpts; + + // FIXME: Need to set -std to get all the implicit options. + + // FIXME: We want to only pass options relative to the defaults, which + // requires constructing a target. :( + // + // It would be better to push the all target specific choices into the driver, + // so that everything below that was more uniform. + + if (Opts.Trigraphs) + Res.push_back("-trigraphs"); + // Implicit based on the input kind: + // AsmPreprocessor, CPlusPlus, ObjC1, ObjC2, OpenCL + // Implicit based on the input language standard: + // BCPLComment, C99, CPlusPlus0x, Digraphs, GNUInline, ImplicitInt, GNUMode + if (Opts.DollarIdents) + Res.push_back("-fdollars-in-identifiers"); + if (Opts.GNUMode && !Opts.GNUKeywords) + Res.push_back("-fno-gnu-keywords"); + if (!Opts.GNUMode && Opts.GNUKeywords) + Res.push_back("-fgnu-keywords"); + if (Opts.Microsoft) + Res.push_back("-fms-extensions"); + if (Opts.Borland) + Res.push_back("-fborland-extensions"); + if (Opts.ObjCNonFragileABI) + Res.push_back("-fobjc-nonfragile-abi"); + if (Opts.ObjCNonFragileABI2) + Res.push_back("-fobjc-nonfragile-abi2"); + // NoInline is implicit. + if (!Opts.CXXOperatorNames) + Res.push_back("-fno-operator-names"); + if (Opts.PascalStrings) + Res.push_back("-fpascal-strings"); + if (Opts.CatchUndefined) + Res.push_back("-fcatch-undefined-behavior"); + if (Opts.WritableStrings) + Res.push_back("-fwritable-strings"); + if (Opts.ConstStrings) + Res.push_back("-Wwrite-strings"); + if (!Opts.LaxVectorConversions) + Res.push_back("-fno-lax-vector-conversions"); + if (Opts.AltiVec) + Res.push_back("-faltivec"); + if (Opts.Exceptions) + Res.push_back("-fexceptions"); + if (Opts.SjLjExceptions) + Res.push_back("-fsjlj-exceptions"); + if (!Opts.RTTI) + Res.push_back("-fno-rtti"); + if (!Opts.NeXTRuntime) + Res.push_back("-fgnu-runtime"); + if (Opts.Freestanding) + Res.push_back("-ffreestanding"); + if (Opts.FormatExtensions) + Res.push_back("-fformat-extensions"); + if (Opts.NoBuiltin) + Res.push_back("-fno-builtin"); + if (!Opts.AssumeSaneOperatorNew) + Res.push_back("-fno-assume-sane-operator-new"); + if (!Opts.ThreadsafeStatics) + Res.push_back("-fno-threadsafe-statics"); + if (Opts.POSIXThreads) + Res.push_back("-pthread"); + if (Opts.Blocks) + Res.push_back("-fblocks"); + if (Opts.EmitAllDecls) + Res.push_back("-femit-all-decls"); + if (Opts.MathErrno) + Res.push_back("-fmath-errno"); + switch (Opts.getSignedOverflowBehavior()) { + case LangOptions::SOB_Undefined: break; + case LangOptions::SOB_Defined: Res.push_back("-fwrapv"); break; + case LangOptions::SOB_Trapping: Res.push_back("-ftrapv"); break; + } + if (Opts.HeinousExtensions) + Res.push_back("-fheinous-gnu-extensions"); + // Optimize is implicit. + // OptimizeSize is implicit. + if (Opts.Static) + Res.push_back("-static-define"); + if (Opts.DumpRecordLayouts) + Res.push_back("-fdump-record-layouts"); + if (Opts.DumpVTableLayouts) + Res.push_back("-fdump-vtable-layouts"); + if (Opts.NoBitFieldTypeAlign) + Res.push_back("-fno-bitfield-type-alignment"); + if (Opts.SjLjExceptions) + Res.push_back("-fsjlj-exceptions"); + if (Opts.PICLevel) { + Res.push_back("-pic-level"); + Res.push_back(llvm::utostr(Opts.PICLevel)); + } + if (Opts.ObjCGCBitmapPrint) + Res.push_back("-print-ivar-layout"); + if (Opts.NoConstantCFStrings) + Res.push_back("-fno-constant-cfstrings"); + if (!Opts.AccessControl) + Res.push_back("-fno-access-control"); + if (!Opts.CharIsSigned) + Res.push_back("-fno-signed-char"); + if (Opts.ShortWChar) + Res.push_back("-fshort-wchar"); + if (!Opts.ElideConstructors) + Res.push_back("-fno-elide-constructors"); + if (Opts.getGCMode() != LangOptions::NonGC) { + if (Opts.getGCMode() == LangOptions::HybridGC) { + Res.push_back("-fobjc-gc"); + } else { + assert(Opts.getGCMode() == LangOptions::GCOnly && "Invalid GC mode!"); + Res.push_back("-fobjc-gc-only"); + } + } + if (Opts.getVisibilityMode() != LangOptions::Default) { + Res.push_back("-fvisibility"); + if (Opts.getVisibilityMode() == LangOptions::Hidden) { + Res.push_back("hidden"); + } else { + assert(Opts.getVisibilityMode() == LangOptions::Protected && + "Invalid visibility!"); + Res.push_back("protected"); + } + } + if (Opts.InlineVisibilityHidden) + Res.push_back("-fvisibility-inlines-hidden"); + + if (Opts.getStackProtectorMode() != 0) { + Res.push_back("-stack-protector"); + Res.push_back(llvm::utostr(Opts.getStackProtectorMode())); + } + if (Opts.InstantiationDepth != DefaultLangOpts.InstantiationDepth) { + Res.push_back("-ftemplate-depth"); + Res.push_back(llvm::utostr(Opts.InstantiationDepth)); + } + if (!Opts.ObjCConstantStringClass.empty()) { + Res.push_back("-fconstant-string-class"); + Res.push_back(Opts.ObjCConstantStringClass); + } +} + +static void PreprocessorOptsToArgs(const PreprocessorOptions &Opts, + std::vector<std::string> &Res) { + for (unsigned i = 0, e = Opts.Macros.size(); i != e; ++i) + Res.push_back(std::string(Opts.Macros[i].second ? "-U" : "-D") + + Opts.Macros[i].first); + for (unsigned i = 0, e = Opts.Includes.size(); i != e; ++i) { + // FIXME: We need to avoid reincluding the implicit PCH and PTH includes. + Res.push_back("-include"); + Res.push_back(Opts.Includes[i]); + } + for (unsigned i = 0, e = Opts.MacroIncludes.size(); i != e; ++i) { + Res.push_back("-imacros"); + Res.push_back(Opts.MacroIncludes[i]); + } + if (!Opts.UsePredefines) + Res.push_back("-undef"); + if (Opts.DetailedRecord) + Res.push_back("-detailed-preprocessing-record"); + if (!Opts.ImplicitPCHInclude.empty()) { + Res.push_back("-include-pch"); + Res.push_back(Opts.ImplicitPCHInclude); + } + if (!Opts.ImplicitPTHInclude.empty()) { + Res.push_back("-include-pth"); + Res.push_back(Opts.ImplicitPTHInclude); + } + if (!Opts.TokenCache.empty()) { + if (Opts.ImplicitPTHInclude.empty()) { + Res.push_back("-token-cache"); + Res.push_back(Opts.TokenCache); + } else + assert(Opts.ImplicitPTHInclude == Opts.TokenCache && + "Unsupported option combination!"); + } + for (unsigned i = 0, e = Opts.RemappedFiles.size(); i != e; ++i) { + Res.push_back("-remap-file"); + Res.push_back(Opts.RemappedFiles[i].first + ";" + + Opts.RemappedFiles[i].second); + } +} + +static void PreprocessorOutputOptsToArgs(const PreprocessorOutputOptions &Opts, + std::vector<std::string> &Res) { + if (!Opts.ShowCPP && !Opts.ShowMacros) + llvm::report_fatal_error("Invalid option combination!"); + + if (Opts.ShowCPP && Opts.ShowMacros) + Res.push_back("-dD"); + else if (!Opts.ShowCPP && Opts.ShowMacros) + Res.push_back("-dM"); + + if (Opts.ShowHeaderIncludes) + Res.push_back("-H"); + if (!Opts.ShowLineMarkers) + Res.push_back("-P"); + if (Opts.ShowComments) + Res.push_back("-C"); + if (Opts.ShowMacroComments) + Res.push_back("-CC"); +} + +static void TargetOptsToArgs(const TargetOptions &Opts, + std::vector<std::string> &Res) { + Res.push_back("-triple"); + Res.push_back(Opts.Triple); + if (!Opts.CPU.empty()) { + Res.push_back("-target-cpu"); + Res.push_back(Opts.CPU); + } + if (!Opts.ABI.empty()) { + Res.push_back("-target-abi"); + Res.push_back(Opts.ABI); + } + if (!Opts.LinkerVersion.empty()) { + Res.push_back("-target-linker-version"); + Res.push_back(Opts.LinkerVersion); + } + if (!Opts.CXXABI.empty()) { + Res.push_back("-cxx-abi"); + Res.push_back(Opts.CXXABI); + } + for (unsigned i = 0, e = Opts.Features.size(); i != e; ++i) { + Res.push_back("-target-feature"); + Res.push_back(Opts.Features[i]); + } +} + +void CompilerInvocation::toArgs(std::vector<std::string> &Res) { + AnalyzerOptsToArgs(getAnalyzerOpts(), Res); + CodeGenOptsToArgs(getCodeGenOpts(), Res); + DependencyOutputOptsToArgs(getDependencyOutputOpts(), Res); + DiagnosticOptsToArgs(getDiagnosticOpts(), Res); + FrontendOptsToArgs(getFrontendOpts(), Res); + HeaderSearchOptsToArgs(getHeaderSearchOpts(), Res); + LangOptsToArgs(getLangOpts(), Res); + PreprocessorOptsToArgs(getPreprocessorOpts(), Res); + PreprocessorOutputOptsToArgs(getPreprocessorOutputOpts(), Res); + TargetOptsToArgs(getTargetOpts(), Res); +} + +//===----------------------------------------------------------------------===// +// Deserialization (to args) +//===----------------------------------------------------------------------===// + +using namespace clang::driver; +using namespace clang::driver::cc1options; + +// + +static void ParseAnalyzerArgs(AnalyzerOptions &Opts, ArgList &Args, + Diagnostic &Diags) { + using namespace cc1options; + + Opts.AnalysisList.clear(); +#define ANALYSIS(NAME, CMDFLAG, DESC, SCOPE) \ + if (Args.hasArg(OPT_analysis_##NAME)) Opts.AnalysisList.push_back(NAME); +#include "clang/Frontend/Analyses.def" + + if (Arg *A = Args.getLastArg(OPT_analyzer_store)) { + llvm::StringRef Name = A->getValue(Args); + AnalysisStores Value = llvm::StringSwitch<AnalysisStores>(Name) +#define ANALYSIS_STORE(NAME, CMDFLAG, DESC, CREATFN) \ + .Case(CMDFLAG, NAME##Model) +#include "clang/Frontend/Analyses.def" + .Default(NumStores); + // FIXME: Error handling. + if (Value == NumStores) + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << Name; + else + Opts.AnalysisStoreOpt = Value; + } + + if (Arg *A = Args.getLastArg(OPT_analyzer_constraints)) { + llvm::StringRef Name = A->getValue(Args); + AnalysisConstraints Value = llvm::StringSwitch<AnalysisConstraints>(Name) +#define ANALYSIS_CONSTRAINTS(NAME, CMDFLAG, DESC, CREATFN) \ + .Case(CMDFLAG, NAME##Model) +#include "clang/Frontend/Analyses.def" + .Default(NumConstraints); + // FIXME: Error handling. + if (Value == NumConstraints) + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << Name; + else + Opts.AnalysisConstraintsOpt = Value; + } + + if (Arg *A = Args.getLastArg(OPT_analyzer_output)) { + llvm::StringRef Name = A->getValue(Args); + AnalysisDiagClients Value = llvm::StringSwitch<AnalysisDiagClients>(Name) +#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATFN, AUTOCREAT) \ + .Case(CMDFLAG, PD_##NAME) +#include "clang/Frontend/Analyses.def" + .Default(NUM_ANALYSIS_DIAG_CLIENTS); + // FIXME: Error handling. + if (Value == NUM_ANALYSIS_DIAG_CLIENTS) + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << Name; + else + Opts.AnalysisDiagOpt = Value; + } + + Opts.VisualizeEGDot = Args.hasArg(OPT_analyzer_viz_egraph_graphviz); + Opts.VisualizeEGUbi = Args.hasArg(OPT_analyzer_viz_egraph_ubigraph); + Opts.AnalyzeAll = Args.hasArg(OPT_analyzer_opt_analyze_headers); + Opts.AnalyzerDisplayProgress = Args.hasArg(OPT_analyzer_display_progress); + Opts.AnalyzeNestedBlocks = + Args.hasArg(OPT_analyzer_opt_analyze_nested_blocks); + Opts.PurgeDead = !Args.hasArg(OPT_analyzer_no_purge_dead); + Opts.EagerlyAssume = Args.hasArg(OPT_analyzer_eagerly_assume); + Opts.AnalyzeSpecificFunction = Args.getLastArgValue(OPT_analyze_function); + Opts.UnoptimizedCFG = Args.hasArg(OPT_analysis_UnoptimizedCFG); + Opts.EnableExperimentalChecks = Args.hasArg(OPT_analyzer_experimental_checks); + Opts.EnableExperimentalInternalChecks = + Args.hasArg(OPT_analyzer_experimental_internal_checks); + Opts.TrimGraph = Args.hasArg(OPT_trim_egraph); + Opts.MaxNodes = Args.getLastArgIntValue(OPT_analyzer_max_nodes, 150000,Diags); + Opts.MaxLoop = Args.getLastArgIntValue(OPT_analyzer_max_loop, 3, Diags); + Opts.InlineCall = Args.hasArg(OPT_analyzer_inline_call); + Opts.IdempotentOps = Args.hasArg(OPT_analysis_WarnIdempotentOps); +} + +static void ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, + Diagnostic &Diags) { + using namespace cc1options; + // -Os implies -O2 + if (Args.hasArg(OPT_Os)) + Opts.OptimizationLevel = 2; + else { + Opts.OptimizationLevel = Args.getLastArgIntValue(OPT_O, 0, Diags); + if (Opts.OptimizationLevel > 3) { + Diags.Report(diag::err_drv_invalid_value) + << Args.getLastArg(OPT_O)->getAsString(Args) << Opts.OptimizationLevel; + Opts.OptimizationLevel = 3; + } + } + + // We must always run at least the always inlining pass. + Opts.Inlining = (Opts.OptimizationLevel > 1) ? CodeGenOptions::NormalInlining + : CodeGenOptions::OnlyAlwaysInlining; + + Opts.DebugInfo = Args.hasArg(OPT_g); + Opts.DisableLLVMOpts = Args.hasArg(OPT_disable_llvm_optzns); + Opts.DisableRedZone = Args.hasArg(OPT_disable_red_zone); + Opts.DwarfDebugFlags = Args.getLastArgValue(OPT_dwarf_debug_flags); + Opts.MergeAllConstants = !Args.hasArg(OPT_fno_merge_all_constants); + Opts.NoCommon = Args.hasArg(OPT_fno_common); + Opts.NoImplicitFloat = Args.hasArg(OPT_no_implicit_float); + Opts.OptimizeSize = Args.hasArg(OPT_Os); + Opts.SimplifyLibCalls = !(Args.hasArg(OPT_fno_builtin) || + Args.hasArg(OPT_ffreestanding)); + Opts.UnrollLoops = Args.hasArg(OPT_funroll_loops) || + (Opts.OptimizationLevel > 1 && !Opts.OptimizeSize); + + Opts.AsmVerbose = Args.hasArg(OPT_masm_verbose); + Opts.CXAAtExit = !Args.hasArg(OPT_fno_use_cxa_atexit); + Opts.CXXCtorDtorAliases = Args.hasArg(OPT_mconstructor_aliases); + Opts.CodeModel = Args.getLastArgValue(OPT_mcode_model); + Opts.DebugPass = Args.getLastArgValue(OPT_mdebug_pass); + Opts.DisableFPElim = Args.hasArg(OPT_mdisable_fp_elim); + Opts.FloatABI = Args.getLastArgValue(OPT_mfloat_abi); + Opts.HiddenWeakVTables = Args.hasArg(OPT_fhidden_weak_vtables); + Opts.LimitFloatPrecision = Args.getLastArgValue(OPT_mlimit_float_precision); + Opts.NoZeroInitializedInBSS = Args.hasArg(OPT_mno_zero_initialized_in_bss); + Opts.RelaxAll = Args.hasArg(OPT_mrelax_all); + Opts.OmitLeafFramePointer = Args.hasArg(OPT_momit_leaf_frame_pointer); + Opts.SoftFloat = Args.hasArg(OPT_msoft_float); + Opts.UnwindTables = Args.hasArg(OPT_munwind_tables); + Opts.RelocationModel = Args.getLastArgValue(OPT_mrelocation_model, "pic"); + + Opts.FunctionSections = Args.hasArg(OPT_ffunction_sections); + Opts.DataSections = Args.hasArg(OPT_fdata_sections); + + Opts.MainFileName = Args.getLastArgValue(OPT_main_file_name); + Opts.VerifyModule = !Args.hasArg(OPT_disable_llvm_verifier); + + Opts.InstrumentFunctions = Args.hasArg(OPT_finstrument_functions); + + if (Arg *A = Args.getLastArg(OPT_fobjc_dispatch_method_EQ)) { + llvm::StringRef Name = A->getValue(Args); + unsigned Method = llvm::StringSwitch<unsigned>(Name) + .Case("legacy", CodeGenOptions::Legacy) + .Case("non-legacy", CodeGenOptions::NonLegacy) + .Case("mixed", CodeGenOptions::Mixed) + .Default(~0U); + if (Method == ~0U) + Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Name; + else + Opts.ObjCDispatchMethod = Method; + } +} + +static void ParseDependencyOutputArgs(DependencyOutputOptions &Opts, + ArgList &Args) { + using namespace cc1options; + Opts.OutputFile = Args.getLastArgValue(OPT_dependency_file); + Opts.Targets = Args.getAllArgValues(OPT_MT); + Opts.IncludeSystemHeaders = Args.hasArg(OPT_sys_header_deps); + Opts.UsePhonyTargets = Args.hasArg(OPT_MP); +} + +static void ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args, + Diagnostic &Diags) { + using namespace cc1options; + Opts.IgnoreWarnings = Args.hasArg(OPT_w); + Opts.NoRewriteMacros = Args.hasArg(OPT_Wno_rewrite_macros); + Opts.Pedantic = Args.hasArg(OPT_pedantic); + Opts.PedanticErrors = Args.hasArg(OPT_pedantic_errors); + Opts.ShowCarets = !Args.hasArg(OPT_fno_caret_diagnostics); + Opts.ShowColors = Args.hasArg(OPT_fcolor_diagnostics); + Opts.ShowColumn = !Args.hasArg(OPT_fno_show_column); + Opts.ShowFixits = !Args.hasArg(OPT_fno_diagnostics_fixit_info); + Opts.ShowLocation = !Args.hasArg(OPT_fno_show_source_location); + Opts.ShowOptionNames = Args.hasArg(OPT_fdiagnostics_show_option); + + llvm::StringRef ShowOverloads = + Args.getLastArgValue(OPT_fshow_overloads_EQ, "all"); + if (ShowOverloads == "best") + Opts.ShowOverloads = Diagnostic::Ovl_Best; + else if (ShowOverloads == "all") + Opts.ShowOverloads = Diagnostic::Ovl_All; + else + Diags.Report(diag::err_drv_invalid_value) + << Args.getLastArg(OPT_fshow_overloads_EQ)->getAsString(Args) + << ShowOverloads; + + llvm::StringRef ShowCategory = + Args.getLastArgValue(OPT_fdiagnostics_show_category, "none"); + if (ShowCategory == "none") + Opts.ShowCategories = 0; + else if (ShowCategory == "id") + Opts.ShowCategories = 1; + else if (ShowCategory == "name") + Opts.ShowCategories = 2; + else + Diags.Report(diag::err_drv_invalid_value) + << Args.getLastArg(OPT_fdiagnostics_show_category)->getAsString(Args) + << ShowCategory; + + Opts.ShowSourceRanges = Args.hasArg(OPT_fdiagnostics_print_source_range_info); + Opts.ShowParseableFixits = Args.hasArg(OPT_fdiagnostics_parseable_fixits); + Opts.VerifyDiagnostics = Args.hasArg(OPT_verify); + Opts.BinaryOutput = Args.hasArg(OPT_fdiagnostics_binary); + Opts.ErrorLimit = Args.getLastArgIntValue(OPT_ferror_limit, 0, Diags); + Opts.MacroBacktraceLimit + = Args.getLastArgIntValue(OPT_fmacro_backtrace_limit, + DiagnosticOptions::DefaultMacroBacktraceLimit, Diags); + Opts.TemplateBacktraceLimit + = Args.getLastArgIntValue(OPT_ftemplate_backtrace_limit, + DiagnosticOptions::DefaultTemplateBacktraceLimit, + Diags); + Opts.TabStop = Args.getLastArgIntValue(OPT_ftabstop, + DiagnosticOptions::DefaultTabStop, Diags); + if (Opts.TabStop == 0 || Opts.TabStop > DiagnosticOptions::MaxTabStop) { + Diags.Report(diag::warn_ignoring_ftabstop_value) + << Opts.TabStop << DiagnosticOptions::DefaultTabStop; + Opts.TabStop = DiagnosticOptions::DefaultTabStop; + } + Opts.MessageLength = Args.getLastArgIntValue(OPT_fmessage_length, 0, Diags); + Opts.DumpBuildInformation = Args.getLastArgValue(OPT_dump_build_information); + Opts.Warnings = Args.getAllArgValues(OPT_W); +} + +static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, + Diagnostic &Diags) { + using namespace cc1options; + Opts.ProgramAction = frontend::ParseSyntaxOnly; + if (const Arg *A = Args.getLastArg(OPT_Action_Group)) { + switch (A->getOption().getID()) { + default: + assert(0 && "Invalid option in group!"); + case OPT_ast_dump: + Opts.ProgramAction = frontend::ASTDump; break; + case OPT_ast_print: + Opts.ProgramAction = frontend::ASTPrint; break; + case OPT_ast_print_xml: + Opts.ProgramAction = frontend::ASTPrintXML; break; + case OPT_ast_view: + Opts.ProgramAction = frontend::ASTView; break; + case OPT_boostcon: + Opts.ProgramAction = frontend::BoostCon; break; + case OPT_dump_raw_tokens: + Opts.ProgramAction = frontend::DumpRawTokens; break; + case OPT_dump_tokens: + Opts.ProgramAction = frontend::DumpTokens; break; + case OPT_S: + Opts.ProgramAction = frontend::EmitAssembly; break; + case OPT_emit_llvm_bc: + Opts.ProgramAction = frontend::EmitBC; break; + case OPT_emit_html: + Opts.ProgramAction = frontend::EmitHTML; break; + case OPT_emit_llvm: + Opts.ProgramAction = frontend::EmitLLVM; break; + case OPT_emit_llvm_only: + Opts.ProgramAction = frontend::EmitLLVMOnly; break; + case OPT_emit_codegen_only: + Opts.ProgramAction = frontend::EmitCodeGenOnly; break; + case OPT_emit_obj: + Opts.ProgramAction = frontend::EmitObj; break; + case OPT_fixit_EQ: + Opts.FixItSuffix = A->getValue(Args); + // fall-through! + case OPT_fixit: + Opts.ProgramAction = frontend::FixIt; break; + case OPT_emit_pch: + Opts.ProgramAction = frontend::GeneratePCH; break; + case OPT_emit_pth: + Opts.ProgramAction = frontend::GeneratePTH; break; + case OPT_init_only: + Opts.ProgramAction = frontend::InitOnly; break; + case OPT_fsyntax_only: + Opts.ProgramAction = frontend::ParseSyntaxOnly; break; + case OPT_print_decl_contexts: + Opts.ProgramAction = frontend::PrintDeclContext; break; + case OPT_print_preamble: + Opts.ProgramAction = frontend::PrintPreamble; break; + case OPT_E: + Opts.ProgramAction = frontend::PrintPreprocessedInput; break; + case OPT_rewrite_macros: + Opts.ProgramAction = frontend::RewriteMacros; break; + case OPT_rewrite_objc: + Opts.ProgramAction = frontend::RewriteObjC; break; + case OPT_rewrite_test: + Opts.ProgramAction = frontend::RewriteTest; break; + case OPT_analyze: + Opts.ProgramAction = frontend::RunAnalysis; break; + case OPT_Eonly: + Opts.ProgramAction = frontend::RunPreprocessorOnly; break; + case OPT_create_module: + Opts.ProgramAction = frontend::CreateModule; break; + } + } + + if (const Arg* A = Args.getLastArg(OPT_plugin)) { + Opts.Plugins.push_back(A->getValue(Args,0)); + Opts.ProgramAction = frontend::PluginAction; + Opts.ActionName = A->getValue(Args); + + for (arg_iterator it = Args.filtered_begin(OPT_plugin_arg), + end = Args.filtered_end(); it != end; ++it) { + if ((*it)->getValue(Args, 0) == Opts.ActionName) + Opts.PluginArgs.push_back((*it)->getValue(Args, 1)); + } + } + + if (const Arg *A = Args.getLastArg(OPT_code_completion_at)) { + Opts.CodeCompletionAt = + ParsedSourceLocation::FromString(A->getValue(Args)); + if (Opts.CodeCompletionAt.FileName.empty()) + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << A->getValue(Args); + } + Opts.DebugCodeCompletionPrinter = + !Args.hasArg(OPT_no_code_completion_debug_printer); + Opts.DisableFree = Args.hasArg(OPT_disable_free); + + Opts.OutputFile = Args.getLastArgValue(OPT_o); + Opts.Plugins = Args.getAllArgValues(OPT_load); + Opts.RelocatablePCH = Args.hasArg(OPT_relocatable_pch); + Opts.ChainedPCH = Args.hasArg(OPT_chained_pch); + Opts.ShowHelp = Args.hasArg(OPT_help); + Opts.ShowMacrosInCodeCompletion = Args.hasArg(OPT_code_completion_macros); + Opts.ShowCodePatternsInCodeCompletion + = Args.hasArg(OPT_code_completion_patterns); + Opts.ShowGlobalSymbolsInCodeCompletion + = !Args.hasArg(OPT_no_code_completion_globals); + Opts.ShowStats = Args.hasArg(OPT_print_stats); + Opts.ShowTimers = Args.hasArg(OPT_ftime_report); + Opts.ShowVersion = Args.hasArg(OPT_version); + Opts.ViewClassInheritance = Args.getLastArgValue(OPT_cxx_inheritance_view); + Opts.ASTMergeFiles = Args.getAllArgValues(OPT_ast_merge); + Opts.LLVMArgs = Args.getAllArgValues(OPT_mllvm); + Opts.FixWhatYouCan = Args.hasArg(OPT_fix_what_you_can); + Opts.Modules = Args.getAllArgValues(OPT_import_module); + + InputKind DashX = IK_None; + if (const Arg *A = Args.getLastArg(OPT_x)) { + DashX = llvm::StringSwitch<InputKind>(A->getValue(Args)) + .Case("c", IK_C) + .Case("cl", IK_OpenCL) + .Case("c", IK_C) + .Case("cl", IK_OpenCL) + .Case("c++", IK_CXX) + .Case("objective-c", IK_ObjC) + .Case("objective-c++", IK_ObjCXX) + .Case("cpp-output", IK_PreprocessedC) + .Case("assembler-with-cpp", IK_Asm) + .Case("c++-cpp-output", IK_PreprocessedCXX) + .Case("objective-c-cpp-output", IK_PreprocessedObjC) + .Case("objective-c++-cpp-output", IK_PreprocessedObjCXX) + .Case("c-header", IK_C) + .Case("objective-c-header", IK_ObjC) + .Case("c++-header", IK_CXX) + .Case("objective-c++-header", IK_ObjCXX) + .Case("ast", IK_AST) + .Case("ir", IK_LLVM_IR) + .Default(IK_None); + if (DashX == IK_None) + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << A->getValue(Args); + } + + // '-' is the default input if none is given. + std::vector<std::string> Inputs = Args.getAllArgValues(OPT_INPUT); + Opts.Inputs.clear(); + if (Inputs.empty()) + Inputs.push_back("-"); + for (unsigned i = 0, e = Inputs.size(); i != e; ++i) { + InputKind IK = DashX; + if (IK == IK_None) { + IK = FrontendOptions::getInputKindForExtension( + llvm::StringRef(Inputs[i]).rsplit('.').second); + // FIXME: Remove this hack. + if (i == 0) + DashX = IK; + } + Opts.Inputs.push_back(std::make_pair(IK, Inputs[i])); + } + + return DashX; +} + +std::string CompilerInvocation::GetResourcesPath(const char *Argv0, + void *MainAddr) { + llvm::sys::Path P = llvm::sys::Path::GetMainExecutable(Argv0, MainAddr); + + if (!P.isEmpty()) { + P.eraseComponent(); // Remove /clang from foo/bin/clang + P.eraseComponent(); // Remove /bin from foo/bin + + // Get foo/lib/clang/<version>/include + P.appendComponent("lib"); + P.appendComponent("clang"); + P.appendComponent(CLANG_VERSION_STRING); + } + + return P.str(); +} + +static void ParseHeaderSearchArgs(HeaderSearchOptions &Opts, ArgList &Args) { + using namespace cc1options; + Opts.Sysroot = Args.getLastArgValue(OPT_isysroot, "/"); + Opts.Verbose = Args.hasArg(OPT_v); + Opts.UseBuiltinIncludes = !Args.hasArg(OPT_nobuiltininc); + Opts.UseStandardIncludes = !Args.hasArg(OPT_nostdinc); + Opts.UseStandardCXXIncludes = !Args.hasArg(OPT_nostdincxx); + Opts.ResourceDir = Args.getLastArgValue(OPT_resource_dir); + + // Add -I... and -F... options in order. + for (arg_iterator it = Args.filtered_begin(OPT_I, OPT_F), + ie = Args.filtered_end(); it != ie; ++it) + Opts.AddPath((*it)->getValue(Args), frontend::Angled, true, + /*IsFramework=*/ (*it)->getOption().matches(OPT_F), true); + + // Add -iprefix/-iwith-prefix/-iwithprefixbefore options. + llvm::StringRef Prefix = ""; // FIXME: This isn't the correct default prefix. + for (arg_iterator it = Args.filtered_begin(OPT_iprefix, OPT_iwithprefix, + OPT_iwithprefixbefore), + ie = Args.filtered_end(); it != ie; ++it) { + const Arg *A = *it; + if (A->getOption().matches(OPT_iprefix)) + Prefix = A->getValue(Args); + else if (A->getOption().matches(OPT_iwithprefix)) + Opts.AddPath(Prefix.str() + A->getValue(Args), + frontend::System, false, false, true); + else + Opts.AddPath(Prefix.str() + A->getValue(Args), + frontend::Angled, false, false, true); + } + + for (arg_iterator it = Args.filtered_begin(OPT_idirafter), + ie = Args.filtered_end(); it != ie; ++it) + Opts.AddPath((*it)->getValue(Args), frontend::After, true, false, true); + for (arg_iterator it = Args.filtered_begin(OPT_iquote), + ie = Args.filtered_end(); it != ie; ++it) + Opts.AddPath((*it)->getValue(Args), frontend::Quoted, true, false, true); + for (arg_iterator it = Args.filtered_begin(OPT_isystem, OPT_iwithsysroot), + ie = Args.filtered_end(); it != ie; ++it) + Opts.AddPath((*it)->getValue(Args), frontend::System, true, false, + (*it)->getOption().matches(OPT_iwithsysroot)); + + // FIXME: Need options for the various environment variables! +} + +static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, + Diagnostic &Diags) { + // FIXME: Cleanup per-file based stuff. + + // Set some properties which depend soley on the input kind; it would be nice + // to move these to the language standard, and have the driver resolve the + // input kind + language standard. + if (IK == IK_Asm) { + Opts.AsmPreprocessor = 1; + } else if (IK == IK_ObjC || + IK == IK_ObjCXX || + IK == IK_PreprocessedObjC || + IK == IK_PreprocessedObjCXX) { + Opts.ObjC1 = Opts.ObjC2 = 1; + } + + LangStandard::Kind LangStd = LangStandard::lang_unspecified; + if (const Arg *A = Args.getLastArg(OPT_std_EQ)) { + LangStd = llvm::StringSwitch<LangStandard::Kind>(A->getValue(Args)) +#define LANGSTANDARD(id, name, desc, features) \ + .Case(name, LangStandard::lang_##id) +#include "clang/Frontend/LangStandards.def" + .Default(LangStandard::lang_unspecified); + if (LangStd == LangStandard::lang_unspecified) + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << A->getValue(Args); + } + + if (LangStd == LangStandard::lang_unspecified) { + // Based on the base language, pick one. + switch (IK) { + case IK_None: + case IK_AST: + case IK_LLVM_IR: + assert(0 && "Invalid input kind!"); + case IK_OpenCL: + LangStd = LangStandard::lang_opencl; + break; + case IK_Asm: + case IK_C: + case IK_PreprocessedC: + case IK_ObjC: + case IK_PreprocessedObjC: + LangStd = LangStandard::lang_gnu99; + break; + case IK_CXX: + case IK_PreprocessedCXX: + case IK_ObjCXX: + case IK_PreprocessedObjCXX: + LangStd = LangStandard::lang_gnucxx98; + break; + } + } + + const LangStandard &Std = LangStandard::getLangStandardForKind(LangStd); + Opts.BCPLComment = Std.hasBCPLComments(); + Opts.C99 = Std.isC99(); + Opts.CPlusPlus = Std.isCPlusPlus(); + Opts.CPlusPlus0x = Std.isCPlusPlus0x(); + Opts.Digraphs = Std.hasDigraphs(); + Opts.GNUMode = Std.isGNUMode(); + Opts.GNUInline = !Std.isC99(); + Opts.HexFloats = Std.hasHexFloats(); + Opts.ImplicitInt = Std.hasImplicitInt(); + + // OpenCL has some additional defaults. + if (LangStd == LangStandard::lang_opencl) { + Opts.OpenCL = 1; + Opts.AltiVec = 1; + Opts.CXXOperatorNames = 1; + Opts.LaxVectorConversions = 1; + } + + // OpenCL and C++ both have bool, true, false keywords. + Opts.Bool = Opts.OpenCL || Opts.CPlusPlus; + + // We abuse '-f[no-]gnu-keywords' to force overriding all GNU-extension + // keywords. This behavior is provided by GCC's poorly named '-fasm' flag, + // while a subset (the non-C++ GNU keywords) is provided by GCC's + // '-fgnu-keywords'. Clang conflates the two for simplicity under the single + // name, as it doesn't seem a useful distinction. + Opts.GNUKeywords = Args.hasFlag(OPT_fgnu_keywords, OPT_fno_gnu_keywords, + Opts.GNUMode); + + if (Opts.CPlusPlus) + Opts.CXXOperatorNames = !Args.hasArg(OPT_fno_operator_names); + + if (Args.hasArg(OPT_fobjc_gc_only)) + Opts.setGCMode(LangOptions::GCOnly); + else if (Args.hasArg(OPT_fobjc_gc)) + Opts.setGCMode(LangOptions::HybridGC); + + if (Args.hasArg(OPT_print_ivar_layout)) + Opts.ObjCGCBitmapPrint = 1; + if (Args.hasArg(OPT_fno_constant_cfstrings)) + Opts.NoConstantCFStrings = 1; + + if (Args.hasArg(OPT_faltivec)) + Opts.AltiVec = 1; + + if (Args.hasArg(OPT_pthread)) + Opts.POSIXThreads = 1; + + llvm::StringRef Vis = Args.getLastArgValue(OPT_fvisibility, "default"); + if (Vis == "default") + Opts.setVisibilityMode(LangOptions::Default); + else if (Vis == "hidden") + Opts.setVisibilityMode(LangOptions::Hidden); + else if (Vis == "protected") + Opts.setVisibilityMode(LangOptions::Protected); + else + Diags.Report(diag::err_drv_invalid_value) + << Args.getLastArg(OPT_fvisibility)->getAsString(Args) << Vis; + + if (Args.hasArg(OPT_fvisibility_inlines_hidden)) + Opts.InlineVisibilityHidden = 1; + + if (Args.hasArg(OPT_ftrapv)) + Opts.setSignedOverflowBehavior(LangOptions::SOB_Trapping); + else if (Args.hasArg(OPT_fwrapv)) + Opts.setSignedOverflowBehavior(LangOptions::SOB_Defined); + + // Mimicing gcc's behavior, trigraphs are only enabled if -trigraphs + // is specified, or -std is set to a conforming mode. + Opts.Trigraphs = !Opts.GNUMode; + if (Args.hasArg(OPT_trigraphs)) + Opts.Trigraphs = 1; + + Opts.DollarIdents = Args.hasFlag(OPT_fdollars_in_identifiers, + OPT_fno_dollars_in_identifiers, + !Opts.AsmPreprocessor); + Opts.PascalStrings = Args.hasArg(OPT_fpascal_strings); + Opts.Microsoft = Args.hasArg(OPT_fms_extensions); + Opts.Borland = Args.hasArg(OPT_fborland_extensions); + Opts.WritableStrings = Args.hasArg(OPT_fwritable_strings); + Opts.ConstStrings = Args.hasArg(OPT_Wwrite_strings); + if (Args.hasArg(OPT_fno_lax_vector_conversions)) + Opts.LaxVectorConversions = 0; + if (Args.hasArg(OPT_fno_threadsafe_statics)) + Opts.ThreadsafeStatics = 0; + Opts.Exceptions = Args.hasArg(OPT_fexceptions); + Opts.RTTI = !Args.hasArg(OPT_fno_rtti); + Opts.Blocks = Args.hasArg(OPT_fblocks); + Opts.CharIsSigned = !Args.hasArg(OPT_fno_signed_char); + Opts.ShortWChar = Args.hasArg(OPT_fshort_wchar); + Opts.Freestanding = Args.hasArg(OPT_ffreestanding); + Opts.FormatExtensions = Args.hasArg(OPT_fformat_extensions); + Opts.NoBuiltin = Args.hasArg(OPT_fno_builtin) || Opts.Freestanding; + Opts.AssumeSaneOperatorNew = !Args.hasArg(OPT_fno_assume_sane_operator_new); + Opts.HeinousExtensions = Args.hasArg(OPT_fheinous_gnu_extensions); + Opts.AccessControl = !Args.hasArg(OPT_fno_access_control); + Opts.ElideConstructors = !Args.hasArg(OPT_fno_elide_constructors); + Opts.MathErrno = Args.hasArg(OPT_fmath_errno); + Opts.InstantiationDepth = Args.getLastArgIntValue(OPT_ftemplate_depth, 1024, + Diags); + Opts.NeXTRuntime = !Args.hasArg(OPT_fgnu_runtime); + Opts.ObjCConstantStringClass = + Args.getLastArgValue(OPT_fconstant_string_class); + Opts.ObjCNonFragileABI = Args.hasArg(OPT_fobjc_nonfragile_abi); + Opts.ObjCNonFragileABI2 = Args.hasArg(OPT_fobjc_nonfragile_abi2); + if (Opts.ObjCNonFragileABI2) + Opts.ObjCNonFragileABI = true; + Opts.CatchUndefined = Args.hasArg(OPT_fcatch_undefined_behavior); + Opts.EmitAllDecls = Args.hasArg(OPT_femit_all_decls); + Opts.PICLevel = Args.getLastArgIntValue(OPT_pic_level, 0, Diags); + Opts.SjLjExceptions = Args.hasArg(OPT_fsjlj_exceptions); + Opts.Static = Args.hasArg(OPT_static_define); + Opts.DumpRecordLayouts = Args.hasArg(OPT_fdump_record_layouts); + Opts.DumpVTableLayouts = Args.hasArg(OPT_fdump_vtable_layouts); + Opts.SpellChecking = !Args.hasArg(OPT_fno_spell_checking); + Opts.NoBitFieldTypeAlign = Args.hasArg(OPT_fno_bitfield_type_align); + Opts.OptimizeSize = 0; + + // FIXME: Eliminate this dependency. + unsigned Opt = + Args.hasArg(OPT_Os) ? 2 : Args.getLastArgIntValue(OPT_O, 0, Diags); + Opts.Optimize = Opt != 0; + + // This is the __NO_INLINE__ define, which just depends on things like the + // optimization level and -fno-inline, not actually whether the backend has + // inlining enabled. + // + // FIXME: This is affected by other options (-fno-inline). + Opts.NoInline = !Opt; + + unsigned SSP = Args.getLastArgIntValue(OPT_stack_protector, 0, Diags); + switch (SSP) { + default: + Diags.Report(diag::err_drv_invalid_value) + << Args.getLastArg(OPT_stack_protector)->getAsString(Args) << SSP; + break; + case 0: Opts.setStackProtectorMode(LangOptions::SSPOff); break; + case 1: Opts.setStackProtectorMode(LangOptions::SSPOn); break; + case 2: Opts.setStackProtectorMode(LangOptions::SSPReq); break; + } +} + +static void ParsePreprocessorArgs(PreprocessorOptions &Opts, ArgList &Args, + Diagnostic &Diags) { + using namespace cc1options; + Opts.ImplicitPCHInclude = Args.getLastArgValue(OPT_include_pch); + Opts.ImplicitPTHInclude = Args.getLastArgValue(OPT_include_pth); + if (const Arg *A = Args.getLastArg(OPT_token_cache)) + Opts.TokenCache = A->getValue(Args); + else + Opts.TokenCache = Opts.ImplicitPTHInclude; + Opts.UsePredefines = !Args.hasArg(OPT_undef); + Opts.DetailedRecord = Args.hasArg(OPT_detailed_preprocessing_record); + Opts.DisablePCHValidation = Args.hasArg(OPT_fno_validate_pch); + + if (const Arg *A = Args.getLastArg(OPT_preamble_bytes_EQ)) { + llvm::StringRef Value(A->getValue(Args)); + size_t Comma = Value.find(','); + unsigned Bytes = 0; + unsigned EndOfLine = 0; + + if (Comma == llvm::StringRef::npos || + Value.substr(0, Comma).getAsInteger(10, Bytes) || + Value.substr(Comma + 1).getAsInteger(10, EndOfLine)) + Diags.Report(diag::err_drv_preamble_format); + else { + Opts.PrecompiledPreambleBytes.first = Bytes; + Opts.PrecompiledPreambleBytes.second = (EndOfLine != 0); + } + } + + // Add macros from the command line. + for (arg_iterator it = Args.filtered_begin(OPT_D, OPT_U), + ie = Args.filtered_end(); it != ie; ++it) { + if ((*it)->getOption().matches(OPT_D)) + Opts.addMacroDef((*it)->getValue(Args)); + else + Opts.addMacroUndef((*it)->getValue(Args)); + } + + Opts.MacroIncludes = Args.getAllArgValues(OPT_imacros); + + // Add the ordered list of -includes. + for (arg_iterator it = Args.filtered_begin(OPT_include, OPT_include_pch, + OPT_include_pth), + ie = Args.filtered_end(); it != ie; ++it) { + const Arg *A = *it; + // PCH is handled specially, we need to extra the original include path. + if (A->getOption().matches(OPT_include_pch)) { + std::string OriginalFile = + ASTReader::getOriginalSourceFile(A->getValue(Args), Diags); + if (OriginalFile.empty()) + continue; + + Opts.Includes.push_back(OriginalFile); + } else + Opts.Includes.push_back(A->getValue(Args)); + } + + // Include 'altivec.h' if -faltivec option present + if (Args.hasArg(OPT_faltivec)) + Opts.Includes.push_back("altivec.h"); + + for (arg_iterator it = Args.filtered_begin(OPT_remap_file), + ie = Args.filtered_end(); it != ie; ++it) { + const Arg *A = *it; + std::pair<llvm::StringRef,llvm::StringRef> Split = + llvm::StringRef(A->getValue(Args)).split(';'); + + if (Split.second.empty()) { + Diags.Report(diag::err_drv_invalid_remap_file) << A->getAsString(Args); + continue; + } + + Opts.addRemappedFile(Split.first, Split.second); + } +} + +static void ParsePreprocessorOutputArgs(PreprocessorOutputOptions &Opts, + ArgList &Args) { + using namespace cc1options; + Opts.ShowCPP = !Args.hasArg(OPT_dM); + Opts.ShowComments = Args.hasArg(OPT_C); + Opts.ShowHeaderIncludes = Args.hasArg(OPT_H); + Opts.ShowLineMarkers = !Args.hasArg(OPT_P); + Opts.ShowMacroComments = Args.hasArg(OPT_CC); + Opts.ShowMacros = Args.hasArg(OPT_dM) || Args.hasArg(OPT_dD); +} + +static void ParseTargetArgs(TargetOptions &Opts, ArgList &Args) { + using namespace cc1options; + Opts.ABI = Args.getLastArgValue(OPT_target_abi); + Opts.CXXABI = Args.getLastArgValue(OPT_cxx_abi); + Opts.CPU = Args.getLastArgValue(OPT_target_cpu); + Opts.Features = Args.getAllArgValues(OPT_target_feature); + Opts.LinkerVersion = Args.getLastArgValue(OPT_target_linker_version); + Opts.Triple = llvm::Triple::normalize(Args.getLastArgValue(OPT_triple)); + + // Use the host triple if unspecified. + if (Opts.Triple.empty()) + Opts.Triple = llvm::sys::getHostTriple(); +} + +// + +void CompilerInvocation::CreateFromArgs(CompilerInvocation &Res, + const char **ArgBegin, + const char **ArgEnd, + Diagnostic &Diags) { + // Parse the arguments. + llvm::OwningPtr<OptTable> Opts(createCC1OptTable()); + unsigned MissingArgIndex, MissingArgCount; + llvm::OwningPtr<InputArgList> Args( + Opts->ParseArgs(ArgBegin, ArgEnd,MissingArgIndex, MissingArgCount)); + + // Check for missing argument error. + if (MissingArgCount) + Diags.Report(diag::err_drv_missing_argument) + << Args->getArgString(MissingArgIndex) << MissingArgCount; + + // Issue errors on unknown arguments. + for (arg_iterator it = Args->filtered_begin(OPT_UNKNOWN), + ie = Args->filtered_end(); it != ie; ++it) + Diags.Report(diag::err_drv_unknown_argument) << (*it)->getAsString(*Args); + + ParseAnalyzerArgs(Res.getAnalyzerOpts(), *Args, Diags); + ParseCodeGenArgs(Res.getCodeGenOpts(), *Args, Diags); + ParseDependencyOutputArgs(Res.getDependencyOutputOpts(), *Args); + ParseDiagnosticArgs(Res.getDiagnosticOpts(), *Args, Diags); + InputKind DashX = ParseFrontendArgs(Res.getFrontendOpts(), *Args, Diags); + ParseHeaderSearchArgs(Res.getHeaderSearchOpts(), *Args); + if (DashX != IK_AST && DashX != IK_LLVM_IR) + ParseLangArgs(Res.getLangOpts(), *Args, DashX, Diags); + ParsePreprocessorArgs(Res.getPreprocessorOpts(), *Args, Diags); + ParsePreprocessorOutputArgs(Res.getPreprocessorOutputOpts(), *Args); + ParseTargetArgs(Res.getTargetOpts(), *Args); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/DeclXML.cpp b/contrib/llvm/tools/clang/lib/Frontend/DeclXML.cpp new file mode 100644 index 0000000..97a7f55 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/DeclXML.cpp @@ -0,0 +1,189 @@ +//===--- DeclXML.cpp - XML implementation for Decl ASTs -------------------===// +// +// 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 XML document class, which provides the means to +// dump out the AST in a XML form that exposes type details and other fields. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/DocumentXML.h" +#include "clang/AST/DeclVisitor.h" +#include "clang/AST/Expr.h" + +namespace clang { + +//--------------------------------------------------------- +class DocumentXML::DeclPrinter : public DeclVisitor<DocumentXML::DeclPrinter> { + DocumentXML& Doc; + + void addSubNodes(FunctionDecl* FD) { + for (unsigned i = 0, e = FD->getNumParams(); i != e; ++i) { + Visit(FD->getParamDecl(i)); + Doc.toParent(); + } + } + + void addFunctionBody(FunctionDecl* FD) { + if (FD->isThisDeclarationADefinition()) { + Doc.addSubNode("Body"); + Doc.PrintStmt(FD->getBody()); + Doc.toParent(); + } + } + + void addSubNodes(RecordDecl* RD) { + for (RecordDecl::field_iterator i = RD->field_begin(), + e = RD->field_end(); i != e; ++i) { + Visit(*i); + Doc.toParent(); + } + } + + void addSubNodes(CXXRecordDecl* RD) { + addSubNodes(cast<RecordDecl>(RD)); + + if (RD->isDefinition()) { + // FIXME: This breaks XML generation + //Doc.addAttribute("num_bases", RD->getNumBases()); + + for (CXXRecordDecl::base_class_iterator + base = RD->bases_begin(), + bend = RD->bases_end(); + base != bend; + ++base) { + Doc.addSubNode("Base"); + Doc.addAttribute("id", base->getType()); + AccessSpecifier as = base->getAccessSpecifierAsWritten(); + const char* as_name = ""; + switch(as) { + case AS_none: as_name = ""; break; + case AS_public: as_name = "public"; break; + case AS_protected: as_name = "protected"; break; + case AS_private: as_name = "private"; break; + } + Doc.addAttributeOptional("access", as_name); + Doc.addAttribute("is_virtual", base->isVirtual()); + Doc.toParent(); + } + + for (CXXRecordDecl::method_iterator i = RD->method_begin(), + e = RD->method_end(); i != e; ++i) { + Visit(*i); + Doc.toParent(); + } + + } + + } + + void addSubNodes(EnumDecl* ED) { + for (EnumDecl::enumerator_iterator i = ED->enumerator_begin(), + e = ED->enumerator_end(); i != e; ++i) { + Visit(*i); + Doc.toParent(); + } + } + + void addSubNodes(EnumConstantDecl* ECD) { + if (ECD->getInitExpr()) + Doc.PrintStmt(ECD->getInitExpr()); + } + + void addSubNodes(FieldDecl* FdD) { + if (FdD->isBitField()) + Doc.PrintStmt(FdD->getBitWidth()); + } + + void addSubNodes(VarDecl* V) { + if (V->getInit()) + Doc.PrintStmt(V->getInit()); + } + + void addSubNodes(ParmVarDecl* argDecl) { + if (argDecl->getDefaultArg()) + Doc.PrintStmt(argDecl->getDefaultArg()); + } + + void addSubNodes(NamespaceDecl* ns) { + + for (DeclContext::decl_iterator + d = ns->decls_begin(), + dend = ns->decls_end(); + d != dend; + ++d) { + Visit(*d); + Doc.toParent(); + } + } + + void addSpecialAttribute(const char* pName, EnumDecl* ED) { + const QualType& enumType = ED->getIntegerType(); + if (!enumType.isNull()) + Doc.addAttribute(pName, enumType); + } + + void addIdAttribute(LinkageSpecDecl* ED) { + Doc.addAttribute("id", ED); + } + + void addIdAttribute(NamedDecl* ND) { + Doc.addAttribute("id", ND); + } + +public: + DeclPrinter(DocumentXML& doc) : Doc(doc) {} + +#define NODE_XML( CLASS, NAME ) \ + void Visit##CLASS(CLASS* T) \ + { \ + Doc.addSubNode(NAME); + +#define ID_ATTRIBUTE_XML addIdAttribute(T); +#define ATTRIBUTE_XML( FN, NAME ) Doc.addAttribute(NAME, T->FN); +#define ATTRIBUTE_OPT_XML( FN, NAME ) Doc.addAttributeOptional(NAME, T->FN); +#define ATTRIBUTE_FILE_LOCATION_XML Doc.addLocation(T->getLocation()); +#define ATTRIBUTE_SPECIAL_XML( FN, NAME ) addSpecialAttribute(NAME, T); + +#define ATTRIBUTE_ENUM_XML( FN, NAME ) \ + { \ + const char* pAttributeName = NAME; \ + const bool optional = false; \ + switch (T->FN) { \ + default: assert(0 && "unknown enum value"); + +#define ATTRIBUTE_ENUM_OPT_XML( FN, NAME ) \ + { \ + const char* pAttributeName = NAME; \ + const bool optional = true; \ + switch (T->FN) { \ + default: assert(0 && "unknown enum value"); + +#define ENUM_XML( VALUE, NAME ) case VALUE: if ((!optional) || NAME[0]) Doc.addAttribute(pAttributeName, NAME); break; +#define END_ENUM_XML } } +#define END_NODE_XML } + +#define SUB_NODE_XML( CLASS ) addSubNodes(T); +#define SUB_NODE_SEQUENCE_XML( CLASS ) addSubNodes(T); +#define SUB_NODE_OPT_XML( CLASS ) addSubNodes(T); + +#define SUB_NODE_FN_BODY_XML addFunctionBody(T); + +#include "clang/Frontend/DeclXML.def" +}; + + +//--------------------------------------------------------- +void DocumentXML::writeDeclToXML(Decl *D) { + DeclPrinter(*this).Visit(D); + toParent(); +} + +//--------------------------------------------------------- +} // NS clang + diff --git a/contrib/llvm/tools/clang/lib/Frontend/DependencyFile.cpp b/contrib/llvm/tools/clang/lib/Frontend/DependencyFile.cpp new file mode 100644 index 0000000..cdff807 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/DependencyFile.cpp @@ -0,0 +1,173 @@ +//===--- DependencyFile.cpp - Generate dependency file --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This code generates dependency files. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/Utils.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Frontend/DependencyOutputOptions.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Lex/DirectoryLookup.h" +#include "clang/Lex/PPCallbacks.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/Support/raw_ostream.h" +#include <string> + +using namespace clang; + +namespace { +class DependencyFileCallback : public PPCallbacks { + std::vector<std::string> Files; + llvm::StringSet<> FilesSet; + const Preprocessor *PP; + std::vector<std::string> Targets; + llvm::raw_ostream *OS; + bool IncludeSystemHeaders; + bool PhonyTarget; +private: + bool FileMatchesDepCriteria(const char *Filename, + SrcMgr::CharacteristicKind FileType); + void OutputDependencyFile(); + +public: + DependencyFileCallback(const Preprocessor *_PP, + llvm::raw_ostream *_OS, + const DependencyOutputOptions &Opts) + : PP(_PP), Targets(Opts.Targets), OS(_OS), + IncludeSystemHeaders(Opts.IncludeSystemHeaders), + PhonyTarget(Opts.UsePhonyTargets) {} + + virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType); + + virtual void EndOfMainFile() { + OutputDependencyFile(); + delete OS; + OS = 0; + } +}; +} + +void clang::AttachDependencyFileGen(Preprocessor &PP, + const DependencyOutputOptions &Opts) { + if (Opts.Targets.empty()) { + PP.getDiagnostics().Report(diag::err_fe_dependency_file_requires_MT); + return; + } + + std::string Err; + llvm::raw_ostream *OS(new llvm::raw_fd_ostream(Opts.OutputFile.c_str(), Err)); + if (!Err.empty()) { + PP.getDiagnostics().Report(diag::err_fe_error_opening) + << Opts.OutputFile << Err; + return; + } + + PP.addPPCallbacks(new DependencyFileCallback(&PP, OS, Opts)); +} + +/// FileMatchesDepCriteria - Determine whether the given Filename should be +/// considered as a dependency. +bool DependencyFileCallback::FileMatchesDepCriteria(const char *Filename, + SrcMgr::CharacteristicKind FileType) { + if (strcmp("<built-in>", Filename) == 0) + return false; + + if (IncludeSystemHeaders) + return true; + + return FileType == SrcMgr::C_User; +} + +void DependencyFileCallback::FileChanged(SourceLocation Loc, + FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType) { + if (Reason != PPCallbacks::EnterFile) + return; + + // Dependency generation really does want to go all the way to the + // file entry for a source location to find out what is depended on. + // We do not want #line markers to affect dependency generation! + SourceManager &SM = PP->getSourceManager(); + + const FileEntry *FE = + SM.getFileEntryForID(SM.getFileID(SM.getInstantiationLoc(Loc))); + if (FE == 0) return; + + const char *Filename = FE->getName(); + if (!FileMatchesDepCriteria(Filename, FileType)) + return; + + // Remove leading "./" + if (Filename[0] == '.' && Filename[1] == '/') + Filename = &Filename[2]; + + if (FilesSet.insert(Filename)) + Files.push_back(Filename); +} + +void DependencyFileCallback::OutputDependencyFile() { + // Write out the dependency targets, trying to avoid overly long + // lines when possible. We try our best to emit exactly the same + // dependency file as GCC (4.2), assuming the included files are the + // same. + const unsigned MaxColumns = 75; + unsigned Columns = 0; + + for (std::vector<std::string>::iterator + I = Targets.begin(), E = Targets.end(); I != E; ++I) { + unsigned N = I->length(); + if (Columns == 0) { + Columns += N; + *OS << *I; + } else if (Columns + N + 2 > MaxColumns) { + Columns = N + 2; + *OS << " \\\n " << *I; + } else { + Columns += N + 1; + *OS << ' ' << *I; + } + } + + *OS << ':'; + Columns += 1; + + // Now add each dependency in the order it was seen, but avoiding + // duplicates. + for (std::vector<std::string>::iterator I = Files.begin(), + E = Files.end(); I != E; ++I) { + // Start a new line if this would exceed the column limit. Make + // sure to leave space for a trailing " \" in case we need to + // break the line on the next iteration. + unsigned N = I->length(); + if (Columns + (N + 1) + 2 > MaxColumns) { + *OS << " \\\n "; + Columns = 2; + } + *OS << ' ' << *I; + Columns += N + 1; + } + *OS << '\n'; + + // Create phony targets if requested. + if (PhonyTarget) { + // Skip the first entry, this is always the input file itself. + for (std::vector<std::string>::iterator I = Files.begin() + 1, + E = Files.end(); I != E; ++I) { + *OS << '\n'; + *OS << *I << ":\n"; + } + } +} + diff --git a/contrib/llvm/tools/clang/lib/Frontend/DiagChecker.cpp b/contrib/llvm/tools/clang/lib/Frontend/DiagChecker.cpp new file mode 100644 index 0000000..66d7ed7 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/DiagChecker.cpp @@ -0,0 +1,301 @@ +//===--- DiagChecker.cpp - Diagnostic Checking Functions ------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Process the input files and check that the diagnostic messages are expected. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/Utils.h" +#include "clang/Frontend/TextDiagnosticBuffer.h" +#include "clang/Parse/ParseAST.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/Support/raw_ostream.h" +using namespace clang; + +typedef TextDiagnosticBuffer::DiagList DiagList; +typedef TextDiagnosticBuffer::const_iterator const_diag_iterator; + +static void EmitError(Preprocessor &PP, SourceLocation Pos, const char *String){ + unsigned ID = PP.getDiagnostics().getCustomDiagID(Diagnostic::Error, String); + PP.Diag(Pos, ID); +} + + +// USING THE DIAGNOSTIC CHECKER: +// +// Indicating that a line expects an error or a warning is simple. Put a comment +// on the line that has the diagnostic, use "expected-{error,warning}" to tag +// if it's an expected error or warning, and place the expected text between {{ +// and }} markers. The full text doesn't have to be included, only enough to +// ensure that the correct diagnostic was emitted. +// +// Here's an example: +// +// int A = B; // expected-error {{use of undeclared identifier 'B'}} +// +// You can place as many diagnostics on one line as you wish. To make the code +// more readable, you can use slash-newline to separate out the diagnostics. +// +// The simple syntax above allows each specification to match exactly one error. +// You can use the extended syntax to customize this. The extended syntax is +// "expected-<type> <n> {{diag text}}", where <type> is one of "error", +// "warning" or "note", and <n> is a positive integer. This allows the +// diagnostic to appear as many times as specified. Example: +// +// void f(); // expected-note 2 {{previous declaration is here}} +// + +/// FindDiagnostics - Go through the comment and see if it indicates expected +/// diagnostics. If so, then put them in a diagnostic list. +/// +static void FindDiagnostics(const char *CommentStart, unsigned CommentLen, + DiagList &ExpectedDiags, + Preprocessor &PP, SourceLocation Pos, + const char *ExpectedStr) { + const char *CommentEnd = CommentStart+CommentLen; + unsigned ExpectedStrLen = strlen(ExpectedStr); + + // Find all expected-foo diagnostics in the string and add them to + // ExpectedDiags. + while (CommentStart != CommentEnd) { + CommentStart = std::find(CommentStart, CommentEnd, 'e'); + if (unsigned(CommentEnd-CommentStart) < ExpectedStrLen) return; + + // If this isn't expected-foo, ignore it. + if (memcmp(CommentStart, ExpectedStr, ExpectedStrLen)) { + ++CommentStart; + continue; + } + + CommentStart += ExpectedStrLen; + + // Skip whitespace. + while (CommentStart != CommentEnd && + isspace(CommentStart[0])) + ++CommentStart; + + // Default, if we find the '{' now, is 1 time. + int Times = 1; + int Temp = 0; + // In extended syntax, there could be a digit now. + while (CommentStart != CommentEnd && + CommentStart[0] >= '0' && CommentStart[0] <= '9') { + Temp *= 10; + Temp += CommentStart[0] - '0'; + ++CommentStart; + } + if (Temp > 0) + Times = Temp; + + // Skip whitespace again. + while (CommentStart != CommentEnd && + isspace(CommentStart[0])) + ++CommentStart; + + // We should have a {{ now. + if (CommentEnd-CommentStart < 2 || + CommentStart[0] != '{' || CommentStart[1] != '{') { + if (std::find(CommentStart, CommentEnd, '{') != CommentEnd) + EmitError(PP, Pos, "bogus characters before '{{' in expected string"); + else + EmitError(PP, Pos, "cannot find start ('{{') of expected string"); + return; + } + CommentStart += 2; + + // Find the }}. + const char *ExpectedEnd = CommentStart; + while (1) { + ExpectedEnd = std::find(ExpectedEnd, CommentEnd, '}'); + if (CommentEnd-ExpectedEnd < 2) { + EmitError(PP, Pos, "cannot find end ('}}') of expected string"); + return; + } + + if (ExpectedEnd[1] == '}') + break; + + ++ExpectedEnd; // Skip over singular }'s + } + + std::string Msg(CommentStart, ExpectedEnd); + std::string::size_type FindPos; + while ((FindPos = Msg.find("\\n")) != std::string::npos) + Msg.replace(FindPos, 2, "\n"); + // Add is possibly multiple times. + for (int i = 0; i < Times; ++i) + ExpectedDiags.push_back(std::make_pair(Pos, Msg)); + + CommentStart = ExpectedEnd; + } +} + +/// FindExpectedDiags - Lex the main source file to find all of the +// expected errors and warnings. +static void FindExpectedDiags(Preprocessor &PP, + DiagList &ExpectedErrors, + DiagList &ExpectedWarnings, + DiagList &ExpectedNotes) { + // Create a raw lexer to pull all the comments out of the main file. We don't + // want to look in #include'd headers for expected-error strings. + FileID FID = PP.getSourceManager().getMainFileID(); + + // Create a lexer to lex all the tokens of the main file in raw mode. + const llvm::MemoryBuffer *FromFile = PP.getSourceManager().getBuffer(FID); + Lexer RawLex(FID, FromFile, PP.getSourceManager(), PP.getLangOptions()); + + // Return comments as tokens, this is how we find expected diagnostics. + RawLex.SetCommentRetentionState(true); + + Token Tok; + Tok.setKind(tok::comment); + while (Tok.isNot(tok::eof)) { + RawLex.Lex(Tok); + if (!Tok.is(tok::comment)) continue; + + std::string Comment = PP.getSpelling(Tok); + if (Comment.empty()) continue; + + + // Find all expected errors. + FindDiagnostics(&Comment[0], Comment.size(), ExpectedErrors, PP, + Tok.getLocation(), "expected-error"); + + // Find all expected warnings. + FindDiagnostics(&Comment[0], Comment.size(), ExpectedWarnings, PP, + Tok.getLocation(), "expected-warning"); + + // Find all expected notes. + FindDiagnostics(&Comment[0], Comment.size(), ExpectedNotes, PP, + Tok.getLocation(), "expected-note"); + }; +} + +/// PrintProblem - This takes a diagnostic map of the delta between expected and +/// seen diagnostics. If there's anything in it, then something unexpected +/// happened. Print the map out in a nice format and return "true". If the map +/// is empty and we're not going to print things, then return "false". +/// +static bool PrintProblem(SourceManager &SourceMgr, + const_diag_iterator diag_begin, + const_diag_iterator diag_end, + const char *Msg) { + if (diag_begin == diag_end) return false; + + llvm::errs() << Msg << "\n"; + for (const_diag_iterator I = diag_begin, E = diag_end; I != E; ++I) + llvm::errs() << " Line " << SourceMgr.getInstantiationLineNumber(I->first) + << " " << I->second << "\n"; + + return true; +} + +/// CompareDiagLists - Compare two diagnostic lists and return the difference +/// between them. +/// +static bool CompareDiagLists(SourceManager &SourceMgr, + const_diag_iterator d1_begin, + const_diag_iterator d1_end, + const_diag_iterator d2_begin, + const_diag_iterator d2_end, + const char *MsgLeftOnly, + const char *MsgRightOnly) { + DiagList LeftOnly; + DiagList Left(d1_begin, d1_end); + DiagList Right(d2_begin, d2_end); + + for (const_diag_iterator I = Left.begin(), E = Left.end(); I != E; ++I) { + unsigned LineNo1 = SourceMgr.getInstantiationLineNumber(I->first); + const std::string &Diag1 = I->second; + + DiagList::iterator II, IE; + for (II = Right.begin(), IE = Right.end(); II != IE; ++II) { + unsigned LineNo2 = SourceMgr.getInstantiationLineNumber(II->first); + if (LineNo1 != LineNo2) continue; + + const std::string &Diag2 = II->second; + if (Diag2.find(Diag1) != std::string::npos || + Diag1.find(Diag2) != std::string::npos) { + break; + } + } + if (II == IE) { + // Not found. + LeftOnly.push_back(*I); + } else { + // Found. The same cannot be found twice. + Right.erase(II); + } + } + // Now all that's left in Right are those that were not matched. + + return PrintProblem(SourceMgr, LeftOnly.begin(), LeftOnly.end(), MsgLeftOnly) + | PrintProblem(SourceMgr, Right.begin(), Right.end(), MsgRightOnly); +} + +/// CheckResults - This compares the expected results to those that +/// were actually reported. It emits any discrepencies. Return "true" if there +/// were problems. Return "false" otherwise. +/// +static bool CheckResults(Preprocessor &PP, + const DiagList &ExpectedErrors, + const DiagList &ExpectedWarnings, + const DiagList &ExpectedNotes) { + const DiagnosticClient *DiagClient = PP.getDiagnostics().getClient(); + assert(DiagClient != 0 && + "DiagChecker requires a valid TextDiagnosticBuffer"); + const TextDiagnosticBuffer &Diags = + static_cast<const TextDiagnosticBuffer&>(*DiagClient); + SourceManager &SourceMgr = PP.getSourceManager(); + + // We want to capture the delta between what was expected and what was + // seen. + // + // Expected \ Seen - set expected but not seen + // Seen \ Expected - set seen but not expected + bool HadProblem = false; + + // See if there are error mismatches. + HadProblem |= CompareDiagLists(SourceMgr, + ExpectedErrors.begin(), ExpectedErrors.end(), + Diags.err_begin(), Diags.err_end(), + "Errors expected but not seen:", + "Errors seen but not expected:"); + + // See if there are warning mismatches. + HadProblem |= CompareDiagLists(SourceMgr, + ExpectedWarnings.begin(), + ExpectedWarnings.end(), + Diags.warn_begin(), Diags.warn_end(), + "Warnings expected but not seen:", + "Warnings seen but not expected:"); + + // See if there are note mismatches. + HadProblem |= CompareDiagLists(SourceMgr, + ExpectedNotes.begin(), + ExpectedNotes.end(), + Diags.note_begin(), Diags.note_end(), + "Notes expected but not seen:", + "Notes seen but not expected:"); + + return HadProblem; +} + + +/// CheckDiagnostics - Gather the expected diagnostics and check them. +bool clang::CheckDiagnostics(Preprocessor &PP) { + // Gather the set of expected diagnostics. + DiagList ExpectedErrors, ExpectedWarnings, ExpectedNotes; + FindExpectedDiags(PP, ExpectedErrors, ExpectedWarnings, ExpectedNotes); + + // Check that the expected diagnostics occurred. + return CheckResults(PP, ExpectedErrors, ExpectedWarnings, ExpectedNotes); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/DocumentXML.cpp b/contrib/llvm/tools/clang/lib/Frontend/DocumentXML.cpp new file mode 100644 index 0000000..894f230 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/DocumentXML.cpp @@ -0,0 +1,367 @@ +//===--- DocumentXML.cpp - XML document for ASTs --------------------------===// +// +// 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 XML document class, which provides the means to +// dump out the AST in a XML form that exposes type details and other fields. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/DocumentXML.h" +#include "clang/AST/Decl.h" +#include "clang/AST/ASTContext.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/ADT/StringExtras.h" + +namespace clang { + +//--------------------------------------------------------- +DocumentXML::DocumentXML(const std::string& rootName, llvm::raw_ostream& out) : + Out(out), + Ctx(0), + HasCurrentNodeSubNodes(false) { + NodeStack.push(rootName); + Out << "<?xml version=\"1.0\"?>\n<" << rootName; +} + +//--------------------------------------------------------- +DocumentXML& DocumentXML::addSubNode(const std::string& name) { + if (!HasCurrentNodeSubNodes) + Out << ">\n"; + NodeStack.push(name); + HasCurrentNodeSubNodes = false; + Indent(); + Out << "<" << NodeStack.top(); + return *this; +} + +//--------------------------------------------------------- +void DocumentXML::Indent() { + for (size_t i = 0, e = (NodeStack.size() - 1) * 2; i < e; ++i) + Out << ' '; +} + +//--------------------------------------------------------- +DocumentXML& DocumentXML::toParent() { + assert(NodeStack.size() > 1 && "too much backtracking"); + + if (HasCurrentNodeSubNodes) { + Indent(); + Out << "</" << NodeStack.top() << ">\n"; + } else + Out << "/>\n"; + NodeStack.pop(); + HasCurrentNodeSubNodes = true; + return *this; +} + +//--------------------------------------------------------- +namespace { + +enum tIdType { ID_NORMAL, ID_FILE, ID_LABEL, ID_LAST }; + +unsigned getNewId(tIdType idType) { + static unsigned int idCounts[ID_LAST] = { 0 }; + return ++idCounts[idType]; +} + +//--------------------------------------------------------- +inline std::string getPrefixedId(unsigned uId, tIdType idType) { + static const char idPrefix[ID_LAST] = { '_', 'f', 'l' }; + char buffer[20]; + char* BufPtr = llvm::utohex_buffer(uId, buffer + 20); + *--BufPtr = idPrefix[idType]; + return BufPtr; +} + +//--------------------------------------------------------- +template<class T, class V> +bool addToMap(T& idMap, const V& value, tIdType idType = ID_NORMAL) { + typename T::iterator i = idMap.find(value); + bool toAdd = i == idMap.end(); + if (toAdd) + idMap.insert(typename T::value_type(value, getNewId(idType))); + return toAdd; +} + +} // anon NS + + +//--------------------------------------------------------- +std::string DocumentXML::escapeString(const char* pStr, + std::string::size_type len) { + std::string value; + value.reserve(len + 1); + char buffer[16]; + for (unsigned i = 0; i < len; ++i) { + switch (char C = pStr[i]) { + default: + if (isprint(C)) + value += C; + else { + sprintf(buffer, "\\%03o", C); + value += buffer; + } + break; + + case '\n': value += "\\n"; break; + case '\t': value += "\\t"; break; + case '\a': value += "\\a"; break; + case '\b': value += "\\b"; break; + case '\r': value += "\\r"; break; + + case '&': value += "&"; break; + case '<': value += "<"; break; + case '>': value += ">"; break; + case '"': value += """; break; + case '\'': value += "'"; break; + + } + } + return value; +} + +//--------------------------------------------------------- +void DocumentXML::finalize() { + assert(NodeStack.size() == 1 && "not completely backtracked"); + + addSubNode("ReferenceSection"); + addSubNode("Types"); + + for (XML::IdMap<QualType>::iterator i = Types.begin(), e = Types.end(); + i != e; ++i) { + if (i->first.hasLocalQualifiers()) { + writeTypeToXML(i->first); + addAttribute("id", getPrefixedId(i->second, ID_NORMAL)); + toParent(); + } + } + + for (XML::IdMap<const Type*>::iterator i = BasicTypes.begin(), + e = BasicTypes.end(); i != e; ++i) { + writeTypeToXML(i->first); + addAttribute("id", getPrefixedId(i->second, ID_NORMAL)); + toParent(); + } + + + toParent().addSubNode("Contexts"); + + for (XML::IdMap<const DeclContext*>::iterator i = Contexts.begin(), + e = Contexts.end(); i != e; ++i) { + addSubNode(i->first->getDeclKindName()); + addAttribute("id", getPrefixedId(i->second, ID_NORMAL)); + if (const NamedDecl *ND = dyn_cast<NamedDecl>(i->first)) + addAttribute("name", ND->getNameAsString()); + if (const TagDecl *TD = dyn_cast<TagDecl>(i->first)) + addAttribute("type", getPrefixedId(BasicTypes[TD->getTypeForDecl()], ID_NORMAL)); + else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(i->first)) + addAttribute("type", getPrefixedId(BasicTypes[FD->getType()->getAs<FunctionType>()], ID_NORMAL)); + + if (const DeclContext* parent = i->first->getParent()) + addAttribute("context", parent); + toParent(); + } + + toParent().addSubNode("Files"); + + for (XML::IdMap<std::string>::iterator i = SourceFiles.begin(), + e = SourceFiles.end(); i != e; ++i) { + addSubNode("File"); + addAttribute("id", getPrefixedId(i->second, ID_FILE)); + addAttribute("name", escapeString(i->first.c_str(), i->first.size())); + toParent(); + } + + toParent().toParent(); + + // write the root closing node (which has always subnodes) + Out << "</" << NodeStack.top() << ">\n"; +} + +//--------------------------------------------------------- +void DocumentXML::addAttribute(const char* pAttributeName, + const QualType& pType) { + addTypeRecursively(pType); + addAttribute(pAttributeName, getPrefixedId(Types[pType], ID_NORMAL)); +} + +//--------------------------------------------------------- +void DocumentXML::addPtrAttribute(const char* pAttributeName, + const Type* pType) { + addTypeRecursively(pType); + addAttribute(pAttributeName, getPrefixedId(BasicTypes[pType], ID_NORMAL)); +} + +//--------------------------------------------------------- +void DocumentXML::addPtrAttribute(const char* pAttributeName, + const NestedNameSpecifier* pNNS) { + switch (pNNS->getKind()) { + case NestedNameSpecifier::Identifier: { + IdentifierInfo *ii = pNNS->getAsIdentifier(); + // FIXME how should we handle those ? + addPtrAttribute(pAttributeName, ii->getName().data()); + break; + } + case NestedNameSpecifier::Namespace: { + addPtrAttribute(pAttributeName, pNNS->getAsNamespace()); + break; + } + case NestedNameSpecifier::TypeSpec: { + addPtrAttribute(pAttributeName, pNNS->getAsType()); + break; + } + case NestedNameSpecifier::TypeSpecWithTemplate: { + addPtrAttribute(pAttributeName, pNNS->getAsType()); + break; + } + case NestedNameSpecifier::Global: { + addPtrAttribute(pAttributeName, "::"); + break; + } + } +} + +//--------------------------------------------------------- +void DocumentXML::addTypeRecursively(const QualType& pType) +{ + if (addToMap(Types, pType)) + { + addTypeRecursively(pType.getTypePtr()); + // beautifier: a non-qualified type shall be transparent + if (!pType.hasLocalQualifiers()) + { + Types[pType] = BasicTypes[pType.getTypePtr()]; + } + } +} + +//--------------------------------------------------------- +void DocumentXML::addTypeRecursively(const Type* pType) +{ + if (addToMap(BasicTypes, pType)) + { + addParentTypes(pType); +/* + // FIXME: doesn't work in the immediate streaming approach + if (const VariableArrayType *VAT = dyn_cast<VariableArrayType>(pType)) + { + addSubNode("VariableArraySizeExpression"); + PrintStmt(VAT->getSizeExpr()); + toParent(); + } +*/ + } +} + +//--------------------------------------------------------- +void DocumentXML::addPtrAttribute(const char* pName, const DeclContext* DC) +{ + addContextsRecursively(DC); + addAttribute(pName, getPrefixedId(Contexts[DC], ID_NORMAL)); +} + +//--------------------------------------------------------- +void DocumentXML::addPtrAttribute(const char* pAttributeName, const NamedDecl* D) +{ + if (const DeclContext* DC = dyn_cast<DeclContext>(D)) + { + addContextsRecursively(DC); + addAttribute(pAttributeName, getPrefixedId(Contexts[DC], ID_NORMAL)); + } + else + { + addToMap(Decls, D); + addAttribute(pAttributeName, getPrefixedId(Decls[D], ID_NORMAL)); + } +} + +//--------------------------------------------------------- +void DocumentXML::addPtrAttribute(const char* pName, const NamespaceDecl* D) +{ + addPtrAttribute(pName, static_cast<const DeclContext*>(D)); +} + +//--------------------------------------------------------- +void DocumentXML::addContextsRecursively(const DeclContext *DC) +{ + if (DC != 0 && addToMap(Contexts, DC)) + { + addContextsRecursively(DC->getParent()); + } +} + +//--------------------------------------------------------- +void DocumentXML::addSourceFileAttribute(const std::string& fileName) +{ + addToMap(SourceFiles, fileName, ID_FILE); + addAttribute("file", getPrefixedId(SourceFiles[fileName], ID_FILE)); +} + + +//--------------------------------------------------------- +void DocumentXML::addPtrAttribute(const char* pName, const LabelStmt* L) +{ + addToMap(Labels, L, ID_LABEL); + addAttribute(pName, getPrefixedId(Labels[L], ID_LABEL)); +} + + +//--------------------------------------------------------- +PresumedLoc DocumentXML::addLocation(const SourceLocation& Loc) +{ + SourceManager& SM = Ctx->getSourceManager(); + SourceLocation SpellingLoc = SM.getSpellingLoc(Loc); + PresumedLoc PLoc; + if (!SpellingLoc.isInvalid()) + { + PLoc = SM.getPresumedLoc(SpellingLoc); + addSourceFileAttribute(PLoc.getFilename()); + addAttribute("line", PLoc.getLine()); + addAttribute("col", PLoc.getColumn()); + } + // else there is no error in some cases (eg. CXXThisExpr) + return PLoc; +} + +//--------------------------------------------------------- +void DocumentXML::addLocationRange(const SourceRange& R) +{ + PresumedLoc PStartLoc = addLocation(R.getBegin()); + if (R.getBegin() != R.getEnd()) + { + SourceManager& SM = Ctx->getSourceManager(); + SourceLocation SpellingLoc = SM.getSpellingLoc(R.getEnd()); + if (!SpellingLoc.isInvalid()) + { + PresumedLoc PLoc = SM.getPresumedLoc(SpellingLoc); + if (PStartLoc.isInvalid() || + strcmp(PLoc.getFilename(), PStartLoc.getFilename()) != 0) { + addToMap(SourceFiles, PLoc.getFilename(), ID_FILE); + addAttribute("endfile", PLoc.getFilename()); + addAttribute("endline", PLoc.getLine()); + addAttribute("endcol", PLoc.getColumn()); + } else if (PLoc.getLine() != PStartLoc.getLine()) { + addAttribute("endline", PLoc.getLine()); + addAttribute("endcol", PLoc.getColumn()); + } else { + addAttribute("endcol", PLoc.getColumn()); + } + } + } +} + +//--------------------------------------------------------- +void DocumentXML::PrintDecl(Decl *D) +{ + writeDeclToXML(D); +} + +//--------------------------------------------------------- +} // NS clang + diff --git a/contrib/llvm/tools/clang/lib/Frontend/FrontendAction.cpp b/contrib/llvm/tools/clang/lib/Frontend/FrontendAction.cpp new file mode 100644 index 0000000..b244c5c --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/FrontendAction.cpp @@ -0,0 +1,271 @@ +//===--- FrontendAction.cpp -----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/FrontendAction.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Parse/ParseAST.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +using namespace clang; + +FrontendAction::FrontendAction() : Instance(0) {} + +FrontendAction::~FrontendAction() {} + +void FrontendAction::setCurrentFile(llvm::StringRef Value, InputKind Kind, + ASTUnit *AST) { + CurrentFile = Value; + CurrentFileKind = Kind; + CurrentASTUnit.reset(AST); +} + +bool FrontendAction::BeginSourceFile(CompilerInstance &CI, + llvm::StringRef Filename, + InputKind InputKind) { + assert(!Instance && "Already processing a source file!"); + assert(!Filename.empty() && "Unexpected empty filename!"); + setCurrentFile(Filename, InputKind); + setCompilerInstance(&CI); + + // AST files follow a very different path, since they share objects via the + // AST unit. + if (InputKind == IK_AST) { + assert(!usesPreprocessorOnly() && + "Attempt to pass AST file to preprocessor only action!"); + assert(hasASTFileSupport() && + "This action does not have AST file support!"); + + llvm::IntrusiveRefCntPtr<Diagnostic> Diags(&CI.getDiagnostics()); + std::string Error; + ASTUnit *AST = ASTUnit::LoadFromASTFile(Filename, Diags); + if (!AST) + goto failure; + + setCurrentFile(Filename, InputKind, AST); + + // Set the shared objects, these are reset when we finish processing the + // file, otherwise the CompilerInstance will happily destroy them. + CI.setFileManager(&AST->getFileManager()); + CI.setSourceManager(&AST->getSourceManager()); + CI.setPreprocessor(&AST->getPreprocessor()); + CI.setASTContext(&AST->getASTContext()); + + // Initialize the action. + if (!BeginSourceFileAction(CI, Filename)) + goto failure; + + /// Create the AST consumer. + CI.setASTConsumer(CreateASTConsumer(CI, Filename)); + if (!CI.hasASTConsumer()) + goto failure; + + return true; + } + + // Set up the file and source managers, if needed. + if (!CI.hasFileManager()) + CI.createFileManager(); + if (!CI.hasSourceManager()) + CI.createSourceManager(); + + // IR files bypass the rest of initialization. + if (InputKind == IK_LLVM_IR) { + assert(hasIRSupport() && + "This action does not have IR file support!"); + + // Inform the diagnostic client we are processing a source file. + CI.getDiagnosticClient().BeginSourceFile(CI.getLangOpts(), 0); + + // Initialize the action. + if (!BeginSourceFileAction(CI, Filename)) + goto failure; + + return true; + } + + // Set up the preprocessor. + CI.createPreprocessor(); + + // Inform the diagnostic client we are processing a source file. + CI.getDiagnosticClient().BeginSourceFile(CI.getLangOpts(), + &CI.getPreprocessor()); + + // Initialize the action. + if (!BeginSourceFileAction(CI, Filename)) + goto failure; + + /// Create the AST context and consumer unless this is a preprocessor only + /// action. + if (!usesPreprocessorOnly()) { + CI.createASTContext(); + + llvm::OwningPtr<ASTConsumer> Consumer(CreateASTConsumer(CI, Filename)); + + /// Use PCH? + if (!CI.getPreprocessorOpts().ImplicitPCHInclude.empty()) { + assert(hasPCHSupport() && "This action does not have PCH support!"); + CI.createPCHExternalASTSource( + CI.getPreprocessorOpts().ImplicitPCHInclude, + CI.getPreprocessorOpts().DisablePCHValidation, + CI.getInvocation().getFrontendOpts().ChainedPCH? + Consumer->GetASTDeserializationListener() : 0); + if (!CI.getASTContext().getExternalSource()) + goto failure; + } + + CI.setASTConsumer(Consumer.take()); + if (!CI.hasASTConsumer()) + goto failure; + } + + // Initialize builtin info as long as we aren't using an external AST + // source. + if (!CI.hasASTContext() || !CI.getASTContext().getExternalSource()) { + Preprocessor &PP = CI.getPreprocessor(); + PP.getBuiltinInfo().InitializeBuiltins(PP.getIdentifierTable(), + PP.getLangOptions().NoBuiltin); + } + + return true; + + // If we failed, reset state since the client will not end up calling the + // matching EndSourceFile(). + failure: + if (isCurrentFileAST()) { + CI.takeASTContext(); + CI.takePreprocessor(); + CI.takeSourceManager(); + CI.takeFileManager(); + } + + CI.getDiagnosticClient().EndSourceFile(); + setCurrentFile("", IK_None); + setCompilerInstance(0); + return false; +} + +void FrontendAction::Execute() { + CompilerInstance &CI = getCompilerInstance(); + + // Initialize the main file entry. This needs to be delayed until after PCH + // has loaded. + if (isCurrentFileAST()) { + // 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, "<dummy input>"); + CI.getSourceManager().createMainFileIDForMemBuffer(SB); + } else { + if (!CI.InitializeSourceManager(getCurrentFile())) + return; + } + + if (CI.hasFrontendTimer()) { + llvm::TimeRegion Timer(CI.getFrontendTimer()); + ExecuteAction(); + } + else ExecuteAction(); +} + +void FrontendAction::EndSourceFile() { + CompilerInstance &CI = getCompilerInstance(); + + // Finalize the action. + EndSourceFileAction(); + + // Release the consumer and the AST, in that order since the consumer may + // perform actions in its destructor which require the context. + // + // FIXME: There is more per-file stuff we could just drop here? + if (CI.getFrontendOpts().DisableFree) { + CI.takeASTConsumer(); + if (!isCurrentFileAST()) { + CI.takeSema(); + CI.takeASTContext(); + } + } else { + if (!isCurrentFileAST()) { + CI.setSema(0); + CI.setASTContext(0); + } + CI.setASTConsumer(0); + } + + // Inform the preprocessor we are done. + if (CI.hasPreprocessor()) + CI.getPreprocessor().EndSourceFile(); + + if (CI.getFrontendOpts().ShowStats) { + llvm::errs() << "\nSTATISTICS FOR '" << getCurrentFile() << "':\n"; + CI.getPreprocessor().PrintStats(); + CI.getPreprocessor().getIdentifierTable().PrintStats(); + CI.getPreprocessor().getHeaderSearchInfo().PrintStats(); + CI.getSourceManager().PrintStats(); + llvm::errs() << "\n"; + } + + // Cleanup the output streams, and erase the output files if we encountered + // an error. + CI.clearOutputFiles(/*EraseFiles=*/CI.getDiagnostics().getNumErrors()); + + // Inform the diagnostic client we are done with this source file. + CI.getDiagnosticClient().EndSourceFile(); + + if (isCurrentFileAST()) { + CI.takeSema(); + CI.takeASTContext(); + CI.takePreprocessor(); + CI.takeSourceManager(); + CI.takeFileManager(); + } + + setCompilerInstance(0); + setCurrentFile("", IK_None); +} + +//===----------------------------------------------------------------------===// +// Utility Actions +//===----------------------------------------------------------------------===// + +void ASTFrontendAction::ExecuteAction() { + CompilerInstance &CI = getCompilerInstance(); + + // FIXME: Move the truncation aspect of this into Sema, we delayed this till + // here so the source manager would be initialized. + if (hasCodeCompletionSupport() && + !CI.getFrontendOpts().CodeCompletionAt.FileName.empty()) + CI.createCodeCompletionConsumer(); + + // Use a code completion consumer? + CodeCompleteConsumer *CompletionConsumer = 0; + if (CI.hasCodeCompletionConsumer()) + CompletionConsumer = &CI.getCodeCompletionConsumer(); + + if (!CI.hasSema()) + CI.createSema(usesCompleteTranslationUnit(), CompletionConsumer); + + ParseAST(CI.getSema(), CI.getFrontendOpts().ShowStats); +} + +ASTConsumer * +PreprocessorFrontendAction::CreateASTConsumer(CompilerInstance &CI, + llvm::StringRef InFile) { + llvm_unreachable("Invalid CreateASTConsumer on preprocessor action!"); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/FrontendActions.cpp b/contrib/llvm/tools/clang/lib/Frontend/FrontendActions.cpp new file mode 100644 index 0000000..5bc6506 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/FrontendActions.cpp @@ -0,0 +1,216 @@ +//===--- FrontendActions.cpp ----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/FrontendActions.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/Lex/Pragma.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Parse/Parser.h" +#include "clang/Basic/FileManager.h" +#include "clang/Frontend/ASTConsumers.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/Utils.h" +#include "clang/Serialization/ASTWriter.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +using namespace clang; + +//===----------------------------------------------------------------------===// +// Custom Actions +//===----------------------------------------------------------------------===// + +ASTConsumer *InitOnlyAction::CreateASTConsumer(CompilerInstance &CI, + llvm::StringRef InFile) { + return new ASTConsumer(); +} + +void InitOnlyAction::ExecuteAction() { +} + +//===----------------------------------------------------------------------===// +// AST Consumer Actions +//===----------------------------------------------------------------------===// + +ASTConsumer *ASTPrintAction::CreateASTConsumer(CompilerInstance &CI, + llvm::StringRef InFile) { + if (llvm::raw_ostream *OS = CI.createDefaultOutputFile(false, InFile)) + return CreateASTPrinter(OS); + return 0; +} + +ASTConsumer *ASTPrintXMLAction::CreateASTConsumer(CompilerInstance &CI, + llvm::StringRef InFile) { + if (llvm::raw_ostream *OS = CI.createDefaultOutputFile(false, InFile, "xml")) + return CreateASTPrinterXML(OS); + return 0; +} + +ASTConsumer *ASTDumpAction::CreateASTConsumer(CompilerInstance &CI, + llvm::StringRef InFile) { + return CreateASTDumper(); +} + +ASTConsumer *ASTViewAction::CreateASTConsumer(CompilerInstance &CI, + llvm::StringRef InFile) { + return CreateASTViewer(); +} + +ASTConsumer *DeclContextPrintAction::CreateASTConsumer(CompilerInstance &CI, + llvm::StringRef InFile) { + return CreateDeclContextPrinter(); +} + +ASTConsumer *GeneratePCHAction::CreateASTConsumer(CompilerInstance &CI, + llvm::StringRef InFile) { + std::string Sysroot; + llvm::raw_ostream *OS = 0; + bool Chaining; + if (ComputeASTConsumerArguments(CI, InFile, Sysroot, OS, Chaining)) + return 0; + + const char *isysroot = CI.getFrontendOpts().RelocatablePCH ? + Sysroot.c_str() : 0; + return new PCHGenerator(CI.getPreprocessor(), Chaining, isysroot, OS); +} + +bool GeneratePCHAction::ComputeASTConsumerArguments(CompilerInstance &CI, + llvm::StringRef InFile, + std::string &Sysroot, + llvm::raw_ostream *&OS, + bool &Chaining) { + Sysroot = CI.getHeaderSearchOpts().Sysroot; + if (CI.getFrontendOpts().RelocatablePCH && Sysroot.empty()) { + CI.getDiagnostics().Report(diag::err_relocatable_without_isysroot); + return true; + } + + OS = CI.createDefaultOutputFile(true, InFile); + if (!OS) + return true; + + Chaining = CI.getInvocation().getFrontendOpts().ChainedPCH && + !CI.getPreprocessorOpts().ImplicitPCHInclude.empty(); + return false; +} + +ASTConsumer *InheritanceViewAction::CreateASTConsumer(CompilerInstance &CI, + llvm::StringRef InFile) { + return CreateInheritanceViewer(CI.getFrontendOpts().ViewClassInheritance); +} + +ASTConsumer *SyntaxOnlyAction::CreateASTConsumer(CompilerInstance &CI, + llvm::StringRef InFile) { + return new ASTConsumer(); +} + +//===----------------------------------------------------------------------===// +// Preprocessor Actions +//===----------------------------------------------------------------------===// + +void DumpRawTokensAction::ExecuteAction() { + Preprocessor &PP = getCompilerInstance().getPreprocessor(); + SourceManager &SM = PP.getSourceManager(); + + // Start lexing the specified input file. + const llvm::MemoryBuffer *FromFile = SM.getBuffer(SM.getMainFileID()); + Lexer RawLex(SM.getMainFileID(), FromFile, SM, PP.getLangOptions()); + RawLex.SetKeepWhitespaceMode(true); + + Token RawTok; + RawLex.LexFromRawLexer(RawTok); + while (RawTok.isNot(tok::eof)) { + PP.DumpToken(RawTok, true); + llvm::errs() << "\n"; + RawLex.LexFromRawLexer(RawTok); + } +} + +void DumpTokensAction::ExecuteAction() { + Preprocessor &PP = getCompilerInstance().getPreprocessor(); + // Start preprocessing the specified input file. + Token Tok; + PP.EnterMainSourceFile(); + do { + PP.Lex(Tok); + PP.DumpToken(Tok, true); + llvm::errs() << "\n"; + } while (Tok.isNot(tok::eof)); +} + +void GeneratePTHAction::ExecuteAction() { + CompilerInstance &CI = getCompilerInstance(); + if (CI.getFrontendOpts().OutputFile.empty() || + CI.getFrontendOpts().OutputFile == "-") { + // FIXME: Don't fail this way. + // FIXME: Verify that we can actually seek in the given file. + llvm::report_fatal_error("PTH requires a seekable file for output!"); + } + llvm::raw_fd_ostream *OS = + CI.createDefaultOutputFile(true, getCurrentFile()); + if (!OS) return; + + CacheTokens(CI.getPreprocessor(), OS); +} + +void PreprocessOnlyAction::ExecuteAction() { + Preprocessor &PP = getCompilerInstance().getPreprocessor(); + + // Ignore unknown pragmas. + PP.AddPragmaHandler(new EmptyPragmaHandler()); + + Token Tok; + // Start parsing the specified input file. + PP.EnterMainSourceFile(); + do { + PP.Lex(Tok); + } while (Tok.isNot(tok::eof)); +} + +void PrintPreprocessedAction::ExecuteAction() { + CompilerInstance &CI = getCompilerInstance(); + // Output file needs to be set to 'Binary', to avoid converting Unix style + // line feeds (<LF>) to Microsoft style line feeds (<CR><LF>). + llvm::raw_ostream *OS = CI.createDefaultOutputFile(true, getCurrentFile()); + if (!OS) return; + + DoPrintPreprocessedInput(CI.getPreprocessor(), OS, + CI.getPreprocessorOutputOpts()); +} + +void PrintPreambleAction::ExecuteAction() { + switch (getCurrentFileKind()) { + case IK_C: + case IK_CXX: + case IK_ObjC: + case IK_ObjCXX: + case IK_OpenCL: + break; + + case IK_None: + case IK_Asm: + case IK_PreprocessedC: + case IK_PreprocessedCXX: + case IK_PreprocessedObjC: + case IK_PreprocessedObjCXX: + case IK_AST: + case IK_LLVM_IR: + // We can't do anything with these. + return; + } + + llvm::MemoryBuffer *Buffer = llvm::MemoryBuffer::getFile(getCurrentFile()); + if (Buffer) { + unsigned Preamble = Lexer::ComputePreamble(Buffer).first; + llvm::outs().write(Buffer->getBufferStart(), Preamble); + delete Buffer; + } +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/FrontendOptions.cpp b/contrib/llvm/tools/clang/lib/Frontend/FrontendOptions.cpp new file mode 100644 index 0000000..9dfee24 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/FrontendOptions.cpp @@ -0,0 +1,31 @@ +//===--- FrontendOptions.cpp ----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/FrontendOptions.h" +#include "llvm/ADT/StringSwitch.h" +using namespace clang; + +InputKind FrontendOptions::getInputKindForExtension(llvm::StringRef Extension) { + return llvm::StringSwitch<InputKind>(Extension) + .Case("ast", IK_AST) + .Case("c", IK_C) + .Cases("S", "s", IK_Asm) + .Case("i", IK_PreprocessedC) + .Case("ii", IK_PreprocessedCXX) + .Case("m", IK_ObjC) + .Case("mi", IK_PreprocessedObjC) + .Cases("mm", "M", IK_ObjCXX) + .Case("mii", IK_PreprocessedObjCXX) + .Case("C", IK_CXX) + .Cases("C", "cc", "cp", IK_CXX) + .Cases("cpp", "CPP", "c++", "cxx", "hpp", IK_CXX) + .Case("cl", IK_OpenCL) + .Cases("ll", "bc", IK_LLVM_IR) + .Default(IK_C); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/InitHeaderSearch.cpp b/contrib/llvm/tools/clang/lib/Frontend/InitHeaderSearch.cpp new file mode 100644 index 0000000..cd2fef8 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/InitHeaderSearch.cpp @@ -0,0 +1,955 @@ +//===--- InitHeaderSearch.cpp - Initialize header search paths ------------===// +// +// 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 InitHeaderSearch class. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/Utils.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/Version.h" +#include "clang/Frontend/HeaderSearchOptions.h" +#include "clang/Lex/HeaderSearch.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/Triple.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/System/Path.h" +#include "llvm/Config/config.h" +#ifdef _MSC_VER + #define WIN32_LEAN_AND_MEAN 1 + #include <windows.h> +#endif +#ifndef CLANG_PREFIX +#define CLANG_PREFIX +#endif +using namespace clang; +using namespace clang::frontend; + +namespace { + +/// InitHeaderSearch - This class makes it easier to set the search paths of +/// a HeaderSearch object. InitHeaderSearch stores several search path lists +/// internally, which can be sent to a HeaderSearch object in one swoop. +class InitHeaderSearch { + std::vector<DirectoryLookup> IncludeGroup[4]; + HeaderSearch& Headers; + bool Verbose; + std::string isysroot; + +public: + + InitHeaderSearch(HeaderSearch &HS, + bool verbose = false, const std::string &iSysroot = "") + : Headers(HS), Verbose(verbose), isysroot(iSysroot) {} + + /// AddPath - Add the specified path to the specified group list. + void AddPath(const llvm::Twine &Path, IncludeDirGroup Group, + bool isCXXAware, bool isUserSupplied, + bool isFramework, bool IgnoreSysRoot = false); + + /// AddGnuCPlusPlusIncludePaths - Add the necessary paths to support a gnu + /// libstdc++. + void AddGnuCPlusPlusIncludePaths(llvm::StringRef Base, + llvm::StringRef ArchDir, + llvm::StringRef Dir32, + llvm::StringRef Dir64, + const llvm::Triple &triple); + + /// AddMinGWCPlusPlusIncludePaths - Add the necessary paths to suport a MinGW + /// libstdc++. + void AddMinGWCPlusPlusIncludePaths(llvm::StringRef Base, + llvm::StringRef Arch, + llvm::StringRef Version); + + /// AddDelimitedPaths - Add a list of paths delimited by the system PATH + /// separator. The processing follows that of the CPATH variable for gcc. + void AddDelimitedPaths(llvm::StringRef String); + + // AddDefaultCIncludePaths - Add paths that should always be searched. + void AddDefaultCIncludePaths(const llvm::Triple &triple, + const HeaderSearchOptions &HSOpts); + + // AddDefaultCPlusPlusIncludePaths - Add paths that should be searched when + // compiling c++. + void AddDefaultCPlusPlusIncludePaths(const llvm::Triple &triple); + + /// AddDefaultSystemIncludePaths - Adds the default system include paths so + /// that e.g. stdio.h is found. + void AddDefaultSystemIncludePaths(const LangOptions &Lang, + const llvm::Triple &triple, + const HeaderSearchOptions &HSOpts); + + /// Realize - Merges all search path lists into one list and send it to + /// HeaderSearch. + void Realize(); +}; + +} + +void InitHeaderSearch::AddPath(const llvm::Twine &Path, + IncludeDirGroup Group, bool isCXXAware, + bool isUserSupplied, bool isFramework, + bool IgnoreSysRoot) { + assert(!Path.isTriviallyEmpty() && "can't handle empty path here"); + FileManager &FM = Headers.getFileMgr(); + + // Compute the actual path, taking into consideration -isysroot. + llvm::SmallString<256> MappedPathStr; + llvm::raw_svector_ostream MappedPath(MappedPathStr); + + // Handle isysroot. + if (Group == System && !IgnoreSysRoot) { + // FIXME: Portability. This should be a sys::Path interface, this doesn't + // handle things like C:\ right, nor win32 \\network\device\blah. + if (isysroot.size() != 1 || isysroot[0] != '/') // Add isysroot if present. + MappedPath << isysroot; + } + + Path.print(MappedPath); + + // Compute the DirectoryLookup type. + SrcMgr::CharacteristicKind Type; + if (Group == Quoted || Group == Angled) + Type = SrcMgr::C_User; + else if (isCXXAware) + Type = SrcMgr::C_System; + else + Type = SrcMgr::C_ExternCSystem; + + + // If the directory exists, add it. + if (const DirectoryEntry *DE = FM.getDirectory(MappedPath.str())) { + IncludeGroup[Group].push_back(DirectoryLookup(DE, Type, isUserSupplied, + isFramework)); + return; + } + + // Check to see if this is an apple-style headermap (which are not allowed to + // be frameworks). + if (!isFramework) { + if (const FileEntry *FE = FM.getFile(MappedPath.str())) { + if (const HeaderMap *HM = Headers.CreateHeaderMap(FE)) { + // It is a headermap, add it to the search path. + IncludeGroup[Group].push_back(DirectoryLookup(HM, Type,isUserSupplied)); + return; + } + } + } + + if (Verbose) + llvm::errs() << "ignoring nonexistent directory \"" + << MappedPath.str() << "\"\n"; +} + + +void InitHeaderSearch::AddDelimitedPaths(llvm::StringRef at) { + if (at.empty()) // Empty string should not add '.' path. + return; + + llvm::StringRef::size_type delim; + while ((delim = at.find(llvm::sys::PathSeparator)) != llvm::StringRef::npos) { + if (delim == 0) + AddPath(".", Angled, false, true, false); + else + AddPath(at.substr(0, delim), Angled, false, true, false); + at = at.substr(delim + 1); + } + + if (at.empty()) + AddPath(".", Angled, false, true, false); + else + AddPath(at, Angled, false, true, false); +} + +void InitHeaderSearch::AddGnuCPlusPlusIncludePaths(llvm::StringRef Base, + llvm::StringRef ArchDir, + llvm::StringRef Dir32, + llvm::StringRef Dir64, + const llvm::Triple &triple) { + // Add the base dir + AddPath(Base, System, true, false, false); + + // Add the multilib dirs + llvm::Triple::ArchType arch = triple.getArch(); + bool is64bit = arch == llvm::Triple::ppc64 || arch == llvm::Triple::x86_64; + if (is64bit) + AddPath(Base + "/" + ArchDir + "/" + Dir64, System, true, false, false); + else + AddPath(Base + "/" + ArchDir + "/" + Dir32, System, true, false, false); + + // Add the backward dir + AddPath(Base + "/backward", System, true, false, false); +} + +void InitHeaderSearch::AddMinGWCPlusPlusIncludePaths(llvm::StringRef Base, + llvm::StringRef Arch, + llvm::StringRef Version) { + AddPath(Base + "/" + Arch + "/" + Version + "/include", + System, true, false, false); + AddPath(Base + "/" + Arch + "/" + Version + "/include/c++", + System, true, false, false); + AddPath(Base + "/" + Arch + "/" + Version + "/include/c++/" + Arch, + System, true, false, false); + AddPath(Base + "/" + Arch + "/" + Version + "/include/c++/backward", + System, true, false, false); +} + + // FIXME: This probably should goto to some platform utils place. +#ifdef _MSC_VER + + // Read registry string. + // This also supports a means to look for high-versioned keys by use + // of a $VERSION placeholder in the key path. + // $VERSION in the key path is a placeholder for the version number, + // causing the highest value path to be searched for and used. + // I.e. "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\$VERSION". + // There can be additional characters in the component. Only the numberic + // characters are compared. +static bool getSystemRegistryString(const char *keyPath, const char *valueName, + char *value, size_t maxLength) { + HKEY hRootKey = NULL; + HKEY hKey = NULL; + const char* subKey = NULL; + DWORD valueType; + DWORD valueSize = maxLength - 1; + long lResult; + bool returnValue = false; + if (strncmp(keyPath, "HKEY_CLASSES_ROOT\\", 18) == 0) { + hRootKey = HKEY_CLASSES_ROOT; + subKey = keyPath + 18; + } + else if (strncmp(keyPath, "HKEY_USERS\\", 11) == 0) { + hRootKey = HKEY_USERS; + subKey = keyPath + 11; + } + else if (strncmp(keyPath, "HKEY_LOCAL_MACHINE\\", 19) == 0) { + hRootKey = HKEY_LOCAL_MACHINE; + subKey = keyPath + 19; + } + else if (strncmp(keyPath, "HKEY_CURRENT_USER\\", 18) == 0) { + hRootKey = HKEY_CURRENT_USER; + subKey = keyPath + 18; + } + else + return(false); + const char *placeHolder = strstr(subKey, "$VERSION"); + char bestName[256]; + bestName[0] = '\0'; + // If we have a $VERSION placeholder, do the highest-version search. + if (placeHolder) { + const char *keyEnd = placeHolder - 1; + const char *nextKey = placeHolder; + // Find end of previous key. + while ((keyEnd > subKey) && (*keyEnd != '\\')) + keyEnd--; + // Find end of key containing $VERSION. + while (*nextKey && (*nextKey != '\\')) + nextKey++; + size_t partialKeyLength = keyEnd - subKey; + char partialKey[256]; + if (partialKeyLength > sizeof(partialKey)) + partialKeyLength = sizeof(partialKey); + strncpy(partialKey, subKey, partialKeyLength); + partialKey[partialKeyLength] = '\0'; + HKEY hTopKey = NULL; + lResult = RegOpenKeyEx(hRootKey, partialKey, 0, KEY_READ, &hTopKey); + if (lResult == ERROR_SUCCESS) { + char keyName[256]; + int bestIndex = -1; + double bestValue = 0.0; + DWORD index, size = sizeof(keyName) - 1; + for (index = 0; RegEnumKeyEx(hTopKey, index, keyName, &size, NULL, + NULL, NULL, NULL) == ERROR_SUCCESS; index++) { + const char *sp = keyName; + while (*sp && !isdigit(*sp)) + sp++; + if (!*sp) + continue; + const char *ep = sp + 1; + while (*ep && (isdigit(*ep) || (*ep == '.'))) + ep++; + char numBuf[32]; + strncpy(numBuf, sp, sizeof(numBuf) - 1); + numBuf[sizeof(numBuf) - 1] = '\0'; + double value = strtod(numBuf, NULL); + if (value > bestValue) { + bestIndex = (int)index; + bestValue = value; + strcpy(bestName, keyName); + } + size = sizeof(keyName) - 1; + } + // If we found the highest versioned key, open the key and get the value. + if (bestIndex != -1) { + // Append rest of key. + strncat(bestName, nextKey, sizeof(bestName) - 1); + bestName[sizeof(bestName) - 1] = '\0'; + // Open the chosen key path remainder. + lResult = RegOpenKeyEx(hTopKey, bestName, 0, KEY_READ, &hKey); + if (lResult == ERROR_SUCCESS) { + lResult = RegQueryValueEx(hKey, valueName, NULL, &valueType, + (LPBYTE)value, &valueSize); + if (lResult == ERROR_SUCCESS) + returnValue = true; + RegCloseKey(hKey); + } + } + RegCloseKey(hTopKey); + } + } + else { + lResult = RegOpenKeyEx(hRootKey, subKey, 0, KEY_READ, &hKey); + if (lResult == ERROR_SUCCESS) { + lResult = RegQueryValueEx(hKey, valueName, NULL, &valueType, + (LPBYTE)value, &valueSize); + if (lResult == ERROR_SUCCESS) + returnValue = true; + RegCloseKey(hKey); + } + } + return(returnValue); +} +#else // _MSC_VER + // Read registry string. +static bool getSystemRegistryString(const char*, const char*, char*, size_t) { + return(false); +} +#endif // _MSC_VER + + // Get Visual Studio installation directory. +static bool getVisualStudioDir(std::string &path) { + // First check the environment variables that vsvars32.bat sets. + const char* vcinstalldir = getenv("VCINSTALLDIR"); + if(vcinstalldir) { + char *p = const_cast<char *>(strstr(vcinstalldir, "\\VC")); + if (p) + *p = '\0'; + path = vcinstalldir; + return(true); + } + + char vsIDEInstallDir[256]; + char vsExpressIDEInstallDir[256]; + // Then try the windows registry. + bool hasVCDir = getSystemRegistryString( + "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\$VERSION", + "InstallDir", vsIDEInstallDir, sizeof(vsIDEInstallDir) - 1); + bool hasVCExpressDir = getSystemRegistryString( + "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VCExpress\\$VERSION", + "InstallDir", vsExpressIDEInstallDir, sizeof(vsExpressIDEInstallDir) - 1); + // If we have both vc80 and vc90, pick version we were compiled with. + if (hasVCDir && vsIDEInstallDir[0]) { + char *p = (char*)strstr(vsIDEInstallDir, "\\Common7\\IDE"); + if (p) + *p = '\0'; + path = vsIDEInstallDir; + return(true); + } + else if (hasVCExpressDir && vsExpressIDEInstallDir[0]) { + char *p = (char*)strstr(vsExpressIDEInstallDir, "\\Common7\\IDE"); + if (p) + *p = '\0'; + path = vsExpressIDEInstallDir; + return(true); + } + else { + // Try the environment. + const char* vs100comntools = getenv("VS100COMNTOOLS"); + const char* vs90comntools = getenv("VS90COMNTOOLS"); + const char* vs80comntools = getenv("VS80COMNTOOLS"); + const char* vscomntools = NULL; + + // Try to find the version that we were compiled with + if(false) {} + #if (_MSC_VER >= 1600) // VC100 + else if(vs100comntools) { + vscomntools = vs100comntools; + } + #elif (_MSC_VER == 1500) // VC80 + else if(vs90comntools) { + vscomntools = vs90comntools; + } + #elif (_MSC_VER == 1400) // VC80 + else if(vs80comntools) { + vscomntools = vs80comntools; + } + #endif + // Otherwise find any version we can + else if (vs100comntools) + vscomntools = vs100comntools; + else if (vs90comntools) + vscomntools = vs90comntools; + else if (vs80comntools) + vscomntools = vs80comntools; + + if (vscomntools && *vscomntools) { + char *p = const_cast<char *>(strstr(vscomntools, "\\Common7\\Tools")); + if (p) + *p = '\0'; + path = vscomntools; + return(true); + } + else + return(false); + } + return(false); +} + + // Get Windows SDK installation directory. +static bool getWindowsSDKDir(std::string &path) { + char windowsSDKInstallDir[256]; + // Try the Windows registry. + bool hasSDKDir = getSystemRegistryString( + "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\$VERSION", + "InstallationFolder", windowsSDKInstallDir, sizeof(windowsSDKInstallDir) - 1); + // If we have both vc80 and vc90, pick version we were compiled with. + if (hasSDKDir && windowsSDKInstallDir[0]) { + path = windowsSDKInstallDir; + return(true); + } + return(false); +} + +void InitHeaderSearch::AddDefaultCIncludePaths(const llvm::Triple &triple, + const HeaderSearchOptions &HSOpts) { + // FIXME: temporary hack: hard-coded paths. +#ifndef __FreeBSD__ + AddPath(CLANG_PREFIX "/usr/local/include", System, true, false, false); +#endif + + // Builtin includes use #include_next directives and should be positioned + // just prior C include dirs. + if (HSOpts.UseBuiltinIncludes) { + // Ignore the sys root, we *always* look for clang headers relative to + // supplied path. + llvm::sys::Path P(HSOpts.ResourceDir); + P.appendComponent("include"); + AddPath(P.str(), System, false, false, false, /*IgnoreSysRoot=*/ true); + } + + // Add dirs specified via 'configure --with-c-include-dirs'. + llvm::StringRef CIncludeDirs(C_INCLUDE_DIRS); + if (CIncludeDirs != "") { + llvm::SmallVector<llvm::StringRef, 5> dirs; + CIncludeDirs.split(dirs, ":"); + for (llvm::SmallVectorImpl<llvm::StringRef>::iterator i = dirs.begin(); + i != dirs.end(); + ++i) + AddPath(*i, System, false, false, false); + return; + } + llvm::Triple::OSType os = triple.getOS(); + switch (os) { + case llvm::Triple::Win32: + { + std::string VSDir; + std::string WindowsSDKDir; + if (getVisualStudioDir(VSDir)) { + AddPath(VSDir + "\\VC\\include", System, false, false, false); + if (getWindowsSDKDir(WindowsSDKDir)) + AddPath(WindowsSDKDir + "\\include", System, false, false, false); + else + AddPath(VSDir + "\\VC\\PlatformSDK\\Include", + System, false, false, false); + } + else { + // Default install paths. + AddPath("C:/Program Files/Microsoft Visual Studio 10.0/VC/include", + System, false, false, false); + AddPath("C:/Program Files/Microsoft Visual Studio 9.0/VC/include", + System, false, false, false); + AddPath( + "C:/Program Files/Microsoft Visual Studio 9.0/VC/PlatformSDK/Include", + System, false, false, false); + AddPath("C:/Program Files/Microsoft Visual Studio 8/VC/include", + System, false, false, false); + AddPath( + "C:/Program Files/Microsoft Visual Studio 8/VC/PlatformSDK/Include", + System, false, false, false); + // For some clang developers. + AddPath("G:/Program Files/Microsoft Visual Studio 9.0/VC/include", + System, false, false, false); + AddPath( + "G:/Program Files/Microsoft Visual Studio 9.0/VC/PlatformSDK/Include", + System, false, false, false); + } + } + break; + case llvm::Triple::Haiku: + AddPath("/boot/common/include", System, true, false, false); + AddPath("/boot/develop/headers/os", System, true, false, false); + AddPath("/boot/develop/headers/os/app", System, true, false, false); + AddPath("/boot/develop/headers/os/arch", System, true, false, false); + AddPath("/boot/develop/headers/os/device", System, true, false, false); + AddPath("/boot/develop/headers/os/drivers", System, true, false, false); + AddPath("/boot/develop/headers/os/game", System, true, false, false); + AddPath("/boot/develop/headers/os/interface", System, true, false, false); + AddPath("/boot/develop/headers/os/kernel", System, true, false, false); + AddPath("/boot/develop/headers/os/locale", System, true, false, false); + AddPath("/boot/develop/headers/os/mail", System, true, false, false); + AddPath("/boot/develop/headers/os/media", System, true, false, false); + AddPath("/boot/develop/headers/os/midi", System, true, false, false); + AddPath("/boot/develop/headers/os/midi2", System, true, false, false); + AddPath("/boot/develop/headers/os/net", System, true, false, false); + AddPath("/boot/develop/headers/os/storage", System, true, false, false); + AddPath("/boot/develop/headers/os/support", System, true, false, false); + AddPath("/boot/develop/headers/os/translation", + System, true, false, false); + AddPath("/boot/develop/headers/os/add-ons/graphics", + System, true, false, false); + AddPath("/boot/develop/headers/os/add-ons/input_server", + System, true, false, false); + AddPath("/boot/develop/headers/os/add-ons/screen_saver", + System, true, false, false); + AddPath("/boot/develop/headers/os/add-ons/tracker", + System, true, false, false); + AddPath("/boot/develop/headers/os/be_apps/Deskbar", + System, true, false, false); + AddPath("/boot/develop/headers/os/be_apps/NetPositive", + System, true, false, false); + AddPath("/boot/develop/headers/os/be_apps/Tracker", + System, true, false, false); + AddPath("/boot/develop/headers/cpp", System, true, false, false); + AddPath("/boot/develop/headers/cpp/i586-pc-haiku", + System, true, false, false); + AddPath("/boot/develop/headers/3rdparty", System, true, false, false); + AddPath("/boot/develop/headers/bsd", System, true, false, false); + AddPath("/boot/develop/headers/glibc", System, true, false, false); + AddPath("/boot/develop/headers/posix", System, true, false, false); + AddPath("/boot/develop/headers", System, true, false, false); + break; + case llvm::Triple::MinGW64: + case llvm::Triple::MinGW32: + AddPath("c:/mingw/include", System, true, false, false); + break; + case llvm::Triple::FreeBSD: + AddPath(CLANG_PREFIX "/usr/include/clang/" CLANG_VERSION_STRING, + System, false, false, false); + break; + default: + break; + } + + AddPath(CLANG_PREFIX "/usr/include", System, false, false, false); +} + +void InitHeaderSearch:: +AddDefaultCPlusPlusIncludePaths(const llvm::Triple &triple) { + llvm::Triple::OSType os = triple.getOS(); + llvm::StringRef CxxIncludeRoot(CXX_INCLUDE_ROOT); + if (CxxIncludeRoot != "") { + llvm::StringRef CxxIncludeArch(CXX_INCLUDE_ARCH); + if (CxxIncludeArch == "") + AddGnuCPlusPlusIncludePaths(CxxIncludeRoot, triple.str().c_str(), + CXX_INCLUDE_32BIT_DIR, CXX_INCLUDE_64BIT_DIR, + triple); + else + AddGnuCPlusPlusIncludePaths(CxxIncludeRoot, CXX_INCLUDE_ARCH, + CXX_INCLUDE_32BIT_DIR, CXX_INCLUDE_64BIT_DIR, + triple); + return; + } + // FIXME: temporary hack: hard-coded paths. + switch (os) { + case llvm::Triple::Cygwin: + AddPath("/lib/gcc/i686-pc-cygwin/3.4.4/include", + System, true, false, false); + AddPath("/lib/gcc/i686-pc-cygwin/3.4.4/include/c++", + System, true, false, false); + AddPath("/lib/gcc/i686-pc-cygwin/3.4.4/include/c++/i686-pc-cygwin", + System, true, false, false); + break; + case llvm::Triple::MinGW64: + // Try gcc 4.5.0 + AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw64", "4.5.0"); + // Try gcc 4.4.0 + AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw64", "4.4.0"); + // Try gcc 4.3.0 + AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw64", "4.3.0"); + // Fall through. + case llvm::Triple::MinGW32: + // Try gcc 4.5.0 + AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw32", "4.5.0"); + // Try gcc 4.4.0 + AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw32", "4.4.0"); + // Try gcc 4.3.0 + AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw32", "4.3.0"); + break; + case llvm::Triple::Darwin: + switch (triple.getArch()) { + default: break; + + case llvm::Triple::ppc: + case llvm::Triple::ppc64: + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.2.1", + "powerpc-apple-darwin10", "", "ppc64", + triple); + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.0.0", + "powerpc-apple-darwin10", "", "ppc64", + triple); + break; + + case llvm::Triple::x86: + case llvm::Triple::x86_64: + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.2.1", + "i686-apple-darwin10", "", "x86_64", triple); + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.0.0", + "i686-apple-darwin8", "", "", triple); + break; + + case llvm::Triple::arm: + case llvm::Triple::thumb: + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.2.1", + "arm-apple-darwin10", "v7", "", triple); + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.2.1", + "arm-apple-darwin10", "v6", "", triple); + break; + } + break; + case llvm::Triple::DragonFly: + AddPath("/usr/include/c++/4.1", System, true, false, false); + break; + case llvm::Triple::Linux: + //===------------------------------------------------------------------===// + // Debian based distros. + // Note: these distros symlink /usr/include/c++/X.Y.Z -> X.Y + //===------------------------------------------------------------------===// + // Ubuntu 10.04 LTS "Lucid Lynx" -- gcc-4.4.3 + // Ubuntu 9.10 "Karmic Koala" -- gcc-4.4.1 + // Debian 6.0 "squeeze" -- gcc-4.4.2 + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.4", + "x86_64-linux-gnu", "32", "", triple); + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.4", + "i486-linux-gnu", "", "64", triple); + // Ubuntu 9.04 "Jaunty Jackalope" -- gcc-4.3.3 + // Ubuntu 8.10 "Intrepid Ibex" -- gcc-4.3.2 + // Debian 5.0 "lenny" -- gcc-4.3.2 + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.3", + "x86_64-linux-gnu", "32", "", triple); + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.3", + "i486-linux-gnu", "", "64", triple); + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.3", + "arm-linux-gnueabi", "", "", triple); + // Ubuntu 8.04.4 LTS "Hardy Heron" -- gcc-4.2.4 + // Ubuntu 8.04.[0-3] LTS "Hardy Heron" -- gcc-4.2.3 + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.2", + "x86_64-linux-gnu", "32", "", triple); + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.2", + "i486-linux-gnu", "", "64", triple); + // Ubuntu 7.10 "Gutsy Gibbon" -- gcc-4.1.3 + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.1", + "x86_64-linux-gnu", "32", "", triple); + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.1", + "i486-linux-gnu", "", "64", triple); + + //===------------------------------------------------------------------===// + // Redhat based distros. + //===------------------------------------------------------------------===// + // Fedora 13 + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.4.4", + "x86_64-redhat-linux", "32", "", triple); + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.4.4", + "i686-redhat-linux","", "", triple); + // Fedora 12 + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.4.3", + "x86_64-redhat-linux", "32", "", triple); + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.4.3", + "i686-redhat-linux","", "", triple); + // Fedora 12 (pre-FEB-2010) + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.4.2", + "x86_64-redhat-linux", "32", "", triple); + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.4.2", + "i686-redhat-linux","", "", triple); + // Fedora 11 + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.4.1", + "x86_64-redhat-linux", "32", "", triple); + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.4.1", + "i586-redhat-linux","", "", triple); + // Fedora 10 + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.3.2", + "x86_64-redhat-linux", "32", "", triple); + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.3.2", + "i386-redhat-linux","", "", triple); + // Fedora 9 + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.3.0", + "x86_64-redhat-linux", "32", "", triple); + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.3.0", + "i386-redhat-linux", "", "", triple); + // Fedora 8 + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.1.2", + "x86_64-redhat-linux", "", "", triple); + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.1.2", + "i386-redhat-linux", "", "", triple); + + //===------------------------------------------------------------------===// + + // Exherbo (2010-01-25) + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.4.3", + "x86_64-pc-linux-gnu", "32", "", triple); + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.4.3", + "i686-pc-linux-gnu", "", "", triple); + + // openSUSE 11.1 32 bit + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.3", + "i586-suse-linux", "", "", triple); + // openSUSE 11.1 64 bit + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.3", + "x86_64-suse-linux", "32", "", triple); + // openSUSE 11.2 + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.4", + "i586-suse-linux", "", "", triple); + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.4", + "x86_64-suse-linux", "", "", triple); + // Arch Linux 2008-06-24 + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.3.1", + "i686-pc-linux-gnu", "", "", triple); + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.3.1", + "x86_64-unknown-linux-gnu", "", "", triple); + // Gentoo x86 2009.1 stable + AddGnuCPlusPlusIncludePaths( + "/usr/lib/gcc/i686-pc-linux-gnu/4.3.4/include/g++-v4", + "i686-pc-linux-gnu", "", "", triple); + // Gentoo x86 2009.0 stable + AddGnuCPlusPlusIncludePaths( + "/usr/lib/gcc/i686-pc-linux-gnu/4.3.2/include/g++-v4", + "i686-pc-linux-gnu", "", "", triple); + // Gentoo x86 2008.0 stable + AddGnuCPlusPlusIncludePaths( + "/usr/lib/gcc/i686-pc-linux-gnu/4.1.2/include/g++-v4", + "i686-pc-linux-gnu", "", "", triple); + // Gentoo amd64 stable + AddGnuCPlusPlusIncludePaths( + "/usr/lib/gcc/x86_64-pc-linux-gnu/4.1.2/include/g++-v4", + "i686-pc-linux-gnu", "", "", triple); + + // Gentoo amd64 gcc 4.3.2 + AddGnuCPlusPlusIncludePaths( + "/usr/lib/gcc/x86_64-pc-linux-gnu/4.3.2/include/g++-v4", + "x86_64-pc-linux-gnu", "", "", triple); + + // Gentoo amd64 gcc 4.4.3 + AddGnuCPlusPlusIncludePaths( + "/usr/lib/gcc/x86_64-pc-linux-gnu/4.4.3/include/g++-v4", + "x86_64-pc-linux-gnu", "32", "", triple); + + // Gentoo amd64 llvm-gcc trunk + AddGnuCPlusPlusIncludePaths( + "/usr/lib/llvm-gcc-4.2-9999/include/c++/4.2.1", + "x86_64-pc-linux-gnu", "", "", triple); + + break; + case llvm::Triple::FreeBSD: + // FreeBSD 8.0 + // FreeBSD 7.3 + AddGnuCPlusPlusIncludePaths(CLANG_PREFIX "/usr/include/c++/4.2", + "", "", "", triple); + AddGnuCPlusPlusIncludePaths(CLANG_PREFIX "/usr/include/c++/4.2/backward", + "", "", "", triple); + break; + case llvm::Triple::NetBSD: + AddGnuCPlusPlusIncludePaths("/usr/include/g++", "", "", "", triple); + break; + case llvm::Triple::OpenBSD: { + std::string t = triple.getTriple(); + if (t.substr(0, 6) == "x86_64") + t.replace(0, 6, "amd64"); + AddGnuCPlusPlusIncludePaths("/usr/include/g++", + t, "", "", triple); + break; + } + case llvm::Triple::Minix: + AddGnuCPlusPlusIncludePaths("/usr/gnu/include/c++/4.4.3", + "", "", "", triple); + break; + case llvm::Triple::Solaris: + // Solaris - Fall though.. + case llvm::Triple::AuroraUX: + // AuroraUX + AddGnuCPlusPlusIncludePaths("/opt/gcc4/include/c++/4.2.4", + "i386-pc-solaris2.11", "", "", triple); + break; + default: + break; + } +} + +void InitHeaderSearch::AddDefaultSystemIncludePaths(const LangOptions &Lang, + const llvm::Triple &triple, + const HeaderSearchOptions &HSOpts) { + if (Lang.CPlusPlus && HSOpts.UseStandardCXXIncludes) + AddDefaultCPlusPlusIncludePaths(triple); + + AddDefaultCIncludePaths(triple, HSOpts); + + // Add the default framework include paths on Darwin. + if (triple.getOS() == llvm::Triple::Darwin) { + AddPath("/System/Library/Frameworks", System, true, false, true); + AddPath("/Library/Frameworks", System, true, false, true); + } +} + +/// RemoveDuplicates - If there are duplicate directory entries in the specified +/// search list, remove the later (dead) ones. +static void RemoveDuplicates(std::vector<DirectoryLookup> &SearchList, + bool Verbose) { + llvm::SmallPtrSet<const DirectoryEntry *, 8> SeenDirs; + llvm::SmallPtrSet<const DirectoryEntry *, 8> SeenFrameworkDirs; + llvm::SmallPtrSet<const HeaderMap *, 8> SeenHeaderMaps; + for (unsigned i = 0; i != SearchList.size(); ++i) { + unsigned DirToRemove = i; + + const DirectoryLookup &CurEntry = SearchList[i]; + + if (CurEntry.isNormalDir()) { + // If this isn't the first time we've seen this dir, remove it. + if (SeenDirs.insert(CurEntry.getDir())) + continue; + } else if (CurEntry.isFramework()) { + // If this isn't the first time we've seen this framework dir, remove it. + if (SeenFrameworkDirs.insert(CurEntry.getFrameworkDir())) + continue; + } else { + assert(CurEntry.isHeaderMap() && "Not a headermap or normal dir?"); + // If this isn't the first time we've seen this headermap, remove it. + if (SeenHeaderMaps.insert(CurEntry.getHeaderMap())) + continue; + } + + // If we have a normal #include dir/framework/headermap that is shadowed + // later in the chain by a system include location, we actually want to + // ignore the user's request and drop the user dir... keeping the system + // dir. This is weird, but required to emulate GCC's search path correctly. + // + // Since dupes of system dirs are rare, just rescan to find the original + // that we're nuking instead of using a DenseMap. + if (CurEntry.getDirCharacteristic() != SrcMgr::C_User) { + // Find the dir that this is the same of. + unsigned FirstDir; + for (FirstDir = 0; ; ++FirstDir) { + assert(FirstDir != i && "Didn't find dupe?"); + + const DirectoryLookup &SearchEntry = SearchList[FirstDir]; + + // If these are different lookup types, then they can't be the dupe. + if (SearchEntry.getLookupType() != CurEntry.getLookupType()) + continue; + + bool isSame; + if (CurEntry.isNormalDir()) + isSame = SearchEntry.getDir() == CurEntry.getDir(); + else if (CurEntry.isFramework()) + isSame = SearchEntry.getFrameworkDir() == CurEntry.getFrameworkDir(); + else { + assert(CurEntry.isHeaderMap() && "Not a headermap or normal dir?"); + isSame = SearchEntry.getHeaderMap() == CurEntry.getHeaderMap(); + } + + if (isSame) + break; + } + + // If the first dir in the search path is a non-system dir, zap it + // instead of the system one. + if (SearchList[FirstDir].getDirCharacteristic() == SrcMgr::C_User) + DirToRemove = FirstDir; + } + + if (Verbose) { + llvm::errs() << "ignoring duplicate directory \"" + << CurEntry.getName() << "\"\n"; + if (DirToRemove != i) + llvm::errs() << " as it is a non-system directory that duplicates " + << "a system directory\n"; + } + + // This is reached if the current entry is a duplicate. Remove the + // DirToRemove (usually the current dir). + SearchList.erase(SearchList.begin()+DirToRemove); + --i; + } +} + + +void InitHeaderSearch::Realize() { + // Concatenate ANGLE+SYSTEM+AFTER chains together into SearchList. + std::vector<DirectoryLookup> SearchList; + SearchList = IncludeGroup[Angled]; + SearchList.insert(SearchList.end(), IncludeGroup[System].begin(), + IncludeGroup[System].end()); + SearchList.insert(SearchList.end(), IncludeGroup[After].begin(), + IncludeGroup[After].end()); + RemoveDuplicates(SearchList, Verbose); + RemoveDuplicates(IncludeGroup[Quoted], Verbose); + + // Prepend QUOTED list on the search list. + SearchList.insert(SearchList.begin(), IncludeGroup[Quoted].begin(), + IncludeGroup[Quoted].end()); + + + bool DontSearchCurDir = false; // TODO: set to true if -I- is set? + Headers.SetSearchPaths(SearchList, IncludeGroup[Quoted].size(), + DontSearchCurDir); + + // If verbose, print the list of directories that will be searched. + if (Verbose) { + llvm::errs() << "#include \"...\" search starts here:\n"; + unsigned QuotedIdx = IncludeGroup[Quoted].size(); + for (unsigned i = 0, e = SearchList.size(); i != e; ++i) { + if (i == QuotedIdx) + llvm::errs() << "#include <...> search starts here:\n"; + const char *Name = SearchList[i].getName(); + const char *Suffix; + if (SearchList[i].isNormalDir()) + Suffix = ""; + else if (SearchList[i].isFramework()) + Suffix = " (framework directory)"; + else { + assert(SearchList[i].isHeaderMap() && "Unknown DirectoryLookup"); + Suffix = " (headermap)"; + } + llvm::errs() << " " << Name << Suffix << "\n"; + } + llvm::errs() << "End of search list.\n"; + } +} + +void clang::ApplyHeaderSearchOptions(HeaderSearch &HS, + const HeaderSearchOptions &HSOpts, + const LangOptions &Lang, + const llvm::Triple &Triple) { + InitHeaderSearch Init(HS, HSOpts.Verbose, HSOpts.Sysroot); + + // Add the user defined entries. + for (unsigned i = 0, e = HSOpts.UserEntries.size(); i != e; ++i) { + const HeaderSearchOptions::Entry &E = HSOpts.UserEntries[i]; + Init.AddPath(E.Path, E.Group, false, E.IsUserSupplied, E.IsFramework, + !E.IsSysRootRelative); + } + + // Add entries from CPATH and friends. + Init.AddDelimitedPaths(HSOpts.EnvIncPath); + if (Lang.CPlusPlus && Lang.ObjC1) + Init.AddDelimitedPaths(HSOpts.ObjCXXEnvIncPath); + else if (Lang.CPlusPlus) + Init.AddDelimitedPaths(HSOpts.CXXEnvIncPath); + else if (Lang.ObjC1) + Init.AddDelimitedPaths(HSOpts.ObjCEnvIncPath); + else + Init.AddDelimitedPaths(HSOpts.CEnvIncPath); + + if (HSOpts.UseStandardIncludes) + Init.AddDefaultSystemIncludePaths(Lang, Triple, HSOpts); + + Init.Realize(); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/InitPreprocessor.cpp b/contrib/llvm/tools/clang/lib/Frontend/InitPreprocessor.cpp new file mode 100644 index 0000000..0d07192 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/InitPreprocessor.cpp @@ -0,0 +1,611 @@ +//===--- InitPreprocessor.cpp - PP initialization code. ---------*- C++ -*-===// +// +// 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::InitializePreprocessor function. +// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/Version.h" +#include "clang/Frontend/Utils.h" +#include "clang/Basic/MacroBuilder.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/FrontendOptions.h" +#include "clang/Frontend/PreprocessorOptions.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/ADT/APFloat.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/System/Path.h" +using namespace clang; + +// Append a #define line to Buf for Macro. Macro should be of the form XXX, +// in which case we emit "#define XXX 1" or "XXX=Y z W" in which case we emit +// "#define XXX Y z W". To get a #define with no value, use "XXX=". +static void DefineBuiltinMacro(MacroBuilder &Builder, llvm::StringRef Macro, + Diagnostic &Diags) { + std::pair<llvm::StringRef, llvm::StringRef> MacroPair = Macro.split('='); + llvm::StringRef MacroName = MacroPair.first; + llvm::StringRef MacroBody = MacroPair.second; + if (MacroName.size() != Macro.size()) { + // Per GCC -D semantics, the macro ends at \n if it exists. + llvm::StringRef::size_type End = MacroBody.find_first_of("\n\r"); + if (End != llvm::StringRef::npos) + Diags.Report(diag::warn_fe_macro_contains_embedded_newline) + << MacroName; + Builder.defineMacro(MacroName, MacroBody.substr(0, End)); + } else { + // Push "macroname 1". + Builder.defineMacro(Macro); + } +} + +std::string clang::NormalizeDashIncludePath(llvm::StringRef File) { + // Implicit include paths should be resolved relative to the current + // working directory first, and then use the regular header search + // mechanism. The proper way to handle this is to have the + // predefines buffer located at the current working directory, but + // it has not file entry. For now, workaround this by using an + // absolute path if we find the file here, and otherwise letting + // header search handle it. + llvm::sys::Path Path(File); + Path.makeAbsolute(); + if (!Path.exists()) + Path = File; + + return Lexer::Stringify(Path.str()); +} + +/// AddImplicitInclude - Add an implicit #include of the specified file to the +/// predefines buffer. +static void AddImplicitInclude(MacroBuilder &Builder, llvm::StringRef File) { + Builder.append("#include \"" + + llvm::Twine(NormalizeDashIncludePath(File)) + "\""); +} + +static void AddImplicitIncludeMacros(MacroBuilder &Builder, + llvm::StringRef File) { + Builder.append("#__include_macros \"" + + llvm::Twine(NormalizeDashIncludePath(File)) + "\""); + // Marker token to stop the __include_macros fetch loop. + Builder.append("##"); // ##? +} + +/// AddImplicitIncludePTH - Add an implicit #include using the original file +/// used to generate a PTH cache. +static void AddImplicitIncludePTH(MacroBuilder &Builder, Preprocessor &PP, + llvm::StringRef ImplicitIncludePTH) { + PTHManager *P = PP.getPTHManager(); + // Null check 'P' in the corner case where it couldn't be created. + const char *OriginalFile = P ? P->getOriginalSourceFile() : 0; + + if (!OriginalFile) { + PP.getDiagnostics().Report(diag::err_fe_pth_file_has_no_source_header) + << ImplicitIncludePTH; + return; + } + + AddImplicitInclude(Builder, OriginalFile); +} + +/// PickFP - This is used to pick a value based on the FP semantics of the +/// specified FP model. +template <typename T> +static T PickFP(const llvm::fltSemantics *Sem, T IEEESingleVal, + T IEEEDoubleVal, T X87DoubleExtendedVal, T PPCDoubleDoubleVal, + T IEEEQuadVal) { + if (Sem == (const llvm::fltSemantics*)&llvm::APFloat::IEEEsingle) + return IEEESingleVal; + if (Sem == (const llvm::fltSemantics*)&llvm::APFloat::IEEEdouble) + return IEEEDoubleVal; + if (Sem == (const llvm::fltSemantics*)&llvm::APFloat::x87DoubleExtended) + return X87DoubleExtendedVal; + if (Sem == (const llvm::fltSemantics*)&llvm::APFloat::PPCDoubleDouble) + return PPCDoubleDoubleVal; + assert(Sem == (const llvm::fltSemantics*)&llvm::APFloat::IEEEquad); + return IEEEQuadVal; +} + +static void DefineFloatMacros(MacroBuilder &Builder, llvm::StringRef Prefix, + const llvm::fltSemantics *Sem) { + const char *DenormMin, *Epsilon, *Max, *Min; + DenormMin = PickFP(Sem, "1.40129846e-45F", "4.9406564584124654e-324", + "3.64519953188247460253e-4951L", + "4.94065645841246544176568792868221e-324L", + "6.47517511943802511092443895822764655e-4966L"); + int Digits = PickFP(Sem, 6, 15, 18, 31, 33); + Epsilon = PickFP(Sem, "1.19209290e-7F", "2.2204460492503131e-16", + "1.08420217248550443401e-19L", + "4.94065645841246544176568792868221e-324L", + "1.92592994438723585305597794258492732e-34L"); + int MantissaDigits = PickFP(Sem, 24, 53, 64, 106, 113); + int Min10Exp = PickFP(Sem, -37, -307, -4931, -291, -4931); + int Max10Exp = PickFP(Sem, 38, 308, 4932, 308, 4932); + int MinExp = PickFP(Sem, -125, -1021, -16381, -968, -16381); + int MaxExp = PickFP(Sem, 128, 1024, 16384, 1024, 16384); + Min = PickFP(Sem, "1.17549435e-38F", "2.2250738585072014e-308", + "3.36210314311209350626e-4932L", + "2.00416836000897277799610805135016e-292L", + "3.36210314311209350626267781732175260e-4932L"); + Max = PickFP(Sem, "3.40282347e+38F", "1.7976931348623157e+308", + "1.18973149535723176502e+4932L", + "1.79769313486231580793728971405301e+308L", + "1.18973149535723176508575932662800702e+4932L"); + + llvm::SmallString<32> DefPrefix; + DefPrefix = "__"; + DefPrefix += Prefix; + DefPrefix += "_"; + + Builder.defineMacro(DefPrefix + "DENORM_MIN__", DenormMin); + Builder.defineMacro(DefPrefix + "HAS_DENORM__"); + Builder.defineMacro(DefPrefix + "DIG__", llvm::Twine(Digits)); + Builder.defineMacro(DefPrefix + "EPSILON__", llvm::Twine(Epsilon)); + Builder.defineMacro(DefPrefix + "HAS_INFINITY__"); + Builder.defineMacro(DefPrefix + "HAS_QUIET_NAN__"); + Builder.defineMacro(DefPrefix + "MANT_DIG__", llvm::Twine(MantissaDigits)); + + Builder.defineMacro(DefPrefix + "MAX_10_EXP__", llvm::Twine(Max10Exp)); + Builder.defineMacro(DefPrefix + "MAX_EXP__", llvm::Twine(MaxExp)); + Builder.defineMacro(DefPrefix + "MAX__", llvm::Twine(Max)); + + Builder.defineMacro(DefPrefix + "MIN_10_EXP__","("+llvm::Twine(Min10Exp)+")"); + Builder.defineMacro(DefPrefix + "MIN_EXP__", "("+llvm::Twine(MinExp)+")"); + Builder.defineMacro(DefPrefix + "MIN__", llvm::Twine(Min)); +} + + +/// DefineTypeSize - Emit a macro to the predefines buffer that declares a macro +/// named MacroName with the max value for a type with width 'TypeWidth' a +/// signedness of 'isSigned' and with a value suffix of 'ValSuffix' (e.g. LL). +static void DefineTypeSize(llvm::StringRef MacroName, unsigned TypeWidth, + llvm::StringRef ValSuffix, bool isSigned, + MacroBuilder& Builder) { + long long MaxVal; + if (isSigned) { + assert(TypeWidth != 1); + MaxVal = ~0ULL >> (65-TypeWidth); + } else + MaxVal = ~0ULL >> (64-TypeWidth); + + Builder.defineMacro(MacroName, llvm::Twine(MaxVal) + ValSuffix); +} + +/// DefineTypeSize - An overloaded helper that uses TargetInfo to determine +/// the width, suffix, and signedness of the given type +static void DefineTypeSize(llvm::StringRef MacroName, TargetInfo::IntType Ty, + const TargetInfo &TI, MacroBuilder &Builder) { + DefineTypeSize(MacroName, TI.getTypeWidth(Ty), TI.getTypeConstantSuffix(Ty), + TI.isTypeSigned(Ty), Builder); +} + +static void DefineType(const llvm::Twine &MacroName, TargetInfo::IntType Ty, + MacroBuilder &Builder) { + Builder.defineMacro(MacroName, TargetInfo::getTypeName(Ty)); +} + +static void DefineTypeWidth(llvm::StringRef MacroName, TargetInfo::IntType Ty, + const TargetInfo &TI, MacroBuilder &Builder) { + Builder.defineMacro(MacroName, llvm::Twine(TI.getTypeWidth(Ty))); +} + +static void DefineTypeSizeof(llvm::StringRef MacroName, unsigned BitWidth, + const TargetInfo &TI, MacroBuilder &Builder) { + Builder.defineMacro(MacroName, + llvm::Twine(BitWidth / TI.getCharWidth())); +} + +static void DefineExactWidthIntType(TargetInfo::IntType Ty, + const TargetInfo &TI, MacroBuilder &Builder) { + int TypeWidth = TI.getTypeWidth(Ty); + + // Use the target specified int64 type, when appropriate, so that [u]int64_t + // ends up being defined in terms of the correct type. + if (TypeWidth == 64) + Ty = TI.getInt64Type(); + + DefineType("__INT" + llvm::Twine(TypeWidth) + "_TYPE__", Ty, Builder); + + llvm::StringRef ConstSuffix(TargetInfo::getTypeConstantSuffix(Ty)); + if (!ConstSuffix.empty()) + Builder.defineMacro("__INT" + llvm::Twine(TypeWidth) + "_C_SUFFIX__", + ConstSuffix); +} + +static void InitializePredefinedMacros(const TargetInfo &TI, + const LangOptions &LangOpts, + const FrontendOptions &FEOpts, + MacroBuilder &Builder) { + // Compiler version introspection macros. + Builder.defineMacro("__llvm__"); // LLVM Backend + Builder.defineMacro("__clang__"); // Clang Frontend +#define TOSTR2(X) #X +#define TOSTR(X) TOSTR2(X) + Builder.defineMacro("__clang_major__", TOSTR(CLANG_VERSION_MAJOR)); + Builder.defineMacro("__clang_minor__", TOSTR(CLANG_VERSION_MINOR)); +#ifdef CLANG_VERSION_PATCHLEVEL + Builder.defineMacro("__clang_patchlevel__", TOSTR(CLANG_VERSION_PATCHLEVEL)); +#else + Builder.defineMacro("__clang_patchlevel__", "0"); +#endif + Builder.defineMacro("__clang_version__", + "\"" CLANG_VERSION_STRING " (" + + getClangFullRepositoryVersion() + ")\""); +#undef TOSTR +#undef TOSTR2 + // Currently claim to be compatible with GCC 4.2.1-5621. + Builder.defineMacro("__GNUC_MINOR__", "2"); + Builder.defineMacro("__GNUC_PATCHLEVEL__", "1"); + Builder.defineMacro("__GNUC__", "4"); + Builder.defineMacro("__GXX_ABI_VERSION", "1002"); + Builder.defineMacro("__VERSION__", "\"4.2.1 Compatible Clang Compiler\""); + + // Initialize language-specific preprocessor defines. + + // These should all be defined in the preprocessor according to the + // current language configuration. + if (!LangOpts.Microsoft) + Builder.defineMacro("__STDC__"); + if (LangOpts.AsmPreprocessor) + Builder.defineMacro("__ASSEMBLER__"); + + if (!LangOpts.CPlusPlus) { + if (LangOpts.C99) + Builder.defineMacro("__STDC_VERSION__", "199901L"); + else if (!LangOpts.GNUMode && LangOpts.Digraphs) + Builder.defineMacro("__STDC_VERSION__", "199409L"); + } + + // Standard conforming mode? + if (!LangOpts.GNUMode) + Builder.defineMacro("__STRICT_ANSI__"); + + if (LangOpts.CPlusPlus0x) + Builder.defineMacro("__GXX_EXPERIMENTAL_CXX0X__"); + + if (LangOpts.Freestanding) + Builder.defineMacro("__STDC_HOSTED__", "0"); + else + Builder.defineMacro("__STDC_HOSTED__"); + + if (LangOpts.ObjC1) { + Builder.defineMacro("__OBJC__"); + if (LangOpts.ObjCNonFragileABI) { + Builder.defineMacro("__OBJC2__"); + Builder.defineMacro("OBJC_ZEROCOST_EXCEPTIONS"); + } + + if (LangOpts.getGCMode() != LangOptions::NonGC) + Builder.defineMacro("__OBJC_GC__"); + + if (LangOpts.NeXTRuntime) + Builder.defineMacro("__NEXT_RUNTIME__"); + } + + // darwin_constant_cfstrings controls this. This is also dependent + // on other things like the runtime I believe. This is set even for C code. + Builder.defineMacro("__CONSTANT_CFSTRINGS__"); + + if (LangOpts.ObjC2) + Builder.defineMacro("OBJC_NEW_PROPERTIES"); + + if (LangOpts.PascalStrings) + Builder.defineMacro("__PASCAL_STRINGS__"); + + if (LangOpts.Blocks) { + Builder.defineMacro("__block", "__attribute__((__blocks__(byref)))"); + Builder.defineMacro("__BLOCKS__"); + } + + if (LangOpts.Exceptions) + Builder.defineMacro("__EXCEPTIONS"); + if (LangOpts.RTTI) + Builder.defineMacro("__GXX_RTTI"); + if (LangOpts.SjLjExceptions) + Builder.defineMacro("__USING_SJLJ_EXCEPTIONS__"); + + if (LangOpts.CPlusPlus) { + Builder.defineMacro("__DEPRECATED"); + Builder.defineMacro("__GNUG__", "4"); + Builder.defineMacro("__GXX_WEAK__"); + if (LangOpts.GNUMode) + Builder.defineMacro("__cplusplus"); + else + // C++ [cpp.predefined]p1: + // The name_ _cplusplusis defined to the value 199711L when compiling a + // C++ translation unit. + Builder.defineMacro("__cplusplus", "199711L"); + Builder.defineMacro("__private_extern__", "extern"); + } + + if (LangOpts.Microsoft) { + // Filter out some microsoft extensions when trying to parse in ms-compat + // mode. + Builder.defineMacro("__int8", "__INT8_TYPE__"); + Builder.defineMacro("__int16", "__INT16_TYPE__"); + Builder.defineMacro("__int32", "__INT32_TYPE__"); + Builder.defineMacro("__int64", "__INT64_TYPE__"); + // Both __PRETTY_FUNCTION__ and __FUNCTION__ are GCC extensions, however + // VC++ appears to only like __FUNCTION__. + Builder.defineMacro("__PRETTY_FUNCTION__", "__FUNCTION__"); + // Work around some issues with Visual C++ headerws. + if (LangOpts.CPlusPlus) { + // Since we define wchar_t in C++ mode. + Builder.defineMacro("_WCHAR_T_DEFINED"); + Builder.defineMacro("_NATIVE_WCHAR_T_DEFINED"); + Builder.append("class type_info;"); + } + } + + if (LangOpts.Optimize) + Builder.defineMacro("__OPTIMIZE__"); + if (LangOpts.OptimizeSize) + Builder.defineMacro("__OPTIMIZE_SIZE__"); + + // Initialize target-specific preprocessor defines. + + // Define type sizing macros based on the target properties. + assert(TI.getCharWidth() == 8 && "Only support 8-bit char so far"); + Builder.defineMacro("__CHAR_BIT__", "8"); + + DefineTypeSize("__SCHAR_MAX__", TI.getCharWidth(), "", true, Builder); + DefineTypeSize("__SHRT_MAX__", TargetInfo::SignedShort, TI, Builder); + DefineTypeSize("__INT_MAX__", TargetInfo::SignedInt, TI, Builder); + DefineTypeSize("__LONG_MAX__", TargetInfo::SignedLong, TI, Builder); + DefineTypeSize("__LONG_LONG_MAX__", TargetInfo::SignedLongLong, TI, Builder); + DefineTypeSize("__WCHAR_MAX__", TI.getWCharType(), TI, Builder); + DefineTypeSize("__INTMAX_MAX__", TI.getIntMaxType(), TI, Builder); + + DefineTypeSizeof("__SIZEOF_DOUBLE__", TI.getDoubleWidth(), TI, Builder); + DefineTypeSizeof("__SIZEOF_FLOAT__", TI.getFloatWidth(), TI, Builder); + DefineTypeSizeof("__SIZEOF_INT__", TI.getIntWidth(), TI, Builder); + DefineTypeSizeof("__SIZEOF_LONG__", TI.getLongWidth(), TI, Builder); + DefineTypeSizeof("__SIZEOF_LONG_DOUBLE__",TI.getLongDoubleWidth(),TI,Builder); + DefineTypeSizeof("__SIZEOF_LONG_LONG__", TI.getLongLongWidth(), TI, Builder); + DefineTypeSizeof("__SIZEOF_POINTER__", TI.getPointerWidth(0), TI, Builder); + DefineTypeSizeof("__SIZEOF_SHORT__", TI.getShortWidth(), TI, Builder); + DefineTypeSizeof("__SIZEOF_PTRDIFF_T__", + TI.getTypeWidth(TI.getPtrDiffType(0)), TI, Builder); + DefineTypeSizeof("__SIZEOF_SIZE_T__", + TI.getTypeWidth(TI.getSizeType()), TI, Builder); + DefineTypeSizeof("__SIZEOF_WCHAR_T__", + TI.getTypeWidth(TI.getWCharType()), TI, Builder); + DefineTypeSizeof("__SIZEOF_WINT_T__", + TI.getTypeWidth(TI.getWIntType()), TI, Builder); + + DefineType("__INTMAX_TYPE__", TI.getIntMaxType(), Builder); + DefineType("__UINTMAX_TYPE__", TI.getUIntMaxType(), Builder); + DefineTypeWidth("__INTMAX_WIDTH__", TI.getIntMaxType(), TI, Builder); + DefineType("__PTRDIFF_TYPE__", TI.getPtrDiffType(0), Builder); + DefineTypeWidth("__PTRDIFF_WIDTH__", TI.getPtrDiffType(0), TI, Builder); + DefineType("__INTPTR_TYPE__", TI.getIntPtrType(), Builder); + DefineTypeWidth("__INTPTR_WIDTH__", TI.getIntPtrType(), TI, Builder); + DefineType("__SIZE_TYPE__", TI.getSizeType(), Builder); + DefineTypeWidth("__SIZE_WIDTH__", TI.getSizeType(), TI, Builder); + DefineType("__WCHAR_TYPE__", TI.getWCharType(), Builder); + DefineTypeWidth("__WCHAR_WIDTH__", TI.getWCharType(), TI, Builder); + DefineType("__WINT_TYPE__", TI.getWIntType(), Builder); + DefineTypeWidth("__WINT_WIDTH__", TI.getWIntType(), TI, Builder); + DefineTypeWidth("__SIG_ATOMIC_WIDTH__", TI.getSigAtomicType(), TI, Builder); + DefineType("__CHAR16_TYPE__", TI.getChar16Type(), Builder); + DefineType("__CHAR32_TYPE__", TI.getChar32Type(), Builder); + + DefineFloatMacros(Builder, "FLT", &TI.getFloatFormat()); + DefineFloatMacros(Builder, "DBL", &TI.getDoubleFormat()); + DefineFloatMacros(Builder, "LDBL", &TI.getLongDoubleFormat()); + + // Define a __POINTER_WIDTH__ macro for stdint.h. + Builder.defineMacro("__POINTER_WIDTH__", + llvm::Twine((int)TI.getPointerWidth(0))); + + if (!LangOpts.CharIsSigned) + Builder.defineMacro("__CHAR_UNSIGNED__"); + + // Define exact-width integer types for stdint.h + Builder.defineMacro("__INT" + llvm::Twine(TI.getCharWidth()) + "_TYPE__", + "char"); + + if (TI.getShortWidth() > TI.getCharWidth()) + DefineExactWidthIntType(TargetInfo::SignedShort, TI, Builder); + + if (TI.getIntWidth() > TI.getShortWidth()) + DefineExactWidthIntType(TargetInfo::SignedInt, TI, Builder); + + if (TI.getLongWidth() > TI.getIntWidth()) + DefineExactWidthIntType(TargetInfo::SignedLong, TI, Builder); + + if (TI.getLongLongWidth() > TI.getLongWidth()) + DefineExactWidthIntType(TargetInfo::SignedLongLong, TI, Builder); + + // Add __builtin_va_list typedef. + Builder.append(TI.getVAListDeclaration()); + + if (const char *Prefix = TI.getUserLabelPrefix()) + Builder.defineMacro("__USER_LABEL_PREFIX__", Prefix); + + // Build configuration options. FIXME: these should be controlled by + // command line options or something. + Builder.defineMacro("__FINITE_MATH_ONLY__", "0"); + + if (LangOpts.GNUInline) + Builder.defineMacro("__GNUC_GNU_INLINE__"); + else + Builder.defineMacro("__GNUC_STDC_INLINE__"); + + if (LangOpts.NoInline) + Builder.defineMacro("__NO_INLINE__"); + + if (unsigned PICLevel = LangOpts.PICLevel) { + Builder.defineMacro("__PIC__", llvm::Twine(PICLevel)); + Builder.defineMacro("__pic__", llvm::Twine(PICLevel)); + } + + // Macros to control C99 numerics and <float.h> + Builder.defineMacro("__FLT_EVAL_METHOD__", "0"); + Builder.defineMacro("__FLT_RADIX__", "2"); + int Dig = PickFP(&TI.getLongDoubleFormat(), -1/*FIXME*/, 17, 21, 33, 36); + Builder.defineMacro("__DECIMAL_DIG__", llvm::Twine(Dig)); + + if (LangOpts.getStackProtectorMode() == LangOptions::SSPOn) + Builder.defineMacro("__SSP__"); + else if (LangOpts.getStackProtectorMode() == LangOptions::SSPReq) + Builder.defineMacro("__SSP_ALL__", "2"); + + if (FEOpts.ProgramAction == frontend::RewriteObjC) + Builder.defineMacro("__weak", "__attribute__((objc_gc(weak)))"); + + // Define a macro that exists only when using the static analyzer. + if (FEOpts.ProgramAction == frontend::RunAnalysis) + Builder.defineMacro("__clang_analyzer__"); + + // Get other target #defines. + TI.getTargetDefines(LangOpts, Builder); +} + +// Initialize the remapping of files to alternative contents, e.g., +// those specified through other files. +static void InitializeFileRemapping(Diagnostic &Diags, + SourceManager &SourceMgr, + FileManager &FileMgr, + const PreprocessorOptions &InitOpts) { + // Remap files in the source manager (with buffers). + for (PreprocessorOptions::const_remapped_file_buffer_iterator + Remap = InitOpts.remapped_file_buffer_begin(), + RemapEnd = InitOpts.remapped_file_buffer_end(); + Remap != RemapEnd; + ++Remap) { + // Create the file entry for the file that we're mapping from. + const FileEntry *FromFile = FileMgr.getVirtualFile(Remap->first, + Remap->second->getBufferSize(), + 0); + if (!FromFile) { + Diags.Report(diag::err_fe_remap_missing_from_file) + << Remap->first; + if (!InitOpts.RetainRemappedFileBuffers) + delete Remap->second; + continue; + } + + // Override the contents of the "from" file with the contents of + // the "to" file. + SourceMgr.overrideFileContents(FromFile, Remap->second, + InitOpts.RetainRemappedFileBuffers); + } + + // Remap files in the source manager (with other files). + for (PreprocessorOptions::const_remapped_file_iterator + Remap = InitOpts.remapped_file_begin(), + RemapEnd = InitOpts.remapped_file_end(); + Remap != RemapEnd; + ++Remap) { + // Find the file that we're mapping to. + const FileEntry *ToFile = FileMgr.getFile(Remap->second); + if (!ToFile) { + Diags.Report(diag::err_fe_remap_missing_to_file) + << Remap->first << Remap->second; + continue; + } + + // Create the file entry for the file that we're mapping from. + const FileEntry *FromFile = FileMgr.getVirtualFile(Remap->first, + ToFile->getSize(), + 0); + if (!FromFile) { + Diags.Report(diag::err_fe_remap_missing_from_file) + << Remap->first; + continue; + } + + // Load the contents of the file we're mapping to. + std::string ErrorStr; + const llvm::MemoryBuffer *Buffer + = llvm::MemoryBuffer::getFile(ToFile->getName(), &ErrorStr); + if (!Buffer) { + Diags.Report(diag::err_fe_error_opening) + << Remap->second << ErrorStr; + continue; + } + + // Override the contents of the "from" file with the contents of + // the "to" file. + SourceMgr.overrideFileContents(FromFile, Buffer); + } +} + +/// InitializePreprocessor - Initialize the preprocessor getting it and the +/// environment ready to process a single file. This returns true on error. +/// +void clang::InitializePreprocessor(Preprocessor &PP, + const PreprocessorOptions &InitOpts, + const HeaderSearchOptions &HSOpts, + const FrontendOptions &FEOpts) { + std::string PredefineBuffer; + PredefineBuffer.reserve(4080); + llvm::raw_string_ostream Predefines(PredefineBuffer); + MacroBuilder Builder(Predefines); + + InitializeFileRemapping(PP.getDiagnostics(), PP.getSourceManager(), + PP.getFileManager(), InitOpts); + + // Emit line markers for various builtin sections of the file. We don't do + // this in asm preprocessor mode, because "# 4" is not a line marker directive + // in this mode. + if (!PP.getLangOptions().AsmPreprocessor) + Builder.append("# 1 \"<built-in>\" 3"); + + // Install things like __POWERPC__, __GNUC__, etc into the macro table. + if (InitOpts.UsePredefines) + InitializePredefinedMacros(PP.getTargetInfo(), PP.getLangOptions(), + FEOpts, Builder); + + // Add on the predefines from the driver. Wrap in a #line directive to report + // that they come from the command line. + if (!PP.getLangOptions().AsmPreprocessor) + Builder.append("# 1 \"<command line>\" 1"); + + // Process #define's and #undef's in the order they are given. + for (unsigned i = 0, e = InitOpts.Macros.size(); i != e; ++i) { + if (InitOpts.Macros[i].second) // isUndef + Builder.undefineMacro(InitOpts.Macros[i].first); + else + DefineBuiltinMacro(Builder, InitOpts.Macros[i].first, + PP.getDiagnostics()); + } + + // If -imacros are specified, include them now. These are processed before + // any -include directives. + for (unsigned i = 0, e = InitOpts.MacroIncludes.size(); i != e; ++i) + AddImplicitIncludeMacros(Builder, InitOpts.MacroIncludes[i]); + + // Process -include directives. + for (unsigned i = 0, e = InitOpts.Includes.size(); i != e; ++i) { + const std::string &Path = InitOpts.Includes[i]; + if (Path == InitOpts.ImplicitPTHInclude) + AddImplicitIncludePTH(Builder, PP, Path); + else + AddImplicitInclude(Builder, Path); + } + + // Exit the command line and go back to <built-in> (2 is LC_LEAVE). + if (!PP.getLangOptions().AsmPreprocessor) + Builder.append("# 1 \"<built-in>\" 2"); + + // Instruct the preprocessor to skip the preamble. + PP.setSkipMainFilePreamble(InitOpts.PrecompiledPreambleBytes.first, + InitOpts.PrecompiledPreambleBytes.second); + + // Copy PredefinedBuffer into the Preprocessor. + PP.setPredefines(Predefines.str()); + + // Initialize the header search object. + ApplyHeaderSearchOptions(PP.getHeaderSearchInfo(), HSOpts, + PP.getLangOptions(), + PP.getTargetInfo().getTriple()); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/LangStandards.cpp b/contrib/llvm/tools/clang/lib/Frontend/LangStandards.cpp new file mode 100644 index 0000000..af1721d --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/LangStandards.cpp @@ -0,0 +1,44 @@ +//===--- LangStandards.cpp - Language Standard Definitions ----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/LangStandard.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/ErrorHandling.h" +using namespace clang; +using namespace clang::frontend; + +#define LANGSTANDARD(id, name, desc, features) \ + static const LangStandard Lang_##id = { name, desc, features }; +#include "clang/Frontend/LangStandards.def" + +const LangStandard &LangStandard::getLangStandardForKind(Kind K) { + switch (K) { + default: + llvm_unreachable("Invalid language kind!"); + case lang_unspecified: + llvm::report_fatal_error("getLangStandardForKind() on unspecified kind"); +#define LANGSTANDARD(id, name, desc, features) \ + case lang_##id: return Lang_##id; +#include "clang/Frontend/LangStandards.def" + } +} + +const LangStandard *LangStandard::getLangStandardForName(llvm::StringRef Name) { + Kind K = llvm::StringSwitch<Kind>(Name) +#define LANGSTANDARD(id, name, desc, features) \ + .Case(name, lang_##id) +#include "clang/Frontend/LangStandards.def" + .Default(lang_unspecified); + if (K == lang_unspecified) + return 0; + + return &getLangStandardForKind(K); +} + + diff --git a/contrib/llvm/tools/clang/lib/Frontend/Makefile b/contrib/llvm/tools/clang/lib/Frontend/Makefile new file mode 100644 index 0000000..3c13ad6 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/Makefile @@ -0,0 +1,14 @@ +##===- clang/lib/Frontend/Makefile -------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +CLANG_LEVEL := ../.. +LIBRARYNAME := clangFrontend + +include $(CLANG_LEVEL)/Makefile + diff --git a/contrib/llvm/tools/clang/lib/Frontend/PrintPreprocessedOutput.cpp b/contrib/llvm/tools/clang/lib/Frontend/PrintPreprocessedOutput.cpp new file mode 100644 index 0000000..cfaf8a2 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/PrintPreprocessedOutput.cpp @@ -0,0 +1,588 @@ +//===--- PrintPreprocessedOutput.cpp - Implement the -E mode --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This code simply runs the preprocessor on the input file and prints out the +// result. This is the traditional behavior of the -E option. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/Utils.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Frontend/PreprocessorOutputOptions.h" +#include "clang/Lex/MacroInfo.h" +#include "clang/Lex/PPCallbacks.h" +#include "clang/Lex/Pragma.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Lex/TokenConcatenation.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Config/config.h" +#include "llvm/Support/raw_ostream.h" +#include <cstdio> +using namespace clang; + +/// PrintMacroDefinition - Print a macro definition in a form that will be +/// properly accepted back as a definition. +static void PrintMacroDefinition(const IdentifierInfo &II, const MacroInfo &MI, + Preprocessor &PP, llvm::raw_ostream &OS) { + OS << "#define " << II.getName(); + + if (MI.isFunctionLike()) { + OS << '('; + if (!MI.arg_empty()) { + MacroInfo::arg_iterator AI = MI.arg_begin(), E = MI.arg_end(); + for (; AI+1 != E; ++AI) { + OS << (*AI)->getName(); + OS << ','; + } + + // Last argument. + if ((*AI)->getName() == "__VA_ARGS__") + OS << "..."; + else + OS << (*AI)->getName(); + } + + if (MI.isGNUVarargs()) + OS << "..."; // #define foo(x...) + + OS << ')'; + } + + // GCC always emits a space, even if the macro body is empty. However, do not + // want to emit two spaces if the first token has a leading space. + if (MI.tokens_empty() || !MI.tokens_begin()->hasLeadingSpace()) + OS << ' '; + + llvm::SmallString<128> SpellingBuffer; + for (MacroInfo::tokens_iterator I = MI.tokens_begin(), E = MI.tokens_end(); + I != E; ++I) { + if (I->hasLeadingSpace()) + OS << ' '; + + OS << PP.getSpelling(*I, SpellingBuffer); + } +} + +//===----------------------------------------------------------------------===// +// Preprocessed token printer +//===----------------------------------------------------------------------===// + +namespace { +class PrintPPOutputPPCallbacks : public PPCallbacks { + Preprocessor &PP; + SourceManager &SM; + TokenConcatenation ConcatInfo; +public: + llvm::raw_ostream &OS; +private: + unsigned CurLine; + + /// The current include nesting level, used by header include dumping (-H). + unsigned CurrentIncludeDepth; + + bool EmittedTokensOnThisLine; + bool EmittedMacroOnThisLine; + SrcMgr::CharacteristicKind FileType; + llvm::SmallString<512> CurFilename; + bool Initialized; + bool DisableLineMarkers; + bool DumpDefines; + bool DumpHeaderIncludes; + bool UseLineDirective; + bool HasProcessedPredefines; +public: + PrintPPOutputPPCallbacks(Preprocessor &pp, llvm::raw_ostream &os, + bool lineMarkers, bool defines, bool headers) + : PP(pp), SM(PP.getSourceManager()), + ConcatInfo(PP), OS(os), DisableLineMarkers(lineMarkers), + DumpDefines(defines), DumpHeaderIncludes(headers) { + CurLine = CurrentIncludeDepth = 0; + CurFilename += "<uninit>"; + EmittedTokensOnThisLine = false; + EmittedMacroOnThisLine = false; + FileType = SrcMgr::C_User; + Initialized = false; + HasProcessedPredefines = false; + + // If we're in microsoft mode, use normal #line instead of line markers. + UseLineDirective = PP.getLangOptions().Microsoft; + } + + void SetEmittedTokensOnThisLine() { EmittedTokensOnThisLine = true; } + bool hasEmittedTokensOnThisLine() const { return EmittedTokensOnThisLine; } + + virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType); + virtual void Ident(SourceLocation Loc, const std::string &str); + virtual void PragmaComment(SourceLocation Loc, const IdentifierInfo *Kind, + const std::string &Str); + virtual void PragmaMessage(SourceLocation Loc, llvm::StringRef Str); + + bool HandleFirstTokOnLine(Token &Tok); + bool MoveToLine(SourceLocation Loc) { + return MoveToLine(SM.getPresumedLoc(Loc).getLine()); + } + bool MoveToLine(unsigned LineNo); + + bool AvoidConcat(const Token &PrevPrevTok, const Token &PrevTok, + const Token &Tok) { + return ConcatInfo.AvoidConcat(PrevPrevTok, PrevTok, Tok); + } + void WriteLineInfo(unsigned LineNo, const char *Extra=0, unsigned ExtraLen=0); + + void HandleNewlinesInToken(const char *TokStr, unsigned Len); + + /// MacroDefined - This hook is called whenever a macro definition is seen. + void MacroDefined(const IdentifierInfo *II, const MacroInfo *MI); + + /// MacroUndefined - This hook is called whenever a macro #undef is seen. + void MacroUndefined(SourceLocation Loc, const IdentifierInfo *II, + const MacroInfo *MI); +}; +} // end anonymous namespace + +void PrintPPOutputPPCallbacks::WriteLineInfo(unsigned LineNo, + const char *Extra, + unsigned ExtraLen) { + if (EmittedTokensOnThisLine || EmittedMacroOnThisLine) { + OS << '\n'; + EmittedTokensOnThisLine = false; + EmittedMacroOnThisLine = false; + } + + // Emit #line directives or GNU line markers depending on what mode we're in. + if (UseLineDirective) { + OS << "#line" << ' ' << LineNo << ' ' << '"'; + OS.write(&CurFilename[0], CurFilename.size()); + OS << '"'; + } else { + OS << '#' << ' ' << LineNo << ' ' << '"'; + OS.write(&CurFilename[0], CurFilename.size()); + OS << '"'; + + if (ExtraLen) + OS.write(Extra, ExtraLen); + + if (FileType == SrcMgr::C_System) + OS.write(" 3", 2); + else if (FileType == SrcMgr::C_ExternCSystem) + OS.write(" 3 4", 4); + } + OS << '\n'; +} + +/// MoveToLine - Move the output to the source line specified by the location +/// object. We can do this by emitting some number of \n's, or be emitting a +/// #line directive. This returns false if already at the specified line, true +/// if some newlines were emitted. +bool PrintPPOutputPPCallbacks::MoveToLine(unsigned LineNo) { + // If this line is "close enough" to the original line, just print newlines, + // otherwise print a #line directive. + if (LineNo-CurLine <= 8) { + if (LineNo-CurLine == 1) + OS << '\n'; + else if (LineNo == CurLine) + return false; // Spelling line moved, but instantiation line didn't. + else { + const char *NewLines = "\n\n\n\n\n\n\n\n"; + OS.write(NewLines, LineNo-CurLine); + } + } else if (!DisableLineMarkers) { + // Emit a #line or line marker. + WriteLineInfo(LineNo, 0, 0); + } else { + // Okay, we're in -P mode, which turns off line markers. However, we still + // need to emit a newline between tokens on different lines. + if (EmittedTokensOnThisLine || EmittedMacroOnThisLine) { + OS << '\n'; + EmittedTokensOnThisLine = false; + EmittedMacroOnThisLine = false; + } + } + + CurLine = LineNo; + return true; +} + + +/// FileChanged - Whenever the preprocessor enters or exits a #include file +/// it invokes this handler. Update our conception of the current source +/// position. +void PrintPPOutputPPCallbacks::FileChanged(SourceLocation Loc, + FileChangeReason Reason, + SrcMgr::CharacteristicKind NewFileType) { + // Unless we are exiting a #include, make sure to skip ahead to the line the + // #include directive was at. + SourceManager &SourceMgr = SM; + + PresumedLoc UserLoc = SourceMgr.getPresumedLoc(Loc); + unsigned NewLine = UserLoc.getLine(); + + if (Reason == PPCallbacks::EnterFile) { + SourceLocation IncludeLoc = SourceMgr.getPresumedLoc(Loc).getIncludeLoc(); + if (IncludeLoc.isValid()) + MoveToLine(IncludeLoc); + } else if (Reason == PPCallbacks::SystemHeaderPragma) { + MoveToLine(NewLine); + + // TODO GCC emits the # directive for this directive on the line AFTER the + // directive and emits a bunch of spaces that aren't needed. Emulate this + // strange behavior. + } + + // Adjust the current include depth. + if (Reason == PPCallbacks::EnterFile) { + ++CurrentIncludeDepth; + } else { + if (CurrentIncludeDepth) + --CurrentIncludeDepth; + + // We track when we are done with the predefines by watching for the first + // place where we drop back to a nesting depth of 0. + if (CurrentIncludeDepth == 0 && !HasProcessedPredefines) + HasProcessedPredefines = true; + } + + CurLine = NewLine; + + CurFilename.clear(); + CurFilename += UserLoc.getFilename(); + Lexer::Stringify(CurFilename); + FileType = NewFileType; + + // Dump the header include information, if enabled and we are past the + // predefines buffer. + if (DumpHeaderIncludes && HasProcessedPredefines && + Reason == PPCallbacks::EnterFile) { + llvm::SmallString<256> Msg; + llvm::raw_svector_ostream OS(Msg); + for (unsigned i = 0; i != CurrentIncludeDepth; ++i) + OS << '.'; + OS << ' ' << CurFilename << '\n'; + llvm::errs() << OS.str(); + } + + if (DisableLineMarkers) return; + + if (!Initialized) { + WriteLineInfo(CurLine); + Initialized = true; + } + + switch (Reason) { + case PPCallbacks::EnterFile: + WriteLineInfo(CurLine, " 1", 2); + break; + case PPCallbacks::ExitFile: + WriteLineInfo(CurLine, " 2", 2); + break; + case PPCallbacks::SystemHeaderPragma: + case PPCallbacks::RenameFile: + WriteLineInfo(CurLine); + break; + } +} + +/// Ident - Handle #ident directives when read by the preprocessor. +/// +void PrintPPOutputPPCallbacks::Ident(SourceLocation Loc, const std::string &S) { + MoveToLine(Loc); + + OS.write("#ident ", strlen("#ident ")); + OS.write(&S[0], S.size()); + EmittedTokensOnThisLine = true; +} + +/// MacroDefined - This hook is called whenever a macro definition is seen. +void PrintPPOutputPPCallbacks::MacroDefined(const IdentifierInfo *II, + const MacroInfo *MI) { + // Only print out macro definitions in -dD mode. + if (!DumpDefines || + // Ignore __FILE__ etc. + MI->isBuiltinMacro()) return; + + MoveToLine(MI->getDefinitionLoc()); + PrintMacroDefinition(*II, *MI, PP, OS); + EmittedMacroOnThisLine = true; +} + +void PrintPPOutputPPCallbacks::MacroUndefined(SourceLocation Loc, + const IdentifierInfo *II, + const MacroInfo *MI) { + // Only print out macro definitions in -dD mode. + if (!DumpDefines) return; + + MoveToLine(Loc); + OS << "#undef " << II->getName(); + EmittedMacroOnThisLine = true; +} + +void PrintPPOutputPPCallbacks::PragmaComment(SourceLocation Loc, + const IdentifierInfo *Kind, + const std::string &Str) { + MoveToLine(Loc); + OS << "#pragma comment(" << Kind->getName(); + + if (!Str.empty()) { + OS << ", \""; + + for (unsigned i = 0, e = Str.size(); i != e; ++i) { + unsigned char Char = Str[i]; + if (isprint(Char) && Char != '\\' && Char != '"') + OS << (char)Char; + else // Output anything hard as an octal escape. + OS << '\\' + << (char)('0'+ ((Char >> 6) & 7)) + << (char)('0'+ ((Char >> 3) & 7)) + << (char)('0'+ ((Char >> 0) & 7)); + } + OS << '"'; + } + + OS << ')'; + EmittedTokensOnThisLine = true; +} + +void PrintPPOutputPPCallbacks::PragmaMessage(SourceLocation Loc, + llvm::StringRef Str) { + MoveToLine(Loc); + OS << "#pragma message("; + + OS << '"'; + + for (unsigned i = 0, e = Str.size(); i != e; ++i) { + unsigned char Char = Str[i]; + if (isprint(Char) && Char != '\\' && Char != '"') + OS << (char)Char; + else // Output anything hard as an octal escape. + OS << '\\' + << (char)('0'+ ((Char >> 6) & 7)) + << (char)('0'+ ((Char >> 3) & 7)) + << (char)('0'+ ((Char >> 0) & 7)); + } + OS << '"'; + + OS << ')'; + EmittedTokensOnThisLine = true; +} + + +/// HandleFirstTokOnLine - When emitting a preprocessed file in -E mode, this +/// is called for the first token on each new line. If this really is the start +/// of a new logical line, handle it and return true, otherwise return false. +/// This may not be the start of a logical line because the "start of line" +/// marker is set for spelling lines, not instantiation ones. +bool PrintPPOutputPPCallbacks::HandleFirstTokOnLine(Token &Tok) { + // Figure out what line we went to and insert the appropriate number of + // newline characters. + if (!MoveToLine(Tok.getLocation())) + return false; + + // Print out space characters so that the first token on a line is + // indented for easy reading. + unsigned ColNo = SM.getInstantiationColumnNumber(Tok.getLocation()); + + // This hack prevents stuff like: + // #define HASH # + // HASH define foo bar + // From having the # character end up at column 1, which makes it so it + // is not handled as a #define next time through the preprocessor if in + // -fpreprocessed mode. + if (ColNo <= 1 && Tok.is(tok::hash)) + OS << ' '; + + // Otherwise, indent the appropriate number of spaces. + for (; ColNo > 1; --ColNo) + OS << ' '; + + return true; +} + +void PrintPPOutputPPCallbacks::HandleNewlinesInToken(const char *TokStr, + unsigned Len) { + unsigned NumNewlines = 0; + for (; Len; --Len, ++TokStr) { + if (*TokStr != '\n' && + *TokStr != '\r') + continue; + + ++NumNewlines; + + // If we have \n\r or \r\n, skip both and count as one line. + if (Len != 1 && + (TokStr[1] == '\n' || TokStr[1] == '\r') && + TokStr[0] != TokStr[1]) + ++TokStr, --Len; + } + + if (NumNewlines == 0) return; + + CurLine += NumNewlines; +} + + +namespace { +struct UnknownPragmaHandler : public PragmaHandler { + const char *Prefix; + PrintPPOutputPPCallbacks *Callbacks; + + UnknownPragmaHandler(const char *prefix, PrintPPOutputPPCallbacks *callbacks) + : Prefix(prefix), Callbacks(callbacks) {} + virtual void HandlePragma(Preprocessor &PP, Token &PragmaTok) { + // Figure out what line we went to and insert the appropriate number of + // newline characters. + Callbacks->MoveToLine(PragmaTok.getLocation()); + Callbacks->OS.write(Prefix, strlen(Prefix)); + + // Read and print all of the pragma tokens. + while (PragmaTok.isNot(tok::eom)) { + if (PragmaTok.hasLeadingSpace()) + Callbacks->OS << ' '; + std::string TokSpell = PP.getSpelling(PragmaTok); + Callbacks->OS.write(&TokSpell[0], TokSpell.size()); + PP.LexUnexpandedToken(PragmaTok); + } + Callbacks->OS << '\n'; + } +}; +} // end anonymous namespace + + +static void PrintPreprocessedTokens(Preprocessor &PP, Token &Tok, + PrintPPOutputPPCallbacks *Callbacks, + llvm::raw_ostream &OS) { + char Buffer[256]; + Token PrevPrevTok, PrevTok; + PrevPrevTok.startToken(); + PrevTok.startToken(); + while (1) { + + // If this token is at the start of a line, emit newlines if needed. + if (Tok.isAtStartOfLine() && Callbacks->HandleFirstTokOnLine(Tok)) { + // done. + } else if (Tok.hasLeadingSpace() || + // If we haven't emitted a token on this line yet, PrevTok isn't + // useful to look at and no concatenation could happen anyway. + (Callbacks->hasEmittedTokensOnThisLine() && + // Don't print "-" next to "-", it would form "--". + Callbacks->AvoidConcat(PrevPrevTok, PrevTok, Tok))) { + OS << ' '; + } + + if (IdentifierInfo *II = Tok.getIdentifierInfo()) { + OS << II->getName(); + } else if (Tok.isLiteral() && !Tok.needsCleaning() && + Tok.getLiteralData()) { + OS.write(Tok.getLiteralData(), Tok.getLength()); + } else if (Tok.getLength() < 256) { + const char *TokPtr = Buffer; + unsigned Len = PP.getSpelling(Tok, TokPtr); + OS.write(TokPtr, Len); + + // Tokens that can contain embedded newlines need to adjust our current + // line number. + if (Tok.getKind() == tok::comment) + Callbacks->HandleNewlinesInToken(TokPtr, Len); + } else { + std::string S = PP.getSpelling(Tok); + OS.write(&S[0], S.size()); + + // Tokens that can contain embedded newlines need to adjust our current + // line number. + if (Tok.getKind() == tok::comment) + Callbacks->HandleNewlinesInToken(&S[0], S.size()); + } + Callbacks->SetEmittedTokensOnThisLine(); + + if (Tok.is(tok::eof)) break; + + PrevPrevTok = PrevTok; + PrevTok = Tok; + PP.Lex(Tok); + } +} + +typedef std::pair<IdentifierInfo*, MacroInfo*> id_macro_pair; +static int MacroIDCompare(const void* a, const void* b) { + const id_macro_pair *LHS = static_cast<const id_macro_pair*>(a); + const id_macro_pair *RHS = static_cast<const id_macro_pair*>(b); + return LHS->first->getName().compare(RHS->first->getName()); +} + +static void DoPrintMacros(Preprocessor &PP, llvm::raw_ostream *OS) { + // Ignore unknown pragmas. + PP.AddPragmaHandler(new EmptyPragmaHandler()); + + // -dM mode just scans and ignores all tokens in the files, then dumps out + // the macro table at the end. + PP.EnterMainSourceFile(); + + Token Tok; + do PP.Lex(Tok); + while (Tok.isNot(tok::eof)); + + llvm::SmallVector<id_macro_pair, 128> + MacrosByID(PP.macro_begin(), PP.macro_end()); + llvm::array_pod_sort(MacrosByID.begin(), MacrosByID.end(), MacroIDCompare); + + for (unsigned i = 0, e = MacrosByID.size(); i != e; ++i) { + MacroInfo &MI = *MacrosByID[i].second; + // Ignore computed macros like __LINE__ and friends. + if (MI.isBuiltinMacro()) continue; + + PrintMacroDefinition(*MacrosByID[i].first, MI, PP, *OS); + *OS << '\n'; + } +} + +/// DoPrintPreprocessedInput - This implements -E mode. +/// +void clang::DoPrintPreprocessedInput(Preprocessor &PP, llvm::raw_ostream *OS, + const PreprocessorOutputOptions &Opts) { + // Show macros with no output is handled specially. + if (!Opts.ShowCPP) { + assert(Opts.ShowMacros && "Not yet implemented!"); + DoPrintMacros(PP, OS); + return; + } + + // Inform the preprocessor whether we want it to retain comments or not, due + // to -C or -CC. + PP.SetCommentRetentionState(Opts.ShowComments, Opts.ShowMacroComments); + + PrintPPOutputPPCallbacks *Callbacks = + new PrintPPOutputPPCallbacks(PP, *OS, !Opts.ShowLineMarkers, + Opts.ShowMacros, Opts.ShowHeaderIncludes); + PP.AddPragmaHandler(new UnknownPragmaHandler("#pragma", Callbacks)); + PP.AddPragmaHandler("GCC", new UnknownPragmaHandler("#pragma GCC", + Callbacks)); + + PP.addPPCallbacks(Callbacks); + + // After we have configured the preprocessor, enter the main file. + PP.EnterMainSourceFile(); + + // Consume all of the tokens that come from the predefines buffer. Those + // should not be emitted into the output and are guaranteed to be at the + // start. + const SourceManager &SourceMgr = PP.getSourceManager(); + Token Tok; + do PP.Lex(Tok); + while (Tok.isNot(tok::eof) && Tok.getLocation().isFileID() && + !strcmp(SourceMgr.getPresumedLoc(Tok.getLocation()).getFilename(), + "<built-in>")); + + // Read all the preprocessed tokens, printing them out to the stream. + PrintPreprocessedTokens(PP, Tok, Callbacks, *OS); + *OS << '\n'; +} + diff --git a/contrib/llvm/tools/clang/lib/Frontend/StmtXML.cpp b/contrib/llvm/tools/clang/lib/Frontend/StmtXML.cpp new file mode 100644 index 0000000..b660734 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/StmtXML.cpp @@ -0,0 +1,453 @@ +//===--- StmtXML.cpp - XML implementation for Stmt ASTs ------------------===// +// +// 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 Stmt::dumpXML methods, which dump out the +// AST to an XML document. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/DocumentXML.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclCXX.h" +#include "clang/Basic/SourceManager.h" +using namespace clang; + +//===----------------------------------------------------------------------===// +// StmtXML Visitor +//===----------------------------------------------------------------------===// + +namespace { + class StmtXML : public StmtVisitor<StmtXML> { + DocumentXML& Doc; + + //static const char *getOpcodeStr(UnaryOperator::Opcode Op); + //static const char *getOpcodeStr(BinaryOperator::Opcode Op); + + + void addSpecialAttribute(const char* pName, StringLiteral* Str) { + Doc.addAttribute(pName, Doc.escapeString(Str->getString().data(), + Str->getString().size())); + } + + void addSpecialAttribute(const char* pName, SizeOfAlignOfExpr* S) { + if (S->isArgumentType()) + Doc.addAttribute(pName, S->getArgumentType()); + } + + void addSpecialAttribute(const char* pName, CXXTypeidExpr* S) { + if (S->isTypeOperand()) + Doc.addAttribute(pName, S->getTypeOperand()); + } + + + public: + StmtXML(DocumentXML& doc) + : Doc(doc) { + } + + void DumpSubTree(Stmt *S) { + if (S) { + Visit(S); + if (DeclStmt* DS = dyn_cast<DeclStmt>(S)) { + for (DeclStmt::decl_iterator DI = DS->decl_begin(), + DE = DS->decl_end(); DI != DE; ++DI) { + Doc.PrintDecl(*DI); + } + } else { + for (Stmt::child_iterator i = S->child_begin(), e = S->child_end(); + i != e; ++i) + DumpSubTree(*i); + } + Doc.toParent(); + } else { + Doc.addSubNode("NULL").toParent(); + } + } + + +#define NODE_XML( CLASS, NAME ) \ + void Visit##CLASS(CLASS* S) \ + { \ + typedef CLASS tStmtType; \ + Doc.addSubNode(NAME); + +#define ATTRIBUTE_XML( FN, NAME ) Doc.addAttribute(NAME, S->FN); +#define TYPE_ATTRIBUTE_XML( FN ) ATTRIBUTE_XML(FN, "type") +#define ATTRIBUTE_OPT_XML( FN, NAME ) Doc.addAttributeOptional(NAME, S->FN); +#define ATTRIBUTE_SPECIAL_XML( FN, NAME ) addSpecialAttribute(NAME, S); +#define ATTRIBUTE_FILE_LOCATION_XML Doc.addLocationRange(S->getSourceRange()); + + +#define ATTRIBUTE_ENUM_XML( FN, NAME ) \ + { \ + const char* pAttributeName = NAME; \ + const bool optional = false; \ + switch (S->FN) { \ + default: assert(0 && "unknown enum value"); + +#define ATTRIBUTE_ENUM_OPT_XML( FN, NAME ) \ + { \ + const char* pAttributeName = NAME; \ + const bool optional = true; \ + switch (S->FN) { \ + default: assert(0 && "unknown enum value"); + +#define ENUM_XML( VALUE, NAME ) case VALUE: if ((!optional) || NAME[0]) Doc.addAttribute(pAttributeName, NAME); break; +#define END_ENUM_XML } } +#define END_NODE_XML } + +#define ID_ATTRIBUTE_XML Doc.addAttribute("id", S); +#define SUB_NODE_XML( CLASS ) +#define SUB_NODE_SEQUENCE_XML( CLASS ) +#define SUB_NODE_OPT_XML( CLASS ) + +#include "clang/Frontend/StmtXML.def" + +#if (0) + // Stmts. + void VisitStmt(Stmt *Node); + void VisitDeclStmt(DeclStmt *Node); + void VisitLabelStmt(LabelStmt *Node); + void VisitGotoStmt(GotoStmt *Node); + + // Exprs + void VisitExpr(Expr *Node); + void VisitDeclRefExpr(DeclRefExpr *Node); + void VisitPredefinedExpr(PredefinedExpr *Node); + void VisitCharacterLiteral(CharacterLiteral *Node); + void VisitIntegerLiteral(IntegerLiteral *Node); + void VisitFloatingLiteral(FloatingLiteral *Node); + void VisitStringLiteral(StringLiteral *Str); + void VisitUnaryOperator(UnaryOperator *Node); + void VisitOffsetOfExpr(OffsetOfExpr *Node); + void VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *Node); + void VisitMemberExpr(MemberExpr *Node); + void VisitExtVectorElementExpr(ExtVectorElementExpr *Node); + void VisitBinaryOperator(BinaryOperator *Node); + void VisitCompoundAssignOperator(CompoundAssignOperator *Node); + void VisitAddrLabelExpr(AddrLabelExpr *Node); + void VisitTypesCompatibleExpr(TypesCompatibleExpr *Node); + + // C++ + void VisitCXXNamedCastExpr(CXXNamedCastExpr *Node); + void VisitCXXBoolLiteralExpr(CXXBoolLiteralExpr *Node); + void VisitCXXThisExpr(CXXThisExpr *Node); + void VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr *Node); + + // ObjC + void VisitObjCEncodeExpr(ObjCEncodeExpr *Node); + void VisitObjCMessageExpr(ObjCMessageExpr* Node); + void VisitObjCSelectorExpr(ObjCSelectorExpr *Node); + void VisitObjCProtocolExpr(ObjCProtocolExpr *Node); + void VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *Node); + void VisitObjCImplicitSetterGetterRefExpr( + ObjCImplicitSetterGetterRefExpr *Node); + void VisitObjCIvarRefExpr(ObjCIvarRefExpr *Node); + void VisitObjCSuperExpr(ObjCSuperExpr *Node); +#endif + }; +} + +//===----------------------------------------------------------------------===// +// Stmt printing methods. +//===----------------------------------------------------------------------===// +#if (0) +void StmtXML::VisitStmt(Stmt *Node) { + // nothing special to do +} + +void StmtXML::VisitDeclStmt(DeclStmt *Node) { + for (DeclStmt::decl_iterator DI = Node->decl_begin(), DE = Node->decl_end(); + DI != DE; ++DI) { + Doc.PrintDecl(*DI); + } +} + +void StmtXML::VisitLabelStmt(LabelStmt *Node) { + Doc.addAttribute("name", Node->getName()); +} + +void StmtXML::VisitGotoStmt(GotoStmt *Node) { + Doc.addAttribute("name", Node->getLabel()->getName()); +} + +//===----------------------------------------------------------------------===// +// Expr printing methods. +//===----------------------------------------------------------------------===// + +void StmtXML::VisitExpr(Expr *Node) { + DumpExpr(Node); +} + +void StmtXML::VisitDeclRefExpr(DeclRefExpr *Node) { + DumpExpr(Node); + + const char* pKind; + switch (Node->getDecl()->getKind()) { + case Decl::Function: pKind = "FunctionDecl"; break; + case Decl::Var: pKind = "Var"; break; + case Decl::ParmVar: pKind = "ParmVar"; break; + case Decl::EnumConstant: pKind = "EnumConstant"; break; + case Decl::Typedef: pKind = "Typedef"; break; + case Decl::Record: pKind = "Record"; break; + case Decl::Enum: pKind = "Enum"; break; + case Decl::CXXRecord: pKind = "CXXRecord"; break; + case Decl::ObjCInterface: pKind = "ObjCInterface"; break; + case Decl::ObjCClass: pKind = "ObjCClass"; break; + default: pKind = "Decl"; break; + } + + Doc.addAttribute("kind", pKind); + Doc.addAttribute("name", Node->getDecl()->getNameAsString()); + Doc.addRefAttribute(Node->getDecl()); +} + +void StmtXML::VisitPredefinedExpr(PredefinedExpr *Node) { + DumpExpr(Node); + switch (Node->getIdentType()) { + default: assert(0 && "unknown case"); + case PredefinedExpr::Func: Doc.addAttribute("predefined", " __func__"); break; + case PredefinedExpr::Function: Doc.addAttribute("predefined", " __FUNCTION__"); break; + case PredefinedExpr::PrettyFunction: Doc.addAttribute("predefined", " __PRETTY_FUNCTION__");break; + } +} + +void StmtXML::VisitCharacterLiteral(CharacterLiteral *Node) { + DumpExpr(Node); + Doc.addAttribute("value", Node->getValue()); +} + +void StmtXML::VisitIntegerLiteral(IntegerLiteral *Node) { + DumpExpr(Node); + bool isSigned = Node->getType()->isSignedIntegerType(); + Doc.addAttribute("value", Node->getValue().toString(10, isSigned)); +} + +void StmtXML::VisitFloatingLiteral(FloatingLiteral *Node) { + DumpExpr(Node); + // FIXME: output float as written in source (no approximation or the like) + //Doc.addAttribute("value", Node->getValueAsApproximateDouble())); + Doc.addAttribute("value", "FIXME"); +} + +void StmtXML::VisitStringLiteral(StringLiteral *Str) { + DumpExpr(Str); + if (Str->isWide()) + Doc.addAttribute("is_wide", "1"); + + Doc.addAttribute("value", Doc.escapeString(Str->getStrData(), Str->getByteLength())); +} + + +const char *StmtXML::getOpcodeStr(UnaryOperator::Opcode Op) { + switch (Op) { + default: assert(0 && "Unknown unary operator"); + case UnaryOperator::PostInc: return "postinc"; + case UnaryOperator::PostDec: return "postdec"; + case UnaryOperator::PreInc: return "preinc"; + case UnaryOperator::PreDec: return "predec"; + case UnaryOperator::AddrOf: return "addrof"; + case UnaryOperator::Deref: return "deref"; + case UnaryOperator::Plus: return "plus"; + case UnaryOperator::Minus: return "minus"; + case UnaryOperator::Not: return "not"; + case UnaryOperator::LNot: return "lnot"; + case UnaryOperator::Real: return "__real"; + case UnaryOperator::Imag: return "__imag"; + case UnaryOperator::Extension: return "__extension__"; + } +} + + +const char *StmtXML::getOpcodeStr(BinaryOperator::Opcode Op) { + switch (Op) { + default: assert(0 && "Unknown binary operator"); + case BinaryOperator::PtrMemD: return "ptrmemd"; + case BinaryOperator::PtrMemI: return "ptrmemi"; + case BinaryOperator::Mul: return "mul"; + case BinaryOperator::Div: return "div"; + case BinaryOperator::Rem: return "rem"; + case BinaryOperator::Add: return "add"; + case BinaryOperator::Sub: return "sub"; + case BinaryOperator::Shl: return "shl"; + case BinaryOperator::Shr: return "shr"; + case BinaryOperator::LT: return "lt"; + case BinaryOperator::GT: return "gt"; + case BinaryOperator::LE: return "le"; + case BinaryOperator::GE: return "ge"; + case BinaryOperator::EQ: return "eq"; + case BinaryOperator::NE: return "ne"; + case BinaryOperator::And: return "and"; + case BinaryOperator::Xor: return "xor"; + case BinaryOperator::Or: return "or"; + case BinaryOperator::LAnd: return "land"; + case BinaryOperator::LOr: return "lor"; + case BinaryOperator::Assign: return "assign"; + case BinaryOperator::MulAssign: return "mulassign"; + case BinaryOperator::DivAssign: return "divassign"; + case BinaryOperator::RemAssign: return "remassign"; + case BinaryOperator::AddAssign: return "addassign"; + case BinaryOperator::SubAssign: return "subassign"; + case BinaryOperator::ShlAssign: return "shlassign"; + case BinaryOperator::ShrAssign: return "shrassign"; + case BinaryOperator::AndAssign: return "andassign"; + case BinaryOperator::XorAssign: return "xorassign"; + case BinaryOperator::OrAssign: return "orassign"; + case BinaryOperator::Comma: return "comma"; + } +} + +void StmtXML::VisitUnaryOperator(UnaryOperator *Node) { + DumpExpr(Node); + Doc.addAttribute("op_code", getOpcodeStr(Node->getOpcode())); +} + +void StmtXML::OffsetOfExpr(OffsetOfExpr *Node) { + DumpExpr(Node); +} + +void StmtXML::VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *Node) { + DumpExpr(Node); + Doc.addAttribute("is_sizeof", Node->isSizeOf() ? "sizeof" : "alignof"); + Doc.addAttribute("is_type", Node->isArgumentType() ? "1" : "0"); + if (Node->isArgumentType()) + DumpTypeExpr(Node->getArgumentType()); +} + +void StmtXML::VisitMemberExpr(MemberExpr *Node) { + DumpExpr(Node); + Doc.addAttribute("is_deref", Node->isArrow() ? "1" : "0"); + Doc.addAttribute("name", Node->getMemberDecl()->getNameAsString()); + Doc.addRefAttribute(Node->getMemberDecl()); +} + +void StmtXML::VisitExtVectorElementExpr(ExtVectorElementExpr *Node) { + DumpExpr(Node); + Doc.addAttribute("name", Node->getAccessor().getName()); +} + +void StmtXML::VisitBinaryOperator(BinaryOperator *Node) { + DumpExpr(Node); + Doc.addAttribute("op_code", getOpcodeStr(Node->getOpcode())); +} + +void StmtXML::VisitCompoundAssignOperator(CompoundAssignOperator *Node) { + VisitBinaryOperator(Node); +/* FIXME: is this needed in the AST? + DumpExpr(Node); + CurrentNode = CurrentNode->addSubNode("ComputeLHSTy"); + DumpType(Node->getComputationLHSType()); + CurrentNode = CurrentNode->Parent->addSubNode("ComputeResultTy"); + DumpType(Node->getComputationResultType()); + Doc.toParent(); +*/ +} + +// GNU extensions. + +void StmtXML::VisitAddrLabelExpr(AddrLabelExpr *Node) { + DumpExpr(Node); + Doc.addAttribute("name", Node->getLabel()->getName()); +} + +void StmtXML::VisitTypesCompatibleExpr(TypesCompatibleExpr *Node) { + DumpExpr(Node); + DumpTypeExpr(Node->getArgType1()); + DumpTypeExpr(Node->getArgType2()); +} + +//===----------------------------------------------------------------------===// +// C++ Expressions +//===----------------------------------------------------------------------===// + +void StmtXML::VisitCXXNamedCastExpr(CXXNamedCastExpr *Node) { + DumpExpr(Node); + Doc.addAttribute("kind", Node->getCastName()); + DumpTypeExpr(Node->getTypeAsWritten()); +} + +void StmtXML::VisitCXXBoolLiteralExpr(CXXBoolLiteralExpr *Node) { + DumpExpr(Node); + Doc.addAttribute("value", Node->getValue() ? "true" : "false"); +} + +void StmtXML::VisitCXXThisExpr(CXXThisExpr *Node) { + DumpExpr(Node); +} + +void StmtXML::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr *Node) { + DumpExpr(Node); + DumpTypeExpr(Node->getTypeAsWritten()); +} + +//===----------------------------------------------------------------------===// +// Obj-C Expressions +//===----------------------------------------------------------------------===// + +void StmtXML::VisitObjCMessageExpr(ObjCMessageExpr* Node) { + DumpExpr(Node); + Doc.addAttribute("selector", Node->getSelector().getAsString()); + IdentifierInfo* clsName = Node->getClassName(); + if (clsName) + Doc.addAttribute("class", clsName->getName()); +} + +void StmtXML::VisitObjCEncodeExpr(ObjCEncodeExpr *Node) { + DumpExpr(Node); + DumpTypeExpr(Node->getEncodedType()); +} + +void StmtXML::VisitObjCSelectorExpr(ObjCSelectorExpr *Node) { + DumpExpr(Node); + Doc.addAttribute("selector", Node->getSelector().getAsString()); +} + +void StmtXML::VisitObjCProtocolExpr(ObjCProtocolExpr *Node) { + DumpExpr(Node); + Doc.addAttribute("protocol", Node->getProtocol()->getNameAsString()); +} + +void StmtXML::VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *Node) { + DumpExpr(Node); + Doc.addAttribute("property", Node->getProperty()->getNameAsString()); +} + +void StmtXML::VisitObjCImplicitSetterGetterRefExpr( + ObjCImplicitSetterGetterRefExpr *Node) { + DumpExpr(Node); + ObjCMethodDecl *Getter = Node->getGetterMethod(); + ObjCMethodDecl *Setter = Node->getSetterMethod(); + Doc.addAttribute("Getter", Getter->getSelector().getAsString()); + Doc.addAttribute("Setter", Setter ? Setter->getSelector().getAsString().c_str() : "(null)"); +} + +void StmtXML::VisitObjCSuperExpr(ObjCSuperExpr *Node) { + DumpExpr(Node); + Doc.addAttribute("super", "1"); +} + +void StmtXML::VisitObjCIvarRefExpr(ObjCIvarRefExpr *Node) { + DumpExpr(Node); + Doc.addAttribute("kind", Node->getDecl()->getDeclKindName()); + Doc.addAttribute("decl", Node->getDecl()->getNameAsString()); + if (Node->isFreeIvar()) + Doc.addAttribute("isFreeIvar", "1"); +} +#endif +//===----------------------------------------------------------------------===// +// Stmt method implementations +//===----------------------------------------------------------------------===// + +/// dumpAll - This does a dump of the specified AST fragment and all subtrees. +void DocumentXML::PrintStmt(const Stmt *S) { + StmtXML P(*this); + P.DumpSubTree(const_cast<Stmt*>(S)); +} + diff --git a/contrib/llvm/tools/clang/lib/Frontend/TextDiagnosticBuffer.cpp b/contrib/llvm/tools/clang/lib/Frontend/TextDiagnosticBuffer.cpp new file mode 100644 index 0000000..fdf2ec8 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/TextDiagnosticBuffer.cpp @@ -0,0 +1,48 @@ +//===--- TextDiagnosticBuffer.cpp - Buffer Text Diagnostics ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This is a concrete diagnostic client, which buffers the diagnostic messages. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/TextDiagnosticBuffer.h" +#include "llvm/ADT/SmallString.h" +using namespace clang; + +/// HandleDiagnostic - Store the errors, warnings, and notes that are +/// reported. +/// +void TextDiagnosticBuffer::HandleDiagnostic(Diagnostic::Level Level, + const DiagnosticInfo &Info) { + llvm::SmallString<100> Buf; + Info.FormatDiagnostic(Buf); + switch (Level) { + default: assert(0 && "Diagnostic not handled during diagnostic buffering!"); + case Diagnostic::Note: + Notes.push_back(std::make_pair(Info.getLocation(), Buf.str())); + break; + case Diagnostic::Warning: + Warnings.push_back(std::make_pair(Info.getLocation(), Buf.str())); + break; + case Diagnostic::Error: + case Diagnostic::Fatal: + Errors.push_back(std::make_pair(Info.getLocation(), Buf.str())); + break; + } +} + +void TextDiagnosticBuffer::FlushDiagnostics(Diagnostic &Diags) const { + // FIXME: Flush the diagnostics in order. + for (const_iterator it = err_begin(), ie = err_end(); it != ie; ++it) + Diags.Report(Diags.getCustomDiagID(Diagnostic::Error, it->second.c_str())); + for (const_iterator it = warn_begin(), ie = warn_end(); it != ie; ++it) + Diags.Report(Diags.getCustomDiagID(Diagnostic::Warning,it->second.c_str())); + for (const_iterator it = note_begin(), ie = note_end(); it != ie; ++it) + Diags.Report(Diags.getCustomDiagID(Diagnostic::Note, it->second.c_str())); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/TextDiagnosticPrinter.cpp b/contrib/llvm/tools/clang/lib/Frontend/TextDiagnosticPrinter.cpp new file mode 100644 index 0000000..1e453a0 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/TextDiagnosticPrinter.cpp @@ -0,0 +1,999 @@ +//===--- TextDiagnosticPrinter.cpp - Diagnostic Printer -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This diagnostic client prints out their diagnostic messages. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Frontend/DiagnosticOptions.h" +#include "clang/Lex/Lexer.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" +#include <algorithm> +using namespace clang; + +static const enum llvm::raw_ostream::Colors noteColor = + llvm::raw_ostream::BLACK; +static const enum llvm::raw_ostream::Colors fixitColor = + llvm::raw_ostream::GREEN; +static const enum llvm::raw_ostream::Colors caretColor = + llvm::raw_ostream::GREEN; +static const enum llvm::raw_ostream::Colors warningColor = + llvm::raw_ostream::MAGENTA; +static const enum llvm::raw_ostream::Colors errorColor = llvm::raw_ostream::RED; +static const enum llvm::raw_ostream::Colors fatalColor = llvm::raw_ostream::RED; +// Used for changing only the bold attribute. +static const enum llvm::raw_ostream::Colors savedColor = + llvm::raw_ostream::SAVEDCOLOR; + +/// \brief Number of spaces to indent when word-wrapping. +const unsigned WordWrapIndentation = 6; + +TextDiagnosticPrinter::TextDiagnosticPrinter(llvm::raw_ostream &os, + const DiagnosticOptions &diags, + bool _OwnsOutputStream) + : OS(os), LangOpts(0), DiagOpts(&diags), + LastCaretDiagnosticWasNote(0), + OwnsOutputStream(_OwnsOutputStream) { +} + +TextDiagnosticPrinter::~TextDiagnosticPrinter() { + if (OwnsOutputStream) + delete &OS; +} + +void TextDiagnosticPrinter:: +PrintIncludeStack(SourceLocation Loc, const SourceManager &SM) { + if (Loc.isInvalid()) return; + + PresumedLoc PLoc = SM.getPresumedLoc(Loc); + + // Print out the other include frames first. + PrintIncludeStack(PLoc.getIncludeLoc(), SM); + + if (DiagOpts->ShowLocation) + OS << "In file included from " << PLoc.getFilename() + << ':' << PLoc.getLine() << ":\n"; + else + OS << "In included file:\n"; +} + +/// HighlightRange - Given a SourceRange and a line number, highlight (with ~'s) +/// any characters in LineNo that intersect the SourceRange. +void TextDiagnosticPrinter::HighlightRange(const CharSourceRange &R, + const SourceManager &SM, + unsigned LineNo, FileID FID, + std::string &CaretLine, + const std::string &SourceLine) { + assert(CaretLine.size() == SourceLine.size() && + "Expect a correspondence between source and caret line!"); + if (!R.isValid()) return; + + SourceLocation Begin = SM.getInstantiationLoc(R.getBegin()); + SourceLocation End = SM.getInstantiationLoc(R.getEnd()); + + // If the End location and the start location are the same and are a macro + // location, then the range was something that came from a macro expansion + // or _Pragma. If this is an object-like macro, the best we can do is to + // highlight the range. If this is a function-like macro, we'd also like to + // highlight the arguments. + if (Begin == End && R.getEnd().isMacroID()) + End = SM.getInstantiationRange(R.getEnd()).second; + + unsigned StartLineNo = SM.getInstantiationLineNumber(Begin); + if (StartLineNo > LineNo || SM.getFileID(Begin) != FID) + return; // No intersection. + + unsigned EndLineNo = SM.getInstantiationLineNumber(End); + if (EndLineNo < LineNo || SM.getFileID(End) != FID) + return; // No intersection. + + // Compute the column number of the start. + unsigned StartColNo = 0; + if (StartLineNo == LineNo) { + StartColNo = SM.getInstantiationColumnNumber(Begin); + if (StartColNo) --StartColNo; // Zero base the col #. + } + + // Compute the column number of the end. + unsigned EndColNo = CaretLine.size(); + if (EndLineNo == LineNo) { + EndColNo = SM.getInstantiationColumnNumber(End); + if (EndColNo) { + --EndColNo; // Zero base the col #. + + // Add in the length of the token, so that we cover multi-char tokens if + // this is a token range. + if (R.isTokenRange()) + EndColNo += Lexer::MeasureTokenLength(End, SM, *LangOpts); + } else { + EndColNo = CaretLine.size(); + } + } + + assert(StartColNo <= EndColNo && "Invalid range!"); + + // Check that a token range does not highlight only whitespace. + if (R.isTokenRange()) { + // Pick the first non-whitespace column. + while (StartColNo < SourceLine.size() && + (SourceLine[StartColNo] == ' ' || SourceLine[StartColNo] == '\t')) + ++StartColNo; + + // Pick the last non-whitespace column. + if (EndColNo > SourceLine.size()) + EndColNo = SourceLine.size(); + while (EndColNo-1 && + (SourceLine[EndColNo-1] == ' ' || SourceLine[EndColNo-1] == '\t')) + --EndColNo; + + // If the start/end passed each other, then we are trying to highlight a range + // that just exists in whitespace, which must be some sort of other bug. + assert(StartColNo <= EndColNo && "Trying to highlight whitespace??"); + } + + // Fill the range with ~'s. + for (unsigned i = StartColNo; i < EndColNo; ++i) + CaretLine[i] = '~'; +} + +/// \brief When the source code line we want to print is too long for +/// the terminal, select the "interesting" region. +static void SelectInterestingSourceRegion(std::string &SourceLine, + std::string &CaretLine, + std::string &FixItInsertionLine, + unsigned EndOfCaretToken, + unsigned Columns) { + unsigned MaxSize = std::max(SourceLine.size(), + std::max(CaretLine.size(), + FixItInsertionLine.size())); + if (MaxSize > SourceLine.size()) + SourceLine.resize(MaxSize, ' '); + if (MaxSize > CaretLine.size()) + CaretLine.resize(MaxSize, ' '); + if (!FixItInsertionLine.empty() && MaxSize > FixItInsertionLine.size()) + FixItInsertionLine.resize(MaxSize, ' '); + + // Find the slice that we need to display the full caret line + // correctly. + unsigned CaretStart = 0, CaretEnd = CaretLine.size(); + for (; CaretStart != CaretEnd; ++CaretStart) + if (!isspace(CaretLine[CaretStart])) + break; + + for (; CaretEnd != CaretStart; --CaretEnd) + if (!isspace(CaretLine[CaretEnd - 1])) + break; + + // Make sure we don't chop the string shorter than the caret token + // itself. + if (CaretEnd < EndOfCaretToken) + CaretEnd = EndOfCaretToken; + + // If we have a fix-it line, make sure the slice includes all of the + // fix-it information. + if (!FixItInsertionLine.empty()) { + unsigned FixItStart = 0, FixItEnd = FixItInsertionLine.size(); + for (; FixItStart != FixItEnd; ++FixItStart) + if (!isspace(FixItInsertionLine[FixItStart])) + break; + + for (; FixItEnd != FixItStart; --FixItEnd) + if (!isspace(FixItInsertionLine[FixItEnd - 1])) + break; + + if (FixItStart < CaretStart) + CaretStart = FixItStart; + if (FixItEnd > CaretEnd) + CaretEnd = FixItEnd; + } + + // CaretLine[CaretStart, CaretEnd) contains all of the interesting + // parts of the caret line. While this slice is smaller than the + // number of columns we have, try to grow the slice to encompass + // more context. + + // If the end of the interesting region comes before we run out of + // space in the terminal, start at the beginning of the line. + if (Columns > 3 && CaretEnd < Columns - 3) + CaretStart = 0; + + unsigned TargetColumns = Columns; + if (TargetColumns > 8) + TargetColumns -= 8; // Give us extra room for the ellipses. + unsigned SourceLength = SourceLine.size(); + while ((CaretEnd - CaretStart) < TargetColumns) { + bool ExpandedRegion = false; + // Move the start of the interesting region left until we've + // pulled in something else interesting. + if (CaretStart == 1) + CaretStart = 0; + else if (CaretStart > 1) { + unsigned NewStart = CaretStart - 1; + + // Skip over any whitespace we see here; we're looking for + // another bit of interesting text. + while (NewStart && isspace(SourceLine[NewStart])) + --NewStart; + + // Skip over this bit of "interesting" text. + while (NewStart && !isspace(SourceLine[NewStart])) + --NewStart; + + // Move up to the non-whitespace character we just saw. + if (NewStart) + ++NewStart; + + // If we're still within our limit, update the starting + // position within the source/caret line. + if (CaretEnd - NewStart <= TargetColumns) { + CaretStart = NewStart; + ExpandedRegion = true; + } + } + + // Move the end of the interesting region right until we've + // pulled in something else interesting. + if (CaretEnd != SourceLength) { + assert(CaretEnd < SourceLength && "Unexpected caret position!"); + unsigned NewEnd = CaretEnd; + + // Skip over any whitespace we see here; we're looking for + // another bit of interesting text. + while (NewEnd != SourceLength && isspace(SourceLine[NewEnd - 1])) + ++NewEnd; + + // Skip over this bit of "interesting" text. + while (NewEnd != SourceLength && !isspace(SourceLine[NewEnd - 1])) + ++NewEnd; + + if (NewEnd - CaretStart <= TargetColumns) { + CaretEnd = NewEnd; + ExpandedRegion = true; + } + } + + if (!ExpandedRegion) + break; + } + + // [CaretStart, CaretEnd) is the slice we want. Update the various + // output lines to show only this slice, with two-space padding + // before the lines so that it looks nicer. + if (CaretEnd < SourceLine.size()) + SourceLine.replace(CaretEnd, std::string::npos, "..."); + if (CaretEnd < CaretLine.size()) + CaretLine.erase(CaretEnd, std::string::npos); + if (FixItInsertionLine.size() > CaretEnd) + FixItInsertionLine.erase(CaretEnd, std::string::npos); + + if (CaretStart > 2) { + SourceLine.replace(0, CaretStart, " ..."); + CaretLine.replace(0, CaretStart, " "); + if (FixItInsertionLine.size() >= CaretStart) + FixItInsertionLine.replace(0, CaretStart, " "); + } +} + +void TextDiagnosticPrinter::EmitCaretDiagnostic(SourceLocation Loc, + CharSourceRange *Ranges, + unsigned NumRanges, + const SourceManager &SM, + const FixItHint *Hints, + unsigned NumHints, + unsigned Columns, + unsigned OnMacroInst, + unsigned MacroSkipStart, + unsigned MacroSkipEnd) { + assert(LangOpts && "Unexpected diagnostic outside source file processing"); + assert(!Loc.isInvalid() && "must have a valid source location here"); + + // If this is a macro ID, first emit information about where this was + // instantiated (recursively) then emit information about where the token was + // spelled from. + if (!Loc.isFileID()) { + // Whether to suppress printing this macro instantiation. + bool Suppressed + = OnMacroInst >= MacroSkipStart && OnMacroInst < MacroSkipEnd; + + + SourceLocation OneLevelUp = SM.getImmediateInstantiationRange(Loc).first; + // FIXME: Map ranges? + EmitCaretDiagnostic(OneLevelUp, Ranges, NumRanges, SM, 0, 0, Columns, + OnMacroInst + 1, MacroSkipStart, MacroSkipEnd); + + // Map the location. + Loc = SM.getImmediateSpellingLoc(Loc); + + // Map the ranges. + for (unsigned i = 0; i != NumRanges; ++i) { + CharSourceRange &R = Ranges[i]; + SourceLocation S = R.getBegin(), E = R.getEnd(); + if (S.isMacroID()) + R.setBegin(SM.getImmediateSpellingLoc(S)); + if (E.isMacroID()) + R.setEnd(SM.getImmediateSpellingLoc(E)); + } + + if (!Suppressed) { + // Get the pretty name, according to #line directives etc. + PresumedLoc PLoc = SM.getPresumedLoc(Loc); + + // If this diagnostic is not in the main file, print out the + // "included from" lines. + if (LastWarningLoc != PLoc.getIncludeLoc()) { + LastWarningLoc = PLoc.getIncludeLoc(); + PrintIncludeStack(LastWarningLoc, SM); + } + + if (DiagOpts->ShowLocation) { + // Emit the file/line/column that this expansion came from. + OS << PLoc.getFilename() << ':' << PLoc.getLine() << ':'; + if (DiagOpts->ShowColumn) + OS << PLoc.getColumn() << ':'; + OS << ' '; + } + OS << "note: instantiated from:\n"; + + EmitCaretDiagnostic(Loc, Ranges, NumRanges, SM, Hints, NumHints, Columns, + OnMacroInst + 1, MacroSkipStart, MacroSkipEnd); + return; + } + + if (OnMacroInst == MacroSkipStart) { + // Tell the user that we've skipped contexts. + OS << "note: (skipping " << (MacroSkipEnd - MacroSkipStart) + << " contexts in backtrace; use -fmacro-backtrace-limit=0 to see " + "all)\n"; + } + + return; + } + + // Decompose the location into a FID/Offset pair. + std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); + FileID FID = LocInfo.first; + unsigned FileOffset = LocInfo.second; + + // Get information about the buffer it points into. + bool Invalid = false; + const char *BufStart = SM.getBufferData(FID, &Invalid).data(); + if (Invalid) + return; + + unsigned ColNo = SM.getColumnNumber(FID, FileOffset); + unsigned CaretEndColNo + = ColNo + Lexer::MeasureTokenLength(Loc, SM, *LangOpts); + + // Rewind from the current position to the start of the line. + const char *TokPtr = BufStart+FileOffset; + const char *LineStart = TokPtr-ColNo+1; // Column # is 1-based. + + + // Compute the line end. Scan forward from the error position to the end of + // the line. + const char *LineEnd = TokPtr; + while (*LineEnd != '\n' && *LineEnd != '\r' && *LineEnd != '\0') + ++LineEnd; + + // FIXME: This shouldn't be necessary, but the CaretEndColNo can extend past + // the source line length as currently being computed. See + // test/Misc/message-length.c. + CaretEndColNo = std::min(CaretEndColNo, unsigned(LineEnd - LineStart)); + + // Copy the line of code into an std::string for ease of manipulation. + std::string SourceLine(LineStart, LineEnd); + + // Create a line for the caret that is filled with spaces that is the same + // length as the line of source code. + std::string CaretLine(LineEnd-LineStart, ' '); + + // Highlight all of the characters covered by Ranges with ~ characters. + if (NumRanges) { + unsigned LineNo = SM.getLineNumber(FID, FileOffset); + + for (unsigned i = 0, e = NumRanges; i != e; ++i) + HighlightRange(Ranges[i], SM, LineNo, FID, CaretLine, SourceLine); + } + + // Next, insert the caret itself. + if (ColNo-1 < CaretLine.size()) + CaretLine[ColNo-1] = '^'; + else + CaretLine.push_back('^'); + + // Scan the source line, looking for tabs. If we find any, manually expand + // them to spaces and update the CaretLine to match. + for (unsigned i = 0; i != SourceLine.size(); ++i) { + if (SourceLine[i] != '\t') continue; + + // Replace this tab with at least one space. + SourceLine[i] = ' '; + + // Compute the number of spaces we need to insert. + unsigned TabStop = DiagOpts->TabStop; + assert(0 < TabStop && TabStop <= DiagnosticOptions::MaxTabStop && + "Invalid -ftabstop value"); + unsigned NumSpaces = ((i+TabStop)/TabStop * TabStop) - (i+1); + assert(NumSpaces < TabStop && "Invalid computation of space amt"); + + // Insert spaces into the SourceLine. + SourceLine.insert(i+1, NumSpaces, ' '); + + // Insert spaces or ~'s into CaretLine. + CaretLine.insert(i+1, NumSpaces, CaretLine[i] == '~' ? '~' : ' '); + } + + // If we are in -fdiagnostics-print-source-range-info mode, we are trying to + // produce easily machine parsable output. Add a space before the source line + // and the caret to make it trivial to tell the main diagnostic line from what + // the user is intended to see. + if (DiagOpts->ShowSourceRanges) { + SourceLine = ' ' + SourceLine; + CaretLine = ' ' + CaretLine; + } + + std::string FixItInsertionLine; + if (NumHints && DiagOpts->ShowFixits) { + for (const FixItHint *Hint = Hints, *LastHint = Hints + NumHints; + Hint != LastHint; ++Hint) { + if (!Hint->CodeToInsert.empty()) { + // We have an insertion hint. Determine whether the inserted + // code is on the same line as the caret. + std::pair<FileID, unsigned> HintLocInfo + = SM.getDecomposedInstantiationLoc(Hint->RemoveRange.getBegin()); + if (SM.getLineNumber(HintLocInfo.first, HintLocInfo.second) == + SM.getLineNumber(FID, FileOffset)) { + // Insert the new code into the line just below the code + // that the user wrote. + unsigned HintColNo + = SM.getColumnNumber(HintLocInfo.first, HintLocInfo.second); + unsigned LastColumnModified + = HintColNo - 1 + Hint->CodeToInsert.size(); + if (LastColumnModified > FixItInsertionLine.size()) + FixItInsertionLine.resize(LastColumnModified, ' '); + std::copy(Hint->CodeToInsert.begin(), Hint->CodeToInsert.end(), + FixItInsertionLine.begin() + HintColNo - 1); + } else { + FixItInsertionLine.clear(); + break; + } + } + } + // Now that we have the entire fixit line, expand the tabs in it. + // Since we don't want to insert spaces in the middle of a word, + // find each word and the column it should line up with and insert + // spaces until they match. + if (!FixItInsertionLine.empty()) { + unsigned FixItPos = 0; + unsigned LinePos = 0; + unsigned TabExpandedCol = 0; + unsigned LineLength = LineEnd - LineStart; + + while (FixItPos < FixItInsertionLine.size() && LinePos < LineLength) { + // Find the next word in the FixIt line. + while (FixItPos < FixItInsertionLine.size() && + FixItInsertionLine[FixItPos] == ' ') + ++FixItPos; + unsigned CharDistance = FixItPos - TabExpandedCol; + + // Walk forward in the source line, keeping track of + // the tab-expanded column. + for (unsigned I = 0; I < CharDistance; ++I, ++LinePos) + if (LinePos >= LineLength || LineStart[LinePos] != '\t') + ++TabExpandedCol; + else + TabExpandedCol = + (TabExpandedCol/DiagOpts->TabStop + 1) * DiagOpts->TabStop; + + // Adjust the fixit line to match this column. + FixItInsertionLine.insert(FixItPos, TabExpandedCol-FixItPos, ' '); + FixItPos = TabExpandedCol; + + // Walk to the end of the word. + while (FixItPos < FixItInsertionLine.size() && + FixItInsertionLine[FixItPos] != ' ') + ++FixItPos; + } + } + } + + // If the source line is too long for our terminal, select only the + // "interesting" source region within that line. + if (Columns && SourceLine.size() > Columns) + SelectInterestingSourceRegion(SourceLine, CaretLine, FixItInsertionLine, + CaretEndColNo, Columns); + + // Finally, remove any blank spaces from the end of CaretLine. + while (CaretLine[CaretLine.size()-1] == ' ') + CaretLine.erase(CaretLine.end()-1); + + // Emit what we have computed. + OS << SourceLine << '\n'; + + if (DiagOpts->ShowColors) + OS.changeColor(caretColor, true); + OS << CaretLine << '\n'; + if (DiagOpts->ShowColors) + OS.resetColor(); + + if (!FixItInsertionLine.empty()) { + if (DiagOpts->ShowColors) + // Print fixit line in color + OS.changeColor(fixitColor, false); + if (DiagOpts->ShowSourceRanges) + OS << ' '; + OS << FixItInsertionLine << '\n'; + if (DiagOpts->ShowColors) + OS.resetColor(); + } + + if (DiagOpts->ShowParseableFixits) { + + // We follow FixItRewriter's example in not (yet) handling + // fix-its in macros. + bool BadApples = false; + for (const FixItHint *Hint = Hints; Hint != Hints + NumHints; ++Hint) { + if (Hint->RemoveRange.isInvalid() || + Hint->RemoveRange.getBegin().isMacroID() || + Hint->RemoveRange.getEnd().isMacroID()) { + BadApples = true; + break; + } + } + + if (!BadApples) { + for (const FixItHint *Hint = Hints; Hint != Hints + NumHints; ++Hint) { + + SourceLocation B = Hint->RemoveRange.getBegin(); + SourceLocation E = Hint->RemoveRange.getEnd(); + + std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(B); + std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(E); + + // Adjust for token ranges. + if (Hint->RemoveRange.isTokenRange()) + EInfo.second += Lexer::MeasureTokenLength(E, SM, *LangOpts); + + // We specifically do not do word-wrapping or tab-expansion here, + // because this is supposed to be easy to parse. + OS << "fix-it:\""; + OS.write_escaped(SM.getPresumedLoc(B).getFilename()); + OS << "\":{" << SM.getLineNumber(BInfo.first, BInfo.second) + << ':' << SM.getColumnNumber(BInfo.first, BInfo.second) + << '-' << SM.getLineNumber(EInfo.first, EInfo.second) + << ':' << SM.getColumnNumber(EInfo.first, EInfo.second) + << "}:\""; + OS.write_escaped(Hint->CodeToInsert); + OS << "\"\n"; + } + } + } +} + +/// \brief Skip over whitespace in the string, starting at the given +/// index. +/// +/// \returns The index of the first non-whitespace character that is +/// greater than or equal to Idx or, if no such character exists, +/// returns the end of the string. +static unsigned skipWhitespace(unsigned Idx, + const llvm::SmallVectorImpl<char> &Str, + unsigned Length) { + while (Idx < Length && isspace(Str[Idx])) + ++Idx; + return Idx; +} + +/// \brief If the given character is the start of some kind of +/// balanced punctuation (e.g., quotes or parentheses), return the +/// character that will terminate the punctuation. +/// +/// \returns The ending punctuation character, if any, or the NULL +/// character if the input character does not start any punctuation. +static inline char findMatchingPunctuation(char c) { + switch (c) { + case '\'': return '\''; + case '`': return '\''; + case '"': return '"'; + case '(': return ')'; + case '[': return ']'; + case '{': return '}'; + default: break; + } + + return 0; +} + +/// \brief Find the end of the word starting at the given offset +/// within a string. +/// +/// \returns the index pointing one character past the end of the +/// word. +static unsigned findEndOfWord(unsigned Start, + const llvm::SmallVectorImpl<char> &Str, + unsigned Length, unsigned Column, + unsigned Columns) { + assert(Start < Str.size() && "Invalid start position!"); + unsigned End = Start + 1; + + // If we are already at the end of the string, take that as the word. + if (End == Str.size()) + return End; + + // Determine if the start of the string is actually opening + // punctuation, e.g., a quote or parentheses. + char EndPunct = findMatchingPunctuation(Str[Start]); + if (!EndPunct) { + // This is a normal word. Just find the first space character. + while (End < Length && !isspace(Str[End])) + ++End; + return End; + } + + // We have the start of a balanced punctuation sequence (quotes, + // parentheses, etc.). Determine the full sequence is. + llvm::SmallString<16> PunctuationEndStack; + PunctuationEndStack.push_back(EndPunct); + while (End < Length && !PunctuationEndStack.empty()) { + if (Str[End] == PunctuationEndStack.back()) + PunctuationEndStack.pop_back(); + else if (char SubEndPunct = findMatchingPunctuation(Str[End])) + PunctuationEndStack.push_back(SubEndPunct); + + ++End; + } + + // Find the first space character after the punctuation ended. + while (End < Length && !isspace(Str[End])) + ++End; + + unsigned PunctWordLength = End - Start; + if (// If the word fits on this line + Column + PunctWordLength <= Columns || + // ... or the word is "short enough" to take up the next line + // without too much ugly white space + PunctWordLength < Columns/3) + return End; // Take the whole thing as a single "word". + + // The whole quoted/parenthesized string is too long to print as a + // single "word". Instead, find the "word" that starts just after + // the punctuation and use that end-point instead. This will recurse + // until it finds something small enough to consider a word. + return findEndOfWord(Start + 1, Str, Length, Column + 1, Columns); +} + +/// \brief Print the given string to a stream, word-wrapping it to +/// some number of columns in the process. +/// +/// \brief OS the stream to which the word-wrapping string will be +/// emitted. +/// +/// \brief Str the string to word-wrap and output. +/// +/// \brief Columns the number of columns to word-wrap to. +/// +/// \brief Column the column number at which the first character of \p +/// Str will be printed. This will be non-zero when part of the first +/// line has already been printed. +/// +/// \brief Indentation the number of spaces to indent any lines beyond +/// the first line. +/// +/// \returns true if word-wrapping was required, or false if the +/// string fit on the first line. +static bool PrintWordWrapped(llvm::raw_ostream &OS, + const llvm::SmallVectorImpl<char> &Str, + unsigned Columns, + unsigned Column = 0, + unsigned Indentation = WordWrapIndentation) { + unsigned Length = Str.size(); + + // If there is a newline in this message somewhere, find that + // newline and split the message into the part before the newline + // (which will be word-wrapped) and the part from the newline one + // (which will be emitted unchanged). + for (unsigned I = 0; I != Length; ++I) + if (Str[I] == '\n') { + Length = I; + break; + } + + // The string used to indent each line. + llvm::SmallString<16> IndentStr; + IndentStr.assign(Indentation, ' '); + bool Wrapped = false; + for (unsigned WordStart = 0, WordEnd; WordStart < Length; + WordStart = WordEnd) { + // Find the beginning of the next word. + WordStart = skipWhitespace(WordStart, Str, Length); + if (WordStart == Length) + break; + + // Find the end of this word. + WordEnd = findEndOfWord(WordStart, Str, Length, Column, Columns); + + // Does this word fit on the current line? + unsigned WordLength = WordEnd - WordStart; + if (Column + WordLength < Columns) { + // This word fits on the current line; print it there. + if (WordStart) { + OS << ' '; + Column += 1; + } + OS.write(&Str[WordStart], WordLength); + Column += WordLength; + continue; + } + + // This word does not fit on the current line, so wrap to the next + // line. + OS << '\n'; + OS.write(&IndentStr[0], Indentation); + OS.write(&Str[WordStart], WordLength); + Column = Indentation + WordLength; + Wrapped = true; + } + + if (Length == Str.size()) + return Wrapped; // We're done. + + // There is a newline in the message, followed by something that + // will not be word-wrapped. Print that. + OS.write(&Str[Length], Str.size() - Length); + return true; +} + +void TextDiagnosticPrinter::HandleDiagnostic(Diagnostic::Level Level, + const DiagnosticInfo &Info) { + // Keeps track of the the starting position of the location + // information (e.g., "foo.c:10:4:") that precedes the error + // message. We use this information to determine how long the + // file+line+column number prefix is. + uint64_t StartOfLocationInfo = OS.tell(); + + if (!Prefix.empty()) + OS << Prefix << ": "; + + // If the location is specified, print out a file/line/col and include trace + // if enabled. + if (Info.getLocation().isValid()) { + const SourceManager &SM = Info.getLocation().getManager(); + PresumedLoc PLoc = SM.getPresumedLoc(Info.getLocation()); + unsigned LineNo = PLoc.getLine(); + + // First, if this diagnostic is not in the main file, print out the + // "included from" lines. + if (LastWarningLoc != PLoc.getIncludeLoc()) { + LastWarningLoc = PLoc.getIncludeLoc(); + PrintIncludeStack(LastWarningLoc, SM); + StartOfLocationInfo = OS.tell(); + } + + // Compute the column number. + if (DiagOpts->ShowLocation) { + if (DiagOpts->ShowColors) + OS.changeColor(savedColor, true); + + // Emit a Visual Studio compatible line number syntax. + if (LangOpts && LangOpts->Microsoft) { + OS << PLoc.getFilename() << '(' << LineNo << ')'; + OS << " : "; + } else { + OS << PLoc.getFilename() << ':' << LineNo << ':'; + if (DiagOpts->ShowColumn) + if (unsigned ColNo = PLoc.getColumn()) + OS << ColNo << ':'; + } + if (DiagOpts->ShowSourceRanges && Info.getNumRanges()) { + FileID CaretFileID = + SM.getFileID(SM.getInstantiationLoc(Info.getLocation())); + bool PrintedRange = false; + + for (unsigned i = 0, e = Info.getNumRanges(); i != e; ++i) { + // Ignore invalid ranges. + if (!Info.getRange(i).isValid()) continue; + + SourceLocation B = Info.getRange(i).getBegin(); + SourceLocation E = Info.getRange(i).getEnd(); + B = SM.getInstantiationLoc(B); + E = SM.getInstantiationLoc(E); + + // If the End location and the start location are the same and are a + // macro location, then the range was something that came from a macro + // expansion or _Pragma. If this is an object-like macro, the best we + // can do is to highlight the range. If this is a function-like + // macro, we'd also like to highlight the arguments. + if (B == E && Info.getRange(i).getEnd().isMacroID()) + E = SM.getInstantiationRange(Info.getRange(i).getEnd()).second; + + std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(B); + std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(E); + + // If the start or end of the range is in another file, just discard + // it. + if (BInfo.first != CaretFileID || EInfo.first != CaretFileID) + continue; + + // Add in the length of the token, so that we cover multi-char tokens. + unsigned TokSize = 0; + if (Info.getRange(i).isTokenRange()) + TokSize = Lexer::MeasureTokenLength(E, SM, *LangOpts); + + OS << '{' << SM.getLineNumber(BInfo.first, BInfo.second) << ':' + << SM.getColumnNumber(BInfo.first, BInfo.second) << '-' + << SM.getLineNumber(EInfo.first, EInfo.second) << ':' + << (SM.getColumnNumber(EInfo.first, EInfo.second)+TokSize) << '}'; + PrintedRange = true; + } + + if (PrintedRange) + OS << ':'; + } + OS << ' '; + if (DiagOpts->ShowColors) + OS.resetColor(); + } + } + + if (DiagOpts->ShowColors) { + // Print diagnostic category in bold and color + switch (Level) { + case Diagnostic::Ignored: assert(0 && "Invalid diagnostic type"); + case Diagnostic::Note: OS.changeColor(noteColor, true); break; + case Diagnostic::Warning: OS.changeColor(warningColor, true); break; + case Diagnostic::Error: OS.changeColor(errorColor, true); break; + case Diagnostic::Fatal: OS.changeColor(fatalColor, true); break; + } + } + + switch (Level) { + case Diagnostic::Ignored: assert(0 && "Invalid diagnostic type"); + case Diagnostic::Note: OS << "note: "; break; + case Diagnostic::Warning: OS << "warning: "; break; + case Diagnostic::Error: OS << "error: "; break; + case Diagnostic::Fatal: OS << "fatal error: "; break; + } + + if (DiagOpts->ShowColors) + OS.resetColor(); + + llvm::SmallString<100> OutStr; + Info.FormatDiagnostic(OutStr); + + std::string OptionName; + if (DiagOpts->ShowOptionNames) { + if (const char *Opt = Diagnostic::getWarningOptionForDiag(Info.getID())) { + OptionName = "-W"; + OptionName += Opt; + } else if (Info.getID() == diag::fatal_too_many_errors) { + OptionName = "-ferror-limit="; + } else { + // If the diagnostic is an extension diagnostic and not enabled by default + // then it must have been turned on with -pedantic. + bool EnabledByDefault; + if (Diagnostic::isBuiltinExtensionDiag(Info.getID(), EnabledByDefault) && + !EnabledByDefault) + OptionName = "-pedantic"; + } + } + + // If the user wants to see category information, include it too. + unsigned DiagCategory = 0; + if (DiagOpts->ShowCategories) + DiagCategory = Diagnostic::getCategoryNumberForDiag(Info.getID()); + + // If there is any categorization information, include it. + if (!OptionName.empty() || DiagCategory != 0) { + bool NeedsComma = false; + OutStr += " ["; + + if (!OptionName.empty()) { + OutStr += OptionName; + NeedsComma = true; + } + + if (DiagCategory) { + if (NeedsComma) OutStr += ','; + if (DiagOpts->ShowCategories == 1) + OutStr += llvm::utostr(DiagCategory); + else { + assert(DiagOpts->ShowCategories == 2 && "Invalid ShowCategories value"); + OutStr += Diagnostic::getCategoryNameFromID(DiagCategory); + } + } + + OutStr += "]"; + } + + + if (DiagOpts->ShowColors) { + // Print warnings, errors and fatal errors in bold, no color + switch (Level) { + case Diagnostic::Warning: OS.changeColor(savedColor, true); break; + case Diagnostic::Error: OS.changeColor(savedColor, true); break; + case Diagnostic::Fatal: OS.changeColor(savedColor, true); break; + default: break; //don't bold notes + } + } + + if (DiagOpts->MessageLength) { + // We will be word-wrapping the error message, so compute the + // column number where we currently are (after printing the + // location information). + unsigned Column = OS.tell() - StartOfLocationInfo; + PrintWordWrapped(OS, OutStr, DiagOpts->MessageLength, Column); + } else { + OS.write(OutStr.begin(), OutStr.size()); + } + OS << '\n'; + if (DiagOpts->ShowColors) + OS.resetColor(); + + // If caret diagnostics are enabled and we have location, we want to + // emit the caret. However, we only do this if the location moved + // from the last diagnostic, if the last diagnostic was a note that + // was part of a different warning or error diagnostic, or if the + // diagnostic has ranges. We don't want to emit the same caret + // multiple times if one loc has multiple diagnostics. + if (DiagOpts->ShowCarets && Info.getLocation().isValid() && + ((LastLoc != Info.getLocation()) || Info.getNumRanges() || + (LastCaretDiagnosticWasNote && Level != Diagnostic::Note) || + Info.getNumFixItHints())) { + // Cache the LastLoc, it allows us to omit duplicate source/caret spewage. + LastLoc = Info.getLocation(); + LastCaretDiagnosticWasNote = (Level == Diagnostic::Note); + + // Get the ranges into a local array we can hack on. + CharSourceRange Ranges[20]; + unsigned NumRanges = Info.getNumRanges(); + assert(NumRanges < 20 && "Out of space"); + for (unsigned i = 0; i != NumRanges; ++i) + Ranges[i] = Info.getRange(i); + + unsigned NumHints = Info.getNumFixItHints(); + for (unsigned i = 0; i != NumHints; ++i) { + const FixItHint &Hint = Info.getFixItHint(i); + if (Hint.RemoveRange.isValid()) { + assert(NumRanges < 20 && "Out of space"); + Ranges[NumRanges++] = Hint.RemoveRange; + } + } + + unsigned MacroInstSkipStart = 0, MacroInstSkipEnd = 0; + if (DiagOpts && DiagOpts->MacroBacktraceLimit && !LastLoc.isFileID()) { + // Compute the length of the macro-instantiation backtrace, so that we + // can establish which steps in the macro backtrace we'll skip. + SourceLocation Loc = LastLoc; + unsigned Depth = 0; + do { + ++Depth; + Loc = LastLoc.getManager().getImmediateInstantiationRange(Loc).first; + } while (!Loc.isFileID()); + + if (Depth > DiagOpts->MacroBacktraceLimit) { + MacroInstSkipStart = DiagOpts->MacroBacktraceLimit / 2 + + DiagOpts->MacroBacktraceLimit % 2; + MacroInstSkipEnd = Depth - DiagOpts->MacroBacktraceLimit / 2; + } + } + + EmitCaretDiagnostic(LastLoc, Ranges, NumRanges, LastLoc.getManager(), + Info.getFixItHints(), + Info.getNumFixItHints(), + DiagOpts->MessageLength, + 0, MacroInstSkipStart, MacroInstSkipEnd); + } + + OS.flush(); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/TypeXML.cpp b/contrib/llvm/tools/clang/lib/Frontend/TypeXML.cpp new file mode 100644 index 0000000..be9db42 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/TypeXML.cpp @@ -0,0 +1,119 @@ +//===--- DocumentXML.cpp - XML document for ASTs --------------------------===// +// +// 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 XML document class, which provides the means to +// dump out the AST in a XML form that exposes type details and other fields. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/DocumentXML.h" +#include "clang/AST/TypeVisitor.h" +#include "clang/AST/Type.h" +#include "clang/AST/Decl.h" + +namespace clang { + namespace XML { + namespace { + +//--------------------------------------------------------- +class TypeWriter : public TypeVisitor<TypeWriter> { + DocumentXML& Doc; + +public: + TypeWriter(DocumentXML& doc) : Doc(doc) {} + +#define NODE_XML( CLASS, NAME ) \ + void Visit##CLASS(CLASS* T) { \ + Doc.addSubNode(NAME); + +#define ID_ATTRIBUTE_XML // done by the Document class itself +#define ATTRIBUTE_XML( FN, NAME ) Doc.addAttribute(NAME, T->FN); +#define TYPE_ATTRIBUTE_XML( FN ) ATTRIBUTE_XML(FN, "type") +#define CONTEXT_ATTRIBUTE_XML( FN ) ATTRIBUTE_XML(FN, "context") +#define ATTRIBUTE_OPT_XML( FN, NAME ) Doc.addAttributeOptional(NAME, T->FN); + +#define ATTRIBUTE_ENUM_XML( FN, NAME ) \ + { \ + const char* pAttributeName = NAME; \ + const bool optional = false; \ + switch (T->FN) { \ + default: assert(0 && "unknown enum value"); + +#define ATTRIBUTE_ENUM_OPT_XML( FN, NAME ) \ + { \ + const char* pAttributeName = NAME; \ + const bool optional = true; \ + switch (T->FN) { \ + default: assert(0 && "unknown enum value"); + +#define ENUM_XML( VALUE, NAME ) case VALUE: if ((!optional) || NAME[0]) Doc.addAttribute(pAttributeName, NAME); break; +#define END_ENUM_XML } } +#define END_NODE_XML } + +#include "clang/Frontend/TypeXML.def" + +}; + +//--------------------------------------------------------- + } // anon clang + } // NS XML + +//--------------------------------------------------------- +class DocumentXML::TypeAdder : public TypeVisitor<DocumentXML::TypeAdder> { + DocumentXML& Doc; + + void addIfType(const Type* pType) { + Doc.addTypeRecursively(pType); + } + + void addIfType(const QualType& pType) { + Doc.addTypeRecursively(pType); + } + + template<class T> void addIfType(T) {} + +public: + TypeAdder(DocumentXML& doc) : Doc(doc) {} + +#define NODE_XML( CLASS, NAME ) \ + void Visit##CLASS(CLASS* T) \ + { + +#define ID_ATTRIBUTE_XML +#define TYPE_ATTRIBUTE_XML( FN ) Doc.addTypeRecursively(T->FN); +#define CONTEXT_ATTRIBUTE_XML( FN ) +#define ATTRIBUTE_XML( FN, NAME ) addIfType(T->FN); +#define ATTRIBUTE_OPT_XML( FN, NAME ) +#define ATTRIBUTE_ENUM_XML( FN, NAME ) +#define ATTRIBUTE_ENUM_OPT_XML( FN, NAME ) +#define ENUM_XML( VALUE, NAME ) +#define END_ENUM_XML +#define END_NODE_XML } + +#include "clang/Frontend/TypeXML.def" +}; + +//--------------------------------------------------------- +void DocumentXML::addParentTypes(const Type* pType) { + TypeAdder(*this).Visit(const_cast<Type*>(pType)); +} + +//--------------------------------------------------------- +void DocumentXML::writeTypeToXML(const Type* pType) { + XML::TypeWriter(*this).Visit(const_cast<Type*>(pType)); +} + +//--------------------------------------------------------- +void DocumentXML::writeTypeToXML(const QualType& pType) { + XML::TypeWriter(*this).VisitQualType(const_cast<QualType*>(&pType)); +} + +//--------------------------------------------------------- +} // NS clang + diff --git a/contrib/llvm/tools/clang/lib/Frontend/VerifyDiagnosticsClient.cpp b/contrib/llvm/tools/clang/lib/Frontend/VerifyDiagnosticsClient.cpp new file mode 100644 index 0000000..31eb28f --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/VerifyDiagnosticsClient.cpp @@ -0,0 +1,521 @@ +//===--- VerifyDiagnosticsClient.cpp - Verifying Diagnostic Client --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This is a concrete diagnostic client, which buffers the diagnostic messages. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/VerifyDiagnosticsClient.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/TextDiagnosticBuffer.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/Regex.h" +#include "llvm/Support/raw_ostream.h" +using namespace clang; + +VerifyDiagnosticsClient::VerifyDiagnosticsClient(Diagnostic &_Diags, + DiagnosticClient *_Primary) + : Diags(_Diags), PrimaryClient(_Primary), + Buffer(new TextDiagnosticBuffer()), CurrentPreprocessor(0), NumErrors(0) { +} + +VerifyDiagnosticsClient::~VerifyDiagnosticsClient() { + CheckDiagnostics(); +} + +// DiagnosticClient interface. + +void VerifyDiagnosticsClient::BeginSourceFile(const LangOptions &LangOpts, + const Preprocessor *PP) { + // FIXME: Const hack, we screw up the preprocessor but in practice its ok + // because it doesn't get reused. It would be better if we could make a copy + // though. + CurrentPreprocessor = const_cast<Preprocessor*>(PP); + + PrimaryClient->BeginSourceFile(LangOpts, PP); +} + +void VerifyDiagnosticsClient::EndSourceFile() { + CheckDiagnostics(); + + PrimaryClient->EndSourceFile(); + + CurrentPreprocessor = 0; +} + +void VerifyDiagnosticsClient::HandleDiagnostic(Diagnostic::Level DiagLevel, + const DiagnosticInfo &Info) { + // Send the diagnostic to the buffer, we will check it once we reach the end + // of the source file (or are destructed). + Buffer->HandleDiagnostic(DiagLevel, Info); +} + +// FIXME: It would be nice to just get this from the primary diagnostic client +// or something. +bool VerifyDiagnosticsClient::HadErrors() { + CheckDiagnostics(); + + return NumErrors != 0; +} + +//===----------------------------------------------------------------------===// +// Checking diagnostics implementation. +//===----------------------------------------------------------------------===// + +typedef TextDiagnosticBuffer::DiagList DiagList; +typedef TextDiagnosticBuffer::const_iterator const_diag_iterator; + +namespace { + +/// Directive - Abstract class representing a parsed verify directive. +/// +class Directive { +public: + static Directive* Create(bool RegexKind, const SourceLocation &Location, + const std::string &Text, unsigned Count); +public: + SourceLocation Location; + const std::string Text; + unsigned Count; + + virtual ~Directive() { } + + // Returns true if directive text is valid. + // Otherwise returns false and populates E. + virtual bool isValid(std::string &Error) = 0; + + // Returns true on match. + virtual bool Match(const std::string &S) = 0; + +protected: + Directive(const SourceLocation &Location, const std::string &Text, + unsigned Count) + : Location(Location), Text(Text), Count(Count) { } + +private: + Directive(const Directive&); // DO NOT IMPLEMENT + void operator=(const Directive&); // DO NOT IMPLEMENT +}; + +/// StandardDirective - Directive with string matching. +/// +class StandardDirective : public Directive { +public: + StandardDirective(const SourceLocation &Location, const std::string &Text, + unsigned Count) + : Directive(Location, Text, Count) { } + + virtual bool isValid(std::string &Error) { + // all strings are considered valid; even empty ones + return true; + } + + virtual bool Match(const std::string &S) { + return S.find(Text) != std::string::npos || + Text.find(S) != std::string::npos; + } +}; + +/// RegexDirective - Directive with regular-expression matching. +/// +class RegexDirective : public Directive { +public: + RegexDirective(const SourceLocation &Location, const std::string &Text, + unsigned Count) + : Directive(Location, Text, Count), Regex(Text) { } + + virtual bool isValid(std::string &Error) { + if (Regex.isValid(Error)) + return true; + return false; + } + + virtual bool Match(const std::string &S) { + return Regex.match(S); + } + +private: + llvm::Regex Regex; +}; + +typedef std::vector<Directive*> DirectiveList; + +/// ExpectedData - owns directive objects and deletes on destructor. +/// +struct ExpectedData { + DirectiveList Errors; + DirectiveList Warnings; + DirectiveList Notes; + + ~ExpectedData() { + DirectiveList* Lists[] = { &Errors, &Warnings, &Notes, 0 }; + for (DirectiveList **PL = Lists; *PL; ++PL) { + DirectiveList * const L = *PL; + for (DirectiveList::iterator I = L->begin(), E = L->end(); I != E; ++I) + delete *I; + } + } +}; + +class ParseHelper +{ +public: + ParseHelper(const char *Begin, const char *End) + : Begin(Begin), End(End), C(Begin), P(Begin), PEnd(NULL) { } + + // Return true if string literal is next. + bool Next(llvm::StringRef S) { + P = C; + PEnd = C + S.size(); + if (PEnd > End) + return false; + return !memcmp(P, S.data(), S.size()); + } + + // Return true if number is next. + // Output N only if number is next. + bool Next(unsigned &N) { + unsigned TMP = 0; + P = C; + for (; P < End && P[0] >= '0' && P[0] <= '9'; ++P) { + TMP *= 10; + TMP += P[0] - '0'; + } + if (P == C) + return false; + PEnd = P; + N = TMP; + return true; + } + + // Return true if string literal is found. + // When true, P marks begin-position of S in content. + bool Search(llvm::StringRef S) { + P = std::search(C, End, S.begin(), S.end()); + PEnd = P + S.size(); + return P != End; + } + + // Advance 1-past previous next/search. + // Behavior is undefined if previous next/search failed. + bool Advance() { + C = PEnd; + return C < End; + } + + // Skip zero or more whitespace. + void SkipWhitespace() { + for (; C < End && isspace(*C); ++C) + ; + } + + // Return true if EOF reached. + bool Done() { + return !(C < End); + } + + const char * const Begin; // beginning of expected content + const char * const End; // end of expected content (1-past) + const char *C; // position of next char in content + const char *P; + +private: + const char *PEnd; // previous next/search subject end (1-past) +}; + +} // namespace anonymous + +/// ParseDirective - Go through the comment and see if it indicates expected +/// diagnostics. If so, then put them in the appropriate directive list. +/// +static void ParseDirective(const char *CommentStart, unsigned CommentLen, + ExpectedData &ED, Preprocessor &PP, + SourceLocation Pos) { + // A single comment may contain multiple directives. + for (ParseHelper PH(CommentStart, CommentStart+CommentLen); !PH.Done();) { + // search for token: expected + if (!PH.Search("expected")) + break; + PH.Advance(); + + // next token: - + if (!PH.Next("-")) + continue; + PH.Advance(); + + // next token: { error | warning | note } + DirectiveList* DL = NULL; + if (PH.Next("error")) + DL = &ED.Errors; + else if (PH.Next("warning")) + DL = &ED.Warnings; + else if (PH.Next("note")) + DL = &ED.Notes; + else + continue; + PH.Advance(); + + // default directive kind + bool RegexKind = false; + const char* KindStr = "string"; + + // next optional token: - + if (PH.Next("-re")) { + PH.Advance(); + RegexKind = true; + KindStr = "regex"; + } + + // skip optional whitespace + PH.SkipWhitespace(); + + // next optional token: positive integer + unsigned Count = 1; + if (PH.Next(Count)) + PH.Advance(); + + // skip optional whitespace + PH.SkipWhitespace(); + + // next token: {{ + if (!PH.Next("{{")) { + PP.Diag(Pos.getFileLocWithOffset(PH.C-PH.Begin), + diag::err_verify_missing_start) << KindStr; + continue; + } + PH.Advance(); + const char* const ContentBegin = PH.C; // mark content begin + + // search for token: }} + if (!PH.Search("}}")) { + PP.Diag(Pos.getFileLocWithOffset(PH.C-PH.Begin), + diag::err_verify_missing_end) << KindStr; + continue; + } + const char* const ContentEnd = PH.P; // mark content end + PH.Advance(); + + // build directive text; convert \n to newlines + std::string Text; + llvm::StringRef NewlineStr = "\\n"; + llvm::StringRef Content(ContentBegin, ContentEnd-ContentBegin); + size_t CPos = 0; + size_t FPos; + while ((FPos = Content.find(NewlineStr, CPos)) != llvm::StringRef::npos) { + Text += Content.substr(CPos, FPos-CPos); + Text += '\n'; + CPos = FPos + NewlineStr.size(); + } + if (Text.empty()) + Text.assign(ContentBegin, ContentEnd); + + // construct new directive + Directive *D = Directive::Create(RegexKind, Pos, Text, Count); + std::string Error; + if (D->isValid(Error)) + DL->push_back(D); + else { + PP.Diag(Pos.getFileLocWithOffset(ContentBegin-PH.Begin), + diag::err_verify_invalid_content) + << KindStr << Error; + } + } +} + +/// FindExpectedDiags - Lex the main source file to find all of the +// expected errors and warnings. +static void FindExpectedDiags(Preprocessor &PP, ExpectedData &ED) { + // Create a raw lexer to pull all the comments out of the main file. We don't + // want to look in #include'd headers for expected-error strings. + SourceManager &SM = PP.getSourceManager(); + FileID FID = SM.getMainFileID(); + if (SM.getMainFileID().isInvalid()) + return; + + // Create a lexer to lex all the tokens of the main file in raw mode. + const llvm::MemoryBuffer *FromFile = SM.getBuffer(FID); + Lexer RawLex(FID, FromFile, SM, PP.getLangOptions()); + + // Return comments as tokens, this is how we find expected diagnostics. + RawLex.SetCommentRetentionState(true); + + Token Tok; + Tok.setKind(tok::comment); + while (Tok.isNot(tok::eof)) { + RawLex.Lex(Tok); + if (!Tok.is(tok::comment)) continue; + + std::string Comment = PP.getSpelling(Tok); + if (Comment.empty()) continue; + + // Find all expected errors/warnings/notes. + ParseDirective(&Comment[0], Comment.size(), ED, PP, Tok.getLocation()); + }; +} + +/// PrintProblem - This takes a diagnostic map of the delta between expected and +/// seen diagnostics. If there's anything in it, then something unexpected +/// happened. Print the map out in a nice format and return "true". If the map +/// is empty and we're not going to print things, then return "false". +/// +static unsigned PrintProblem(Diagnostic &Diags, SourceManager *SourceMgr, + const_diag_iterator diag_begin, + const_diag_iterator diag_end, + const char *Kind, bool Expected) { + if (diag_begin == diag_end) return 0; + + llvm::SmallString<256> Fmt; + llvm::raw_svector_ostream OS(Fmt); + for (const_diag_iterator I = diag_begin, E = diag_end; I != E; ++I) { + if (I->first.isInvalid() || !SourceMgr) + OS << "\n (frontend)"; + else + OS << "\n Line " << SourceMgr->getInstantiationLineNumber(I->first); + OS << ": " << I->second; + } + + Diags.Report(diag::err_verify_inconsistent_diags) + << Kind << !Expected << OS.str(); + return std::distance(diag_begin, diag_end); +} + +static unsigned PrintProblem(Diagnostic &Diags, SourceManager *SourceMgr, + DirectiveList &DL, const char *Kind, + bool Expected) { + if (DL.empty()) + return 0; + + llvm::SmallString<256> Fmt; + llvm::raw_svector_ostream OS(Fmt); + for (DirectiveList::iterator I = DL.begin(), E = DL.end(); I != E; ++I) { + Directive& D = **I; + if (D.Location.isInvalid() || !SourceMgr) + OS << "\n (frontend)"; + else + OS << "\n Line " << SourceMgr->getInstantiationLineNumber(D.Location); + OS << ": " << D.Text; + } + + Diags.Report(diag::err_verify_inconsistent_diags) + << Kind << !Expected << OS.str(); + return DL.size(); +} + +/// CheckLists - Compare expected to seen diagnostic lists and return the +/// the difference between them. +/// +static unsigned CheckLists(Diagnostic &Diags, SourceManager &SourceMgr, + const char *Label, + DirectiveList &Left, + const_diag_iterator d2_begin, + const_diag_iterator d2_end) { + DirectiveList LeftOnly; + DiagList Right(d2_begin, d2_end); + + for (DirectiveList::iterator I = Left.begin(), E = Left.end(); I != E; ++I) { + Directive& D = **I; + unsigned LineNo1 = SourceMgr.getInstantiationLineNumber(D.Location); + + for (unsigned i = 0; i < D.Count; ++i) { + DiagList::iterator II, IE; + for (II = Right.begin(), IE = Right.end(); II != IE; ++II) { + unsigned LineNo2 = SourceMgr.getInstantiationLineNumber(II->first); + if (LineNo1 != LineNo2) + continue; + + const std::string &RightText = II->second; + if (D.Match(RightText)) + break; + } + if (II == IE) { + // Not found. + LeftOnly.push_back(*I); + } else { + // Found. The same cannot be found twice. + Right.erase(II); + } + } + } + // Now all that's left in Right are those that were not matched. + + return (PrintProblem(Diags, &SourceMgr, LeftOnly, Label, true) + + PrintProblem(Diags, &SourceMgr, Right.begin(), Right.end(), + Label, false)); +} + +/// CheckResults - This compares the expected results to those that +/// were actually reported. It emits any discrepencies. Return "true" if there +/// were problems. Return "false" otherwise. +/// +static unsigned CheckResults(Diagnostic &Diags, SourceManager &SourceMgr, + const TextDiagnosticBuffer &Buffer, + ExpectedData &ED) { + // We want to capture the delta between what was expected and what was + // seen. + // + // Expected \ Seen - set expected but not seen + // Seen \ Expected - set seen but not expected + unsigned NumProblems = 0; + + // See if there are error mismatches. + NumProblems += CheckLists(Diags, SourceMgr, "error", ED.Errors, + Buffer.err_begin(), Buffer.err_end()); + + // See if there are warning mismatches. + NumProblems += CheckLists(Diags, SourceMgr, "warning", ED.Warnings, + Buffer.warn_begin(), Buffer.warn_end()); + + // See if there are note mismatches. + NumProblems += CheckLists(Diags, SourceMgr, "note", ED.Notes, + Buffer.note_begin(), Buffer.note_end()); + + return NumProblems; +} + +void VerifyDiagnosticsClient::CheckDiagnostics() { + ExpectedData ED; + + // Ensure any diagnostics go to the primary client. + DiagnosticClient *CurClient = Diags.takeClient(); + Diags.setClient(PrimaryClient.get()); + + // If we have a preprocessor, scan the source for expected diagnostic + // markers. If not then any diagnostics are unexpected. + if (CurrentPreprocessor) { + FindExpectedDiags(*CurrentPreprocessor, ED); + + // Check that the expected diagnostics occurred. + NumErrors += CheckResults(Diags, CurrentPreprocessor->getSourceManager(), + *Buffer, ED); + } else { + NumErrors += (PrintProblem(Diags, 0, + Buffer->err_begin(), Buffer->err_end(), + "error", false) + + PrintProblem(Diags, 0, + Buffer->warn_begin(), Buffer->warn_end(), + "warn", false) + + PrintProblem(Diags, 0, + Buffer->note_begin(), Buffer->note_end(), + "note", false)); + } + + Diags.takeClient(); + Diags.setClient(CurClient); + + // Reset the buffer, we have processed all the diagnostics in it. + Buffer.reset(new TextDiagnosticBuffer()); +} + +Directive* Directive::Create(bool RegexKind, const SourceLocation &Location, + const std::string &Text, unsigned Count) { + if (RegexKind) + return new RegexDirective(Location, Text, Count); + return new StandardDirective(Location, Text, Count); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/Warnings.cpp b/contrib/llvm/tools/clang/lib/Frontend/Warnings.cpp new file mode 100644 index 0000000..8cc5616 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/Warnings.cpp @@ -0,0 +1,133 @@ +//===--- Warnings.cpp - C-Language Front-end ------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Command line warning options handler. +// +//===----------------------------------------------------------------------===// +// +// This file is responsible for handling all warning options. This includes +// a number of -Wfoo options and their variants, which are driven by TableGen- +// generated data, and the special cases -pedantic, -pedantic-errors, -w, +// -Werror and -Wfatal-errors. +// +// Each warning option controls any number of actual warnings. +// Given a warning option 'foo', the following are valid: +// -Wfoo, -Wno-foo, -Werror=foo, -Wfatal-errors=foo +// +#include "clang/Frontend/Utils.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Sema/SemaDiagnostic.h" +#include "clang/Lex/LexDiagnostic.h" +#include "clang/Frontend/DiagnosticOptions.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include <cstring> +#include <utility> +#include <algorithm> +using namespace clang; + +void clang::ProcessWarningOptions(Diagnostic &Diags, + const DiagnosticOptions &Opts) { + Diags.setSuppressSystemWarnings(true); // Default to -Wno-system-headers + Diags.setIgnoreAllWarnings(Opts.IgnoreWarnings); + Diags.setShowOverloads( + static_cast<Diagnostic::OverloadsShown>(Opts.ShowOverloads)); + + // Handle -ferror-limit + if (Opts.ErrorLimit) + Diags.setErrorLimit(Opts.ErrorLimit); + if (Opts.TemplateBacktraceLimit) + Diags.setTemplateBacktraceLimit(Opts.TemplateBacktraceLimit); + + // If -pedantic or -pedantic-errors was specified, then we want to map all + // extension diagnostics onto WARNING or ERROR unless the user has futz'd + // around with them explicitly. + if (Opts.PedanticErrors) + Diags.setExtensionHandlingBehavior(Diagnostic::Ext_Error); + else if (Opts.Pedantic) + Diags.setExtensionHandlingBehavior(Diagnostic::Ext_Warn); + else + Diags.setExtensionHandlingBehavior(Diagnostic::Ext_Ignore); + + for (unsigned i = 0, e = Opts.Warnings.size(); i != e; ++i) { + const std::string &Opt = Opts.Warnings[i]; + const char *OptStart = &Opt[0]; + const char *OptEnd = OptStart+Opt.size(); + assert(*OptEnd == 0 && "Expect null termination for lower-bound search"); + + // Check to see if this warning starts with "no-", if so, this is a negative + // form of the option. + bool isPositive = true; + if (OptEnd-OptStart > 3 && memcmp(OptStart, "no-", 3) == 0) { + isPositive = false; + OptStart += 3; + } + + // Figure out how this option affects the warning. If -Wfoo, map the + // diagnostic to a warning, if -Wno-foo, map it to ignore. + diag::Mapping Mapping = isPositive ? diag::MAP_WARNING : diag::MAP_IGNORE; + + // -Wsystem-headers is a special case, not driven by the option table. It + // cannot be controlled with -Werror. + if (OptEnd-OptStart == 14 && memcmp(OptStart, "system-headers", 14) == 0) { + Diags.setSuppressSystemWarnings(!isPositive); + continue; + } + + // -Werror/-Wno-error is a special case, not controlled by the option table. + // It also has the "specifier" form of -Werror=foo and -Werror-foo. + if (OptEnd-OptStart >= 5 && memcmp(OptStart, "error", 5) == 0) { + const char *Specifier = 0; + if (OptEnd-OptStart != 5) { // Specifier must be present. + if ((OptStart[5] != '=' && OptStart[5] != '-') || + OptEnd-OptStart == 6) { + Diags.Report(diag::warn_unknown_warning_specifier) + << "-Werror" << ("-W" + Opt); + continue; + } + Specifier = OptStart+6; + } + + if (Specifier == 0) { + Diags.setWarningsAsErrors(isPositive); + continue; + } + + // -Werror=foo maps foo to Error, -Wno-error=foo maps it to Warning. + Mapping = isPositive ? diag::MAP_ERROR : diag::MAP_WARNING_NO_WERROR; + OptStart = Specifier; + } + + // -Wfatal-errors is yet another special case. + if (OptEnd-OptStart >= 12 && memcmp(OptStart, "fatal-errors", 12) == 0) { + const char* Specifier = 0; + if (OptEnd-OptStart != 12) { + if ((OptStart[12] != '=' && OptStart[12] != '-') || + OptEnd-OptStart == 13) { + Diags.Report(diag::warn_unknown_warning_specifier) + << "-Wfatal-errors" << ("-W" + Opt); + continue; + } + Specifier = OptStart + 13; + } + + if (Specifier == 0) { + Diags.setErrorsAsFatal(isPositive); + continue; + } + + // -Wfatal-errors=foo maps foo to Fatal, -Wno-fatal-errors=foo + // maps it to Error. + Mapping = isPositive ? diag::MAP_FATAL : diag::MAP_ERROR_NO_WFATAL; + OptStart = Specifier; + } + + if (Diags.setDiagnosticGroupMapping(OptStart, Mapping)) + Diags.Report(diag::warn_unknown_warning_option) << ("-W" + Opt); + } +} |