diff options
Diffstat (limited to 'contrib/llvm/tools/clang/lib/Frontend')
29 files changed, 16281 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..1ef4c18 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/ASTConsumers.cpp @@ -0,0 +1,482 @@ +//===--- 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/AST/AST.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/PrettyPrinter.h" +#include "clang/AST/RecordLayout.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/raw_ostream.h" +using namespace clang; + +//===----------------------------------------------------------------------===// +/// ASTPrinter - Pretty-printer and dumper of ASTs + +namespace { + class ASTPrinter : public ASTConsumer, + public RecursiveASTVisitor<ASTPrinter> { + typedef RecursiveASTVisitor<ASTPrinter> base; + + public: + ASTPrinter(raw_ostream *Out = NULL, bool Dump = false, + StringRef FilterString = "", bool DumpLookups = false) + : Out(Out ? *Out : llvm::outs()), Dump(Dump), + FilterString(FilterString), DumpLookups(DumpLookups) {} + + virtual void HandleTranslationUnit(ASTContext &Context) { + TranslationUnitDecl *D = Context.getTranslationUnitDecl(); + + if (FilterString.empty()) + return print(D); + + TraverseDecl(D); + } + + bool shouldWalkTypesOfTypeLocs() const { return false; } + + bool TraverseDecl(Decl *D) { + if (D != NULL && filterMatches(D)) { + bool ShowColors = Out.has_colors(); + if (ShowColors) + Out.changeColor(raw_ostream::BLUE); + Out << (Dump ? "Dumping " : "Printing ") << getName(D) << ":\n"; + if (ShowColors) + Out.resetColor(); + print(D); + Out << "\n"; + // Don't traverse child nodes to avoid output duplication. + return true; + } + return base::TraverseDecl(D); + } + + private: + std::string getName(Decl *D) { + if (isa<NamedDecl>(D)) + return cast<NamedDecl>(D)->getQualifiedNameAsString(); + return ""; + } + bool filterMatches(Decl *D) { + return getName(D).find(FilterString) != std::string::npos; + } + void print(Decl *D) { + if (DumpLookups) { + if (DeclContext *DC = dyn_cast<DeclContext>(D)) + DC->dumpLookups(Out); + else + Out << "Not a DeclContext\n"; + } else if (Dump) + D->dump(Out); + else + D->print(Out, /*Indentation=*/0, /*PrintInstantiation=*/true); + } + + raw_ostream &Out; + bool Dump; + std::string FilterString; + bool DumpLookups; + }; + + class ASTDeclNodeLister : public ASTConsumer, + public RecursiveASTVisitor<ASTDeclNodeLister> { + public: + ASTDeclNodeLister(raw_ostream *Out = NULL) + : Out(Out ? *Out : llvm::outs()) {} + + virtual void HandleTranslationUnit(ASTContext &Context) { + TraverseDecl(Context.getTranslationUnitDecl()); + } + + bool shouldWalkTypesOfTypeLocs() const { return false; } + + virtual bool VisitNamedDecl(NamedDecl *D) { + D->printQualifiedName(Out); + Out << '\n'; + return true; + } + + private: + raw_ostream &Out; + }; +} // end anonymous namespace + +ASTConsumer *clang::CreateASTPrinter(raw_ostream *Out, + StringRef FilterString) { + return new ASTPrinter(Out, /*Dump=*/ false, FilterString); +} + +ASTConsumer *clang::CreateASTDumper(StringRef FilterString, bool DumpLookups) { + return new ASTPrinter(0, /*Dump=*/ true, FilterString, DumpLookups); +} + +ASTConsumer *clang::CreateASTDeclNodeLister() { + return new ASTDeclNodeLister(0); +} + +//===----------------------------------------------------------------------===// +/// ASTViewer - AST Visualization + +namespace { + class ASTViewer : public ASTConsumer { + ASTContext *Context; + public: + void Initialize(ASTContext &Context) { + this->Context = &Context; + } + + virtual bool HandleTopLevelDecl(DeclGroupRef D) { + for (DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; ++I) + HandleTopLevelSingleDecl(*I); + return true; + } + + 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 { + 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->isCompleteDefinition()) + Out << "[enum] "; + else + Out << "<enum> "; + Out << *ED; + break; + } + case Decl::Record: { + const RecordDecl* RD = cast<RecordDecl>(DC); + if (RD->isCompleteDefinition()) + Out << "[struct] "; + else + Out << "<struct> "; + Out << *RD; + break; + } + case Decl::CXXRecord: { + const CXXRecordDecl* RD = cast<CXXRecordDecl>(DC); + if (RD->isCompleteDefinition()) + 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->doesThisDeclarationHaveABody()) + 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: + llvm_unreachable("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::IndirectField: { + IndirectFieldDecl* IFD = cast<IndirectFieldDecl>(*I); + Out << "<IndirectField> " << *IFD << '\n'; + break; + } + case Decl::Label: { + LabelDecl *LD = cast<LabelDecl>(*I); + Out << "<Label> " << *LD << '\n'; + break; + } + case Decl::Field: { + FieldDecl *FD = cast<FieldDecl>(*I); + Out << "<field> " << *FD << '\n'; + break; + } + case Decl::Typedef: + case Decl::TypeAlias: { + TypedefNameDecl* TD = cast<TypedefNameDecl>(*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; + } + case Decl::OMPThreadPrivate: { + Out << "<omp threadprivate> " << '"' << *I << "\"\n"; + break; + } + default: + Out << "DeclKind: " << DK << '"' << *I << "\"\n"; + llvm_unreachable("decl unhandled"); + } + } +} +ASTConsumer *clang::CreateDeclContextPrinter() { + return new DeclContextPrinter(); +} 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..b6c644e --- /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/AST/ASTContext.h" +#include "clang/AST/ASTDiagnostic.h" +#include "clang/AST/ASTImporter.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" + +using namespace clang; + +ASTConsumer *ASTMergeAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + return AdaptedAction->CreateASTConsumer(CI, InFile); +} + +bool ASTMergeAction::BeginSourceFileAction(CompilerInstance &CI, + 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->setCurrentInput(getCurrentInput(), takeCurrentASTUnit()); + AdaptedAction->setCompilerInstance(&CI); + return AdaptedAction->BeginSourceFileAction(CI, Filename); +} + +void ASTMergeAction::ExecuteAction() { + CompilerInstance &CI = getCompilerInstance(); + CI.getDiagnostics().getClient()->BeginSourceFile( + CI.getASTContext().getLangOpts()); + CI.getDiagnostics().SetArgToStringFn(&FormatASTNodeDiagnosticArgument, + &CI.getASTContext()); + IntrusiveRefCntPtr<DiagnosticIDs> + DiagIDs(CI.getDiagnostics().getDiagnosticIDs()); + for (unsigned I = 0, N = ASTFiles.size(); I != N; ++I) { + IntrusiveRefCntPtr<DiagnosticsEngine> + Diags(new DiagnosticsEngine(DiagIDs, &CI.getDiagnosticOpts(), + new ForwardingDiagnosticConsumer( + *CI.getDiagnostics().getClient()), + /*ShouldOwnClient=*/true)); + ASTUnit *Unit = ASTUnit::LoadFromASTFile(ASTFiles[I], Diags, + CI.getFileSystemOpts(), false); + if (!Unit) + continue; + + ASTImporter Importer(CI.getASTContext(), + CI.getFileManager(), + Unit->getASTContext(), + Unit->getFileManager(), + /*MinimalImport=*/false); + + 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, + ArrayRef<std::string> ASTFiles) + : AdaptedAction(AdaptedAction), ASTFiles(ASTFiles.begin(), ASTFiles.end()) { + assert(AdaptedAction && "ASTMergeAction needs an action to adapt"); +} + +ASTMergeAction::~ASTMergeAction() { + delete AdaptedAction; +} + +bool ASTMergeAction::usesPreprocessorOnly() const { + return AdaptedAction->usesPreprocessorOnly(); +} + +TranslationUnitKind ASTMergeAction::getTranslationUnitKind() { + return AdaptedAction->getTranslationUnitKind(); +} + +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..a8c5876 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/ASTUnit.cpp @@ -0,0 +1,2961 @@ +//===--- 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/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclVisitor.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/AST/TypeOrdering.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Basic/TargetOptions.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/FrontendOptions.h" +#include "clang/Frontend/MultiplexConsumer.h" +#include "clang/Frontend/Utils.h" +#include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Lex/PreprocessorOptions.h" +#include "clang/Sema/Sema.h" +#include "clang/Serialization/ASTReader.h" +#include "clang/Serialization/ASTWriter.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/Support/Atomic.h" +#include "llvm/Support/CrashRecoveryContext.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Mutex.h" +#include "llvm/Support/MutexGuard.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/raw_ostream.h" +#include <cstdio> +#include <cstdlib> +#include <sys/stat.h> +using namespace clang; + +using llvm::TimeRecord; + +namespace { + class SimpleTimer { + bool WantTiming; + TimeRecord Start; + std::string Output; + + public: + explicit SimpleTimer(bool WantTiming) : WantTiming(WantTiming) { + if (WantTiming) + Start = TimeRecord::getCurrentTime(); + } + + void setOutput(const Twine &Output) { + if (WantTiming) + this->Output = Output.str(); + } + + ~SimpleTimer() { + if (WantTiming) { + TimeRecord Elapsed = TimeRecord::getCurrentTime(); + Elapsed -= Start; + llvm::errs() << Output << ':'; + Elapsed.print(Elapsed, llvm::errs()); + llvm::errs() << '\n'; + } + } + }; + + struct OnDiskData { + /// \brief The file in which the precompiled preamble is stored. + std::string PreambleFile; + + /// \brief Temporary files that should be removed when the ASTUnit is + /// destroyed. + SmallVector<std::string, 4> TemporaryFiles; + + /// \brief Erase temporary files. + void CleanTemporaryFiles(); + + /// \brief Erase the preamble file. + void CleanPreambleFile(); + + /// \brief Erase temporary files and the preamble file. + void Cleanup(); + }; +} + +static llvm::sys::SmartMutex<false> &getOnDiskMutex() { + static llvm::sys::SmartMutex<false> M(/* recursive = */ true); + return M; +} + +static void cleanupOnDiskMapAtExit(); + +typedef llvm::DenseMap<const ASTUnit *, OnDiskData *> OnDiskDataMap; +static OnDiskDataMap &getOnDiskDataMap() { + static OnDiskDataMap M; + static bool hasRegisteredAtExit = false; + if (!hasRegisteredAtExit) { + hasRegisteredAtExit = true; + atexit(cleanupOnDiskMapAtExit); + } + return M; +} + +static void cleanupOnDiskMapAtExit() { + // Use the mutex because there can be an alive thread destroying an ASTUnit. + llvm::MutexGuard Guard(getOnDiskMutex()); + OnDiskDataMap &M = getOnDiskDataMap(); + for (OnDiskDataMap::iterator I = M.begin(), E = M.end(); I != E; ++I) { + // We don't worry about freeing the memory associated with OnDiskDataMap. + // All we care about is erasing stale files. + I->second->Cleanup(); + } +} + +static OnDiskData &getOnDiskData(const ASTUnit *AU) { + // We require the mutex since we are modifying the structure of the + // DenseMap. + llvm::MutexGuard Guard(getOnDiskMutex()); + OnDiskDataMap &M = getOnDiskDataMap(); + OnDiskData *&D = M[AU]; + if (!D) + D = new OnDiskData(); + return *D; +} + +static void erasePreambleFile(const ASTUnit *AU) { + getOnDiskData(AU).CleanPreambleFile(); +} + +static void removeOnDiskEntry(const ASTUnit *AU) { + // We require the mutex since we are modifying the structure of the + // DenseMap. + llvm::MutexGuard Guard(getOnDiskMutex()); + OnDiskDataMap &M = getOnDiskDataMap(); + OnDiskDataMap::iterator I = M.find(AU); + if (I != M.end()) { + I->second->Cleanup(); + delete I->second; + M.erase(AU); + } +} + +static void setPreambleFile(const ASTUnit *AU, StringRef preambleFile) { + getOnDiskData(AU).PreambleFile = preambleFile; +} + +static const std::string &getPreambleFile(const ASTUnit *AU) { + return getOnDiskData(AU).PreambleFile; +} + +void OnDiskData::CleanTemporaryFiles() { + for (unsigned I = 0, N = TemporaryFiles.size(); I != N; ++I) + llvm::sys::fs::remove(TemporaryFiles[I]); + TemporaryFiles.clear(); +} + +void OnDiskData::CleanPreambleFile() { + if (!PreambleFile.empty()) { + llvm::sys::fs::remove(PreambleFile); + PreambleFile.clear(); + } +} + +void OnDiskData::Cleanup() { + CleanTemporaryFiles(); + CleanPreambleFile(); +} + +struct ASTUnit::ASTWriterData { + SmallString<128> Buffer; + llvm::BitstreamWriter Stream; + ASTWriter Writer; + + ASTWriterData() : Stream(Buffer), Writer(Stream) { } +}; + +void ASTUnit::clearFileLevelDecls() { + for (FileDeclsTy::iterator + I = FileDecls.begin(), E = FileDecls.end(); I != E; ++I) + delete I->second; + FileDecls.clear(); +} + +void ASTUnit::CleanTemporaryFiles() { + getOnDiskData(this).CleanTemporaryFiles(); +} + +void ASTUnit::addTemporaryFile(StringRef TempFile) { + getOnDiskData(this).TemporaryFiles.push_back(TempFile); +} + +/// \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; + +/// \brief Tracks the number of ASTUnit objects that are currently active. +/// +/// Used for debugging purposes only. +static llvm::sys::cas_flag ActiveASTUnitObjects; + +ASTUnit::ASTUnit(bool _MainFileIsAST) + : Reader(0), HadModuleLoaderFatalFailure(false), + OnlyLocalDecls(false), CaptureDiagnostics(false), + MainFileIsAST(_MainFileIsAST), + TUKind(TU_Complete), WantTiming(getenv("LIBCLANG_TIMING")), + OwnsRemappedFileBuffers(true), + NumStoredDiagnosticsFromDriver(0), + PreambleRebuildCounter(0), SavedMainFileBuffer(0), PreambleBuffer(0), + NumWarningsInPreamble(0), + ShouldCacheCodeCompletionResults(false), + IncludeBriefCommentsInCodeCompletion(false), UserFilesAreVolatile(false), + CompletionCacheTopLevelHashValue(0), + PreambleTopLevelHashValue(0), + CurrentTopLevelHashValue(0), + UnsafeToFree(false) { + if (getenv("LIBCLANG_OBJTRACKING")) { + llvm::sys::AtomicIncrement(&ActiveASTUnitObjects); + fprintf(stderr, "+++ %d translation units\n", ActiveASTUnitObjects); + } +} + +ASTUnit::~ASTUnit() { + // If we loaded from an AST file, balance out the BeginSourceFile call. + if (MainFileIsAST && getDiagnostics().getClient()) { + getDiagnostics().getClient()->EndSourceFile(); + } + + clearFileLevelDecls(); + + // Clean up the temporary files and the preamble file. + removeOnDiskEntry(this); + + // 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.getPtr() && OwnsRemappedFileBuffers) { + 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(); + + if (getenv("LIBCLANG_OBJTRACKING")) { + llvm::sys::AtomicDecrement(&ActiveASTUnitObjects); + fprintf(stderr, "--- %d translation units\n", ActiveASTUnitObjects); + } +} + +void ASTUnit::setPreprocessor(Preprocessor *pp) { PP = pp; } + +/// \brief Determine the set of code-completion contexts in which this +/// declaration should be shown. +static unsigned getDeclShowContexts(const NamedDecl *ND, + const LangOptions &LangOpts, + bool &IsNestedNameSpecifier) { + IsNestedNameSpecifier = false; + + if (isa<UsingShadowDecl>(ND)) + ND = dyn_cast<NamedDecl>(ND->getUnderlyingDecl()); + if (!ND) + return 0; + + uint64_t 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 |= (1LL << CodeCompletionContext::CCC_TopLevel) + | (1LL << CodeCompletionContext::CCC_ObjCIvarList) + | (1LL << CodeCompletionContext::CCC_ClassStructUnion) + | (1LL << CodeCompletionContext::CCC_Statement) + | (1LL << CodeCompletionContext::CCC_Type) + | (1LL << CodeCompletionContext::CCC_ParenthesizedExpression); + + // In C++, types can appear in expressions contexts (for functional casts). + if (LangOpts.CPlusPlus) + Contexts |= (1LL << CodeCompletionContext::CCC_Expression); + + // 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 |= (1LL << CodeCompletionContext::CCC_ObjCMessageReceiver); + + // In Objective-C, you can only be a subclass of another Objective-C class + if (isa<ObjCInterfaceDecl>(ND)) + Contexts |= (1LL << CodeCompletionContext::CCC_ObjCInterfaceName); + + // Deal with tag names. + if (isa<EnumDecl>(ND)) { + Contexts |= (1LL << CodeCompletionContext::CCC_EnumTag); + + // Part of the nested-name-specifier in C++0x. + if (LangOpts.CPlusPlus11) + IsNestedNameSpecifier = true; + } else if (const RecordDecl *Record = dyn_cast<RecordDecl>(ND)) { + if (Record->isUnion()) + Contexts |= (1LL << CodeCompletionContext::CCC_UnionTag); + else + Contexts |= (1LL << CodeCompletionContext::CCC_ClassOrStructTag); + + if (LangOpts.CPlusPlus) + IsNestedNameSpecifier = true; + } else if (isa<ClassTemplateDecl>(ND)) + IsNestedNameSpecifier = true; + } else if (isa<ValueDecl>(ND) || isa<FunctionTemplateDecl>(ND)) { + // Values can appear in these contexts. + Contexts = (1LL << CodeCompletionContext::CCC_Statement) + | (1LL << CodeCompletionContext::CCC_Expression) + | (1LL << CodeCompletionContext::CCC_ParenthesizedExpression) + | (1LL << CodeCompletionContext::CCC_ObjCMessageReceiver); + } else if (isa<ObjCProtocolDecl>(ND)) { + Contexts = (1LL << CodeCompletionContext::CCC_ObjCProtocolName); + } else if (isa<ObjCCategoryDecl>(ND)) { + Contexts = (1LL << CodeCompletionContext::CCC_ObjCCategoryName); + } else if (isa<NamespaceDecl>(ND) || isa<NamespaceAliasDecl>(ND)) { + Contexts = (1LL << CodeCompletionContext::CCC_Namespace); + + // Part of the nested-name-specifier. + IsNestedNameSpecifier = true; + } + + return Contexts; +} + +void ASTUnit::CacheCodeCompletionResults() { + if (!TheSema) + return; + + SimpleTimer Timer(WantTiming); + Timer.setOutput("Cache global code completions for " + getMainFileName()); + + // Clear out the previous results. + ClearCachedCompletionResults(); + + // Gather the set of global code completions. + typedef CodeCompletionResult Result; + SmallVector<Result, 8> Results; + CachedCompletionAllocator = new GlobalCodeCompletionAllocator; + CodeCompletionTUInfo CCTUInfo(CachedCompletionAllocator); + TheSema->GatherGlobalCodeCompletions(*CachedCompletionAllocator, + CCTUInfo, 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, + *CachedCompletionAllocator, + CCTUInfo, + IncludeBriefCommentsInCodeCompletion); + CachedResult.ShowInContexts = getDeclShowContexts(Results[I].Declaration, + Ctx->getLangOpts(), + 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.getLangOpts().CPlusPlus && + IsNestedNameSpecifier && !Results[I].StartsNestedNameSpecifier) { + // The contexts in which a nested-name-specifier can appear in C++. + uint64_t NNSContexts + = (1LL << CodeCompletionContext::CCC_TopLevel) + | (1LL << CodeCompletionContext::CCC_ObjCIvarList) + | (1LL << CodeCompletionContext::CCC_ClassStructUnion) + | (1LL << CodeCompletionContext::CCC_Statement) + | (1LL << CodeCompletionContext::CCC_Expression) + | (1LL << CodeCompletionContext::CCC_ObjCMessageReceiver) + | (1LL << CodeCompletionContext::CCC_EnumTag) + | (1LL << CodeCompletionContext::CCC_UnionTag) + | (1LL << CodeCompletionContext::CCC_ClassOrStructTag) + | (1LL << CodeCompletionContext::CCC_Type) + | (1LL << CodeCompletionContext::CCC_PotentiallyQualifiedName) + | (1LL << CodeCompletionContext::CCC_ParenthesizedExpression); + + if (isa<NamespaceDecl>(Results[I].Declaration) || + isa<NamespaceAliasDecl>(Results[I].Declaration)) + NNSContexts |= (1LL << CodeCompletionContext::CCC_Namespace); + + 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, + *CachedCompletionAllocator, + CCTUInfo, + IncludeBriefCommentsInCodeCompletion); + 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, + *CachedCompletionAllocator, + CCTUInfo, + IncludeBriefCommentsInCodeCompletion); + CachedResult.ShowInContexts + = (1LL << CodeCompletionContext::CCC_TopLevel) + | (1LL << CodeCompletionContext::CCC_ObjCInterface) + | (1LL << CodeCompletionContext::CCC_ObjCImplementation) + | (1LL << CodeCompletionContext::CCC_ObjCIvarList) + | (1LL << CodeCompletionContext::CCC_ClassStructUnion) + | (1LL << CodeCompletionContext::CCC_Statement) + | (1LL << CodeCompletionContext::CCC_Expression) + | (1LL << CodeCompletionContext::CCC_ObjCMessageReceiver) + | (1LL << CodeCompletionContext::CCC_MacroNameUse) + | (1LL << CodeCompletionContext::CCC_PreprocessorExpression) + | (1LL << CodeCompletionContext::CCC_ParenthesizedExpression) + | (1LL << CodeCompletionContext::CCC_OtherWithMacros); + + 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; + } + } + } + + // Save the current top-level hash value. + CompletionCacheTopLevelHashValue = CurrentTopLevelHashValue; +} + +void ASTUnit::ClearCachedCompletionResults() { + CachedCompletionResults.clear(); + CachedCompletionTypes.clear(); + CachedCompletionAllocator = 0; +} + +namespace { + +/// \brief Gathers information from ASTReader that will be used to initialize +/// a Preprocessor. +class ASTInfoCollector : public ASTReaderListener { + Preprocessor &PP; + ASTContext &Context; + LangOptions &LangOpt; + IntrusiveRefCntPtr<TargetOptions> &TargetOpts; + IntrusiveRefCntPtr<TargetInfo> &Target; + unsigned &Counter; + + bool InitializedLanguage; +public: + ASTInfoCollector(Preprocessor &PP, ASTContext &Context, LangOptions &LangOpt, + IntrusiveRefCntPtr<TargetOptions> &TargetOpts, + IntrusiveRefCntPtr<TargetInfo> &Target, + unsigned &Counter) + : PP(PP), Context(Context), LangOpt(LangOpt), + TargetOpts(TargetOpts), Target(Target), + Counter(Counter), + InitializedLanguage(false) {} + + virtual bool ReadLanguageOptions(const LangOptions &LangOpts, + bool Complain) { + if (InitializedLanguage) + return false; + + LangOpt = LangOpts; + InitializedLanguage = true; + + updated(); + return false; + } + + virtual bool ReadTargetOptions(const TargetOptions &TargetOpts, + bool Complain) { + // If we've already initialized the target, don't do it again. + if (Target) + return false; + + this->TargetOpts = new TargetOptions(TargetOpts); + Target = TargetInfo::CreateTargetInfo(PP.getDiagnostics(), + &*this->TargetOpts); + + updated(); + return false; + } + + virtual void ReadCounter(const serialization::ModuleFile &M, unsigned Value) { + Counter = Value; + } + +private: + void updated() { + if (!Target || !InitializedLanguage) + 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. + Target->setForcedLangOptions(LangOpt); + + // Initialize the preprocessor. + PP.Initialize(*Target); + + // Initialize the ASTContext + Context.InitBuiltinTypes(*Target); + + // We didn't have access to the comment options when the ASTContext was + // constructed, so register them now. + Context.getCommentCommandTraits().registerCommentOptions( + LangOpt.CommentOpts); + } +}; + + /// \brief Diagnostic consumer that saves each diagnostic it is given. +class StoredDiagnosticConsumer : public DiagnosticConsumer { + SmallVectorImpl<StoredDiagnostic> &StoredDiags; + SourceManager *SourceMgr; + +public: + explicit StoredDiagnosticConsumer( + SmallVectorImpl<StoredDiagnostic> &StoredDiags) + : StoredDiags(StoredDiags), SourceMgr(0) { } + + virtual void BeginSourceFile(const LangOptions &LangOpts, + const Preprocessor *PP = 0) { + if (PP) + SourceMgr = &PP->getSourceManager(); + } + + virtual void HandleDiagnostic(DiagnosticsEngine::Level Level, + const Diagnostic &Info); +}; + +/// \brief RAII object that optionally captures diagnostics, if +/// there is no diagnostic client to capture them already. +class CaptureDroppedDiagnostics { + DiagnosticsEngine &Diags; + StoredDiagnosticConsumer Client; + DiagnosticConsumer *PreviousClient; + +public: + CaptureDroppedDiagnostics(bool RequestCapture, DiagnosticsEngine &Diags, + 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 StoredDiagnosticConsumer::HandleDiagnostic(DiagnosticsEngine::Level Level, + const Diagnostic &Info) { + // Default implementation (Warnings/errors count). + DiagnosticConsumer::HandleDiagnostic(Level, Info); + + // Only record the diagnostic if it's part of the source manager we know + // about. This effectively drops diagnostics from modules we're building. + // FIXME: In the long run, ee don't want to drop source managers from modules. + if (!Info.hasSourceManager() || &Info.getSourceManager() == SourceMgr) + StoredDiags.push_back(StoredDiagnostic(Level, Info)); +} + +ASTMutationListener *ASTUnit::getASTMutationListener() { + if (WriterData) + return &WriterData->Writer; + return 0; +} + +ASTDeserializationListener *ASTUnit::getDeserializationListener() { + if (WriterData) + return &WriterData->Writer; + return 0; +} + +llvm::MemoryBuffer *ASTUnit::getBufferForFile(StringRef Filename, + std::string *ErrorStr) { + assert(FileMgr); + return FileMgr->getBufferForFile(Filename, ErrorStr); +} + +/// \brief Configure the diagnostics object for use with ASTUnit. +void ASTUnit::ConfigureDiags(IntrusiveRefCntPtr<DiagnosticsEngine> &Diags, + const char **ArgBegin, const char **ArgEnd, + ASTUnit &AST, bool CaptureDiagnostics) { + if (!Diags.getPtr()) { + // No diagnostics engine was provided, so create our own diagnostics object + // with the default options. + DiagnosticConsumer *Client = 0; + if (CaptureDiagnostics) + Client = new StoredDiagnosticConsumer(AST.StoredDiagnostics); + Diags = CompilerInstance::createDiagnostics(new DiagnosticOptions(), + Client, + /*ShouldOwnClient=*/true); + } else if (CaptureDiagnostics) { + Diags->setClient(new StoredDiagnosticConsumer(AST.StoredDiagnostics)); + } +} + +ASTUnit *ASTUnit::LoadFromASTFile(const std::string &Filename, + IntrusiveRefCntPtr<DiagnosticsEngine> Diags, + const FileSystemOptions &FileSystemOpts, + bool OnlyLocalDecls, + RemappedFile *RemappedFiles, + unsigned NumRemappedFiles, + bool CaptureDiagnostics, + bool AllowPCHWithCompilerErrors, + bool UserFilesAreVolatile) { + OwningPtr<ASTUnit> AST(new ASTUnit(true)); + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<ASTUnit> + ASTUnitCleanup(AST.get()); + llvm::CrashRecoveryContextCleanupRegistrar<DiagnosticsEngine, + llvm::CrashRecoveryContextReleaseRefCleanup<DiagnosticsEngine> > + DiagCleanup(Diags.getPtr()); + + ConfigureDiags(Diags, 0, 0, *AST, CaptureDiagnostics); + + AST->OnlyLocalDecls = OnlyLocalDecls; + AST->CaptureDiagnostics = CaptureDiagnostics; + AST->Diagnostics = Diags; + AST->FileMgr = new FileManager(FileSystemOpts); + AST->UserFilesAreVolatile = UserFilesAreVolatile; + AST->SourceMgr = new SourceManager(AST->getDiagnostics(), + AST->getFileManager(), + UserFilesAreVolatile); + AST->HSOpts = new HeaderSearchOptions(); + + AST->HeaderInfo.reset(new HeaderSearch(AST->HSOpts, + AST->getSourceManager(), + AST->getDiagnostics(), + AST->ASTFileLangOpts, + /*Target=*/0)); + + for (unsigned I = 0; I != NumRemappedFiles; ++I) { + FilenameOrMemBuf fileOrBuf = RemappedFiles[I].second; + if (const llvm::MemoryBuffer * + memBuf = fileOrBuf.dyn_cast<const llvm::MemoryBuffer *>()) { + // Create the file entry for the file that we're mapping from. + const FileEntry *FromFile + = AST->getFileManager().getVirtualFile(RemappedFiles[I].first, + memBuf->getBufferSize(), + 0); + if (!FromFile) { + AST->getDiagnostics().Report(diag::err_fe_remap_missing_from_file) + << RemappedFiles[I].first; + delete memBuf; + continue; + } + + // Override the contents of the "from" file with the contents of + // the "to" file. + AST->getSourceManager().overrideFileContents(FromFile, memBuf); + + } else { + const char *fname = fileOrBuf.get<const char *>(); + const FileEntry *ToFile = AST->FileMgr->getFile(fname); + if (!ToFile) { + AST->getDiagnostics().Report(diag::err_fe_remap_missing_to_file) + << RemappedFiles[I].first << fname; + continue; + } + + // Create the file entry for the file that we're mapping from. + const FileEntry *FromFile + = AST->getFileManager().getVirtualFile(RemappedFiles[I].first, + ToFile->getSize(), + 0); + if (!FromFile) { + AST->getDiagnostics().Report(diag::err_fe_remap_missing_from_file) + << RemappedFiles[I].first; + delete memBuf; + continue; + } + + // Override the contents of the "from" file with the contents of + // the "to" file. + AST->getSourceManager().overrideFileContents(FromFile, ToFile); + } + } + + // Gather Info for preprocessor construction later on. + + HeaderSearch &HeaderInfo = *AST->HeaderInfo.get(); + unsigned Counter; + + OwningPtr<ASTReader> Reader; + + AST->PP = new Preprocessor(new PreprocessorOptions(), + AST->getDiagnostics(), AST->ASTFileLangOpts, + /*Target=*/0, AST->getSourceManager(), HeaderInfo, + *AST, + /*IILookup=*/0, + /*OwnsHeaderSearch=*/false, + /*DelayInitialization=*/true); + Preprocessor &PP = *AST->PP; + + AST->Ctx = new ASTContext(AST->ASTFileLangOpts, + AST->getSourceManager(), + /*Target=*/0, + PP.getIdentifierTable(), + PP.getSelectorTable(), + PP.getBuiltinInfo(), + /* size_reserve = */0, + /*DelayInitialization=*/true); + ASTContext &Context = *AST->Ctx; + + bool disableValid = false; + if (::getenv("LIBCLANG_DISABLE_PCH_VALIDATION")) + disableValid = true; + Reader.reset(new ASTReader(PP, Context, + /*isysroot=*/"", + /*DisableValidation=*/disableValid, + AllowPCHWithCompilerErrors)); + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<ASTReader> + ReaderCleanup(Reader.get()); + + Reader->setListener(new ASTInfoCollector(*AST->PP, Context, + AST->ASTFileLangOpts, + AST->TargetOpts, AST->Target, + Counter)); + + switch (Reader->ReadAST(Filename, serialization::MK_MainFile, + SourceLocation(), ASTReader::ARR_None)) { + case ASTReader::Success: + break; + + case ASTReader::Failure: + case ASTReader::Missing: + case ASTReader::OutOfDate: + case ASTReader::VersionMismatch: + case ASTReader::ConfigurationMismatch: + case ASTReader::HadErrors: + AST->getDiagnostics().Report(diag::err_fe_unable_to_load_pch); + return NULL; + } + + AST->OriginalSourceFile = Reader->getOriginalSourceFile(); + + PP.setCounterValue(Counter); + + // 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(); + OwningPtr<ExternalASTSource> Source(Reader.take()); + + // Unregister the cleanup for ASTReader. It will get cleaned up + // by the ASTUnit cleanup. + ReaderCleanup.unregister(); + + 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); + AST->Reader = ReaderPtr; + + // Tell the diagnostic client that we have started a source file. + AST->getDiagnostics().getClient()->BeginSourceFile(Context.getLangOpts(),&PP); + + return AST.take(); +} + +namespace { + +/// \brief Preprocessor callback class that updates a hash value with the names +/// of all macros that have been defined by the translation unit. +class MacroDefinitionTrackerPPCallbacks : public PPCallbacks { + unsigned &Hash; + +public: + explicit MacroDefinitionTrackerPPCallbacks(unsigned &Hash) : Hash(Hash) { } + + virtual void MacroDefined(const Token &MacroNameTok, + const MacroDirective *MD) { + Hash = llvm::HashString(MacroNameTok.getIdentifierInfo()->getName(), Hash); + } +}; + +/// \brief Add the given declaration to the hash of all top-level entities. +void AddTopLevelDeclarationToHash(Decl *D, unsigned &Hash) { + if (!D) + return; + + DeclContext *DC = D->getDeclContext(); + if (!DC) + return; + + if (!(DC->isTranslationUnit() || DC->getLookupParent()->isTranslationUnit())) + return; + + if (NamedDecl *ND = dyn_cast<NamedDecl>(D)) { + if (EnumDecl *EnumD = dyn_cast<EnumDecl>(D)) { + // For an unscoped enum include the enumerators in the hash since they + // enter the top-level namespace. + if (!EnumD->isScoped()) { + for (EnumDecl::enumerator_iterator EI = EnumD->enumerator_begin(), + EE = EnumD->enumerator_end(); EI != EE; ++EI) { + if ((*EI)->getIdentifier()) + Hash = llvm::HashString((*EI)->getIdentifier()->getName(), Hash); + } + } + } + + if (ND->getIdentifier()) + Hash = llvm::HashString(ND->getIdentifier()->getName(), Hash); + else if (DeclarationName Name = ND->getDeclName()) { + std::string NameStr = Name.getAsString(); + Hash = llvm::HashString(NameStr, Hash); + } + return; + } + + if (ImportDecl *ImportD = dyn_cast<ImportDecl>(D)) { + if (Module *Mod = ImportD->getImportedModule()) { + std::string ModName = Mod->getFullModuleName(); + Hash = llvm::HashString(ModName, Hash); + } + return; + } +} + +class TopLevelDeclTrackerConsumer : public ASTConsumer { + ASTUnit &Unit; + unsigned &Hash; + +public: + TopLevelDeclTrackerConsumer(ASTUnit &_Unit, unsigned &Hash) + : Unit(_Unit), Hash(Hash) { + Hash = 0; + } + + void handleTopLevelDecl(Decl *D) { + if (!D) + return; + + // 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)) + return; + + AddTopLevelDeclarationToHash(D, Hash); + Unit.addTopLevelDecl(D); + + handleFileLevelDecl(D); + } + + void handleFileLevelDecl(Decl *D) { + Unit.addFileLevelDecl(D); + if (NamespaceDecl *NSD = dyn_cast<NamespaceDecl>(D)) { + for (NamespaceDecl::decl_iterator + I = NSD->decls_begin(), E = NSD->decls_end(); I != E; ++I) + handleFileLevelDecl(*I); + } + } + + bool HandleTopLevelDecl(DeclGroupRef D) { + for (DeclGroupRef::iterator it = D.begin(), ie = D.end(); it != ie; ++it) + handleTopLevelDecl(*it); + return true; + } + + // We're not interested in "interesting" decls. + void HandleInterestingDecl(DeclGroupRef) {} + + void HandleTopLevelDeclInObjCContainer(DeclGroupRef D) { + for (DeclGroupRef::iterator it = D.begin(), ie = D.end(); it != ie; ++it) + handleTopLevelDecl(*it); + } + + virtual ASTMutationListener *GetASTMutationListener() { + return Unit.getASTMutationListener(); + } + + virtual ASTDeserializationListener *GetASTDeserializationListener() { + return Unit.getDeserializationListener(); + } +}; + +class TopLevelDeclTrackerAction : public ASTFrontendAction { +public: + ASTUnit &Unit; + + virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + CI.getPreprocessor().addPPCallbacks( + new MacroDefinitionTrackerPPCallbacks(Unit.getCurrentTopLevelHashValue())); + return new TopLevelDeclTrackerConsumer(Unit, + Unit.getCurrentTopLevelHashValue()); + } + +public: + TopLevelDeclTrackerAction(ASTUnit &_Unit) : Unit(_Unit) {} + + virtual bool hasCodeCompletionSupport() const { return false; } + virtual TranslationUnitKind getTranslationUnitKind() { + return Unit.getTranslationUnitKind(); + } +}; + +class PrecompilePreambleAction : public ASTFrontendAction { + ASTUnit &Unit; + bool HasEmittedPreamblePCH; + +public: + explicit PrecompilePreambleAction(ASTUnit &Unit) + : Unit(Unit), HasEmittedPreamblePCH(false) {} + + virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI, + StringRef InFile); + bool hasEmittedPreamblePCH() const { return HasEmittedPreamblePCH; } + void setHasEmittedPreamblePCH() { HasEmittedPreamblePCH = true; } + virtual bool shouldEraseOutputFiles() { return !hasEmittedPreamblePCH(); } + + virtual bool hasCodeCompletionSupport() const { return false; } + virtual bool hasASTFileSupport() const { return false; } + virtual TranslationUnitKind getTranslationUnitKind() { return TU_Prefix; } +}; + +class PrecompilePreambleConsumer : public PCHGenerator { + ASTUnit &Unit; + unsigned &Hash; + std::vector<Decl *> TopLevelDecls; + PrecompilePreambleAction *Action; + +public: + PrecompilePreambleConsumer(ASTUnit &Unit, PrecompilePreambleAction *Action, + const Preprocessor &PP, StringRef isysroot, + raw_ostream *Out) + : PCHGenerator(PP, "", 0, isysroot, Out, /*AllowASTWithErrors=*/true), + Unit(Unit), Hash(Unit.getCurrentTopLevelHashValue()), Action(Action) { + Hash = 0; + } + + virtual bool 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; + AddTopLevelDeclarationToHash(D, Hash); + TopLevelDecls.push_back(D); + } + return true; + } + + virtual void HandleTranslationUnit(ASTContext &Ctx) { + PCHGenerator::HandleTranslationUnit(Ctx); + if (hasEmittedPCH()) { + // 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) { + Decl *D = TopLevelDecls[I]; + // Invalid top-level decls may not have been serialized. + if (D->isInvalidDecl()) + continue; + Unit.addTopLevelDeclFromPreamble(getWriter().getDeclID(D)); + } + + Action->setHasEmittedPreamblePCH(); + } + } +}; + +} + +ASTConsumer *PrecompilePreambleAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + std::string Sysroot; + std::string OutputFile; + raw_ostream *OS = 0; + if (GeneratePCHAction::ComputeASTConsumerArguments(CI, InFile, Sysroot, + OutputFile, OS)) + return 0; + + if (!CI.getFrontendOpts().RelocatablePCH) + Sysroot.clear(); + + CI.getPreprocessor().addPPCallbacks(new MacroDefinitionTrackerPPCallbacks( + Unit.getCurrentTopLevelHashValue())); + return new PrecompilePreambleConsumer(Unit, this, CI.getPreprocessor(), + Sysroot, OS); +} + +static bool isNonDriverDiag(const StoredDiagnostic &StoredDiag) { + return StoredDiag.getLocation().isValid(); +} + +static void +checkAndRemoveNonDriverDiags(SmallVectorImpl<StoredDiagnostic> &StoredDiags) { + // Get rid of stored diagnostics except the ones from the driver which do not + // have a source location. + StoredDiags.erase( + std::remove_if(StoredDiags.begin(), StoredDiags.end(), isNonDriverDiag), + StoredDiags.end()); +} + +static void checkAndSanitizeDiags(SmallVectorImpl<StoredDiagnostic> & + StoredDiagnostics, + SourceManager &SM) { + // 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) { + if (StoredDiagnostics[I].getLocation().isValid()) { + FullSourceLoc Loc(StoredDiagnostics[I].getLocation(), SM); + StoredDiagnostics[I].setLocation(Loc); + } + } +} + +/// 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) { + delete OverrideMainBuffer; + return true; + } + + // Create the compiler instance to use for building the AST. + OwningPtr<CompilerInstance> Clang(new CompilerInstance()); + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<CompilerInstance> + CICleanup(Clang.get()); + + IntrusiveRefCntPtr<CompilerInvocation> + CCInvocation(new CompilerInvocation(*Invocation)); + + Clang->setInvocation(CCInvocation.getPtr()); + OriginalSourceFile = Clang->getFrontendOpts().Inputs[0].getFile(); + + // Set up diagnostics, capturing any diagnostics that would + // otherwise be dropped. + Clang->setDiagnostics(&getDiagnostics()); + + // 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].getKind() != IK_AST && + "FIXME: AST inputs not yet supported here!"); + assert(Clang->getFrontendOpts().Inputs[0].getKind() != IK_LLVM_IR && + "IR inputs not support here!"); + + // Configure the various subsystems. + // FIXME: Should we retain the previous file manager? + LangOpts = &Clang->getLangOpts(); + FileSystemOpts = Clang->getFileSystemOpts(); + FileMgr = new FileManager(FileSystemOpts); + SourceMgr = new SourceManager(getDiagnostics(), *FileMgr, + UserFilesAreVolatile); + TheSema.reset(); + Ctx = 0; + PP = 0; + Reader = 0; + + // Clear out old caches and data. + TopLevelDecls.clear(); + clearFileLevelDecls(); + CleanTemporaryFiles(); + + if (!OverrideMainBuffer) { + checkAndRemoveNonDriverDiags(StoredDiagnostics); + 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(); + if (OverrideMainBuffer) { + PreprocessorOpts.addRemappedFile(OriginalSourceFile, OverrideMainBuffer); + PreprocessorOpts.PrecompiledPreambleBytes.first = Preamble.size(); + PreprocessorOpts.PrecompiledPreambleBytes.second + = PreambleEndsAtStartOfLine; + PreprocessorOpts.ImplicitPCHInclude = getPreambleFile(this); + PreprocessorOpts.DisablePCHValidation = true; + + // 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. + checkAndSanitizeDiags(StoredDiagnostics, getSourceManager()); + + // Keep track of the override buffer; + SavedMainFileBuffer = OverrideMainBuffer; + } + + OwningPtr<TopLevelDeclTrackerAction> Act( + new TopLevelDeclTrackerAction(*this)); + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<TopLevelDeclTrackerAction> + ActCleanup(Act.get()); + + if (!Act->BeginSourceFile(*Clang.get(), Clang->getFrontendOpts().Inputs[0])) + goto error; + + if (OverrideMainBuffer) { + std::string ModName = getPreambleFile(this); + TranslateStoredDiagnostics(Clang->getModuleManager(), ModName, + getSourceManager(), PreambleDiagnostics, + StoredDiagnostics); + } + + if (!Act->Execute()) + goto error; + + transferASTDataFromCompilerInstance(*Clang); + + Act->EndSourceFile(); + + FailedParseDiagnostics.clear(); + + return false; + +error: + // Remove the overridden buffer we used for the preamble. + if (OverrideMainBuffer) { + delete OverrideMainBuffer; + SavedMainFileBuffer = 0; + } + + // Keep the ownership of the data in the ASTUnit because the client may + // want to see the diagnostics. + transferASTDataFromCompilerInstance(*Clang); + FailedParseDiagnostics.swap(StoredDiagnostics); + StoredDiagnostics.clear(); + NumStoredDiagnosticsFromDriver = 0; + return true; +} + +/// \brief Simple function to retrieve a path for a preamble precompiled header. +static std::string GetPreamblePCHPath() { + // FIXME: This is a hack so that we can override the preamble file during + // crash-recovery testing, which is the only case where the preamble files + // are not necessarily cleaned up. + const char *TmpFile = ::getenv("CINDEXTEST_PREAMBLE_FILE"); + if (TmpFile) + return TmpFile; + + SmallString<128> Path; + llvm::sys::fs::createTemporaryFile("preamble", "pch", Path); + + return Path.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; + std::string MainFilePath(FrontendOpts.Inputs[0].getFile()); + llvm::sys::fs::UniqueID MainFileID; + if (!llvm::sys::fs::getUniqueID(MainFilePath, MainFileID)) { + // 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) { + std::string MPath(M->first); + llvm::sys::fs::UniqueID MID; + if (!llvm::sys::fs::getUniqueID(MPath, MID)) { + if (MainFileID == MID) { + // We found a remapping. Try to load the resulting, remapped source. + if (CreatedBuffer) { + delete Buffer; + CreatedBuffer = false; + } + + Buffer = getBufferForFile(M->second); + if (!Buffer) + return std::make_pair((llvm::MemoryBuffer*)0, + std::make_pair(0, true)); + CreatedBuffer = true; + } + } + } + + // 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) { + std::string MPath(M->first); + llvm::sys::fs::UniqueID MID; + if (!llvm::sys::fs::getUniqueID(MPath, MID)) { + if (MainFileID == MID) { + // We found a remapping. + if (CreatedBuffer) { + delete Buffer; + CreatedBuffer = false; + } + + Buffer = const_cast<llvm::MemoryBuffer *>(M->second); + } + } + } + } + + // If the main source file was not remapped, load it now. + if (!Buffer) { + Buffer = getBufferForFile(FrontendOpts.Inputs[0].getFile()); + if (!Buffer) + return std::make_pair((llvm::MemoryBuffer*)0, std::make_pair(0, true)); + + CreatedBuffer = true; + } + + return std::make_pair(Buffer, Lexer::ComputePreamble(Buffer, + *Invocation.getLangOpts(), + MaxLines)); +} + +static llvm::MemoryBuffer *CreatePaddedMainFileBuffer(llvm::MemoryBuffer *Old, + unsigned NewSize, + 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'; + + 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( + const CompilerInvocation &PreambleInvocationIn, + bool AllowRebuild, + unsigned MaxLines) { + + IntrusiveRefCntPtr<CompilerInvocation> + PreambleInvocation(new CompilerInvocation(PreambleInvocationIn)); + 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 ComputePreamble() Take ownership of the preamble buffer. + OwningPtr<llvm::MemoryBuffer> OwnedPreambleBuffer; + if (CreatedPreambleBuffer) + OwnedPreambleBuffer.reset(NewPreamble.first); + + 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(); + erasePreambleFile(this); + + // 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.getBufferStart(), 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) { + llvm::sys::fs::file_status Status; + if (FileMgr->getNoncachedStatValue(R->second, Status)) { + // 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( + Status.getSize(), Status.getLastModificationTime().toEpochTime()); + } + 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. + llvm::sys::fs::file_status Status; + if (FileMgr->getNoncachedStatValue(F->first(), Status)) { + // If we can't stat the file, assume that something horrible happened. + AnyFileChanged = true; + } else if (Status.getSize() != uint64_t(F->second.first) || + Status.getLastModificationTime().toEpochTime() != + uint64_t(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(); + ProcessWarningOptions(getDiagnostics(), + PreambleInvocation->getDiagnosticOpts()); + getDiagnostics().setNumWarnings(NumWarningsInPreamble); + + // Create a version of the main file buffer that is padded to + // buffer size we reserved when creating the preamble. + return CreatePaddedMainFileBuffer(NewPreamble.first, + PreambleReservedSize, + FrontendOpts.Inputs[0].getFile()); + } + } + + // 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(); + PreambleDiagnostics.clear(); + erasePreambleFile(this); + 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; + } + + // Create a temporary file for the precompiled preamble. In rare + // circumstances, this can fail. + std::string PreamblePCHPath = GetPreamblePCHPath(); + if (PreamblePCHPath.empty()) { + // Try again next time. + PreambleRebuildCounter = 1; + return 0; + } + + // We did not previously compute a preamble, or it can't be reused anyway. + SimpleTimer PreambleTimer(WantTiming); + PreambleTimer.setOutput("Precompiling preamble"); + + // 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. + StringRef MainFilename = PreambleInvocation->getFrontendOpts().Inputs[0].getFile(); + Preamble.assign(FileMgr->getFile(MainFilename), + NewPreamble.first->getBufferStart(), + NewPreamble.first->getBufferStart() + + NewPreamble.second.first); + PreambleEndsAtStartOfLine = NewPreamble.second.second; + + delete PreambleBuffer; + PreambleBuffer + = llvm::MemoryBuffer::getNewUninitMemBuffer(PreambleReservedSize, + FrontendOpts.Inputs[0].getFile()); + 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. + StringRef MainFilePath = FrontendOpts.Inputs[0].getFile(); + PreprocessorOpts.addRemappedFile(MainFilePath, PreambleBuffer); + + // Tell the compiler invocation to generate a temporary precompiled header. + FrontendOpts.ProgramAction = frontend::GeneratePCH; + // FIXME: Generate the precompiled header into memory? + FrontendOpts.OutputFile = PreamblePCHPath; + PreprocessorOpts.PrecompiledPreambleBytes.first = 0; + PreprocessorOpts.PrecompiledPreambleBytes.second = false; + + // Create the compiler instance to use for building the precompiled preamble. + OwningPtr<CompilerInstance> Clang(new CompilerInstance()); + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<CompilerInstance> + CICleanup(Clang.get()); + + Clang->setInvocation(&*PreambleInvocation); + OriginalSourceFile = Clang->getFrontendOpts().Inputs[0].getFile(); + + // Set up diagnostics, capturing all of the diagnostics produced. + Clang->setDiagnostics(&getDiagnostics()); + + // Create the target instance. + Clang->setTarget(TargetInfo::CreateTargetInfo(Clang->getDiagnostics(), + &Clang->getTargetOpts())); + if (!Clang->hasTarget()) { + llvm::sys::fs::remove(FrontendOpts.OutputFile); + Preamble.clear(); + 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].getKind() != IK_AST && + "FIXME: AST inputs not yet supported here!"); + assert(Clang->getFrontendOpts().Inputs[0].getKind() != IK_LLVM_IR && + "IR inputs not support here!"); + + // Clear out old caches and data. + getDiagnostics().Reset(); + ProcessWarningOptions(getDiagnostics(), Clang->getDiagnosticOpts()); + checkAndRemoveNonDriverDiags(StoredDiagnostics); + TopLevelDecls.clear(); + TopLevelDeclsInPreamble.clear(); + + // Create a file manager object to provide access to and cache the filesystem. + Clang->setFileManager(new FileManager(Clang->getFileSystemOpts())); + + // Create the source manager. + Clang->setSourceManager(new SourceManager(getDiagnostics(), + Clang->getFileManager())); + + OwningPtr<PrecompilePreambleAction> Act; + Act.reset(new PrecompilePreambleAction(*this)); + if (!Act->BeginSourceFile(*Clang.get(), Clang->getFrontendOpts().Inputs[0])) { + llvm::sys::fs::remove(FrontendOpts.OutputFile); + Preamble.clear(); + PreambleRebuildCounter = DefaultPreambleRebuildInterval; + PreprocessorOpts.eraseRemappedFile( + PreprocessorOpts.remapped_file_buffer_end() - 1); + return 0; + } + + Act->Execute(); + Act->EndSourceFile(); + + if (!Act->hasEmittedPreamblePCH()) { + // The preamble PCH failed (e.g. there was a module loading fatal error), + // so no precompiled header was generated. Forget that we even tried. + // FIXME: Should we leave a note for ourselves to try again? + llvm::sys::fs::remove(FrontendOpts.OutputFile); + Preamble.clear(); + TopLevelDeclsInPreamble.clear(); + PreambleRebuildCounter = DefaultPreambleRebuildInterval; + PreprocessorOpts.eraseRemappedFile( + PreprocessorOpts.remapped_file_buffer_end() - 1); + return 0; + } + + // Transfer any diagnostics generated when parsing the preamble into the set + // of preamble diagnostics. + PreambleDiagnostics.clear(); + PreambleDiagnostics.insert(PreambleDiagnostics.end(), + stored_diag_afterDriver_begin(), stored_diag_end()); + checkAndRemoveNonDriverDiags(StoredDiagnostics); + + // Keep track of the preamble we precompiled. + setPreambleFile(this, FrontendOpts.OutputFile); + 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->OrigEntry; + if (!File || F->second->getRawBuffer() == MainFileBuffer) + continue; + + FilesInPreamble[File->getName()] + = std::make_pair(F->second->getSize(), File->getModificationTime()); + } + + PreambleRebuildCounter = 1; + PreprocessorOpts.eraseRemappedFile( + PreprocessorOpts.remapped_file_buffer_end() - 1); + + // If the hash of top-level entities differs from the hash of the top-level + // entities the last time we rebuilt the preamble, clear out the completion + // cache. + if (CurrentTopLevelHashValue != PreambleTopLevelHashValue) { + CompletionCacheTopLevelHashValue = 0; + PreambleTopLevelHashValue = CurrentTopLevelHashValue; + } + + return CreatePaddedMainFileBuffer(NewPreamble.first, + PreambleReservedSize, + FrontendOpts.Inputs[0].getFile()); +} + +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()); +} + +void ASTUnit::transferASTDataFromCompilerInstance(CompilerInstance &CI) { + // Steal the created target, context, and preprocessor. + TheSema.reset(CI.takeSema()); + Consumer.reset(CI.takeASTConsumer()); + Ctx = &CI.getASTContext(); + PP = &CI.getPreprocessor(); + CI.setSourceManager(0); + CI.setFileManager(0); + Target = &CI.getTarget(); + Reader = CI.getModuleManager(); + HadModuleLoaderFatalFailure = CI.hadModuleLoaderFatalFailure(); +} + +StringRef ASTUnit::getMainFileName() const { + if (Invocation && !Invocation->getFrontendOpts().Inputs.empty()) { + const FrontendInputFile &Input = Invocation->getFrontendOpts().Inputs[0]; + if (Input.isFile()) + return Input.getFile(); + else + return Input.getBuffer()->getBufferIdentifier(); + } + + if (SourceMgr) { + if (const FileEntry * + FE = SourceMgr->getFileEntryForID(SourceMgr->getMainFileID())) + return FE->getName(); + } + + return StringRef(); +} + +StringRef ASTUnit::getASTFileName() const { + if (!isMainFileAST()) + return StringRef(); + + serialization::ModuleFile & + Mod = Reader->getModuleManager().getPrimaryModule(); + return Mod.FileName; +} + +ASTUnit *ASTUnit::create(CompilerInvocation *CI, + IntrusiveRefCntPtr<DiagnosticsEngine> Diags, + bool CaptureDiagnostics, + bool UserFilesAreVolatile) { + OwningPtr<ASTUnit> AST; + AST.reset(new ASTUnit(false)); + ConfigureDiags(Diags, 0, 0, *AST, CaptureDiagnostics); + AST->Diagnostics = Diags; + AST->Invocation = CI; + AST->FileSystemOpts = CI->getFileSystemOpts(); + AST->FileMgr = new FileManager(AST->FileSystemOpts); + AST->UserFilesAreVolatile = UserFilesAreVolatile; + AST->SourceMgr = new SourceManager(AST->getDiagnostics(), *AST->FileMgr, + UserFilesAreVolatile); + + return AST.take(); +} + +ASTUnit *ASTUnit::LoadFromCompilerInvocationAction(CompilerInvocation *CI, + IntrusiveRefCntPtr<DiagnosticsEngine> Diags, + ASTFrontendAction *Action, + ASTUnit *Unit, + bool Persistent, + StringRef ResourceFilesPath, + bool OnlyLocalDecls, + bool CaptureDiagnostics, + bool PrecompilePreamble, + bool CacheCodeCompletionResults, + bool IncludeBriefCommentsInCodeCompletion, + bool UserFilesAreVolatile, + OwningPtr<ASTUnit> *ErrAST) { + assert(CI && "A CompilerInvocation is required"); + + OwningPtr<ASTUnit> OwnAST; + ASTUnit *AST = Unit; + if (!AST) { + // Create the AST unit. + OwnAST.reset(create(CI, Diags, CaptureDiagnostics, UserFilesAreVolatile)); + AST = OwnAST.get(); + } + + if (!ResourceFilesPath.empty()) { + // Override the resources path. + CI->getHeaderSearchOpts().ResourceDir = ResourceFilesPath; + } + AST->OnlyLocalDecls = OnlyLocalDecls; + AST->CaptureDiagnostics = CaptureDiagnostics; + if (PrecompilePreamble) + AST->PreambleRebuildCounter = 2; + AST->TUKind = Action ? Action->getTranslationUnitKind() : TU_Complete; + AST->ShouldCacheCodeCompletionResults = CacheCodeCompletionResults; + AST->IncludeBriefCommentsInCodeCompletion + = IncludeBriefCommentsInCodeCompletion; + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<ASTUnit> + ASTUnitCleanup(OwnAST.get()); + llvm::CrashRecoveryContextCleanupRegistrar<DiagnosticsEngine, + llvm::CrashRecoveryContextReleaseRefCleanup<DiagnosticsEngine> > + DiagCleanup(Diags.getPtr()); + + // We'll manage file buffers ourselves. + CI->getPreprocessorOpts().RetainRemappedFileBuffers = true; + CI->getFrontendOpts().DisableFree = false; + ProcessWarningOptions(AST->getDiagnostics(), CI->getDiagnosticOpts()); + + // Create the compiler instance to use for building the AST. + OwningPtr<CompilerInstance> Clang(new CompilerInstance()); + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<CompilerInstance> + CICleanup(Clang.get()); + + Clang->setInvocation(CI); + AST->OriginalSourceFile = Clang->getFrontendOpts().Inputs[0].getFile(); + + // Set up diagnostics, capturing any diagnostics that would + // otherwise be dropped. + Clang->setDiagnostics(&AST->getDiagnostics()); + + // Create the target instance. + Clang->setTarget(TargetInfo::CreateTargetInfo(Clang->getDiagnostics(), + &Clang->getTargetOpts())); + if (!Clang->hasTarget()) + 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].getKind() != IK_AST && + "FIXME: AST inputs not yet supported here!"); + assert(Clang->getFrontendOpts().Inputs[0].getKind() != IK_LLVM_IR && + "IR inputs not supported here!"); + + // Configure the various subsystems. + AST->TheSema.reset(); + AST->Ctx = 0; + AST->PP = 0; + AST->Reader = 0; + + // Create a file manager object to provide access to and cache the filesystem. + Clang->setFileManager(&AST->getFileManager()); + + // Create the source manager. + Clang->setSourceManager(&AST->getSourceManager()); + + ASTFrontendAction *Act = Action; + + OwningPtr<TopLevelDeclTrackerAction> TrackerAct; + if (!Act) { + TrackerAct.reset(new TopLevelDeclTrackerAction(*AST)); + Act = TrackerAct.get(); + } + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<TopLevelDeclTrackerAction> + ActCleanup(TrackerAct.get()); + + if (!Act->BeginSourceFile(*Clang.get(), Clang->getFrontendOpts().Inputs[0])) { + AST->transferASTDataFromCompilerInstance(*Clang); + if (OwnAST && ErrAST) + ErrAST->swap(OwnAST); + + return 0; + } + + if (Persistent && !TrackerAct) { + Clang->getPreprocessor().addPPCallbacks( + new MacroDefinitionTrackerPPCallbacks(AST->getCurrentTopLevelHashValue())); + std::vector<ASTConsumer*> Consumers; + if (Clang->hasASTConsumer()) + Consumers.push_back(Clang->takeASTConsumer()); + Consumers.push_back(new TopLevelDeclTrackerConsumer(*AST, + AST->getCurrentTopLevelHashValue())); + Clang->setASTConsumer(new MultiplexConsumer(Consumers)); + } + if (!Act->Execute()) { + AST->transferASTDataFromCompilerInstance(*Clang); + if (OwnAST && ErrAST) + ErrAST->swap(OwnAST); + + return 0; + } + + // Steal the created target, context, and preprocessor. + AST->transferASTDataFromCompilerInstance(*Clang); + + Act->EndSourceFile(); + + if (OwnAST) + return OwnAST.take(); + else + return AST; +} + +bool ASTUnit::LoadFromCompilerInvocation(bool PrecompilePreamble) { + if (!Invocation) + return true; + + // We'll manage file buffers ourselves. + Invocation->getPreprocessorOpts().RetainRemappedFileBuffers = true; + Invocation->getFrontendOpts().DisableFree = false; + ProcessWarningOptions(getDiagnostics(), Invocation->getDiagnosticOpts()); + + llvm::MemoryBuffer *OverrideMainBuffer = 0; + if (PrecompilePreamble) { + PreambleRebuildCounter = 2; + OverrideMainBuffer + = getMainBufferWithPrecompiledPreamble(*Invocation); + } + + SimpleTimer ParsingTimer(WantTiming); + ParsingTimer.setOutput("Parsing " + getMainFileName()); + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<llvm::MemoryBuffer> + MemBufferCleanup(OverrideMainBuffer); + + return Parse(OverrideMainBuffer); +} + +ASTUnit *ASTUnit::LoadFromCompilerInvocation(CompilerInvocation *CI, + IntrusiveRefCntPtr<DiagnosticsEngine> Diags, + bool OnlyLocalDecls, + bool CaptureDiagnostics, + bool PrecompilePreamble, + TranslationUnitKind TUKind, + bool CacheCodeCompletionResults, + bool IncludeBriefCommentsInCodeCompletion, + bool UserFilesAreVolatile) { + // Create the AST unit. + OwningPtr<ASTUnit> AST; + AST.reset(new ASTUnit(false)); + ConfigureDiags(Diags, 0, 0, *AST, CaptureDiagnostics); + AST->Diagnostics = Diags; + AST->OnlyLocalDecls = OnlyLocalDecls; + AST->CaptureDiagnostics = CaptureDiagnostics; + AST->TUKind = TUKind; + AST->ShouldCacheCodeCompletionResults = CacheCodeCompletionResults; + AST->IncludeBriefCommentsInCodeCompletion + = IncludeBriefCommentsInCodeCompletion; + AST->Invocation = CI; + AST->FileSystemOpts = CI->getFileSystemOpts(); + AST->FileMgr = new FileManager(AST->FileSystemOpts); + AST->UserFilesAreVolatile = UserFilesAreVolatile; + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<ASTUnit> + ASTUnitCleanup(AST.get()); + llvm::CrashRecoveryContextCleanupRegistrar<DiagnosticsEngine, + llvm::CrashRecoveryContextReleaseRefCleanup<DiagnosticsEngine> > + DiagCleanup(Diags.getPtr()); + + return AST->LoadFromCompilerInvocation(PrecompilePreamble)? 0 : AST.take(); +} + +ASTUnit *ASTUnit::LoadFromCommandLine(const char **ArgBegin, + const char **ArgEnd, + IntrusiveRefCntPtr<DiagnosticsEngine> Diags, + StringRef ResourceFilesPath, + bool OnlyLocalDecls, + bool CaptureDiagnostics, + RemappedFile *RemappedFiles, + unsigned NumRemappedFiles, + bool RemappedFilesKeepOriginalName, + bool PrecompilePreamble, + TranslationUnitKind TUKind, + bool CacheCodeCompletionResults, + bool IncludeBriefCommentsInCodeCompletion, + bool AllowPCHWithCompilerErrors, + bool SkipFunctionBodies, + bool UserFilesAreVolatile, + bool ForSerialization, + OwningPtr<ASTUnit> *ErrAST) { + if (!Diags.getPtr()) { + // No diagnostics engine was provided, so create our own diagnostics object + // with the default options. + Diags = CompilerInstance::createDiagnostics(new DiagnosticOptions()); + } + + SmallVector<StoredDiagnostic, 4> StoredDiagnostics; + + IntrusiveRefCntPtr<CompilerInvocation> CI; + + { + + CaptureDroppedDiagnostics Capture(CaptureDiagnostics, *Diags, + StoredDiagnostics); + + CI = clang::createInvocationFromCommandLine( + llvm::makeArrayRef(ArgBegin, ArgEnd), + Diags); + if (!CI) + return 0; + } + + // Override any files that need remapping + for (unsigned I = 0; I != NumRemappedFiles; ++I) { + FilenameOrMemBuf fileOrBuf = RemappedFiles[I].second; + if (const llvm::MemoryBuffer * + memBuf = fileOrBuf.dyn_cast<const llvm::MemoryBuffer *>()) { + CI->getPreprocessorOpts().addRemappedFile(RemappedFiles[I].first, memBuf); + } else { + const char *fname = fileOrBuf.get<const char *>(); + CI->getPreprocessorOpts().addRemappedFile(RemappedFiles[I].first, fname); + } + } + PreprocessorOptions &PPOpts = CI->getPreprocessorOpts(); + PPOpts.RemappedFilesKeepOriginalName = RemappedFilesKeepOriginalName; + PPOpts.AllowPCHWithCompilerErrors = AllowPCHWithCompilerErrors; + + // Override the resources path. + CI->getHeaderSearchOpts().ResourceDir = ResourceFilesPath; + + CI->getFrontendOpts().SkipFunctionBodies = SkipFunctionBodies; + + // Create the AST unit. + OwningPtr<ASTUnit> AST; + AST.reset(new ASTUnit(false)); + ConfigureDiags(Diags, ArgBegin, ArgEnd, *AST, CaptureDiagnostics); + AST->Diagnostics = Diags; + Diags = 0; // Zero out now to ease cleanup during crash recovery. + AST->FileSystemOpts = CI->getFileSystemOpts(); + AST->FileMgr = new FileManager(AST->FileSystemOpts); + AST->OnlyLocalDecls = OnlyLocalDecls; + AST->CaptureDiagnostics = CaptureDiagnostics; + AST->TUKind = TUKind; + AST->ShouldCacheCodeCompletionResults = CacheCodeCompletionResults; + AST->IncludeBriefCommentsInCodeCompletion + = IncludeBriefCommentsInCodeCompletion; + AST->UserFilesAreVolatile = UserFilesAreVolatile; + AST->NumStoredDiagnosticsFromDriver = StoredDiagnostics.size(); + AST->StoredDiagnostics.swap(StoredDiagnostics); + AST->Invocation = CI; + if (ForSerialization) + AST->WriterData.reset(new ASTWriterData()); + CI = 0; // Zero out now to ease cleanup during crash recovery. + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<ASTUnit> + ASTUnitCleanup(AST.get()); + + if (AST->LoadFromCompilerInvocation(PrecompilePreamble)) { + // Some error occurred, if caller wants to examine diagnostics, pass it the + // ASTUnit. + if (ErrAST) { + AST->StoredDiagnostics.swap(AST->FailedParseDiagnostics); + ErrAST->swap(AST); + } + return 0; + } + + return AST.take(); +} + +bool ASTUnit::Reparse(RemappedFile *RemappedFiles, unsigned NumRemappedFiles) { + if (!Invocation) + return true; + + clearFileLevelDecls(); + + SimpleTimer ParsingTimer(WantTiming); + ParsingTimer.setOutput("Reparsing " + getMainFileName()); + + // 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) { + FilenameOrMemBuf fileOrBuf = RemappedFiles[I].second; + if (const llvm::MemoryBuffer * + memBuf = fileOrBuf.dyn_cast<const llvm::MemoryBuffer *>()) { + Invocation->getPreprocessorOpts().addRemappedFile(RemappedFiles[I].first, + memBuf); + } else { + const char *fname = fileOrBuf.get<const char *>(); + Invocation->getPreprocessorOpts().addRemappedFile(RemappedFiles[I].first, + fname); + } + } + + // 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 (!getPreambleFile(this).empty() || PreambleRebuildCounter > 0) + OverrideMainBuffer = getMainBufferWithPrecompiledPreamble(*Invocation); + + // Clear out the diagnostics state. + getDiagnostics().Reset(); + ProcessWarningOptions(getDiagnostics(), Invocation->getDiagnosticOpts()); + if (OverrideMainBuffer) + getDiagnostics().setNumWarnings(NumWarningsInPreamble); + + // Parse the sources + bool Result = Parse(OverrideMainBuffer); + + // If we're caching global code-completion results, and the top-level + // declarations have changed, clear out the code-completion cache. + if (!Result && ShouldCacheCodeCompletionResults && + CurrentTopLevelHashValue != CompletionCacheTopLevelHashValue) + CacheCodeCompletionResults(); + + // We now need to clear out the completion info related to this translation + // unit; it'll be recreated if necessary. + CCTUInfo.reset(); + + 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 { + uint64_t NormalContexts; + ASTUnit &AST; + CodeCompleteConsumer &Next; + + public: + AugmentedCodeCompleteConsumer(ASTUnit &AST, CodeCompleteConsumer &Next, + const CodeCompleteOptions &CodeCompleteOpts) + : CodeCompleteConsumer(CodeCompleteOpts, 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 + = (1LL << CodeCompletionContext::CCC_TopLevel) + | (1LL << CodeCompletionContext::CCC_ObjCInterface) + | (1LL << CodeCompletionContext::CCC_ObjCImplementation) + | (1LL << CodeCompletionContext::CCC_ObjCIvarList) + | (1LL << CodeCompletionContext::CCC_Statement) + | (1LL << CodeCompletionContext::CCC_Expression) + | (1LL << CodeCompletionContext::CCC_ObjCMessageReceiver) + | (1LL << CodeCompletionContext::CCC_DotMemberAccess) + | (1LL << CodeCompletionContext::CCC_ArrowMemberAccess) + | (1LL << CodeCompletionContext::CCC_ObjCPropertyAccess) + | (1LL << CodeCompletionContext::CCC_ObjCProtocolName) + | (1LL << CodeCompletionContext::CCC_ParenthesizedExpression) + | (1LL << CodeCompletionContext::CCC_Recovery); + + if (AST.getASTContext().getLangOpts().CPlusPlus) + NormalContexts |= (1LL << CodeCompletionContext::CCC_EnumTag) + | (1LL << CodeCompletionContext::CCC_UnionTag) + | (1LL << CodeCompletionContext::CCC_ClassOrStructTag); + } + + 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); + } + + virtual CodeCompletionAllocator &getAllocator() { + return Next.getAllocator(); + } + + virtual CodeCompletionTUInfo &getCodeCompletionTUInfo() { + return Next.getCodeCompletionTUInfo(); + } + }; +} + +/// \brief Helper function that computes which global names are hidden by the +/// local code-completion results. +static void CalculateHiddenNames(const CodeCompletionContext &Context, + CodeCompletionResult *Results, + unsigned NumResults, + ASTContext &Ctx, + llvm::StringSet<llvm::BumpPtrAllocator> &HiddenNames){ + bool OnlyTagNames = false; + switch (Context.getKind()) { + case CodeCompletionContext::CCC_Recovery: + 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_DotMemberAccess: + case CodeCompletionContext::CCC_ArrowMemberAccess: + case CodeCompletionContext::CCC_ObjCPropertyAccess: + case CodeCompletionContext::CCC_Namespace: + case CodeCompletionContext::CCC_Type: + case CodeCompletionContext::CCC_Name: + case CodeCompletionContext::CCC_PotentiallyQualifiedName: + case CodeCompletionContext::CCC_ParenthesizedExpression: + case CodeCompletionContext::CCC_ObjCInterfaceName: + 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: + case CodeCompletionContext::CCC_Other: + case CodeCompletionContext::CCC_OtherWithMacros: + case CodeCompletionContext::CCC_ObjCInstanceMessage: + case CodeCompletionContext::CCC_ObjCClassMessage: + case CodeCompletionContext::CCC_ObjCCategoryName: + // 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.getLangOpts().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; + uint64_t InContexts = + Context.getKind() == CodeCompletionContext::CCC_Recovery + ? NormalContexts : (1LL << Context.getKind()); + // Contains the set of names that are hidden by "local" completion results. + llvm::StringSet<llvm::BumpPtrAllocator> HiddenNames; + typedef CodeCompletionResult Result; + 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; + CodeCompletionString *Completion = C->Completion; + if (!Context.getPreferredType().isNull()) { + if (C->Kind == CXCursor_MacroDefinition) { + Priority = getMacroUsagePriority(C->Completion->getTypedText(), + S.getLangOpts(), + 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. + CodeCompletionBuilder Builder(getAllocator(), getCodeCompletionTUInfo(), + CCP_CodePattern, C->Availability); + Builder.AddTypedTextChunk(C->Completion->getTypedText()); + Priority = CCP_CodePattern; + Completion = Builder.TakeString(); + } + + AllResults.push_back(Result(Completion, Priority, C->Kind, + 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()); +} + + + +void ASTUnit::CodeComplete(StringRef File, unsigned Line, unsigned Column, + RemappedFile *RemappedFiles, + unsigned NumRemappedFiles, + bool IncludeMacros, + bool IncludeCodePatterns, + bool IncludeBriefComments, + CodeCompleteConsumer &Consumer, + DiagnosticsEngine &Diag, LangOptions &LangOpts, + SourceManager &SourceMgr, FileManager &FileMgr, + SmallVectorImpl<StoredDiagnostic> &StoredDiagnostics, + SmallVectorImpl<const llvm::MemoryBuffer *> &OwnedBuffers) { + if (!Invocation) + return; + + SimpleTimer CompletionTimer(WantTiming); + CompletionTimer.setOutput("Code completion @ " + File + ":" + + Twine(Line) + ":" + Twine(Column)); + + IntrusiveRefCntPtr<CompilerInvocation> + CCInvocation(new CompilerInvocation(*Invocation)); + + FrontendOptions &FrontendOpts = CCInvocation->getFrontendOpts(); + CodeCompleteOptions &CodeCompleteOpts = FrontendOpts.CodeCompleteOpts; + PreprocessorOptions &PreprocessorOpts = CCInvocation->getPreprocessorOpts(); + + CodeCompleteOpts.IncludeMacros = IncludeMacros && + CachedCompletionResults.empty(); + CodeCompleteOpts.IncludeCodePatterns = IncludeCodePatterns; + CodeCompleteOpts.IncludeGlobals = CachedCompletionResults.empty(); + CodeCompleteOpts.IncludeBriefComments = IncludeBriefComments; + + assert(IncludeBriefComments == this->IncludeBriefCommentsInCodeCompletion); + + FrontendOpts.CodeCompletionAt.FileName = File; + FrontendOpts.CodeCompletionAt.Line = Line; + FrontendOpts.CodeCompletionAt.Column = Column; + + // Set the language options appropriately. + LangOpts = *CCInvocation->getLangOpts(); + + OwningPtr<CompilerInstance> Clang(new CompilerInstance()); + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<CompilerInstance> + CICleanup(Clang.get()); + + Clang->setInvocation(&*CCInvocation); + OriginalSourceFile = Clang->getFrontendOpts().Inputs[0].getFile(); + + // Set up diagnostics, capturing any diagnostics produced. + Clang->setDiagnostics(&Diag); + CaptureDroppedDiagnostics Capture(true, + Clang->getDiagnostics(), + StoredDiagnostics); + ProcessWarningOptions(Diag, CCInvocation->getDiagnosticOpts()); + + // Create the target instance. + Clang->setTarget(TargetInfo::CreateTargetInfo(Clang->getDiagnostics(), + &Clang->getTargetOpts())); + if (!Clang->hasTarget()) { + Clang->setInvocation(0); + 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].getKind() != IK_AST && + "FIXME: AST inputs not yet supported here!"); + assert(Clang->getFrontendOpts().Inputs[0].getKind() != 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) { + FilenameOrMemBuf fileOrBuf = RemappedFiles[I].second; + if (const llvm::MemoryBuffer * + memBuf = fileOrBuf.dyn_cast<const llvm::MemoryBuffer *>()) { + PreprocessorOpts.addRemappedFile(RemappedFiles[I].first, memBuf); + OwnedBuffers.push_back(memBuf); + } else { + const char *fname = fileOrBuf.get<const char *>(); + PreprocessorOpts.addRemappedFile(RemappedFiles[I].first, fname); + } + } + + // Use the code completion consumer we were given, but adding any cached + // code-completion results. + AugmentedCodeCompleteConsumer *AugmentedConsumer + = new AugmentedCodeCompleteConsumer(*this, Consumer, CodeCompleteOpts); + 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 (!getPreambleFile(this).empty()) { + std::string CompleteFilePath(File); + llvm::sys::fs::UniqueID CompleteFileID; + + if (!llvm::sys::fs::getUniqueID(CompleteFilePath, CompleteFileID)) { + std::string MainPath(OriginalSourceFile); + llvm::sys::fs::UniqueID MainID; + if (!llvm::sys::fs::getUniqueID(MainPath, MainID)) { + if (CompleteFileID == MainID && Line > 1) + 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 = getPreambleFile(this); + PreprocessorOpts.DisablePCHValidation = true; + + OwnedBuffers.push_back(OverrideMainBuffer); + } else { + PreprocessorOpts.PrecompiledPreambleBytes.first = 0; + PreprocessorOpts.PrecompiledPreambleBytes.second = false; + } + + // Disable the preprocessing record if modules are not enabled. + if (!Clang->getLangOpts().Modules) + PreprocessorOpts.DetailedRecord = false; + + OwningPtr<SyntaxOnlyAction> Act; + Act.reset(new SyntaxOnlyAction); + if (Act->BeginSourceFile(*Clang.get(), Clang->getFrontendOpts().Inputs[0])) { + Act->Execute(); + Act->EndSourceFile(); + } +} + +bool ASTUnit::Save(StringRef File) { + if (HadModuleLoaderFatalFailure) + return true; + + // Write to a temporary file and later rename it to the actual file, to avoid + // possible race conditions. + SmallString<128> TempPath; + TempPath = File; + TempPath += "-%%%%%%%%"; + int fd; + if (llvm::sys::fs::createUniqueFile(TempPath.str(), fd, TempPath)) + 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? + llvm::raw_fd_ostream Out(fd, /*shouldClose=*/true); + + serialize(Out); + Out.close(); + if (Out.has_error()) { + Out.clear_error(); + return true; + } + + if (llvm::sys::fs::rename(TempPath.str(), File)) { + bool exists; + llvm::sys::fs::remove(TempPath.str(), exists); + return true; + } + + return false; +} + +static bool serializeUnit(ASTWriter &Writer, + SmallVectorImpl<char> &Buffer, + Sema &S, + bool hasErrors, + raw_ostream &OS) { + Writer.WriteAST(S, std::string(), 0, "", hasErrors); + + // Write the generated bitstream to "Out". + if (!Buffer.empty()) + OS.write(Buffer.data(), Buffer.size()); + + return false; +} + +bool ASTUnit::serialize(raw_ostream &OS) { + bool hasErrors = getDiagnostics().hasErrorOccurred(); + + if (WriterData) + return serializeUnit(WriterData->Writer, WriterData->Buffer, + getSema(), hasErrors, OS); + + SmallString<128> Buffer; + llvm::BitstreamWriter Stream(Buffer); + ASTWriter Writer(Stream); + return serializeUnit(Writer, Buffer, getSema(), hasErrors, OS); +} + +typedef ContinuousRangeMap<unsigned, int, 2> SLocRemap; + +static void TranslateSLoc(SourceLocation &L, SLocRemap &Remap) { + unsigned Raw = L.getRawEncoding(); + const unsigned MacroBit = 1U << 31; + L = SourceLocation::getFromRawEncoding((Raw & MacroBit) | + ((Raw & ~MacroBit) + Remap.find(Raw & ~MacroBit)->second)); +} + +void ASTUnit::TranslateStoredDiagnostics( + ASTReader *MMan, + StringRef ModName, + SourceManager &SrcMgr, + const SmallVectorImpl<StoredDiagnostic> &Diags, + SmallVectorImpl<StoredDiagnostic> &Out) { + // The stored diagnostic has the old source manager in it; update + // the locations to refer into the new source manager. We also need to remap + // all the locations to the new view. This includes the diag location, any + // associated source ranges, and the source ranges of associated fix-its. + // FIXME: There should be a cleaner way to do this. + + SmallVector<StoredDiagnostic, 4> Result; + Result.reserve(Diags.size()); + assert(MMan && "Don't have a module manager"); + serialization::ModuleFile *Mod = MMan->ModuleMgr.lookup(ModName); + assert(Mod && "Don't have preamble module"); + SLocRemap &Remap = Mod->SLocRemap; + for (unsigned I = 0, N = Diags.size(); I != N; ++I) { + // Rebuild the StoredDiagnostic. + const StoredDiagnostic &SD = Diags[I]; + SourceLocation L = SD.getLocation(); + TranslateSLoc(L, Remap); + FullSourceLoc Loc(L, SrcMgr); + + SmallVector<CharSourceRange, 4> Ranges; + Ranges.reserve(SD.range_size()); + for (StoredDiagnostic::range_iterator I = SD.range_begin(), + E = SD.range_end(); + I != E; ++I) { + SourceLocation BL = I->getBegin(); + TranslateSLoc(BL, Remap); + SourceLocation EL = I->getEnd(); + TranslateSLoc(EL, Remap); + Ranges.push_back(CharSourceRange(SourceRange(BL, EL), I->isTokenRange())); + } + + SmallVector<FixItHint, 2> FixIts; + FixIts.reserve(SD.fixit_size()); + for (StoredDiagnostic::fixit_iterator I = SD.fixit_begin(), + E = SD.fixit_end(); + I != E; ++I) { + FixIts.push_back(FixItHint()); + FixItHint &FH = FixIts.back(); + FH.CodeToInsert = I->CodeToInsert; + SourceLocation BL = I->RemoveRange.getBegin(); + TranslateSLoc(BL, Remap); + SourceLocation EL = I->RemoveRange.getEnd(); + TranslateSLoc(EL, Remap); + FH.RemoveRange = CharSourceRange(SourceRange(BL, EL), + I->RemoveRange.isTokenRange()); + } + + Result.push_back(StoredDiagnostic(SD.getLevel(), SD.getID(), + SD.getMessage(), Loc, Ranges, FixIts)); + } + Result.swap(Out); +} + +void ASTUnit::addFileLevelDecl(Decl *D) { + assert(D); + + // We only care about local declarations. + if (D->isFromASTFile()) + return; + + SourceManager &SM = *SourceMgr; + SourceLocation Loc = D->getLocation(); + if (Loc.isInvalid() || !SM.isLocalSourceLocation(Loc)) + return; + + // We only keep track of the file-level declarations of each file. + if (!D->getLexicalDeclContext()->isFileContext()) + return; + + SourceLocation FileLoc = SM.getFileLoc(Loc); + assert(SM.isLocalSourceLocation(FileLoc)); + FileID FID; + unsigned Offset; + llvm::tie(FID, Offset) = SM.getDecomposedLoc(FileLoc); + if (FID.isInvalid()) + return; + + LocDeclsTy *&Decls = FileDecls[FID]; + if (!Decls) + Decls = new LocDeclsTy(); + + std::pair<unsigned, Decl *> LocDecl(Offset, D); + + if (Decls->empty() || Decls->back().first <= Offset) { + Decls->push_back(LocDecl); + return; + } + + LocDeclsTy::iterator I = std::upper_bound(Decls->begin(), Decls->end(), + LocDecl, llvm::less_first()); + + Decls->insert(I, LocDecl); +} + +void ASTUnit::findFileRegionDecls(FileID File, unsigned Offset, unsigned Length, + SmallVectorImpl<Decl *> &Decls) { + if (File.isInvalid()) + return; + + if (SourceMgr->isLoadedFileID(File)) { + assert(Ctx->getExternalSource() && "No external source!"); + return Ctx->getExternalSource()->FindFileRegionDecls(File, Offset, Length, + Decls); + } + + FileDeclsTy::iterator I = FileDecls.find(File); + if (I == FileDecls.end()) + return; + + LocDeclsTy &LocDecls = *I->second; + if (LocDecls.empty()) + return; + + LocDeclsTy::iterator BeginIt = + std::lower_bound(LocDecls.begin(), LocDecls.end(), + std::make_pair(Offset, (Decl *)0), llvm::less_first()); + if (BeginIt != LocDecls.begin()) + --BeginIt; + + // If we are pointing at a top-level decl inside an objc container, we need + // to backtrack until we find it otherwise we will fail to report that the + // region overlaps with an objc container. + while (BeginIt != LocDecls.begin() && + BeginIt->second->isTopLevelDeclInObjCContainer()) + --BeginIt; + + LocDeclsTy::iterator EndIt = std::upper_bound( + LocDecls.begin(), LocDecls.end(), + std::make_pair(Offset + Length, (Decl *)0), llvm::less_first()); + if (EndIt != LocDecls.end()) + ++EndIt; + + for (LocDeclsTy::iterator DIt = BeginIt; DIt != EndIt; ++DIt) + Decls.push_back(DIt->second); +} + +SourceLocation ASTUnit::getLocation(const FileEntry *File, + unsigned Line, unsigned Col) const { + const SourceManager &SM = getSourceManager(); + SourceLocation Loc = SM.translateFileLineCol(File, Line, Col); + return SM.getMacroArgExpandedLocation(Loc); +} + +SourceLocation ASTUnit::getLocation(const FileEntry *File, + unsigned Offset) const { + const SourceManager &SM = getSourceManager(); + SourceLocation FileLoc = SM.translateFileLineCol(File, 1, 1); + return SM.getMacroArgExpandedLocation(FileLoc.getLocWithOffset(Offset)); +} + +/// \brief If \arg Loc is a loaded location from the preamble, returns +/// the corresponding local location of the main file, otherwise it returns +/// \arg Loc. +SourceLocation ASTUnit::mapLocationFromPreamble(SourceLocation Loc) { + FileID PreambleID; + if (SourceMgr) + PreambleID = SourceMgr->getPreambleFileID(); + + if (Loc.isInvalid() || Preamble.empty() || PreambleID.isInvalid()) + return Loc; + + unsigned Offs; + if (SourceMgr->isInFileID(Loc, PreambleID, &Offs) && Offs < Preamble.size()) { + SourceLocation FileLoc + = SourceMgr->getLocForStartOfFile(SourceMgr->getMainFileID()); + return FileLoc.getLocWithOffset(Offs); + } + + return Loc; +} + +/// \brief If \arg Loc is a local location of the main file but inside the +/// preamble chunk, returns the corresponding loaded location from the +/// preamble, otherwise it returns \arg Loc. +SourceLocation ASTUnit::mapLocationToPreamble(SourceLocation Loc) { + FileID PreambleID; + if (SourceMgr) + PreambleID = SourceMgr->getPreambleFileID(); + + if (Loc.isInvalid() || Preamble.empty() || PreambleID.isInvalid()) + return Loc; + + unsigned Offs; + if (SourceMgr->isInFileID(Loc, SourceMgr->getMainFileID(), &Offs) && + Offs < Preamble.size()) { + SourceLocation FileLoc = SourceMgr->getLocForStartOfFile(PreambleID); + return FileLoc.getLocWithOffset(Offs); + } + + return Loc; +} + +bool ASTUnit::isInPreambleFileID(SourceLocation Loc) { + FileID FID; + if (SourceMgr) + FID = SourceMgr->getPreambleFileID(); + + if (Loc.isInvalid() || FID.isInvalid()) + return false; + + return SourceMgr->isInFileID(Loc, FID); +} + +bool ASTUnit::isInMainFileID(SourceLocation Loc) { + FileID FID; + if (SourceMgr) + FID = SourceMgr->getMainFileID(); + + if (Loc.isInvalid() || FID.isInvalid()) + return false; + + return SourceMgr->isInFileID(Loc, FID); +} + +SourceLocation ASTUnit::getEndOfPreambleFileID() { + FileID FID; + if (SourceMgr) + FID = SourceMgr->getPreambleFileID(); + + if (FID.isInvalid()) + return SourceLocation(); + + return SourceMgr->getLocForEndOfFile(FID); +} + +SourceLocation ASTUnit::getStartOfMainFileID() { + FileID FID; + if (SourceMgr) + FID = SourceMgr->getMainFileID(); + + if (FID.isInvalid()) + return SourceLocation(); + + return SourceMgr->getLocForStartOfFile(FID); +} + +std::pair<PreprocessingRecord::iterator, PreprocessingRecord::iterator> +ASTUnit::getLocalPreprocessingEntities() const { + if (isMainFileAST()) { + serialization::ModuleFile & + Mod = Reader->getModuleManager().getPrimaryModule(); + return Reader->getModulePreprocessedEntities(Mod); + } + + if (PreprocessingRecord *PPRec = PP->getPreprocessingRecord()) + return std::make_pair(PPRec->local_begin(), PPRec->local_end()); + + return std::make_pair(PreprocessingRecord::iterator(), + PreprocessingRecord::iterator()); +} + +bool ASTUnit::visitLocalTopLevelDecls(void *context, DeclVisitorFn Fn) { + if (isMainFileAST()) { + serialization::ModuleFile & + Mod = Reader->getModuleManager().getPrimaryModule(); + ASTReader::ModuleDeclIterator MDI, MDE; + llvm::tie(MDI, MDE) = Reader->getModuleFileLevelDecls(Mod); + for (; MDI != MDE; ++MDI) { + if (!Fn(context, *MDI)) + return false; + } + + return true; + } + + for (ASTUnit::top_level_iterator TL = top_level_begin(), + TLEnd = top_level_end(); + TL != TLEnd; ++TL) { + if (!Fn(context, *TL)) + return false; + } + + return true; +} + +namespace { +struct PCHLocatorInfo { + serialization::ModuleFile *Mod; + PCHLocatorInfo() : Mod(0) {} +}; +} + +static bool PCHLocator(serialization::ModuleFile &M, void *UserData) { + PCHLocatorInfo &Info = *static_cast<PCHLocatorInfo*>(UserData); + switch (M.Kind) { + case serialization::MK_Module: + return true; // skip dependencies. + case serialization::MK_PCH: + Info.Mod = &M; + return true; // found it. + case serialization::MK_Preamble: + return false; // look in dependencies. + case serialization::MK_MainFile: + return false; // look in dependencies. + } + + return true; +} + +const FileEntry *ASTUnit::getPCHFile() { + if (!Reader) + return 0; + + PCHLocatorInfo Info; + Reader->getModuleManager().visit(PCHLocator, &Info); + if (Info.Mod) + return Info.Mod->File; + + return 0; +} + +bool ASTUnit::isModuleFile() { + return isMainFileAST() && !ASTFileLangOpts.CurrentModule.empty(); +} + +void ASTUnit::PreambleData::countLines() const { + NumLines = 0; + if (empty()) + return; + + for (std::vector<char>::const_iterator + I = Buffer.begin(), E = Buffer.end(); I != E; ++I) { + if (*I == '\n') + ++NumLines; + } + if (Buffer.back() != '\n') + ++NumLines; +} + +#ifndef NDEBUG +ASTUnit::ConcurrencyState::ConcurrencyState() { + Mutex = new llvm::sys::MutexImpl(/*recursive=*/true); +} + +ASTUnit::ConcurrencyState::~ConcurrencyState() { + delete static_cast<llvm::sys::MutexImpl *>(Mutex); +} + +void ASTUnit::ConcurrencyState::start() { + bool acquired = static_cast<llvm::sys::MutexImpl *>(Mutex)->tryacquire(); + assert(acquired && "Concurrent access to ASTUnit!"); +} + +void ASTUnit::ConcurrencyState::finish() { + static_cast<llvm::sys::MutexImpl *>(Mutex)->release(); +} + +#else // NDEBUG + +ASTUnit::ConcurrencyState::ConcurrencyState() {} +ASTUnit::ConcurrencyState::~ConcurrencyState() {} +void ASTUnit::ConcurrencyState::start() {} +void ASTUnit::ConcurrencyState::finish() {} + +#endif 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..0c30b04 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/CacheTokens.cpp @@ -0,0 +1,652 @@ +//===--- 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/Diagnostic.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/FileSystemStatCache.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/OnDiskHashTable.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/Lexer.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.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; + FileData *Data; + +public: + PTHEntryKeyVariant(const FileEntry *fe) : FE(fe), Kind(IsFE), Data(0) {} + + PTHEntryKeyVariant(FileData *Data, const char *path) + : Path(path), Kind(IsDE), Data(new FileData(*Data)) {} + + explicit PTHEntryKeyVariant(const char *path) + : Path(path), Kind(IsNoExist), Data(0) {} + + bool isFile() const { return Kind == IsFE; } + + StringRef getString() const { + return Kind == IsFE ? FE->getName() : Path; + } + + unsigned getKind() const { return (unsigned) Kind; } + + void EmitData(raw_ostream& Out) { + switch (Kind) { + case IsFE: { + // Emit stat information. + llvm::sys::fs::UniqueID UID = FE->getUniqueID(); + ::Emit64(Out, UID.getFile()); + ::Emit64(Out, UID.getDevice()); + ::Emit64(Out, FE->getModificationTime()); + ::Emit64(Out, FE->getSize()); + } break; + case IsDE: + // Emit stat information. + ::Emit64(Out, Data->UniqueID.getFile()); + ::Emit64(Out, Data->UniqueID.getDevice()); + ::Emit64(Out, Data->ModTime); + ::Emit64(Out, Data->Size); + delete Data; + 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(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(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(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 Emit32(uint32_t V) { ::Emit32(Out, V); } + + void EmitBuf(const char *Ptr, unsigned NumBytes) { + Out.write(Ptr, NumBytes); + } + + void EmitString(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. + StringRef s(T.getLiteralData(), T.getLength()); + + // Get the string entry. + llvm::StringMapEntry<OffsetOpt> *E = &CachedStrs.GetOrCreateValue(s); + + // If this is a new string entry, bump the PTH offset. + if (!E->getValue().hasOffset()) { + E->getValue().setOffset(CurStrOffset); + StrEntries.push_back(E); + CurStrOffset += s.size() + 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 eod 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::eod); + Tmp.clearFlag(Token::StartOfLine); + Tmp.setIdentifierInfo(0); + EmitToken(Tmp); + ParsingPreprocessorDirective = false; + } + + if (Tok.is(tok::raw_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::raw_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::raw_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" << '\0'; + 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.getLangOpts(); + + 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.OrigEntry; + + // FIXME: Handle files with non-absolute paths. + if (llvm::sys::path::is_relative(FE->getName())) + 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 FileSystemStatCache { + PTHMap &PM; +public: + StatListener(PTHMap &pm) : PM(pm) {} + ~StatListener() {} + + LookupResult getStat(const char *Path, FileData &Data, bool isFile, + int *FileDescriptor) { + LookupResult Result = statChained(Path, Data, isFile, FileDescriptor); + + if (Result == CacheMissing) // Failed 'stat'. + PM.insert(PTHEntryKeyVariant(Path), PTHEntry()); + else if (Data.IsDirectory) { + // Only cache directories with absolute paths. + if (llvm::sys::path::is_relative(Path)) + return Result; + + PM.insert(PTHEntryKeyVariant(&Data, 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()); + SmallString<128> MainFilePath(MainFile->getName()); + + llvm::sys::fs::make_absolute(MainFilePath); + + // 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(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(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(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/ChainedDiagnosticConsumer.cpp b/contrib/llvm/tools/clang/lib/Frontend/ChainedDiagnosticConsumer.cpp new file mode 100644 index 0000000..d77fd18 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/ChainedDiagnosticConsumer.cpp @@ -0,0 +1,14 @@ +//===- ChainedDiagnosticConsumer.cpp - Chain Diagnostic Clients -----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/ChainedDiagnosticConsumer.h" + +using namespace clang; + +void ChainedDiagnosticConsumer::anchor() { } diff --git a/contrib/llvm/tools/clang/lib/Frontend/ChainedIncludesSource.cpp b/contrib/llvm/tools/clang/lib/Frontend/ChainedIncludesSource.cpp new file mode 100644 index 0000000..442177e --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/ChainedIncludesSource.cpp @@ -0,0 +1,246 @@ +//===- ChainedIncludesSource.cpp - Chained PCHs in Memory -------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the ChainedIncludesSource class, which converts headers +// to chained PCHs in memory, mainly used for testing. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/ChainedIncludesSource.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Parse/ParseAST.h" +#include "clang/Serialization/ASTReader.h" +#include "clang/Serialization/ASTWriter.h" +#include "llvm/Support/MemoryBuffer.h" + +using namespace clang; + +static ASTReader *createASTReader(CompilerInstance &CI, + StringRef pchFile, + SmallVectorImpl<llvm::MemoryBuffer *> &memBufs, + SmallVectorImpl<std::string> &bufNames, + ASTDeserializationListener *deserialListener = 0) { + Preprocessor &PP = CI.getPreprocessor(); + OwningPtr<ASTReader> Reader; + Reader.reset(new ASTReader(PP, CI.getASTContext(), /*isysroot=*/"", + /*DisableValidation=*/true)); + for (unsigned ti = 0; ti < bufNames.size(); ++ti) { + StringRef sr(bufNames[ti]); + Reader->addInMemoryBuffer(sr, memBufs[ti]); + } + Reader->setDeserializationListener(deserialListener); + switch (Reader->ReadAST(pchFile, serialization::MK_PCH, SourceLocation(), + ASTReader::ARR_None)) { + case ASTReader::Success: + // Set the predefines buffer as suggested by the PCH reader. + PP.setPredefines(Reader->getSuggestedPredefines()); + return Reader.take(); + + case ASTReader::Failure: + case ASTReader::Missing: + case ASTReader::OutOfDate: + case ASTReader::VersionMismatch: + case ASTReader::ConfigurationMismatch: + case ASTReader::HadErrors: + break; + } + return 0; +} + +ChainedIncludesSource::~ChainedIncludesSource() { + for (unsigned i = 0, e = CIs.size(); i != e; ++i) + delete CIs[i]; +} + +ChainedIncludesSource *ChainedIncludesSource::create(CompilerInstance &CI) { + + std::vector<std::string> &includes = CI.getPreprocessorOpts().ChainedIncludes; + assert(!includes.empty() && "No '-chain-include' in options!"); + + OwningPtr<ChainedIncludesSource> source(new ChainedIncludesSource()); + InputKind IK = CI.getFrontendOpts().Inputs[0].getKind(); + + SmallVector<llvm::MemoryBuffer *, 4> serialBufs; + SmallVector<std::string, 4> serialBufNames; + + for (unsigned i = 0, e = includes.size(); i != e; ++i) { + bool firstInclude = (i == 0); + OwningPtr<CompilerInvocation> CInvok; + CInvok.reset(new CompilerInvocation(CI.getInvocation())); + + CInvok->getPreprocessorOpts().ChainedIncludes.clear(); + CInvok->getPreprocessorOpts().ImplicitPCHInclude.clear(); + CInvok->getPreprocessorOpts().ImplicitPTHInclude.clear(); + CInvok->getPreprocessorOpts().DisablePCHValidation = true; + CInvok->getPreprocessorOpts().Includes.clear(); + CInvok->getPreprocessorOpts().MacroIncludes.clear(); + CInvok->getPreprocessorOpts().Macros.clear(); + + CInvok->getFrontendOpts().Inputs.clear(); + FrontendInputFile InputFile(includes[i], IK); + CInvok->getFrontendOpts().Inputs.push_back(InputFile); + + TextDiagnosticPrinter *DiagClient = + new TextDiagnosticPrinter(llvm::errs(), new DiagnosticOptions()); + IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + IntrusiveRefCntPtr<DiagnosticsEngine> Diags( + new DiagnosticsEngine(DiagID, &CI.getDiagnosticOpts(), DiagClient)); + + OwningPtr<CompilerInstance> Clang(new CompilerInstance()); + Clang->setInvocation(CInvok.take()); + Clang->setDiagnostics(Diags.getPtr()); + Clang->setTarget(TargetInfo::CreateTargetInfo(Clang->getDiagnostics(), + &Clang->getTargetOpts())); + Clang->createFileManager(); + Clang->createSourceManager(Clang->getFileManager()); + Clang->createPreprocessor(); + Clang->getDiagnosticClient().BeginSourceFile(Clang->getLangOpts(), + &Clang->getPreprocessor()); + Clang->createASTContext(); + + SmallVector<char, 256> serialAST; + llvm::raw_svector_ostream OS(serialAST); + OwningPtr<ASTConsumer> consumer; + consumer.reset(new PCHGenerator(Clang->getPreprocessor(), "-", 0, + /*isysroot=*/"", &OS)); + Clang->getASTContext().setASTMutationListener( + consumer->GetASTMutationListener()); + Clang->setASTConsumer(consumer.take()); + Clang->createSema(TU_Prefix, 0); + + if (firstInclude) { + Preprocessor &PP = Clang->getPreprocessor(); + PP.getBuiltinInfo().InitializeBuiltins(PP.getIdentifierTable(), + PP.getLangOpts()); + } else { + assert(!serialBufs.empty()); + SmallVector<llvm::MemoryBuffer *, 4> bufs; + for (unsigned si = 0, se = serialBufs.size(); si != se; ++si) { + bufs.push_back(llvm::MemoryBuffer::getMemBufferCopy( + StringRef(serialBufs[si]->getBufferStart(), + serialBufs[si]->getBufferSize()))); + } + std::string pchName = includes[i-1]; + llvm::raw_string_ostream os(pchName); + os << ".pch" << i-1; + os.flush(); + + serialBufNames.push_back(pchName); + + OwningPtr<ExternalASTSource> Reader; + + Reader.reset(createASTReader(*Clang, pchName, bufs, serialBufNames, + Clang->getASTConsumer().GetASTDeserializationListener())); + if (!Reader) + return 0; + Clang->setModuleManager(static_cast<ASTReader*>(Reader.get())); + Clang->getASTContext().setExternalSource(Reader); + } + + if (!Clang->InitializeSourceManager(InputFile)) + return 0; + + ParseAST(Clang->getSema()); + OS.flush(); + Clang->getDiagnosticClient().EndSourceFile(); + serialBufs.push_back( + llvm::MemoryBuffer::getMemBufferCopy(StringRef(serialAST.data(), + serialAST.size()))); + source->CIs.push_back(Clang.take()); + } + + assert(!serialBufs.empty()); + std::string pchName = includes.back() + ".pch-final"; + serialBufNames.push_back(pchName); + OwningPtr<ASTReader> Reader; + Reader.reset(createASTReader(CI, pchName, serialBufs, serialBufNames)); + if (!Reader) + return 0; + + source->FinalReader.reset(Reader.take()); + return source.take(); +} + +//===----------------------------------------------------------------------===// +// ExternalASTSource interface. +//===----------------------------------------------------------------------===// + +Decl *ChainedIncludesSource::GetExternalDecl(uint32_t ID) { + return getFinalReader().GetExternalDecl(ID); +} +Selector ChainedIncludesSource::GetExternalSelector(uint32_t ID) { + return getFinalReader().GetExternalSelector(ID); +} +uint32_t ChainedIncludesSource::GetNumExternalSelectors() { + return getFinalReader().GetNumExternalSelectors(); +} +Stmt *ChainedIncludesSource::GetExternalDeclStmt(uint64_t Offset) { + return getFinalReader().GetExternalDeclStmt(Offset); +} +CXXBaseSpecifier * +ChainedIncludesSource::GetExternalCXXBaseSpecifiers(uint64_t Offset) { + return getFinalReader().GetExternalCXXBaseSpecifiers(Offset); +} +bool +ChainedIncludesSource::FindExternalVisibleDeclsByName(const DeclContext *DC, + DeclarationName Name) { + return getFinalReader().FindExternalVisibleDeclsByName(DC, Name); +} +ExternalLoadResult +ChainedIncludesSource::FindExternalLexicalDecls(const DeclContext *DC, + bool (*isKindWeWant)(Decl::Kind), + SmallVectorImpl<Decl*> &Result) { + return getFinalReader().FindExternalLexicalDecls(DC, isKindWeWant, Result); +} +void ChainedIncludesSource::CompleteType(TagDecl *Tag) { + return getFinalReader().CompleteType(Tag); +} +void ChainedIncludesSource::CompleteType(ObjCInterfaceDecl *Class) { + return getFinalReader().CompleteType(Class); +} +void ChainedIncludesSource::StartedDeserializing() { + return getFinalReader().StartedDeserializing(); +} +void ChainedIncludesSource::FinishedDeserializing() { + return getFinalReader().FinishedDeserializing(); +} +void ChainedIncludesSource::StartTranslationUnit(ASTConsumer *Consumer) { + return getFinalReader().StartTranslationUnit(Consumer); +} +void ChainedIncludesSource::PrintStats() { + return getFinalReader().PrintStats(); +} +void ChainedIncludesSource::getMemoryBufferSizes(MemoryBufferSizes &sizes)const{ + for (unsigned i = 0, e = CIs.size(); i != e; ++i) { + if (const ExternalASTSource *eSrc = + CIs[i]->getASTContext().getExternalSource()) { + eSrc->getMemoryBufferSizes(sizes); + } + } + + getFinalReader().getMemoryBufferSizes(sizes); +} + +void ChainedIncludesSource::InitializeSema(Sema &S) { + return getFinalReader().InitializeSema(S); +} +void ChainedIncludesSource::ForgetSema() { + return getFinalReader().ForgetSema(); +} +void ChainedIncludesSource::ReadMethodPool(Selector Sel) { + getFinalReader().ReadMethodPool(Sel); +} +bool ChainedIncludesSource::LookupUnqualified(LookupResult &R, Scope *S) { + return getFinalReader().LookupUnqualified(R, S); +} + 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..eccb94c --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/CompilerInstance.cpp @@ -0,0 +1,1407 @@ +//===--- 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/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.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/Frontend/ChainedDiagnosticConsumer.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/LogDiagnosticPrinter.h" +#include "clang/Frontend/SerializedDiagnosticPrinter.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Frontend/Utils.h" +#include "clang/Frontend/VerifyDiagnosticConsumer.h" +#include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/PTHManager.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Sema/CodeCompleteConsumer.h" +#include "clang/Sema/Sema.h" +#include "clang/Serialization/ASTReader.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/Config/config.h" +#include "llvm/Support/CrashRecoveryContext.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/LockFileManager.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/system_error.h" +#include <sys/stat.h> +#include <time.h> + +using namespace clang; + +CompilerInstance::CompilerInstance() + : Invocation(new CompilerInvocation()), ModuleManager(0), + BuildGlobalModuleIndex(false), ModuleBuildFailed(false) { +} + +CompilerInstance::~CompilerInstance() { + assert(OutputFiles.empty() && "Still output files in flight?"); +} + +void CompilerInstance::setInvocation(CompilerInvocation *Value) { + Invocation = Value; +} + +bool CompilerInstance::shouldBuildGlobalModuleIndex() const { + return (BuildGlobalModuleIndex || + (ModuleManager && ModuleManager->isGlobalIndexUnavailable() && + getFrontendOpts().GenerateGlobalModuleIndex)) && + !ModuleBuildFailed; +} + +void CompilerInstance::setDiagnostics(DiagnosticsEngine *Value) { + Diagnostics = Value; +} + +void CompilerInstance::setTarget(TargetInfo *Value) { + Target = Value; +} + +void CompilerInstance::setFileManager(FileManager *Value) { + FileMgr = Value; +} + +void CompilerInstance::setSourceManager(SourceManager *Value) { + SourceMgr = Value; +} + +void CompilerInstance::setPreprocessor(Preprocessor *Value) { PP = Value; } + +void CompilerInstance::setASTContext(ASTContext *Value) { Context = 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 +static void SetUpDiagnosticLog(DiagnosticOptions *DiagOpts, + const CodeGenOptions *CodeGenOpts, + DiagnosticsEngine &Diags) { + std::string ErrorInfo; + bool OwnsStream = false; + raw_ostream *OS = &llvm::errs(); + if (DiagOpts->DiagnosticLogFile != "-") { + // Create the output stream. + llvm::raw_fd_ostream *FileOS( + new llvm::raw_fd_ostream(DiagOpts->DiagnosticLogFile.c_str(), ErrorInfo, + llvm::sys::fs::F_Append)); + if (!ErrorInfo.empty()) { + Diags.Report(diag::warn_fe_cc_log_diagnostics_failure) + << DiagOpts->DiagnosticLogFile << ErrorInfo; + } else { + FileOS->SetUnbuffered(); + FileOS->SetUseAtomicWrites(true); + OS = FileOS; + OwnsStream = true; + } + } + + // Chain in the diagnostic client which will log the diagnostics. + LogDiagnosticPrinter *Logger = new LogDiagnosticPrinter(*OS, DiagOpts, + OwnsStream); + if (CodeGenOpts) + Logger->setDwarfDebugFlags(CodeGenOpts->DwarfDebugFlags); + Diags.setClient(new ChainedDiagnosticConsumer(Diags.takeClient(), Logger)); +} + +static void SetupSerializedDiagnostics(DiagnosticOptions *DiagOpts, + DiagnosticsEngine &Diags, + StringRef OutputFile) { + std::string ErrorInfo; + OwningPtr<llvm::raw_fd_ostream> OS; + OS.reset(new llvm::raw_fd_ostream(OutputFile.str().c_str(), ErrorInfo, + llvm::sys::fs::F_Binary)); + + if (!ErrorInfo.empty()) { + Diags.Report(diag::warn_fe_serialized_diag_failure) + << OutputFile << ErrorInfo; + return; + } + + DiagnosticConsumer *SerializedConsumer = + clang::serialized_diags::create(OS.take(), DiagOpts); + + + Diags.setClient(new ChainedDiagnosticConsumer(Diags.takeClient(), + SerializedConsumer)); +} + +void CompilerInstance::createDiagnostics(DiagnosticConsumer *Client, + bool ShouldOwnClient) { + Diagnostics = createDiagnostics(&getDiagnosticOpts(), Client, + ShouldOwnClient, &getCodeGenOpts()); +} + +IntrusiveRefCntPtr<DiagnosticsEngine> +CompilerInstance::createDiagnostics(DiagnosticOptions *Opts, + DiagnosticConsumer *Client, + bool ShouldOwnClient, + const CodeGenOptions *CodeGenOpts) { + IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + IntrusiveRefCntPtr<DiagnosticsEngine> + Diags(new DiagnosticsEngine(DiagID, Opts)); + + // Create the diagnostic client for reporting errors or for + // implementing -verify. + if (Client) { + Diags->setClient(Client, ShouldOwnClient); + } else + Diags->setClient(new TextDiagnosticPrinter(llvm::errs(), Opts)); + + // Chain in -verify checker, if requested. + if (Opts->VerifyDiagnostics) + Diags->setClient(new VerifyDiagnosticConsumer(*Diags)); + + // Chain in -diagnostic-log-file dumper, if requested. + if (!Opts->DiagnosticLogFile.empty()) + SetUpDiagnosticLog(Opts, CodeGenOpts, *Diags); + + if (!Opts->DiagnosticSerializationFile.empty()) + SetupSerializedDiagnostics(Opts, *Diags, + Opts->DiagnosticSerializationFile); + + // Configure our handling of diagnostics. + ProcessWarningOptions(*Diags, *Opts); + + return Diags; +} + +// File Manager + +void CompilerInstance::createFileManager() { + FileMgr = new FileManager(getFileSystemOpts()); +} + +// Source Manager + +void CompilerInstance::createSourceManager(FileManager &FileMgr) { + SourceMgr = new SourceManager(getDiagnostics(), FileMgr); +} + +// Preprocessor + +void CompilerInstance::createPreprocessor() { + const PreprocessorOptions &PPOpts = getPreprocessorOpts(); + + // 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, getDiagnostics()); + + // Create the Preprocessor. + HeaderSearch *HeaderInfo = new HeaderSearch(&getHeaderSearchOpts(), + getSourceManager(), + getDiagnostics(), + getLangOpts(), + &getTarget()); + PP = new Preprocessor(&getPreprocessorOpts(), + getDiagnostics(), getLangOpts(), &getTarget(), + getSourceManager(), *HeaderInfo, *this, 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, getHeaderSearchOpts(), getFrontendOpts()); + + PP->setPreprocessedOutput(getPreprocessorOutputOpts().ShowCPP); + + // Set up the module path, including the hash for the + // module-creation options. + SmallString<256> SpecificModuleCache( + getHeaderSearchOpts().ModuleCachePath); + if (!getHeaderSearchOpts().DisableModuleHash) + llvm::sys::path::append(SpecificModuleCache, + getInvocation().getModuleHash()); + PP->getHeaderSearchInfo().setModuleCachePath(SpecificModuleCache); + + // Handle generating dependencies, if requested. + const DependencyOutputOptions &DepOpts = getDependencyOutputOpts(); + if (!DepOpts.OutputFile.empty()) + AttachDependencyFileGen(*PP, DepOpts); + if (!DepOpts.DOTOutputFile.empty()) + AttachDependencyGraphGen(*PP, DepOpts.DOTOutputFile, + getHeaderSearchOpts().Sysroot); + + + // Handle generating header include information, if requested. + if (DepOpts.ShowHeaderIncludes) + AttachHeaderIncludeGen(*PP); + if (!DepOpts.HeaderIncludeOutputFile.empty()) { + StringRef OutputPath = DepOpts.HeaderIncludeOutputFile; + if (OutputPath == "-") + OutputPath = ""; + AttachHeaderIncludeGen(*PP, /*ShowAllHeaders=*/true, OutputPath, + /*ShowDepth=*/false); + } + + if (DepOpts.PrintShowIncludes) { + AttachHeaderIncludeGen(*PP, /*ShowAllHeaders=*/false, /*OutputPath=*/"", + /*ShowDepth=*/true, /*MSStyle=*/true); + } +} + +// ASTContext + +void CompilerInstance::createASTContext() { + Preprocessor &PP = getPreprocessor(); + Context = new ASTContext(getLangOpts(), PP.getSourceManager(), + &getTarget(), PP.getIdentifierTable(), + PP.getSelectorTable(), PP.getBuiltinInfo(), + /*size_reserve=*/ 0); +} + +// ExternalASTSource + +void CompilerInstance::createPCHExternalASTSource(StringRef Path, + bool DisablePCHValidation, + bool AllowPCHWithCompilerErrors, + void *DeserializationListener){ + OwningPtr<ExternalASTSource> Source; + bool Preamble = getPreprocessorOpts().PrecompiledPreambleBytes.first != 0; + Source.reset(createPCHExternalASTSource(Path, getHeaderSearchOpts().Sysroot, + DisablePCHValidation, + AllowPCHWithCompilerErrors, + getPreprocessor(), getASTContext(), + DeserializationListener, + Preamble, + getFrontendOpts().UseGlobalModuleIndex)); + ModuleManager = static_cast<ASTReader*>(Source.get()); + getASTContext().setExternalSource(Source); +} + +ExternalASTSource * +CompilerInstance::createPCHExternalASTSource(StringRef Path, + const std::string &Sysroot, + bool DisablePCHValidation, + bool AllowPCHWithCompilerErrors, + Preprocessor &PP, + ASTContext &Context, + void *DeserializationListener, + bool Preamble, + bool UseGlobalModuleIndex) { + OwningPtr<ASTReader> Reader; + Reader.reset(new ASTReader(PP, Context, + Sysroot.empty() ? "" : Sysroot.c_str(), + DisablePCHValidation, + AllowPCHWithCompilerErrors, + UseGlobalModuleIndex)); + + Reader->setDeserializationListener( + static_cast<ASTDeserializationListener *>(DeserializationListener)); + switch (Reader->ReadAST(Path, + Preamble ? serialization::MK_Preamble + : serialization::MK_PCH, + SourceLocation(), + ASTReader::ARR_None)) { + 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::Missing: + case ASTReader::OutOfDate: + case ASTReader::VersionMismatch: + case ASTReader::ConfigurationMismatch: + case ASTReader::HadErrors: + // 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) { + setCodeCompletionConsumer( + createCodeCompletionConsumer(getPreprocessor(), + Loc.FileName, Loc.Line, Loc.Column, + getFrontendOpts().CodeCompleteOpts, + llvm::outs())); + if (!CompletionConsumer) + return; + } else if (EnableCodeCompletion(getPreprocessor(), Loc.FileName, + Loc.Line, Loc.Column)) { + setCodeCompletionConsumer(0); + return; + } + + if (CompletionConsumer->isOutputBinary() && + llvm::sys::ChangeStdoutToBinary()) { + getPreprocessor().getDiagnostics().Report(diag::err_fe_stdout_binary); + setCodeCompletionConsumer(0); + } +} + +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, + const CodeCompleteOptions &Opts, + raw_ostream &OS) { + if (EnableCodeCompletion(PP, Filename, Line, Column)) + return 0; + + // Set up the creation routine for code-completion. + return new PrintingCodeCompleteConsumer(Opts, OS); +} + +void CompilerInstance::createSema(TranslationUnitKind TUKind, + CodeCompleteConsumer *CompletionConsumer) { + TheSema.reset(new Sema(getPreprocessor(), getASTContext(), getASTConsumer(), + TUKind, CompletionConsumer)); +} + +// Output Files + +void CompilerInstance::addOutputFile(const OutputFile &OutFile) { + assert(OutFile.OS && "Attempt to add empty stream to output list!"); + OutputFiles.push_back(OutFile); +} + +void CompilerInstance::clearOutputFiles(bool EraseFiles) { + for (std::list<OutputFile>::iterator + it = OutputFiles.begin(), ie = OutputFiles.end(); it != ie; ++it) { + delete it->OS; + if (!it->TempFilename.empty()) { + if (EraseFiles) { + bool existed; + llvm::sys::fs::remove(it->TempFilename, existed); + } else { + SmallString<128> NewOutFile(it->Filename); + + // If '-working-directory' was passed, the output filename should be + // relative to that. + FileMgr->FixupRelativePath(NewOutFile); + if (llvm::error_code ec = llvm::sys::fs::rename(it->TempFilename, + NewOutFile.str())) { + getDiagnostics().Report(diag::err_unable_to_rename_temp) + << it->TempFilename << it->Filename << ec.message(); + + bool existed; + llvm::sys::fs::remove(it->TempFilename, existed); + } + } + } else if (!it->Filename.empty() && EraseFiles) + llvm::sys::fs::remove(it->Filename); + + } + OutputFiles.clear(); +} + +llvm::raw_fd_ostream * +CompilerInstance::createDefaultOutputFile(bool Binary, + StringRef InFile, + StringRef Extension) { + return createOutputFile(getFrontendOpts().OutputFile, Binary, + /*RemoveFileOnSignal=*/true, InFile, Extension, + /*UseTemporary=*/true); +} + +llvm::raw_fd_ostream * +CompilerInstance::createOutputFile(StringRef OutputPath, + bool Binary, bool RemoveFileOnSignal, + StringRef InFile, + StringRef Extension, + bool UseTemporary, + bool CreateMissingDirectories) { + std::string Error, OutputPathName, TempPathName; + llvm::raw_fd_ostream *OS = createOutputFile(OutputPath, Error, Binary, + RemoveFileOnSignal, + InFile, Extension, + UseTemporary, + CreateMissingDirectories, + &OutputPathName, + &TempPathName); + 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(OutputFile((OutputPathName != "-") ? OutputPathName : "", + TempPathName, OS)); + + return OS; +} + +llvm::raw_fd_ostream * +CompilerInstance::createOutputFile(StringRef OutputPath, + std::string &Error, + bool Binary, + bool RemoveFileOnSignal, + StringRef InFile, + StringRef Extension, + bool UseTemporary, + bool CreateMissingDirectories, + std::string *ResultPathName, + std::string *TempPathName) { + assert((!CreateMissingDirectories || UseTemporary) && + "CreateMissingDirectories is only allowed when using temporary files"); + + std::string OutFile, TempFile; + if (!OutputPath.empty()) { + OutFile = OutputPath; + } else if (InFile == "-") { + OutFile = "-"; + } else if (!Extension.empty()) { + SmallString<128> Path(InFile); + llvm::sys::path::replace_extension(Path, Extension); + OutFile = Path.str(); + } else { + OutFile = "-"; + } + + OwningPtr<llvm::raw_fd_ostream> OS; + std::string OSFile; + + if (UseTemporary) { + if (OutFile == "-") + UseTemporary = false; + else { + llvm::sys::fs::file_status Status; + llvm::sys::fs::status(OutputPath, Status); + if (llvm::sys::fs::exists(Status)) { + // Fail early if we can't write to the final destination. + if (!llvm::sys::fs::can_write(OutputPath)) + return 0; + + // Don't use a temporary if the output is a special file. This handles + // things like '-o /dev/null' + if (!llvm::sys::fs::is_regular_file(Status)) + UseTemporary = false; + } + } + } + + if (UseTemporary) { + // Create a temporary file. + SmallString<128> TempPath; + TempPath = OutFile; + TempPath += "-%%%%%%%%"; + int fd; + llvm::error_code EC = + llvm::sys::fs::createUniqueFile(TempPath.str(), fd, TempPath); + + if (CreateMissingDirectories && + EC == llvm::errc::no_such_file_or_directory) { + StringRef Parent = llvm::sys::path::parent_path(OutputPath); + EC = llvm::sys::fs::create_directories(Parent); + if (!EC) { + EC = llvm::sys::fs::createUniqueFile(TempPath.str(), fd, TempPath); + } + } + + if (!EC) { + OS.reset(new llvm::raw_fd_ostream(fd, /*shouldClose=*/true)); + OSFile = TempFile = TempPath.str(); + } + // If we failed to create the temporary, fallback to writing to the file + // directly. This handles the corner case where we cannot write to the + // directory, but can write to the file. + } + + if (!OS) { + OSFile = OutFile; + OS.reset(new llvm::raw_fd_ostream( + OSFile.c_str(), Error, + (Binary ? llvm::sys::fs::F_Binary : llvm::sys::fs::F_None))); + if (!Error.empty()) + return 0; + } + + // Make sure the out stream file gets removed if we crash. + if (RemoveFileOnSignal) + llvm::sys::RemoveFileOnSignal(OSFile); + + if (ResultPathName) + *ResultPathName = OutFile; + if (TempPathName) + *TempPathName = TempFile; + + return OS.take(); +} + +// Initialization Utilities + +bool CompilerInstance::InitializeSourceManager(const FrontendInputFile &Input){ + return InitializeSourceManager(Input, getDiagnostics(), + getFileManager(), getSourceManager(), + getFrontendOpts()); +} + +bool CompilerInstance::InitializeSourceManager(const FrontendInputFile &Input, + DiagnosticsEngine &Diags, + FileManager &FileMgr, + SourceManager &SourceMgr, + const FrontendOptions &Opts) { + SrcMgr::CharacteristicKind + Kind = Input.isSystem() ? SrcMgr::C_System : SrcMgr::C_User; + + if (Input.isBuffer()) { + SourceMgr.createMainFileIDForMemBuffer(Input.getBuffer(), Kind); + assert(!SourceMgr.getMainFileID().isInvalid() && + "Couldn't establish MainFileID!"); + return true; + } + + StringRef InputFile = Input.getFile(); + + // Figure out where to get and map in the main file. + if (InputFile != "-") { + const FileEntry *File = FileMgr.getFile(InputFile, /*OpenFile=*/true); + if (!File) { + Diags.Report(diag::err_fe_error_reading) << InputFile; + return false; + } + + // The natural SourceManager infrastructure can't currently handle named + // pipes, but we would at least like to accept them for the main + // file. Detect them here, read them with the volatile flag so FileMgr will + // pick up the correct size, and simply override their contents as we do for + // STDIN. + if (File->isNamedPipe()) { + std::string ErrorStr; + if (llvm::MemoryBuffer *MB = + FileMgr.getBufferForFile(File, &ErrorStr, /*isVolatile=*/true)) { + // Create a new virtual file that will have the correct size. + File = FileMgr.getVirtualFile(InputFile, MB->getBufferSize(), 0); + SourceMgr.overrideFileContents(File, MB); + } else { + Diags.Report(diag::err_cannot_open_file) << InputFile << ErrorStr; + return false; + } + } + + SourceMgr.createMainFileID(File, Kind); + } else { + OwningPtr<llvm::MemoryBuffer> SB; + if (llvm::error_code ec = llvm::MemoryBuffer::getSTDIN(SB)) { + Diags.Report(diag::err_fe_error_reading_stdin) << ec.message(); + return false; + } + const FileEntry *File = FileMgr.getVirtualFile(SB->getBufferIdentifier(), + SB->getBufferSize(), 0); + SourceMgr.createMainFileID(File, Kind); + SourceMgr.overrideFileContents(File, SB.take()); + } + + assert(!SourceMgr.getMainFileID().isInvalid() && + "Couldn't establish MainFileID!"); + 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. + 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()); + + // rewriter project will change target built-in bool type from its default. + if (getFrontendOpts().ProgramAction == frontend::RewriteObjC) + getTarget().noSignedCharForObjCBool(); + + // Validate/process some options. + if (getHeaderSearchOpts().Verbose) + OS << "clang -cc1 version " CLANG_VERSION_STRING + << " based upon " << PACKAGE_STRING + << " default target " << llvm::sys::getDefaultTargetTriple() << "\n"; + + if (getFrontendOpts().ShowTimers) + createFrontendTimer(); + + if (getFrontendOpts().ShowStats) + llvm::EnableStatistics(); + + for (unsigned i = 0, e = getFrontendOpts().Inputs.size(); i != e; ++i) { + // Reset the ID tables if we are reusing the SourceManager. + if (hasSourceManager()) + getSourceManager().clearIDTables(); + + if (Act.BeginSourceFile(*this, getFrontendOpts().Inputs[i])) { + Act.Execute(); + Act.EndSourceFile(); + } + } + + // Notify the diagnostic client that all files were processed. + getDiagnostics().getClient()->finish(); + + if (getDiagnosticOpts().ShowCarets) { + // We can have multiple diagnostics sharing one diagnostic client. + // Get the total number of warnings/errors from the client. + unsigned NumWarnings = getDiagnostics().getClient()->getNumWarnings(); + unsigned NumErrors = getDiagnostics().getClient()->getNumErrors(); + + 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 !getDiagnostics().getClient()->getNumErrors(); +} + +/// \brief Determine the appropriate source input kind based on language +/// options. +static InputKind getSourceInputKindFromOptions(const LangOptions &LangOpts) { + if (LangOpts.OpenCL) + return IK_OpenCL; + if (LangOpts.CUDA) + return IK_CUDA; + if (LangOpts.ObjC1) + return LangOpts.CPlusPlus? IK_ObjCXX : IK_ObjC; + return LangOpts.CPlusPlus? IK_CXX : IK_C; +} + +namespace { + struct CompileModuleMapData { + CompilerInstance &Instance; + GenerateModuleAction &CreateModuleAction; + }; +} + +/// \brief Helper function that executes the module-generating action under +/// a crash recovery context. +static void doCompileMapModule(void *UserData) { + CompileModuleMapData &Data + = *reinterpret_cast<CompileModuleMapData *>(UserData); + Data.Instance.ExecuteAction(Data.CreateModuleAction); +} + +namespace { + /// \brief Function object that checks with the given macro definition should + /// be removed, because it is one of the ignored macros. + class RemoveIgnoredMacro { + const HeaderSearchOptions &HSOpts; + + public: + explicit RemoveIgnoredMacro(const HeaderSearchOptions &HSOpts) + : HSOpts(HSOpts) { } + + bool operator()(const std::pair<std::string, bool> &def) const { + StringRef MacroDef = def.first; + return HSOpts.ModulesIgnoreMacros.count(MacroDef.split('=').first) > 0; + } + }; +} + +/// \brief Compile a module file for the given module, using the options +/// provided by the importing compiler instance. +static void compileModule(CompilerInstance &ImportingInstance, + SourceLocation ImportLoc, + Module *Module, + StringRef ModuleFileName) { + // FIXME: have LockFileManager return an error_code so that we can + // avoid the mkdir when the directory already exists. + StringRef Dir = llvm::sys::path::parent_path(ModuleFileName); + llvm::sys::fs::create_directories(Dir); + + llvm::LockFileManager Locked(ModuleFileName); + switch (Locked) { + case llvm::LockFileManager::LFS_Error: + return; + + case llvm::LockFileManager::LFS_Owned: + // We're responsible for building the module ourselves. Do so below. + break; + + case llvm::LockFileManager::LFS_Shared: + // Someone else is responsible for building the module. Wait for them to + // finish. + Locked.waitForUnlock(); + return; + } + + ModuleMap &ModMap + = ImportingInstance.getPreprocessor().getHeaderSearchInfo().getModuleMap(); + + // Construct a compiler invocation for creating this module. + IntrusiveRefCntPtr<CompilerInvocation> Invocation + (new CompilerInvocation(ImportingInstance.getInvocation())); + + PreprocessorOptions &PPOpts = Invocation->getPreprocessorOpts(); + + // For any options that aren't intended to affect how a module is built, + // reset them to their default values. + Invocation->getLangOpts()->resetNonModularOptions(); + PPOpts.resetNonModularOptions(); + + // Remove any macro definitions that are explicitly ignored by the module. + // They aren't supposed to affect how the module is built anyway. + const HeaderSearchOptions &HSOpts = Invocation->getHeaderSearchOpts(); + PPOpts.Macros.erase(std::remove_if(PPOpts.Macros.begin(), PPOpts.Macros.end(), + RemoveIgnoredMacro(HSOpts)), + PPOpts.Macros.end()); + + + // Note the name of the module we're building. + Invocation->getLangOpts()->CurrentModule = Module->getTopLevelModuleName(); + + // Make sure that the failed-module structure has been allocated in + // the importing instance, and propagate the pointer to the newly-created + // instance. + PreprocessorOptions &ImportingPPOpts + = ImportingInstance.getInvocation().getPreprocessorOpts(); + if (!ImportingPPOpts.FailedModules) + ImportingPPOpts.FailedModules = new PreprocessorOptions::FailedModulesSet; + PPOpts.FailedModules = ImportingPPOpts.FailedModules; + + // If there is a module map file, build the module using the module map. + // Set up the inputs/outputs so that we build the module from its umbrella + // header. + FrontendOptions &FrontendOpts = Invocation->getFrontendOpts(); + FrontendOpts.OutputFile = ModuleFileName.str(); + FrontendOpts.DisableFree = false; + FrontendOpts.GenerateGlobalModuleIndex = false; + FrontendOpts.Inputs.clear(); + InputKind IK = getSourceInputKindFromOptions(*Invocation->getLangOpts()); + + // Don't free the remapped file buffers; they are owned by our caller. + PPOpts.RetainRemappedFileBuffers = true; + + Invocation->getDiagnosticOpts().VerifyDiagnostics = 0; + assert(ImportingInstance.getInvocation().getModuleHash() == + Invocation->getModuleHash() && "Module hash mismatch!"); + + // Construct a compiler instance that will be used to actually create the + // module. + CompilerInstance Instance; + Instance.setInvocation(&*Invocation); + + Instance.createDiagnostics(new ForwardingDiagnosticConsumer( + ImportingInstance.getDiagnosticClient()), + /*ShouldOwnClient=*/true); + + // Note that this module is part of the module build stack, so that we + // can detect cycles in the module graph. + Instance.createFileManager(); // FIXME: Adopt file manager from importer? + Instance.createSourceManager(Instance.getFileManager()); + SourceManager &SourceMgr = Instance.getSourceManager(); + SourceMgr.setModuleBuildStack( + ImportingInstance.getSourceManager().getModuleBuildStack()); + SourceMgr.pushModuleBuildStack(Module->getTopLevelModuleName(), + FullSourceLoc(ImportLoc, ImportingInstance.getSourceManager())); + + // Get or create the module map that we'll use to build this module. + std::string InferredModuleMapContent; + if (const FileEntry *ModuleMapFile = + ModMap.getContainingModuleMapFile(Module)) { + // Use the module map where this module resides. + FrontendOpts.Inputs.push_back( + FrontendInputFile(ModuleMapFile->getName(), IK)); + } else { + llvm::raw_string_ostream OS(InferredModuleMapContent); + Module->print(OS); + OS.flush(); + FrontendOpts.Inputs.push_back( + FrontendInputFile("__inferred_module.map", IK)); + + const llvm::MemoryBuffer *ModuleMapBuffer = + llvm::MemoryBuffer::getMemBuffer(InferredModuleMapContent); + ModuleMapFile = Instance.getFileManager().getVirtualFile( + "__inferred_module.map", InferredModuleMapContent.size(), 0); + SourceMgr.overrideFileContents(ModuleMapFile, ModuleMapBuffer); + } + + // Construct a module-generating action. + GenerateModuleAction CreateModuleAction(Module->IsSystem); + + // Execute the action to actually build the module in-place. Use a separate + // thread so that we get a stack large enough. + const unsigned ThreadStackSize = 8 << 20; + llvm::CrashRecoveryContext CRC; + CompileModuleMapData Data = { Instance, CreateModuleAction }; + CRC.RunSafelyOnThread(&doCompileMapModule, &Data, ThreadStackSize); + + + // Delete the temporary module map file. + // FIXME: Even though we're executing under crash protection, it would still + // be nice to do this with RemoveFileOnSignal when we can. However, that + // doesn't make sense for all clients, so clean this up manually. + Instance.clearOutputFiles(/*EraseFiles=*/true); + + // We've rebuilt a module. If we're allowed to generate or update the global + // module index, record that fact in the importing compiler instance. + if (ImportingInstance.getFrontendOpts().GenerateGlobalModuleIndex) { + ImportingInstance.setBuildGlobalModuleIndex(true); + } +} + +/// \brief Diagnose differences between the current definition of the given +/// configuration macro and the definition provided on the command line. +static void checkConfigMacro(Preprocessor &PP, StringRef ConfigMacro, + Module *Mod, SourceLocation ImportLoc) { + IdentifierInfo *Id = PP.getIdentifierInfo(ConfigMacro); + SourceManager &SourceMgr = PP.getSourceManager(); + + // If this identifier has never had a macro definition, then it could + // not have changed. + if (!Id->hadMacroDefinition()) + return; + + // If this identifier does not currently have a macro definition, + // check whether it had one on the command line. + if (!Id->hasMacroDefinition()) { + MacroDirective::DefInfo LatestDef = + PP.getMacroDirectiveHistory(Id)->getDefinition(); + for (MacroDirective::DefInfo Def = LatestDef; Def; + Def = Def.getPreviousDefinition()) { + FileID FID = SourceMgr.getFileID(Def.getLocation()); + if (FID.isInvalid()) + continue; + + // We only care about the predefines buffer. + if (FID != PP.getPredefinesFileID()) + continue; + + // This macro was defined on the command line, then #undef'd later. + // Complain. + PP.Diag(ImportLoc, diag::warn_module_config_macro_undef) + << true << ConfigMacro << Mod->getFullModuleName(); + if (LatestDef.isUndefined()) + PP.Diag(LatestDef.getUndefLocation(), diag::note_module_def_undef_here) + << true; + return; + } + + // Okay: no definition in the predefines buffer. + return; + } + + // This identifier has a macro definition. Check whether we had a definition + // on the command line. + MacroDirective::DefInfo LatestDef = + PP.getMacroDirectiveHistory(Id)->getDefinition(); + MacroDirective::DefInfo PredefinedDef; + for (MacroDirective::DefInfo Def = LatestDef; Def; + Def = Def.getPreviousDefinition()) { + FileID FID = SourceMgr.getFileID(Def.getLocation()); + if (FID.isInvalid()) + continue; + + // We only care about the predefines buffer. + if (FID != PP.getPredefinesFileID()) + continue; + + PredefinedDef = Def; + break; + } + + // If there was no definition for this macro in the predefines buffer, + // complain. + if (!PredefinedDef || + (!PredefinedDef.getLocation().isValid() && + PredefinedDef.getUndefLocation().isValid())) { + PP.Diag(ImportLoc, diag::warn_module_config_macro_undef) + << false << ConfigMacro << Mod->getFullModuleName(); + PP.Diag(LatestDef.getLocation(), diag::note_module_def_undef_here) + << false; + return; + } + + // If the current macro definition is the same as the predefined macro + // definition, it's okay. + if (LatestDef.getMacroInfo() == PredefinedDef.getMacroInfo() || + LatestDef.getMacroInfo()->isIdenticalTo(*PredefinedDef.getMacroInfo(),PP, + /*Syntactically=*/true)) + return; + + // The macro definitions differ. + PP.Diag(ImportLoc, diag::warn_module_config_macro_undef) + << false << ConfigMacro << Mod->getFullModuleName(); + PP.Diag(LatestDef.getLocation(), diag::note_module_def_undef_here) + << false; +} + +/// \brief Write a new timestamp file with the given path. +static void writeTimestampFile(StringRef TimestampFile) { + std::string ErrorInfo; + llvm::raw_fd_ostream Out(TimestampFile.str().c_str(), ErrorInfo, + llvm::sys::fs::F_Binary); +} + +/// \brief Prune the module cache of modules that haven't been accessed in +/// a long time. +static void pruneModuleCache(const HeaderSearchOptions &HSOpts) { + struct stat StatBuf; + llvm::SmallString<128> TimestampFile; + TimestampFile = HSOpts.ModuleCachePath; + llvm::sys::path::append(TimestampFile, "modules.timestamp"); + + // Try to stat() the timestamp file. + if (::stat(TimestampFile.c_str(), &StatBuf)) { + // If the timestamp file wasn't there, create one now. + if (errno == ENOENT) { + writeTimestampFile(TimestampFile); + } + return; + } + + // Check whether the time stamp is older than our pruning interval. + // If not, do nothing. + time_t TimeStampModTime = StatBuf.st_mtime; + time_t CurrentTime = time(0); + if (CurrentTime - TimeStampModTime <= time_t(HSOpts.ModuleCachePruneInterval)) + return; + + // Write a new timestamp file so that nobody else attempts to prune. + // There is a benign race condition here, if two Clang instances happen to + // notice at the same time that the timestamp is out-of-date. + writeTimestampFile(TimestampFile); + + // Walk the entire module cache, looking for unused module files and module + // indices. + llvm::error_code EC; + SmallString<128> ModuleCachePathNative; + llvm::sys::path::native(HSOpts.ModuleCachePath, ModuleCachePathNative); + for (llvm::sys::fs::directory_iterator + Dir(ModuleCachePathNative.str(), EC), DirEnd; + Dir != DirEnd && !EC; Dir.increment(EC)) { + // If we don't have a directory, there's nothing to look into. + if (!llvm::sys::fs::is_directory(Dir->path())) + continue; + + // Walk all of the files within this directory. + bool RemovedAllFiles = true; + for (llvm::sys::fs::directory_iterator File(Dir->path(), EC), FileEnd; + File != FileEnd && !EC; File.increment(EC)) { + // We only care about module and global module index files. + if (llvm::sys::path::extension(File->path()) != ".pcm" && + llvm::sys::path::filename(File->path()) != "modules.idx") { + RemovedAllFiles = false; + continue; + } + + // Look at this file. If we can't stat it, there's nothing interesting + // there. + if (::stat(File->path().c_str(), &StatBuf)) { + RemovedAllFiles = false; + continue; + } + + // If the file has been used recently enough, leave it there. + time_t FileAccessTime = StatBuf.st_atime; + if (CurrentTime - FileAccessTime <= + time_t(HSOpts.ModuleCachePruneAfter)) { + RemovedAllFiles = false; + continue; + } + + // Remove the file. + bool Existed; + if (llvm::sys::fs::remove(File->path(), Existed) || !Existed) { + RemovedAllFiles = false; + } + } + + // If we removed all of the files in the directory, remove the directory + // itself. + if (RemovedAllFiles) { + bool Existed; + llvm::sys::fs::remove(Dir->path(), Existed); + } + } +} + +ModuleLoadResult +CompilerInstance::loadModule(SourceLocation ImportLoc, + ModuleIdPath Path, + Module::NameVisibilityKind Visibility, + bool IsInclusionDirective) { + // Determine what file we're searching from. + StringRef ModuleName = Path[0].first->getName(); + SourceLocation ModuleNameLoc = Path[0].second; + + // If we've already handled this import, just return the cached result. + // This one-element cache is important to eliminate redundant diagnostics + // when both the preprocessor and parser see the same import declaration. + if (!ImportLoc.isInvalid() && LastModuleImportLoc == ImportLoc) { + // Make the named module visible. + if (LastModuleImportResult && ModuleName != getLangOpts().CurrentModule) + ModuleManager->makeModuleVisible(LastModuleImportResult, Visibility, + ImportLoc, /*Complain=*/false); + return LastModuleImportResult; + } + + clang::Module *Module = 0; + + // If we don't already have information on this module, load the module now. + llvm::DenseMap<const IdentifierInfo *, clang::Module *>::iterator Known + = KnownModules.find(Path[0].first); + if (Known != KnownModules.end()) { + // Retrieve the cached top-level module. + Module = Known->second; + } else if (ModuleName == getLangOpts().CurrentModule) { + // This is the module we're building. + Module = PP->getHeaderSearchInfo().getModuleMap().findModule(ModuleName); + Known = KnownModules.insert(std::make_pair(Path[0].first, Module)).first; + } else { + // Search for a module with the given name. + Module = PP->getHeaderSearchInfo().lookupModule(ModuleName); + std::string ModuleFileName; + if (Module) { + ModuleFileName = PP->getHeaderSearchInfo().getModuleFileName(Module); + } else + ModuleFileName = PP->getHeaderSearchInfo().getModuleFileName(ModuleName); + + // If we don't already have an ASTReader, create one now. + if (!ModuleManager) { + if (!hasASTContext()) + createASTContext(); + + // If we're not recursively building a module, check whether we + // need to prune the module cache. + if (getSourceManager().getModuleBuildStack().empty() && + getHeaderSearchOpts().ModuleCachePruneInterval > 0 && + getHeaderSearchOpts().ModuleCachePruneAfter > 0) { + pruneModuleCache(getHeaderSearchOpts()); + } + + std::string Sysroot = getHeaderSearchOpts().Sysroot; + const PreprocessorOptions &PPOpts = getPreprocessorOpts(); + ModuleManager = new ASTReader(getPreprocessor(), *Context, + Sysroot.empty() ? "" : Sysroot.c_str(), + PPOpts.DisablePCHValidation, + /*AllowASTWithCompilerErrors=*/false, + getFrontendOpts().UseGlobalModuleIndex); + if (hasASTConsumer()) { + ModuleManager->setDeserializationListener( + getASTConsumer().GetASTDeserializationListener()); + getASTContext().setASTMutationListener( + getASTConsumer().GetASTMutationListener()); + } + OwningPtr<ExternalASTSource> Source; + Source.reset(ModuleManager); + getASTContext().setExternalSource(Source); + if (hasSema()) + ModuleManager->InitializeSema(getSema()); + if (hasASTConsumer()) + ModuleManager->StartTranslationUnit(&getASTConsumer()); + } + + // Try to load the module file. + unsigned ARRFlags = ASTReader::ARR_OutOfDate | ASTReader::ARR_Missing; + switch (ModuleManager->ReadAST(ModuleFileName, serialization::MK_Module, + ImportLoc, ARRFlags)) { + case ASTReader::Success: + break; + + case ASTReader::OutOfDate: + case ASTReader::Missing: { + // The module file is missing or out-of-date. Build it. + + // If we don't have a module, we don't know how to build the module file. + // Complain and return. + if (!Module) { + getDiagnostics().Report(ModuleNameLoc, diag::err_module_not_found) + << ModuleName + << SourceRange(ImportLoc, ModuleNameLoc); + ModuleBuildFailed = true; + return ModuleLoadResult(); + } + + // Check whether there is a cycle in the module graph. + ModuleBuildStack ModPath = getSourceManager().getModuleBuildStack(); + ModuleBuildStack::iterator Pos = ModPath.begin(), PosEnd = ModPath.end(); + for (; Pos != PosEnd; ++Pos) { + if (Pos->first == ModuleName) + break; + } + + if (Pos != PosEnd) { + SmallString<256> CyclePath; + for (; Pos != PosEnd; ++Pos) { + CyclePath += Pos->first; + CyclePath += " -> "; + } + CyclePath += ModuleName; + + getDiagnostics().Report(ModuleNameLoc, diag::err_module_cycle) + << ModuleName << CyclePath; + return ModuleLoadResult(); + } + + // Check whether we have already attempted to build this module (but + // failed). + if (getPreprocessorOpts().FailedModules && + getPreprocessorOpts().FailedModules->hasAlreadyFailed(ModuleName)) { + getDiagnostics().Report(ModuleNameLoc, diag::err_module_not_built) + << ModuleName + << SourceRange(ImportLoc, ModuleNameLoc); + ModuleBuildFailed = true; + return ModuleLoadResult(); + } + + // Try to compile the module. + compileModule(*this, ModuleNameLoc, Module, ModuleFileName); + + // Try to read the module file, now that we've compiled it. + ASTReader::ASTReadResult ReadResult + = ModuleManager->ReadAST(ModuleFileName, + serialization::MK_Module, ImportLoc, + ASTReader::ARR_Missing); + if (ReadResult != ASTReader::Success) { + if (ReadResult == ASTReader::Missing) { + getDiagnostics().Report(ModuleNameLoc, + Module? diag::err_module_not_built + : diag::err_module_not_found) + << ModuleName + << SourceRange(ImportLoc, ModuleNameLoc); + } + + if (getPreprocessorOpts().FailedModules) + getPreprocessorOpts().FailedModules->addFailed(ModuleName); + KnownModules[Path[0].first] = 0; + ModuleBuildFailed = true; + return ModuleLoadResult(); + } + + // Okay, we've rebuilt and now loaded the module. + break; + } + + case ASTReader::VersionMismatch: + case ASTReader::ConfigurationMismatch: + case ASTReader::HadErrors: + ModuleLoader::HadFatalFailure = true; + // FIXME: The ASTReader will already have complained, but can we showhorn + // that diagnostic information into a more useful form? + KnownModules[Path[0].first] = 0; + return ModuleLoadResult(); + + case ASTReader::Failure: + ModuleLoader::HadFatalFailure = true; + // Already complained, but note now that we failed. + KnownModules[Path[0].first] = 0; + ModuleBuildFailed = true; + return ModuleLoadResult(); + } + + if (!Module) { + // If we loaded the module directly, without finding a module map first, + // we'll have loaded the module's information from the module itself. + Module = PP->getHeaderSearchInfo().getModuleMap() + .findModule((Path[0].first->getName())); + } + + // Cache the result of this top-level module lookup for later. + Known = KnownModules.insert(std::make_pair(Path[0].first, Module)).first; + } + + // If we never found the module, fail. + if (!Module) + return ModuleLoadResult(); + + // Verify that the rest of the module path actually corresponds to + // a submodule. + if (Path.size() > 1) { + for (unsigned I = 1, N = Path.size(); I != N; ++I) { + StringRef Name = Path[I].first->getName(); + clang::Module *Sub = Module->findSubmodule(Name); + + if (!Sub) { + // Attempt to perform typo correction to find a module name that works. + SmallVector<StringRef, 2> Best; + unsigned BestEditDistance = (std::numeric_limits<unsigned>::max)(); + + for (clang::Module::submodule_iterator J = Module->submodule_begin(), + JEnd = Module->submodule_end(); + J != JEnd; ++J) { + unsigned ED = Name.edit_distance((*J)->Name, + /*AllowReplacements=*/true, + BestEditDistance); + if (ED <= BestEditDistance) { + if (ED < BestEditDistance) { + Best.clear(); + BestEditDistance = ED; + } + + Best.push_back((*J)->Name); + } + } + + // If there was a clear winner, user it. + if (Best.size() == 1) { + getDiagnostics().Report(Path[I].second, + diag::err_no_submodule_suggest) + << Path[I].first << Module->getFullModuleName() << Best[0] + << SourceRange(Path[0].second, Path[I-1].second) + << FixItHint::CreateReplacement(SourceRange(Path[I].second), + Best[0]); + + Sub = Module->findSubmodule(Best[0]); + } + } + + if (!Sub) { + // No submodule by this name. Complain, and don't look for further + // submodules. + getDiagnostics().Report(Path[I].second, diag::err_no_submodule) + << Path[I].first << Module->getFullModuleName() + << SourceRange(Path[0].second, Path[I-1].second); + break; + } + + Module = Sub; + } + } + + // Make the named module visible, if it's not already part of the module + // we are parsing. + if (ModuleName != getLangOpts().CurrentModule) { + if (!Module->IsFromModuleFile) { + // We have an umbrella header or directory that doesn't actually include + // all of the headers within the directory it covers. Complain about + // this missing submodule and recover by forgetting that we ever saw + // this submodule. + // FIXME: Should we detect this at module load time? It seems fairly + // expensive (and rare). + getDiagnostics().Report(ImportLoc, diag::warn_missing_submodule) + << Module->getFullModuleName() + << SourceRange(Path.front().second, Path.back().second); + + return ModuleLoadResult(0, true); + } + + // Check whether this module is available. + clang::Module::Requirement Requirement; + if (!Module->isAvailable(getLangOpts(), getTarget(), Requirement)) { + getDiagnostics().Report(ImportLoc, diag::err_module_unavailable) + << Module->getFullModuleName() + << Requirement.second << Requirement.first + << SourceRange(Path.front().second, Path.back().second); + LastModuleImportLoc = ImportLoc; + LastModuleImportResult = ModuleLoadResult(); + return ModuleLoadResult(); + } + + ModuleManager->makeModuleVisible(Module, Visibility, ImportLoc, + /*Complain=*/true); + } + + // Check for any configuration macros that have changed. + clang::Module *TopModule = Module->getTopLevelModule(); + for (unsigned I = 0, N = TopModule->ConfigMacros.size(); I != N; ++I) { + checkConfigMacro(getPreprocessor(), TopModule->ConfigMacros[I], + Module, ImportLoc); + } + + // If this module import was due to an inclusion directive, create an + // implicit import declaration to capture it in the AST. + if (IsInclusionDirective && hasASTContext()) { + TranslationUnitDecl *TU = getASTContext().getTranslationUnitDecl(); + ImportDecl *ImportD = ImportDecl::CreateImplicit(getASTContext(), TU, + ImportLoc, Module, + Path.back().second); + TU->addDecl(ImportD); + if (Consumer) + Consumer->HandleImplicitImportDecl(ImportD); + } + + LastModuleImportLoc = ImportLoc; + LastModuleImportResult = ModuleLoadResult(Module, false); + return LastModuleImportResult; +} + +void CompilerInstance::makeModuleVisible(Module *Mod, + Module::NameVisibilityKind Visibility, + SourceLocation ImportLoc, + bool Complain){ + ModuleManager->makeModuleVisible(Mod, Visibility, ImportLoc, Complain); +} + 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..ec1e53f --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/CompilerInvocation.cpp @@ -0,0 +1,1829 @@ +//===--- 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/FileManager.h" +#include "clang/Basic/Version.h" +#include "clang/Driver/DriverDiagnostic.h" +#include "clang/Driver/Options.h" +#include "clang/Driver/Util.h" +#include "clang/Frontend/LangStandard.h" +#include "clang/Frontend/Utils.h" +#include "clang/Lex/HeaderSearchOptions.h" +#include "clang/Serialization/ASTReader.h" +#include "llvm/ADT/Hashing.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/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/OptTable.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/system_error.h" +#include <sys/stat.h> +using namespace clang; + +//===----------------------------------------------------------------------===// +// Initialization. +//===----------------------------------------------------------------------===// + +CompilerInvocationBase::CompilerInvocationBase() + : LangOpts(new LangOptions()), TargetOpts(new TargetOptions()), + DiagnosticOpts(new DiagnosticOptions()), + HeaderSearchOpts(new HeaderSearchOptions()), + PreprocessorOpts(new PreprocessorOptions()) {} + +CompilerInvocationBase::CompilerInvocationBase(const CompilerInvocationBase &X) + : RefCountedBase<CompilerInvocation>(), + LangOpts(new LangOptions(*X.getLangOpts())), + TargetOpts(new TargetOptions(X.getTargetOpts())), + DiagnosticOpts(new DiagnosticOptions(X.getDiagnosticOpts())), + HeaderSearchOpts(new HeaderSearchOptions(X.getHeaderSearchOpts())), + PreprocessorOpts(new PreprocessorOptions(X.getPreprocessorOpts())) {} + +//===----------------------------------------------------------------------===// +// Deserialization (from args) +//===----------------------------------------------------------------------===// + +using namespace clang::driver; +using namespace clang::driver::options; +using namespace llvm::opt; + +// + +static unsigned getOptimizationLevel(ArgList &Args, InputKind IK, + DiagnosticsEngine &Diags) { + unsigned DefaultOpt = 0; + if (IK == IK_OpenCL && !Args.hasArg(OPT_cl_opt_disable)) + DefaultOpt = 2; + + if (Arg *A = Args.getLastArg(options::OPT_O_Group)) { + if (A->getOption().matches(options::OPT_O0)) + return 0; + + if (A->getOption().matches(options::OPT_Ofast)) + return 3; + + assert (A->getOption().matches(options::OPT_O)); + + StringRef S(A->getValue()); + if (S == "s" || S == "z" || S.empty()) + return 2; + + return getLastArgIntValue(Args, OPT_O, DefaultOpt, Diags); + } + + return DefaultOpt; +} + +static unsigned getOptimizationLevelSize(ArgList &Args) { + if (Arg *A = Args.getLastArg(options::OPT_O_Group)) { + if (A->getOption().matches(options::OPT_O)) { + switch (A->getValue()[0]) { + default: + return 0; + case 's': + return 1; + case 'z': + return 2; + } + } + } + return 0; +} + +static void addWarningArgs(ArgList &Args, std::vector<std::string> &Warnings) { + for (arg_iterator I = Args.filtered_begin(OPT_W_Group), + E = Args.filtered_end(); I != E; ++I) { + Arg *A = *I; + // If the argument is a pure flag, add its name (minus the "W" at the beginning) + // to the warning list. Else, add its value (for the OPT_W case). + if (A->getOption().getKind() == Option::FlagClass) { + Warnings.push_back(A->getOption().getName().substr(1)); + } else { + for (unsigned Idx = 0, End = A->getNumValues(); + Idx < End; ++Idx) { + StringRef V = A->getValue(Idx); + // "-Wl," and such are not warning options. + // FIXME: Should be handled by putting these in separate flags. + if (V.startswith("l,") || V.startswith("a,") || V.startswith("p,")) + continue; + + Warnings.push_back(V); + } + } + } +} + +static bool ParseAnalyzerArgs(AnalyzerOptions &Opts, ArgList &Args, + DiagnosticsEngine &Diags) { + using namespace options; + bool Success = true; + if (Arg *A = Args.getLastArg(OPT_analyzer_store)) { + StringRef Name = A->getValue(); + AnalysisStores Value = llvm::StringSwitch<AnalysisStores>(Name) +#define ANALYSIS_STORE(NAME, CMDFLAG, DESC, CREATFN) \ + .Case(CMDFLAG, NAME##Model) +#include "clang/StaticAnalyzer/Core/Analyses.def" + .Default(NumStores); + if (Value == NumStores) { + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << Name; + Success = false; + } else { + Opts.AnalysisStoreOpt = Value; + } + } + + if (Arg *A = Args.getLastArg(OPT_analyzer_constraints)) { + StringRef Name = A->getValue(); + AnalysisConstraints Value = llvm::StringSwitch<AnalysisConstraints>(Name) +#define ANALYSIS_CONSTRAINTS(NAME, CMDFLAG, DESC, CREATFN) \ + .Case(CMDFLAG, NAME##Model) +#include "clang/StaticAnalyzer/Core/Analyses.def" + .Default(NumConstraints); + if (Value == NumConstraints) { + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << Name; + Success = false; + } else { + Opts.AnalysisConstraintsOpt = Value; + } + } + + if (Arg *A = Args.getLastArg(OPT_analyzer_output)) { + StringRef Name = A->getValue(); + AnalysisDiagClients Value = llvm::StringSwitch<AnalysisDiagClients>(Name) +#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATFN) \ + .Case(CMDFLAG, PD_##NAME) +#include "clang/StaticAnalyzer/Core/Analyses.def" + .Default(NUM_ANALYSIS_DIAG_CLIENTS); + if (Value == NUM_ANALYSIS_DIAG_CLIENTS) { + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << Name; + Success = false; + } else { + Opts.AnalysisDiagOpt = Value; + } + } + + if (Arg *A = Args.getLastArg(OPT_analyzer_purge)) { + StringRef Name = A->getValue(); + AnalysisPurgeMode Value = llvm::StringSwitch<AnalysisPurgeMode>(Name) +#define ANALYSIS_PURGE(NAME, CMDFLAG, DESC) \ + .Case(CMDFLAG, NAME) +#include "clang/StaticAnalyzer/Core/Analyses.def" + .Default(NumPurgeModes); + if (Value == NumPurgeModes) { + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << Name; + Success = false; + } else { + Opts.AnalysisPurgeOpt = Value; + } + } + + if (Arg *A = Args.getLastArg(OPT_analyzer_inlining_mode)) { + StringRef Name = A->getValue(); + AnalysisInliningMode Value = llvm::StringSwitch<AnalysisInliningMode>(Name) +#define ANALYSIS_INLINING_MODE(NAME, CMDFLAG, DESC) \ + .Case(CMDFLAG, NAME) +#include "clang/StaticAnalyzer/Core/Analyses.def" + .Default(NumInliningModes); + if (Value == NumInliningModes) { + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << Name; + Success = false; + } else { + Opts.InliningMode = Value; + } + } + + Opts.ShowCheckerHelp = Args.hasArg(OPT_analyzer_checker_help); + Opts.visualizeExplodedGraphWithGraphViz = + Args.hasArg(OPT_analyzer_viz_egraph_graphviz); + Opts.visualizeExplodedGraphWithUbiGraph = + Args.hasArg(OPT_analyzer_viz_egraph_ubigraph); + Opts.NoRetryExhausted = Args.hasArg(OPT_analyzer_disable_retry_exhausted); + 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.eagerlyAssumeBinOpBifurcation = Args.hasArg(OPT_analyzer_eagerly_assume); + Opts.AnalyzeSpecificFunction = Args.getLastArgValue(OPT_analyze_function); + Opts.UnoptimizedCFG = Args.hasArg(OPT_analysis_UnoptimizedCFG); + Opts.TrimGraph = Args.hasArg(OPT_trim_egraph); + Opts.maxBlockVisitOnPath = + getLastArgIntValue(Args, OPT_analyzer_max_loop, 4, Diags); + Opts.PrintStats = Args.hasArg(OPT_analyzer_stats); + Opts.InlineMaxStackDepth = + getLastArgIntValue(Args, OPT_analyzer_inline_max_stack_depth, + Opts.InlineMaxStackDepth, Diags); + + Opts.CheckersControlList.clear(); + for (arg_iterator it = Args.filtered_begin(OPT_analyzer_checker, + OPT_analyzer_disable_checker), + ie = Args.filtered_end(); it != ie; ++it) { + const Arg *A = *it; + A->claim(); + bool enable = (A->getOption().getID() == OPT_analyzer_checker); + // We can have a list of comma separated checker names, e.g: + // '-analyzer-checker=cocoa,unix' + StringRef checkerList = A->getValue(); + SmallVector<StringRef, 4> checkers; + checkerList.split(checkers, ","); + for (unsigned i = 0, e = checkers.size(); i != e; ++i) + Opts.CheckersControlList.push_back(std::make_pair(checkers[i], enable)); + } + + // Go through the analyzer configuration options. + for (arg_iterator it = Args.filtered_begin(OPT_analyzer_config), + ie = Args.filtered_end(); it != ie; ++it) { + const Arg *A = *it; + A->claim(); + // We can have a list of comma separated config names, e.g: + // '-analyzer-config key1=val1,key2=val2' + StringRef configList = A->getValue(); + SmallVector<StringRef, 4> configVals; + configList.split(configVals, ","); + for (unsigned i = 0, e = configVals.size(); i != e; ++i) { + StringRef key, val; + llvm::tie(key, val) = configVals[i].split("="); + if (val.empty()) { + Diags.Report(SourceLocation(), + diag::err_analyzer_config_no_value) << configVals[i]; + Success = false; + break; + } + if (val.find('=') != StringRef::npos) { + Diags.Report(SourceLocation(), + diag::err_analyzer_config_multiple_values) + << configVals[i]; + Success = false; + break; + } + Opts.Config[key] = val; + } + } + + return Success; +} + +static bool ParseMigratorArgs(MigratorOptions &Opts, ArgList &Args) { + Opts.NoNSAllocReallocError = Args.hasArg(OPT_migrator_no_nsalloc_error); + Opts.NoFinalizeRemoval = Args.hasArg(OPT_migrator_no_finalize_removal); + return true; +} + +static void ParseCommentArgs(CommentOptions &Opts, ArgList &Args) { + Opts.BlockCommandNames = Args.getAllArgValues(OPT_fcomment_block_commands); + Opts.ParseAllComments = Args.hasArg(OPT_fparse_all_comments); +} + +static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, + DiagnosticsEngine &Diags) { + using namespace options; + bool Success = true; + + Opts.OptimizationLevel = getOptimizationLevel(Args, IK, Diags); + // TODO: This could be done in Driver + unsigned MaxOptLevel = 3; + if (Opts.OptimizationLevel > MaxOptLevel) { + // If the optimization level is not supported, fall back on the default optimization + Diags.Report(diag::warn_drv_optimization_value) + << Args.getLastArg(OPT_O)->getAsString(Args) << "-O" << MaxOptLevel; + Opts.OptimizationLevel = MaxOptLevel; + } + + // We must always run at least the always inlining pass. + Opts.setInlining( + (Opts.OptimizationLevel > 1) ? CodeGenOptions::NormalInlining + : CodeGenOptions::OnlyAlwaysInlining); + // -fno-inline-functions overrides OptimizationLevel > 1. + Opts.NoInline = Args.hasArg(OPT_fno_inline); + Opts.setInlining(Args.hasArg(OPT_fno_inline_functions) ? + CodeGenOptions::OnlyAlwaysInlining : Opts.getInlining()); + + if (Args.hasArg(OPT_gline_tables_only)) { + Opts.setDebugInfo(CodeGenOptions::DebugLineTablesOnly); + } else if (Args.hasArg(OPT_g_Flag) || Args.hasArg(OPT_gdwarf_2) || + Args.hasArg(OPT_gdwarf_3) || Args.hasArg(OPT_gdwarf_4)) { + if (Args.hasFlag(OPT_flimit_debug_info, OPT_fno_limit_debug_info, true)) + Opts.setDebugInfo(CodeGenOptions::LimitedDebugInfo); + else + Opts.setDebugInfo(CodeGenOptions::FullDebugInfo); + } + Opts.DebugColumnInfo = Args.hasArg(OPT_dwarf_column_info); + Opts.SplitDwarfFile = Args.getLastArgValue(OPT_split_dwarf_file); + if (Args.hasArg(OPT_gdwarf_2)) + Opts.DwarfVersion = 2; + else if (Args.hasArg(OPT_gdwarf_3)) + Opts.DwarfVersion = 3; + else if (Args.hasArg(OPT_gdwarf_4)) + Opts.DwarfVersion = 4; + else if (Opts.getDebugInfo() != CodeGenOptions::NoDebugInfo) + // Default Dwarf version is 4 if we are generating debug information. + Opts.DwarfVersion = 4; + + Opts.DisableLLVMOpts = Args.hasArg(OPT_disable_llvm_optzns); + Opts.DisableRedZone = Args.hasArg(OPT_disable_red_zone); + Opts.ForbidGuardVariables = Args.hasArg(OPT_fforbid_guard_variables); + Opts.UseRegisterSizedBitfieldAccess = Args.hasArg( + OPT_fuse_register_sized_bitfield_access); + Opts.RelaxedAliasing = Args.hasArg(OPT_relaxed_aliasing); + Opts.StructPathTBAA = !Args.hasArg(OPT_no_struct_path_tbaa); + 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 = getOptimizationLevelSize(Args); + Opts.SimplifyLibCalls = !(Args.hasArg(OPT_fno_builtin) || + Args.hasArg(OPT_ffreestanding)); + Opts.UnrollLoops = + Args.hasFlag(OPT_funroll_loops, OPT_fno_unroll_loops, + (Opts.OptimizationLevel > 1 && !Opts.OptimizeSize)); + Opts.RerollLoops = Args.hasArg(OPT_freroll_loops); + + Opts.Autolink = !Args.hasArg(OPT_fno_autolink); + Opts.SampleProfileFile = Args.getLastArgValue(OPT_fprofile_sample_use_EQ); + Opts.AsmVerbose = Args.hasArg(OPT_masm_verbose); + Opts.ObjCAutoRefCountExceptions = Args.hasArg(OPT_fobjc_arc_exceptions); + Opts.CUDAIsDevice = Args.hasArg(OPT_fcuda_is_device); + 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.DisableFree = Args.hasArg(OPT_disable_free); + Opts.DisableTailCalls = Args.hasArg(OPT_mdisable_tail_calls); + Opts.FloatABI = Args.getLastArgValue(OPT_mfloat_abi); + Opts.HiddenWeakVTables = Args.hasArg(OPT_fhidden_weak_vtables); + Opts.LessPreciseFPMAD = Args.hasArg(OPT_cl_mad_enable); + Opts.LimitFloatPrecision = Args.getLastArgValue(OPT_mlimit_float_precision); + Opts.NoInfsFPMath = (Args.hasArg(OPT_menable_no_infinities) || + Args.hasArg(OPT_cl_finite_math_only)|| + Args.hasArg(OPT_cl_fast_relaxed_math)); + Opts.NoNaNsFPMath = (Args.hasArg(OPT_menable_no_nans) || + Args.hasArg(OPT_cl_finite_math_only)|| + Args.hasArg(OPT_cl_fast_relaxed_math)); + Opts.NoZeroInitializedInBSS = Args.hasArg(OPT_mno_zero_initialized_in_bss); + Opts.BackendOptions = Args.getAllArgValues(OPT_backend_option); + Opts.NumRegisterParameters = getLastArgIntValue(Args, OPT_mregparm, 0, Diags); + Opts.NoGlobalMerge = Args.hasArg(OPT_mno_global_merge); + Opts.NoExecStack = Args.hasArg(OPT_mno_exec_stack); + Opts.EnableSegmentedStacks = Args.hasArg(OPT_split_stacks); + Opts.RelaxAll = Args.hasArg(OPT_mrelax_all); + Opts.OmitLeafFramePointer = Args.hasArg(OPT_momit_leaf_frame_pointer); + Opts.SaveTempLabels = Args.hasArg(OPT_msave_temp_labels); + Opts.NoDwarf2CFIAsm = Args.hasArg(OPT_fno_dwarf2_cfi_asm); + Opts.NoDwarfDirectoryAsm = Args.hasArg(OPT_fno_dwarf_directory_asm); + Opts.SoftFloat = Args.hasArg(OPT_msoft_float); + Opts.StrictEnums = Args.hasArg(OPT_fstrict_enums); + Opts.UnsafeFPMath = Args.hasArg(OPT_menable_unsafe_fp_math) || + Args.hasArg(OPT_cl_unsafe_math_optimizations) || + Args.hasArg(OPT_cl_fast_relaxed_math); + Opts.UnwindTables = Args.hasArg(OPT_munwind_tables); + Opts.RelocationModel = Args.getLastArgValue(OPT_mrelocation_model, "pic"); + Opts.TrapFuncName = Args.getLastArgValue(OPT_ftrap_function_EQ); + Opts.UseInitArray = Args.hasArg(OPT_fuse_init_array); + + Opts.FunctionSections = Args.hasFlag(OPT_ffunction_sections, + OPT_fno_function_sections, false); + Opts.DataSections = Args.hasFlag(OPT_fdata_sections, + OPT_fno_data_sections, false); + + Opts.VectorizeBB = Args.hasArg(OPT_vectorize_slp_aggressive); + Opts.VectorizeLoop = Args.hasArg(OPT_vectorize_loops); + Opts.VectorizeSLP = Args.hasArg(OPT_vectorize_slp); + + Opts.MainFileName = Args.getLastArgValue(OPT_main_file_name); + Opts.VerifyModule = !Args.hasArg(OPT_disable_llvm_verifier); + Opts.SanitizeRecover = !Args.hasArg(OPT_fno_sanitize_recover); + + Opts.DisableGCov = Args.hasArg(OPT_test_coverage); + Opts.EmitGcovArcs = Args.hasArg(OPT_femit_coverage_data); + Opts.EmitGcovNotes = Args.hasArg(OPT_femit_coverage_notes); + if (Opts.EmitGcovArcs || Opts.EmitGcovNotes) { + Opts.CoverageFile = Args.getLastArgValue(OPT_coverage_file); + Opts.CoverageExtraChecksum = Args.hasArg(OPT_coverage_cfg_checksum); + Opts.CoverageNoFunctionNamesInData = + Args.hasArg(OPT_coverage_no_function_names_in_data); + if (Args.hasArg(OPT_coverage_version_EQ)) { + StringRef CoverageVersion = Args.getLastArgValue(OPT_coverage_version_EQ); + if (CoverageVersion.size() != 4) { + Diags.Report(diag::err_drv_invalid_value) + << Args.getLastArg(OPT_coverage_version_EQ)->getAsString(Args) + << CoverageVersion; + } else { + memcpy(Opts.CoverageVersion, CoverageVersion.data(), 4); + } + } + } + + Opts.InstrumentFunctions = Args.hasArg(OPT_finstrument_functions); + Opts.InstrumentForProfiling = Args.hasArg(OPT_pg); + Opts.EmitOpenCLArgMetadata = Args.hasArg(OPT_cl_kernel_arg_info); + Opts.DebugCompilationDir = Args.getLastArgValue(OPT_fdebug_compilation_dir); + Opts.LinkBitcodeFile = Args.getLastArgValue(OPT_mlink_bitcode_file); + Opts.SanitizerBlacklistFile = Args.getLastArgValue(OPT_fsanitize_blacklist); + Opts.SanitizeMemoryTrackOrigins = + Args.hasArg(OPT_fsanitize_memory_track_origins); + Opts.SanitizeAddressZeroBaseShadow = + Args.hasArg(OPT_fsanitize_address_zero_base_shadow); + Opts.SanitizeUndefinedTrapOnError = + Args.hasArg(OPT_fsanitize_undefined_trap_on_error); + Opts.SSPBufferSize = + getLastArgIntValue(Args, OPT_stack_protector_buffer_size, 8, Diags); + Opts.StackRealignment = Args.hasArg(OPT_mstackrealign); + if (Arg *A = Args.getLastArg(OPT_mstack_alignment)) { + StringRef Val = A->getValue(); + unsigned StackAlignment = Opts.StackAlignment; + Val.getAsInteger(10, StackAlignment); + Opts.StackAlignment = StackAlignment; + } + + if (Arg *A = Args.getLastArg(OPT_fobjc_dispatch_method_EQ)) { + StringRef Name = A->getValue(); + 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; + Success = false; + } else { + Opts.setObjCDispatchMethod( + static_cast<CodeGenOptions::ObjCDispatchMethodKind>(Method)); + } + } + + if (Arg *A = Args.getLastArg(OPT_ftlsmodel_EQ)) { + StringRef Name = A->getValue(); + unsigned Model = llvm::StringSwitch<unsigned>(Name) + .Case("global-dynamic", CodeGenOptions::GeneralDynamicTLSModel) + .Case("local-dynamic", CodeGenOptions::LocalDynamicTLSModel) + .Case("initial-exec", CodeGenOptions::InitialExecTLSModel) + .Case("local-exec", CodeGenOptions::LocalExecTLSModel) + .Default(~0U); + if (Model == ~0U) { + Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Name; + Success = false; + } else { + Opts.setDefaultTLSModel(static_cast<CodeGenOptions::TLSModel>(Model)); + } + } + + if (Arg *A = Args.getLastArg(OPT_ffp_contract)) { + StringRef Val = A->getValue(); + if (Val == "fast") + Opts.setFPContractMode(CodeGenOptions::FPC_Fast); + else if (Val == "on") + Opts.setFPContractMode(CodeGenOptions::FPC_On); + else if (Val == "off") + Opts.setFPContractMode(CodeGenOptions::FPC_Off); + else + Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Val; + } + + if (Arg *A = Args.getLastArg(OPT_fpcc_struct_return, OPT_freg_struct_return)) { + if (A->getOption().matches(OPT_fpcc_struct_return)) { + Opts.setStructReturnConvention(CodeGenOptions::SRCK_OnStack); + } else { + assert(A->getOption().matches(OPT_freg_struct_return)); + Opts.setStructReturnConvention(CodeGenOptions::SRCK_InRegs); + } + } + + Opts.DependentLibraries = Args.getAllArgValues(OPT_dependent_lib); + + return Success; +} + +static void ParseDependencyOutputArgs(DependencyOutputOptions &Opts, + ArgList &Args) { + using namespace options; + 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); + Opts.ShowHeaderIncludes = Args.hasArg(OPT_H); + Opts.HeaderIncludeOutputFile = Args.getLastArgValue(OPT_header_include_file); + Opts.AddMissingHeaderDeps = Args.hasArg(OPT_MG); + Opts.PrintShowIncludes = Args.hasArg(OPT_show_includes); + Opts.DOTOutputFile = Args.getLastArgValue(OPT_dependency_dot); +} + +bool clang::ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args, + DiagnosticsEngine *Diags) { + using namespace options; + bool Success = true; + + Opts.DiagnosticLogFile = Args.getLastArgValue(OPT_diagnostic_log_file); + Opts.DiagnosticSerializationFile = + Args.getLastArgValue(OPT_diagnostic_serialized_file); + 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.hasFlag(OPT_fshow_column, + OPT_fno_show_column, + /*Default=*/true); + 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::sys::Process::UseANSIEscapeCodes(Args.hasArg(OPT_fansi_escape_codes)); + + // Default behavior is to not to show note include stacks. + Opts.ShowNoteIncludeStack = false; + if (Arg *A = Args.getLastArg(OPT_fdiagnostics_show_note_include_stack, + OPT_fno_diagnostics_show_note_include_stack)) + if (A->getOption().matches(OPT_fdiagnostics_show_note_include_stack)) + Opts.ShowNoteIncludeStack = true; + + StringRef ShowOverloads = + Args.getLastArgValue(OPT_fshow_overloads_EQ, "all"); + if (ShowOverloads == "best") + Opts.setShowOverloads(Ovl_Best); + else if (ShowOverloads == "all") + Opts.setShowOverloads(Ovl_All); + else { + Success = false; + if (Diags) + Diags->Report(diag::err_drv_invalid_value) + << Args.getLastArg(OPT_fshow_overloads_EQ)->getAsString(Args) + << ShowOverloads; + } + + 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 { + Success = false; + if (Diags) + Diags->Report(diag::err_drv_invalid_value) + << Args.getLastArg(OPT_fdiagnostics_show_category)->getAsString(Args) + << ShowCategory; + } + + StringRef Format = + Args.getLastArgValue(OPT_fdiagnostics_format, "clang"); + if (Format == "clang") + Opts.setFormat(DiagnosticOptions::Clang); + else if (Format == "msvc") + Opts.setFormat(DiagnosticOptions::Msvc); + else if (Format == "msvc-fallback") { + Opts.setFormat(DiagnosticOptions::Msvc); + Opts.CLFallbackMode = true; + } else if (Format == "vi") + Opts.setFormat(DiagnosticOptions::Vi); + else { + Success = false; + if (Diags) + Diags->Report(diag::err_drv_invalid_value) + << Args.getLastArg(OPT_fdiagnostics_format)->getAsString(Args) + << Format; + } + + Opts.ShowSourceRanges = Args.hasArg(OPT_fdiagnostics_print_source_range_info); + Opts.ShowParseableFixits = Args.hasArg(OPT_fdiagnostics_parseable_fixits); + Opts.ShowPresumedLoc = !Args.hasArg(OPT_fno_diagnostics_use_presumed_location); + Opts.VerifyDiagnostics = Args.hasArg(OPT_verify); + Opts.ElideType = !Args.hasArg(OPT_fno_elide_type); + Opts.ShowTemplateTree = Args.hasArg(OPT_fdiagnostics_show_template_tree); + Opts.ErrorLimit = getLastArgIntValue(Args, OPT_ferror_limit, 0, Diags); + Opts.MacroBacktraceLimit = + getLastArgIntValue(Args, OPT_fmacro_backtrace_limit, + DiagnosticOptions::DefaultMacroBacktraceLimit, Diags); + Opts.TemplateBacktraceLimit = getLastArgIntValue( + Args, OPT_ftemplate_backtrace_limit, + DiagnosticOptions::DefaultTemplateBacktraceLimit, Diags); + Opts.ConstexprBacktraceLimit = getLastArgIntValue( + Args, OPT_fconstexpr_backtrace_limit, + DiagnosticOptions::DefaultConstexprBacktraceLimit, Diags); + Opts.TabStop = getLastArgIntValue(Args, OPT_ftabstop, + DiagnosticOptions::DefaultTabStop, Diags); + if (Opts.TabStop == 0 || Opts.TabStop > DiagnosticOptions::MaxTabStop) { + Opts.TabStop = DiagnosticOptions::DefaultTabStop; + if (Diags) + Diags->Report(diag::warn_ignoring_ftabstop_value) + << Opts.TabStop << DiagnosticOptions::DefaultTabStop; + } + Opts.MessageLength = getLastArgIntValue(Args, OPT_fmessage_length, 0, Diags); + addWarningArgs(Args, Opts.Warnings); + + return Success; +} + +static void ParseFileSystemArgs(FileSystemOptions &Opts, ArgList &Args) { + Opts.WorkingDir = Args.getLastArgValue(OPT_working_directory); +} + +static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, + DiagnosticsEngine &Diags) { + using namespace options; + Opts.ProgramAction = frontend::ParseSyntaxOnly; + if (const Arg *A = Args.getLastArg(OPT_Action_Group)) { + switch (A->getOption().getID()) { + default: + llvm_unreachable("Invalid option in group!"); + case OPT_ast_list: + Opts.ProgramAction = frontend::ASTDeclList; break; + case OPT_ast_dump: + Opts.ProgramAction = frontend::ASTDump; break; + case OPT_ast_print: + Opts.ProgramAction = frontend::ASTPrint; break; + case OPT_ast_view: + Opts.ProgramAction = frontend::ASTView; 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(); + // fall-through! + case OPT_fixit: + Opts.ProgramAction = frontend::FixIt; break; + case OPT_emit_module: + Opts.ProgramAction = frontend::GenerateModule; 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_module_file_info: + Opts.ProgramAction = frontend::ModuleFileInfo; 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_migrate: + Opts.ProgramAction = frontend::MigrateSource; break; + case OPT_Eonly: + Opts.ProgramAction = frontend::RunPreprocessorOnly; break; + } + } + + if (const Arg* A = Args.getLastArg(OPT_plugin)) { + Opts.Plugins.push_back(A->getValue(0)); + Opts.ProgramAction = frontend::PluginAction; + Opts.ActionName = A->getValue(); + + for (arg_iterator it = Args.filtered_begin(OPT_plugin_arg), + end = Args.filtered_end(); it != end; ++it) { + if ((*it)->getValue(0) == Opts.ActionName) + Opts.PluginArgs.push_back((*it)->getValue(1)); + } + } + + Opts.AddPluginActions = Args.getAllArgValues(OPT_add_plugin); + Opts.AddPluginArgs.resize(Opts.AddPluginActions.size()); + for (int i = 0, e = Opts.AddPluginActions.size(); i != e; ++i) { + for (arg_iterator it = Args.filtered_begin(OPT_plugin_arg), + end = Args.filtered_end(); it != end; ++it) { + if ((*it)->getValue(0) == Opts.AddPluginActions[i]) + Opts.AddPluginArgs[i].push_back((*it)->getValue(1)); + } + } + + if (const Arg *A = Args.getLastArg(OPT_code_completion_at)) { + Opts.CodeCompletionAt = + ParsedSourceLocation::FromString(A->getValue()); + if (Opts.CodeCompletionAt.FileName.empty()) + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << A->getValue(); + } + 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.ShowHelp = Args.hasArg(OPT_help); + Opts.ShowStats = Args.hasArg(OPT_print_stats); + Opts.ShowTimers = Args.hasArg(OPT_ftime_report); + Opts.ShowVersion = Args.hasArg(OPT_version); + Opts.ASTMergeFiles = Args.getAllArgValues(OPT_ast_merge); + Opts.LLVMArgs = Args.getAllArgValues(OPT_mllvm); + Opts.FixWhatYouCan = Args.hasArg(OPT_fix_what_you_can); + Opts.FixOnlyWarnings = Args.hasArg(OPT_fix_only_warnings); + Opts.FixAndRecompile = Args.hasArg(OPT_fixit_recompile); + Opts.FixToTemporaries = Args.hasArg(OPT_fixit_to_temp); + Opts.ASTDumpFilter = Args.getLastArgValue(OPT_ast_dump_filter); + Opts.ASTDumpLookups = Args.hasArg(OPT_ast_dump_lookups); + Opts.UseGlobalModuleIndex = !Args.hasArg(OPT_fno_modules_global_index); + Opts.GenerateGlobalModuleIndex = Opts.UseGlobalModuleIndex; + + Opts.CodeCompleteOpts.IncludeMacros + = Args.hasArg(OPT_code_completion_macros); + Opts.CodeCompleteOpts.IncludeCodePatterns + = Args.hasArg(OPT_code_completion_patterns); + Opts.CodeCompleteOpts.IncludeGlobals + = !Args.hasArg(OPT_no_code_completion_globals); + Opts.CodeCompleteOpts.IncludeBriefComments + = Args.hasArg(OPT_code_completion_brief_comments); + + Opts.OverrideRecordLayoutsFile + = Args.getLastArgValue(OPT_foverride_record_layout_EQ); + if (const Arg *A = Args.getLastArg(OPT_arcmt_check, + OPT_arcmt_modify, + OPT_arcmt_migrate)) { + switch (A->getOption().getID()) { + default: + llvm_unreachable("missed a case"); + case OPT_arcmt_check: + Opts.ARCMTAction = FrontendOptions::ARCMT_Check; + break; + case OPT_arcmt_modify: + Opts.ARCMTAction = FrontendOptions::ARCMT_Modify; + break; + case OPT_arcmt_migrate: + Opts.ARCMTAction = FrontendOptions::ARCMT_Migrate; + break; + } + } + Opts.MTMigrateDir = Args.getLastArgValue(OPT_mt_migrate_directory); + Opts.ARCMTMigrateReportOut + = Args.getLastArgValue(OPT_arcmt_migrate_report_output); + Opts.ARCMTMigrateEmitARCErrors + = Args.hasArg(OPT_arcmt_migrate_emit_arc_errors); + + if (Args.hasArg(OPT_objcmt_migrate_literals)) + Opts.ObjCMTAction |= FrontendOptions::ObjCMT_Literals; + if (Args.hasArg(OPT_objcmt_migrate_subscripting)) + Opts.ObjCMTAction |= FrontendOptions::ObjCMT_Subscripting; + if (Args.hasArg(OPT_objcmt_migrate_property)) + Opts.ObjCMTAction |= FrontendOptions::ObjCMT_Property; + if (Args.hasArg(OPT_objcmt_migrate_readonly_property)) + Opts.ObjCMTAction |= FrontendOptions::ObjCMT_ReadonlyProperty; + if (Args.hasArg(OPT_objcmt_migrate_readwrite_property)) + Opts.ObjCMTAction |= FrontendOptions::ObjCMT_ReadwriteProperty; + if (Args.hasArg(OPT_objcmt_migrate_annotation)) + Opts.ObjCMTAction |= FrontendOptions::ObjCMT_Annotation; + if (Args.hasArg(OPT_objcmt_returns_innerpointer_property)) + Opts.ObjCMTAction |= FrontendOptions::ObjCMT_ReturnsInnerPointerProperty; + if (Args.hasArg(OPT_objcmt_migrate_instancetype)) + Opts.ObjCMTAction |= FrontendOptions::ObjCMT_Instancetype; + if (Args.hasArg(OPT_objcmt_migrate_nsmacros)) + Opts.ObjCMTAction |= FrontendOptions::ObjCMT_NsMacros; + if (Args.hasArg(OPT_objcmt_migrate_protocol_conformance)) + Opts.ObjCMTAction |= FrontendOptions::ObjCMT_ProtocolConformance; + if (Args.hasArg(OPT_objcmt_atomic_property)) + Opts.ObjCMTAction |= FrontendOptions::ObjCMT_AtomicProperty; + if (Args.hasArg(OPT_objcmt_ns_nonatomic_iosonly)) + Opts.ObjCMTAction |= FrontendOptions::ObjCMT_NsAtomicIOSOnlyProperty; + if (Args.hasArg(OPT_objcmt_migrate_all)) + Opts.ObjCMTAction |= FrontendOptions::ObjCMT_MigrateDecls; + + Opts.ObjCMTWhiteListPath = Args.getLastArgValue(OPT_objcmt_white_list_dir_path); + + if (Opts.ARCMTAction != FrontendOptions::ARCMT_None && + Opts.ObjCMTAction != FrontendOptions::ObjCMT_None) { + Diags.Report(diag::err_drv_argument_not_allowed_with) + << "ARC migration" << "ObjC migration"; + } + + InputKind DashX = IK_None; + if (const Arg *A = Args.getLastArg(OPT_x)) { + DashX = llvm::StringSwitch<InputKind>(A->getValue()) + .Case("c", IK_C) + .Case("cl", IK_OpenCL) + .Case("cuda", IK_CUDA) + .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("objc-cpp-output", IK_PreprocessedObjC) + .Case("objective-c++-cpp-output", IK_PreprocessedObjCXX) + .Case("objc++-cpp-output", IK_PreprocessedObjCXX) + .Case("c-header", IK_C) + .Case("cl-header", IK_OpenCL) + .Case("objective-c-header", IK_ObjC) + .Case("c++-header", IK_CXX) + .Case("objective-c++-header", IK_ObjCXX) + .Cases("ast", "pcm", 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(); + } + + // '-' 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( + StringRef(Inputs[i]).rsplit('.').second); + // FIXME: Remove this hack. + if (i == 0) + DashX = IK; + } + Opts.Inputs.push_back(FrontendInputFile(Inputs[i], IK)); + } + + return DashX; +} + +std::string CompilerInvocation::GetResourcesPath(const char *Argv0, + void *MainAddr) { + SmallString<128> P(llvm::sys::fs::getMainExecutable(Argv0, MainAddr)); + + if (!P.empty()) { + llvm::sys::path::remove_filename(P); // Remove /clang from foo/bin/clang + llvm::sys::path::remove_filename(P); // Remove /bin from foo/bin + + // Get foo/lib/clang/<version>/include + llvm::sys::path::append(P, "lib", "clang", CLANG_VERSION_STRING); + } + + return P.str(); +} + +static void ParseHeaderSearchArgs(HeaderSearchOptions &Opts, ArgList &Args) { + using namespace options; + Opts.Sysroot = Args.getLastArgValue(OPT_isysroot, "/"); + Opts.Verbose = Args.hasArg(OPT_v); + Opts.UseBuiltinIncludes = !Args.hasArg(OPT_nobuiltininc); + Opts.UseStandardSystemIncludes = !Args.hasArg(OPT_nostdsysteminc); + Opts.UseStandardCXXIncludes = !Args.hasArg(OPT_nostdincxx); + if (const Arg *A = Args.getLastArg(OPT_stdlib_EQ)) + Opts.UseLibcxx = (strcmp(A->getValue(), "libc++") == 0); + Opts.ResourceDir = Args.getLastArgValue(OPT_resource_dir); + Opts.ModuleCachePath = Args.getLastArgValue(OPT_fmodules_cache_path); + Opts.DisableModuleHash = Args.hasArg(OPT_fdisable_module_hash); + // -fmodules implies -fmodule-maps + Opts.ModuleMaps = Args.hasArg(OPT_fmodule_maps) || Args.hasArg(OPT_fmodules); + Opts.ModuleCachePruneInterval = + getLastArgIntValue(Args, OPT_fmodules_prune_interval, 7 * 24 * 60 * 60); + Opts.ModuleCachePruneAfter = + getLastArgIntValue(Args, OPT_fmodules_prune_after, 31 * 24 * 60 * 60); + for (arg_iterator it = Args.filtered_begin(OPT_fmodules_ignore_macro), + ie = Args.filtered_end(); + it != ie; ++it) { + StringRef MacroDef = (*it)->getValue(); + Opts.ModulesIgnoreMacros.insert(MacroDef.split('=').first); + } + std::vector<std::string> ModuleMapFiles = + Args.getAllArgValues(OPT_fmodule_map_file); + Opts.ModuleMapFiles.insert(ModuleMapFiles.begin(), ModuleMapFiles.end()); + + // Add -I..., -F..., and -index-header-map options in order. + bool IsIndexHeaderMap = false; + for (arg_iterator it = Args.filtered_begin(OPT_I, OPT_F, + OPT_index_header_map), + ie = Args.filtered_end(); it != ie; ++it) { + if ((*it)->getOption().matches(OPT_index_header_map)) { + // -index-header-map applies to the next -I or -F. + IsIndexHeaderMap = true; + continue; + } + + frontend::IncludeDirGroup Group + = IsIndexHeaderMap? frontend::IndexHeaderMap : frontend::Angled; + + Opts.AddPath((*it)->getValue(), Group, + /*IsFramework=*/ (*it)->getOption().matches(OPT_F), true); + IsIndexHeaderMap = false; + } + + // Add -iprefix/-iwithprefix/-iwithprefixbefore options. + 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(); + else if (A->getOption().matches(OPT_iwithprefix)) + Opts.AddPath(Prefix.str() + A->getValue(), + frontend::After, false, true); + else + Opts.AddPath(Prefix.str() + A->getValue(), + frontend::Angled, false, true); + } + + for (arg_iterator it = Args.filtered_begin(OPT_idirafter), + ie = Args.filtered_end(); it != ie; ++it) + Opts.AddPath((*it)->getValue(), frontend::After, false, true); + for (arg_iterator it = Args.filtered_begin(OPT_iquote), + ie = Args.filtered_end(); it != ie; ++it) + Opts.AddPath((*it)->getValue(), frontend::Quoted, false, true); + for (arg_iterator it = Args.filtered_begin(OPT_isystem, + OPT_iwithsysroot), ie = Args.filtered_end(); it != ie; ++it) + Opts.AddPath((*it)->getValue(), frontend::System, false, + !(*it)->getOption().matches(OPT_iwithsysroot)); + for (arg_iterator it = Args.filtered_begin(OPT_iframework), + ie = Args.filtered_end(); it != ie; ++it) + Opts.AddPath((*it)->getValue(), frontend::System, true, true); + + // Add the paths for the various language specific isystem flags. + for (arg_iterator it = Args.filtered_begin(OPT_c_isystem), + ie = Args.filtered_end(); it != ie; ++it) + Opts.AddPath((*it)->getValue(), frontend::CSystem, false, true); + for (arg_iterator it = Args.filtered_begin(OPT_cxx_isystem), + ie = Args.filtered_end(); it != ie; ++it) + Opts.AddPath((*it)->getValue(), frontend::CXXSystem, false, true); + for (arg_iterator it = Args.filtered_begin(OPT_objc_isystem), + ie = Args.filtered_end(); it != ie; ++it) + Opts.AddPath((*it)->getValue(), frontend::ObjCSystem, false,true); + for (arg_iterator it = Args.filtered_begin(OPT_objcxx_isystem), + ie = Args.filtered_end(); it != ie; ++it) + Opts.AddPath((*it)->getValue(), frontend::ObjCXXSystem, false, true); + + // Add the internal paths from a driver that detects standard include paths. + for (arg_iterator I = Args.filtered_begin(OPT_internal_isystem, + OPT_internal_externc_isystem), + E = Args.filtered_end(); + I != E; ++I) { + frontend::IncludeDirGroup Group = frontend::System; + if ((*I)->getOption().matches(OPT_internal_externc_isystem)) + Group = frontend::ExternCSystem; + Opts.AddPath((*I)->getValue(), Group, false, true); + } + + // Add the path prefixes which are implicitly treated as being system headers. + for (arg_iterator I = Args.filtered_begin(OPT_isystem_prefix, + OPT_ino_system_prefix), + E = Args.filtered_end(); + I != E; ++I) + Opts.AddSystemHeaderPrefix((*I)->getValue(), + (*I)->getOption().matches(OPT_isystem_prefix)); +} + +void CompilerInvocation::setLangDefaults(LangOptions &Opts, InputKind IK, + LangStandard::Kind LangStd) { + // Set some properties which depend solely 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; + } + + if (LangStd == LangStandard::lang_unspecified) { + // Based on the base language, pick one. + switch (IK) { + case IK_None: + case IK_AST: + case IK_LLVM_IR: + llvm_unreachable("Invalid input kind!"); + case IK_OpenCL: + LangStd = LangStandard::lang_opencl; + break; + case IK_CUDA: + LangStd = LangStandard::lang_cuda; + 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.LineComment = Std.hasLineComments(); + Opts.C99 = Std.isC99(); + Opts.C11 = Std.isC11(); + Opts.CPlusPlus = Std.isCPlusPlus(); + Opts.CPlusPlus11 = Std.isCPlusPlus11(); + Opts.CPlusPlus1y = Std.isCPlusPlus1y(); + Opts.Digraphs = Std.hasDigraphs(); + Opts.GNUMode = Std.isGNUMode(); + Opts.GNUInline = !Std.isC99(); + Opts.HexFloats = Std.hasHexFloats(); + Opts.ImplicitInt = Std.hasImplicitInt(); + + // Set OpenCL Version. + if (LangStd == LangStandard::lang_opencl) { + Opts.OpenCL = 1; + Opts.OpenCLVersion = 100; + } + else if (LangStd == LangStandard::lang_opencl11) { + Opts.OpenCL = 1; + Opts.OpenCLVersion = 110; + } + else if (LangStd == LangStandard::lang_opencl12) { + Opts.OpenCL = 1; + Opts.OpenCLVersion = 120; + } + + // OpenCL has some additional defaults. + if (Opts.OpenCL) { + Opts.AltiVec = 0; + Opts.CXXOperatorNames = 1; + Opts.LaxVectorConversions = 0; + Opts.DefaultFPContract = 1; + Opts.NativeHalfType = 1; + } + + if (LangStd == LangStandard::lang_cuda) + Opts.CUDA = 1; + + // OpenCL and C++ both have bool, true, false keywords. + Opts.Bool = Opts.OpenCL || Opts.CPlusPlus; + + // C++ has wchar_t keyword. + Opts.WChar = Opts.CPlusPlus; + + Opts.GNUKeywords = Opts.GNUMode; + Opts.CXXOperatorNames = Opts.CPlusPlus; + + // Mimicing gcc's behavior, trigraphs are only enabled if -trigraphs + // is specified, or -std is set to a conforming mode. + Opts.Trigraphs = !Opts.GNUMode; + + Opts.DollarIdents = !Opts.AsmPreprocessor; + + // C++1y onwards has sized global deallocation functions. + Opts.SizedDeallocation = Opts.CPlusPlus1y; +} + +/// Attempt to parse a visibility value out of the given argument. +static Visibility parseVisibility(Arg *arg, ArgList &args, + DiagnosticsEngine &diags) { + StringRef value = arg->getValue(); + if (value == "default") { + return DefaultVisibility; + } else if (value == "hidden") { + return HiddenVisibility; + } else if (value == "protected") { + // FIXME: diagnose if target does not support protected visibility + return ProtectedVisibility; + } + + diags.Report(diag::err_drv_invalid_value) + << arg->getAsString(args) << value; + return DefaultVisibility; +} + +static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, + DiagnosticsEngine &Diags) { + // FIXME: Cleanup per-file based stuff. + LangStandard::Kind LangStd = LangStandard::lang_unspecified; + if (const Arg *A = Args.getLastArg(OPT_std_EQ)) { + LangStd = llvm::StringSwitch<LangStandard::Kind>(A->getValue()) +#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(); + else { + // Valid standard, check to make sure language and standard are compatable. + const LangStandard &Std = LangStandard::getLangStandardForKind(LangStd); + switch (IK) { + case IK_C: + case IK_ObjC: + case IK_PreprocessedC: + case IK_PreprocessedObjC: + if (!(Std.isC89() || Std.isC99())) + Diags.Report(diag::err_drv_argument_not_allowed_with) + << A->getAsString(Args) << "C/ObjC"; + break; + case IK_CXX: + case IK_ObjCXX: + case IK_PreprocessedCXX: + case IK_PreprocessedObjCXX: + if (!Std.isCPlusPlus()) + Diags.Report(diag::err_drv_argument_not_allowed_with) + << A->getAsString(Args) << "C++/ObjC++"; + break; + case IK_OpenCL: + if (!Std.isC99()) + Diags.Report(diag::err_drv_argument_not_allowed_with) + << A->getAsString(Args) << "OpenCL"; + break; + case IK_CUDA: + if (!Std.isCPlusPlus()) + Diags.Report(diag::err_drv_argument_not_allowed_with) + << A->getAsString(Args) << "CUDA"; + break; + default: + break; + } + } + } + + // -cl-std only applies for OpenCL language standards. + // Override the -std option in this case. + if (const Arg *A = Args.getLastArg(OPT_cl_std_EQ)) { + LangStandard::Kind OpenCLLangStd + = llvm::StringSwitch<LangStandard::Kind>(A->getValue()) + .Case("CL", LangStandard::lang_opencl) + .Case("CL1.1", LangStandard::lang_opencl11) + .Case("CL1.2", LangStandard::lang_opencl12) + .Default(LangStandard::lang_unspecified); + + if (OpenCLLangStd == LangStandard::lang_unspecified) { + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << A->getValue(); + } + else + LangStd = OpenCLLangStd; + } + + CompilerInvocation::setLangDefaults(Opts, IK, LangStd); + + // 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.GNUKeywords); + + if (Args.hasArg(OPT_fno_operator_names)) + Opts.CXXOperatorNames = 0; + + if (Opts.ObjC1) { + if (Arg *arg = Args.getLastArg(OPT_fobjc_runtime_EQ)) { + StringRef value = arg->getValue(); + if (Opts.ObjCRuntime.tryParse(value)) + Diags.Report(diag::err_drv_unknown_objc_runtime) << value; + } + + if (Args.hasArg(OPT_fobjc_gc_only)) + Opts.setGC(LangOptions::GCOnly); + else if (Args.hasArg(OPT_fobjc_gc)) + Opts.setGC(LangOptions::HybridGC); + else if (Args.hasArg(OPT_fobjc_arc)) { + Opts.ObjCAutoRefCount = 1; + if (!Opts.ObjCRuntime.allowsARC()) + Diags.Report(diag::err_arc_unsupported_on_runtime); + + // Only set ObjCARCWeak if ARC is enabled. + if (Args.hasArg(OPT_fobjc_runtime_has_weak)) + Opts.ObjCARCWeak = 1; + else + Opts.ObjCARCWeak = Opts.ObjCRuntime.allowsWeak(); + } + + if (Args.hasArg(OPT_fno_objc_infer_related_result_type)) + Opts.ObjCInferRelatedResultType = 0; + + if (Args.hasArg(OPT_fobjc_subscripting_legacy_runtime)) + Opts.ObjCSubscriptingLegacyRuntime = + (Opts.ObjCRuntime.getKind() == ObjCRuntime::FragileMacOSX); + } + + if (Args.hasArg(OPT_fgnu89_inline)) + Opts.GNUInline = 1; + + if (Args.hasArg(OPT_fapple_kext)) { + if (!Opts.CPlusPlus) + Diags.Report(diag::warn_c_kext); + else + Opts.AppleKext = 1; + } + + 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; + + // The value-visibility mode defaults to "default". + if (Arg *visOpt = Args.getLastArg(OPT_fvisibility)) { + Opts.setValueVisibilityMode(parseVisibility(visOpt, Args, Diags)); + } else { + Opts.setValueVisibilityMode(DefaultVisibility); + } + + // The type-visibility mode defaults to the value-visibility mode. + if (Arg *typeVisOpt = Args.getLastArg(OPT_ftype_visibility)) { + Opts.setTypeVisibilityMode(parseVisibility(typeVisOpt, Args, Diags)); + } else { + Opts.setTypeVisibilityMode(Opts.getValueVisibilityMode()); + } + + if (Args.hasArg(OPT_fvisibility_inlines_hidden)) + Opts.InlineVisibilityHidden = 1; + + if (Args.hasArg(OPT_ftrapv)) { + Opts.setSignedOverflowBehavior(LangOptions::SOB_Trapping); + // Set the handler, if one is specified. + Opts.OverflowHandler = + Args.getLastArgValue(OPT_ftrapv_handler); + } + else if (Args.hasArg(OPT_fwrapv)) + Opts.setSignedOverflowBehavior(LangOptions::SOB_Defined); + + if (Args.hasArg(OPT_trigraphs)) + Opts.Trigraphs = 1; + + Opts.DollarIdents = Args.hasFlag(OPT_fdollars_in_identifiers, + OPT_fno_dollars_in_identifiers, + Opts.DollarIdents); + Opts.PascalStrings = Args.hasArg(OPT_fpascal_strings); + Opts.MicrosoftExt + = Args.hasArg(OPT_fms_extensions) || Args.hasArg(OPT_fms_compatibility); + Opts.MicrosoftMode = Args.hasArg(OPT_fms_compatibility); + Opts.AsmBlocks = Args.hasArg(OPT_fasm_blocks) || Opts.MicrosoftExt; + Opts.MSCVersion = getLastArgIntValue(Args, OPT_fmsc_version, 0, Diags); + Opts.Borland = Args.hasArg(OPT_fborland_extensions); + Opts.WritableStrings = Args.hasArg(OPT_fwritable_strings); + Opts.ConstStrings = Args.hasFlag(OPT_fconst_strings, OPT_fno_const_strings, + Opts.ConstStrings); + 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.ObjCExceptions = Args.hasArg(OPT_fobjc_exceptions); + Opts.CXXExceptions = Args.hasArg(OPT_fcxx_exceptions); + Opts.SjLjExceptions = Args.hasArg(OPT_fsjlj_exceptions); + Opts.TraditionalCPP = Args.hasArg(OPT_traditional_cpp); + + Opts.RTTI = !Args.hasArg(OPT_fno_rtti); + Opts.Blocks = Args.hasArg(OPT_fblocks); + Opts.BlocksRuntimeOptional = Args.hasArg(OPT_fblocks_runtime_optional); + Opts.Modules = Args.hasArg(OPT_fmodules); + Opts.ModulesDeclUse = Args.hasArg(OPT_fmodules_decluse); + Opts.CharIsSigned = Opts.OpenCL || !Args.hasArg(OPT_fno_signed_char); + Opts.WChar = Opts.CPlusPlus && !Args.hasArg(OPT_fno_wchar); + Opts.ShortWChar = Args.hasArg(OPT_fshort_wchar); + Opts.ShortEnums = Args.hasArg(OPT_fshort_enums); + Opts.Freestanding = Args.hasArg(OPT_ffreestanding); + Opts.FormatExtensions = Args.hasArg(OPT_fformat_extensions); + Opts.NoBuiltin = Args.hasArg(OPT_fno_builtin) || Opts.Freestanding; + Opts.NoMathBuiltin = Args.hasArg(OPT_fno_math_builtin); + Opts.AssumeSaneOperatorNew = !Args.hasArg(OPT_fno_assume_sane_operator_new); + Opts.SizedDeallocation |= Args.hasArg(OPT_fsized_deallocation); + 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 = !Opts.OpenCL && Args.hasArg(OPT_fmath_errno); + Opts.InstantiationDepth = + getLastArgIntValue(Args, OPT_ftemplate_depth, 256, Diags); + Opts.ArrowDepth = + getLastArgIntValue(Args, OPT_foperator_arrow_depth, 256, Diags); + Opts.ConstexprCallDepth = + getLastArgIntValue(Args, OPT_fconstexpr_depth, 512, Diags); + Opts.ConstexprStepLimit = + getLastArgIntValue(Args, OPT_fconstexpr_steps, 1048576, Diags); + Opts.BracketDepth = getLastArgIntValue(Args, OPT_fbracket_depth, 256, Diags); + Opts.DelayedTemplateParsing = Args.hasArg(OPT_fdelayed_template_parsing); + Opts.NumLargeByValueCopy = + getLastArgIntValue(Args, OPT_Wlarge_by_value_copy_EQ, 0, Diags); + Opts.MSBitfields = Args.hasArg(OPT_mms_bitfields); + Opts.ObjCConstantStringClass = + Args.getLastArgValue(OPT_fconstant_string_class); + Opts.ObjCDefaultSynthProperties = + !Args.hasArg(OPT_disable_objc_default_synthesize_properties); + Opts.EncodeExtendedBlockSig = + Args.hasArg(OPT_fencode_extended_block_signature); + Opts.EmitAllDecls = Args.hasArg(OPT_femit_all_decls); + Opts.PackStruct = getLastArgIntValue(Args, OPT_fpack_struct_EQ, 0, Diags); + Opts.PICLevel = getLastArgIntValue(Args, OPT_pic_level, 0, Diags); + Opts.PIELevel = getLastArgIntValue(Args, OPT_pie_level, 0, Diags); + Opts.Static = Args.hasArg(OPT_static_define); + Opts.DumpRecordLayoutsSimple = Args.hasArg(OPT_fdump_record_layouts_simple); + Opts.DumpRecordLayouts = Opts.DumpRecordLayoutsSimple + || 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.SinglePrecisionConstants = Args.hasArg(OPT_cl_single_precision_constant); + Opts.FastRelaxedMath = Args.hasArg(OPT_cl_fast_relaxed_math); + Opts.MRTD = Args.hasArg(OPT_mrtd); + Opts.HexagonQdsp6Compat = Args.hasArg(OPT_mqdsp6_compat); + Opts.FakeAddressSpaceMap = Args.hasArg(OPT_ffake_address_space_map); + Opts.ParseUnknownAnytype = Args.hasArg(OPT_funknown_anytype); + Opts.DebuggerSupport = Args.hasArg(OPT_fdebugger_support); + Opts.DebuggerCastResultToId = Args.hasArg(OPT_fdebugger_cast_result_to_id); + Opts.DebuggerObjCLiteral = Args.hasArg(OPT_fdebugger_objc_literal); + Opts.ApplePragmaPack = Args.hasArg(OPT_fapple_pragma_pack); + Opts.CurrentModule = Args.getLastArgValue(OPT_fmodule_name); + + if (Arg *A = Args.getLastArg(OPT_faddress_space_map_mangling_EQ)) { + switch (llvm::StringSwitch<unsigned>(A->getValue()) + .Case("target", LangOptions::ASMM_Target) + .Case("no", LangOptions::ASMM_Off) + .Case("yes", LangOptions::ASMM_On) + .Default(255)) { + default: + Diags.Report(diag::err_drv_invalid_value) + << "-faddress-space-map-mangling=" << A->getValue(); + break; + case LangOptions::ASMM_Target: + Opts.setAddressSpaceMapMangling(LangOptions::ASMM_Target); + break; + case LangOptions::ASMM_On: + Opts.setAddressSpaceMapMangling(LangOptions::ASMM_On); + break; + case LangOptions::ASMM_Off: + Opts.setAddressSpaceMapMangling(LangOptions::ASMM_Off); + break; + } + } + + // Check if -fopenmp is specified. + Opts.OpenMP = Args.hasArg(OPT_fopenmp); + + // Record whether the __DEPRECATED define was requested. + Opts.Deprecated = Args.hasFlag(OPT_fdeprecated_macro, + OPT_fno_deprecated_macro, + Opts.Deprecated); + + // FIXME: Eliminate this dependency. + unsigned Opt = getOptimizationLevel(Args, IK, Diags), + OptSize = getOptimizationLevelSize(Args); + Opts.Optimize = Opt != 0; + Opts.OptimizeSize = OptSize != 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. + Opts.NoInlineDefine = !Opt || Args.hasArg(OPT_fno_inline); + + Opts.FastMath = Args.hasArg(OPT_ffast_math); + Opts.FiniteMathOnly = Args.hasArg(OPT_ffinite_math_only); + + Opts.RetainCommentsFromSystemHeaders = + Args.hasArg(OPT_fretain_comments_from_system_headers); + + unsigned SSP = getLastArgIntValue(Args, 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.setStackProtector(LangOptions::SSPOff); break; + case 1: Opts.setStackProtector(LangOptions::SSPOn); break; + case 2: Opts.setStackProtector(LangOptions::SSPReq); break; + } + + // Parse -fsanitize= arguments. + std::vector<std::string> Sanitizers = Args.getAllArgValues(OPT_fsanitize_EQ); + for (unsigned I = 0, N = Sanitizers.size(); I != N; ++I) { + // Since the Opts.Sanitize* values are bitfields, it's a little tricky to + // efficiently map string values to them. Perform the mapping indirectly: + // convert strings to enumerated values, then switch over the enum to set + // the right bitfield value. + enum Sanitizer { +#define SANITIZER(NAME, ID) \ + ID, +#include "clang/Basic/Sanitizers.def" + Unknown + }; + switch (llvm::StringSwitch<unsigned>(Sanitizers[I]) +#define SANITIZER(NAME, ID) \ + .Case(NAME, ID) +#include "clang/Basic/Sanitizers.def" + .Default(Unknown)) { +#define SANITIZER(NAME, ID) \ + case ID: \ + Opts.Sanitize.ID = true; \ + break; +#include "clang/Basic/Sanitizers.def" + + case Unknown: + Diags.Report(diag::err_drv_invalid_value) + << "-fsanitize=" << Sanitizers[I]; + break; + } + } +} + +static void ParsePreprocessorArgs(PreprocessorOptions &Opts, ArgList &Args, + FileManager &FileMgr, + DiagnosticsEngine &Diags) { + using namespace options; + 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(); + 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); + + Opts.DumpDeserializedPCHDecls = Args.hasArg(OPT_dump_deserialized_pch_decls); + for (arg_iterator it = Args.filtered_begin(OPT_error_on_deserialized_pch_decl), + ie = Args.filtered_end(); it != ie; ++it) { + const Arg *A = *it; + Opts.DeserializedPCHDeclsToErrorOn.insert(A->getValue()); + } + + if (const Arg *A = Args.getLastArg(OPT_preamble_bytes_EQ)) { + StringRef Value(A->getValue()); + size_t Comma = Value.find(','); + unsigned Bytes = 0; + unsigned EndOfLine = 0; + + if (Comma == 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()); + else + Opts.addMacroUndef((*it)->getValue()); + } + + Opts.MacroIncludes = Args.getAllArgValues(OPT_imacros); + + // Add the ordered list of -includes. + for (arg_iterator it = Args.filtered_begin(OPT_include), + ie = Args.filtered_end(); it != ie; ++it) { + const Arg *A = *it; + Opts.Includes.push_back(A->getValue()); + } + + for (arg_iterator it = Args.filtered_begin(OPT_chain_include), + ie = Args.filtered_end(); it != ie; ++it) { + const Arg *A = *it; + Opts.ChainedIncludes.push_back(A->getValue()); + } + + // 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<StringRef,StringRef> Split = + StringRef(A->getValue()).split(';'); + + if (Split.second.empty()) { + Diags.Report(diag::err_drv_invalid_remap_file) << A->getAsString(Args); + continue; + } + + Opts.addRemappedFile(Split.first, Split.second); + } + + if (Arg *A = Args.getLastArg(OPT_fobjc_arc_cxxlib_EQ)) { + StringRef Name = A->getValue(); + unsigned Library = llvm::StringSwitch<unsigned>(Name) + .Case("libc++", ARCXX_libcxx) + .Case("libstdc++", ARCXX_libstdcxx) + .Case("none", ARCXX_nolib) + .Default(~0U); + if (Library == ~0U) + Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Name; + else + Opts.ObjCXXARCStandardLibrary = (ObjCXXARCStandardLibraryKind)Library; + } +} + +static void ParsePreprocessorOutputArgs(PreprocessorOutputOptions &Opts, + ArgList &Args, + frontend::ActionKind Action) { + using namespace options; + + switch (Action) { + case frontend::ASTDeclList: + case frontend::ASTDump: + case frontend::ASTPrint: + case frontend::ASTView: + case frontend::EmitAssembly: + case frontend::EmitBC: + case frontend::EmitHTML: + case frontend::EmitLLVM: + case frontend::EmitLLVMOnly: + case frontend::EmitCodeGenOnly: + case frontend::EmitObj: + case frontend::FixIt: + case frontend::GenerateModule: + case frontend::GeneratePCH: + case frontend::GeneratePTH: + case frontend::ParseSyntaxOnly: + case frontend::ModuleFileInfo: + case frontend::PluginAction: + case frontend::PrintDeclContext: + case frontend::RewriteObjC: + case frontend::RewriteTest: + case frontend::RunAnalysis: + case frontend::MigrateSource: + Opts.ShowCPP = 0; + break; + + case frontend::DumpRawTokens: + case frontend::DumpTokens: + case frontend::InitOnly: + case frontend::PrintPreamble: + case frontend::PrintPreprocessedInput: + case frontend::RewriteMacros: + case frontend::RunPreprocessorOnly: + Opts.ShowCPP = !Args.hasArg(OPT_dM); + break; + } + + Opts.ShowComments = Args.hasArg(OPT_C); + Opts.ShowLineMarkers = !Args.hasArg(OPT_P); + Opts.ShowMacroComments = Args.hasArg(OPT_CC); + Opts.ShowMacros = Args.hasArg(OPT_dM) || Args.hasArg(OPT_dD); + Opts.RewriteIncludes = Args.hasArg(OPT_frewrite_includes); +} + +static void ParseTargetArgs(TargetOptions &Opts, ArgList &Args) { + using namespace options; + Opts.ABI = Args.getLastArgValue(OPT_target_abi); + Opts.CXXABI = Args.getLastArgValue(OPT_cxx_abi); + Opts.CPU = Args.getLastArgValue(OPT_target_cpu); + Opts.FPMath = Args.getLastArgValue(OPT_mfpmath); + Opts.FeaturesAsWritten = Args.getAllArgValues(OPT_target_feature); + Opts.LinkerVersion = Args.getLastArgValue(OPT_target_linker_version); + Opts.Triple = llvm::Triple::normalize(Args.getLastArgValue(OPT_triple)); + + // Use the default target triple if unspecified. + if (Opts.Triple.empty()) + Opts.Triple = llvm::sys::getDefaultTargetTriple(); +} + +// + +bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res, + const char *const *ArgBegin, + const char *const *ArgEnd, + DiagnosticsEngine &Diags) { + bool Success = true; + + // Parse the arguments. + OwningPtr<OptTable> Opts(createDriverOptTable()); + const unsigned IncludedFlagsBitmask = options::CC1Option; + unsigned MissingArgIndex, MissingArgCount; + OwningPtr<InputArgList> Args( + Opts->ParseArgs(ArgBegin, ArgEnd, MissingArgIndex, MissingArgCount, + IncludedFlagsBitmask)); + + // Check for missing argument error. + if (MissingArgCount) { + Diags.Report(diag::err_drv_missing_argument) + << Args->getArgString(MissingArgIndex) << MissingArgCount; + Success = false; + } + + // 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); + Success = false; + } + + Success = ParseAnalyzerArgs(*Res.getAnalyzerOpts(), *Args, Diags) && Success; + Success = ParseMigratorArgs(Res.getMigratorOpts(), *Args) && Success; + ParseDependencyOutputArgs(Res.getDependencyOutputOpts(), *Args); + Success = ParseDiagnosticArgs(Res.getDiagnosticOpts(), *Args, &Diags) + && Success; + ParseCommentArgs(Res.getLangOpts()->CommentOpts, *Args); + ParseFileSystemArgs(Res.getFileSystemOpts(), *Args); + // FIXME: We shouldn't have to pass the DashX option around here + InputKind DashX = ParseFrontendArgs(Res.getFrontendOpts(), *Args, Diags); + Success = ParseCodeGenArgs(Res.getCodeGenOpts(), *Args, DashX, Diags) + && Success; + ParseHeaderSearchArgs(Res.getHeaderSearchOpts(), *Args); + if (DashX != IK_AST && DashX != IK_LLVM_IR) { + ParseLangArgs(*Res.getLangOpts(), *Args, DashX, Diags); + if (Res.getFrontendOpts().ProgramAction == frontend::RewriteObjC) + Res.getLangOpts()->ObjCExceptions = 1; + } + // FIXME: ParsePreprocessorArgs uses the FileManager to read the contents of + // PCH file and find the original header name. Remove the need to do that in + // ParsePreprocessorArgs and remove the FileManager + // parameters from the function and the "FileManager.h" #include. + FileManager FileMgr(Res.getFileSystemOpts()); + ParsePreprocessorArgs(Res.getPreprocessorOpts(), *Args, FileMgr, Diags); + ParsePreprocessorOutputArgs(Res.getPreprocessorOutputOpts(), *Args, + Res.getFrontendOpts().ProgramAction); + ParseTargetArgs(Res.getTargetOpts(), *Args); + + return Success; +} + +namespace { + + class ModuleSignature { + SmallVector<uint64_t, 16> Data; + unsigned CurBit; + uint64_t CurValue; + + public: + ModuleSignature() : CurBit(0), CurValue(0) { } + + void add(uint64_t Value, unsigned Bits); + void add(StringRef Value); + void flush(); + + llvm::APInt getAsInteger() const; + }; +} + +void ModuleSignature::add(uint64_t Value, unsigned int NumBits) { + CurValue |= Value << CurBit; + if (CurBit + NumBits < 64) { + CurBit += NumBits; + return; + } + + // Add the current word. + Data.push_back(CurValue); + + if (CurBit) + CurValue = Value >> (64-CurBit); + else + CurValue = 0; + CurBit = (CurBit+NumBits) & 63; +} + +void ModuleSignature::flush() { + if (CurBit == 0) + return; + + Data.push_back(CurValue); + CurBit = 0; + CurValue = 0; +} + +void ModuleSignature::add(StringRef Value) { + for (StringRef::iterator I = Value.begin(), IEnd = Value.end(); I != IEnd;++I) + add(*I, 8); +} + +llvm::APInt ModuleSignature::getAsInteger() const { + return llvm::APInt(Data.size() * 64, Data); +} + +std::string CompilerInvocation::getModuleHash() const { + // Note: For QoI reasons, the things we use as a hash here should all be + // dumped via the -module-info flag. + using llvm::hash_code; + using llvm::hash_value; + using llvm::hash_combine; + + // Start the signature with the compiler version. + // FIXME: We'd rather use something more cryptographically sound than + // CityHash, but this will do for now. + hash_code code = hash_value(getClangFullRepositoryVersion()); + + // Extend the signature with the language options +#define LANGOPT(Name, Bits, Default, Description) \ + code = hash_combine(code, LangOpts->Name); +#define ENUM_LANGOPT(Name, Type, Bits, Default, Description) \ + code = hash_combine(code, static_cast<unsigned>(LangOpts->get##Name())); +#define BENIGN_LANGOPT(Name, Bits, Default, Description) +#define BENIGN_ENUM_LANGOPT(Name, Type, Bits, Default, Description) +#include "clang/Basic/LangOptions.def" + + // Extend the signature with the target options. + code = hash_combine(code, TargetOpts->Triple, TargetOpts->CPU, + TargetOpts->ABI, TargetOpts->CXXABI, + TargetOpts->LinkerVersion); + for (unsigned i = 0, n = TargetOpts->FeaturesAsWritten.size(); i != n; ++i) + code = hash_combine(code, TargetOpts->FeaturesAsWritten[i]); + + // Extend the signature with preprocessor options. + const PreprocessorOptions &ppOpts = getPreprocessorOpts(); + const HeaderSearchOptions &hsOpts = getHeaderSearchOpts(); + code = hash_combine(code, ppOpts.UsePredefines, ppOpts.DetailedRecord); + + std::vector<StringRef> MacroDefs; + for (std::vector<std::pair<std::string, bool/*isUndef*/> >::const_iterator + I = getPreprocessorOpts().Macros.begin(), + IEnd = getPreprocessorOpts().Macros.end(); + I != IEnd; ++I) { + // If we're supposed to ignore this macro for the purposes of modules, + // don't put it into the hash. + if (!hsOpts.ModulesIgnoreMacros.empty()) { + // Check whether we're ignoring this macro. + StringRef MacroDef = I->first; + if (hsOpts.ModulesIgnoreMacros.count(MacroDef.split('=').first)) + continue; + } + + code = hash_combine(code, I->first, I->second); + } + + // Extend the signature with the sysroot. + code = hash_combine(code, hsOpts.Sysroot, hsOpts.UseBuiltinIncludes, + hsOpts.UseStandardSystemIncludes, + hsOpts.UseStandardCXXIncludes, + hsOpts.UseLibcxx); + + // Darwin-specific hack: if we have a sysroot, use the contents and + // modification time of + // $sysroot/System/Library/CoreServices/SystemVersion.plist + // as part of the module hash. + if (!hsOpts.Sysroot.empty()) { + llvm::OwningPtr<llvm::MemoryBuffer> buffer; + SmallString<128> systemVersionFile; + systemVersionFile += hsOpts.Sysroot; + llvm::sys::path::append(systemVersionFile, "System"); + llvm::sys::path::append(systemVersionFile, "Library"); + llvm::sys::path::append(systemVersionFile, "CoreServices"); + llvm::sys::path::append(systemVersionFile, "SystemVersion.plist"); + if (!llvm::MemoryBuffer::getFile(systemVersionFile.str(), buffer)) { + code = hash_combine(code, buffer.get()->getBuffer()); + + struct stat statBuf; + if (stat(systemVersionFile.c_str(), &statBuf) == 0) + code = hash_combine(code, statBuf.st_mtime); + } + } + + return llvm::APInt(64, code).toString(36, /*Signed=*/false); +} + +namespace clang { + +// Declared in clang/Frontend/Utils.h. +int getLastArgIntValue(const ArgList &Args, OptSpecifier Id, int Default, + DiagnosticsEngine *Diags) { + int Res = Default; + if (Arg *A = Args.getLastArg(Id)) { + if (StringRef(A->getValue()).getAsInteger(10, Res)) { + if (Diags) + Diags->Report(diag::err_drv_invalid_int_value) << A->getAsString(Args) + << A->getValue(); + } + } + return Res; +} +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/CreateInvocationFromCommandLine.cpp b/contrib/llvm/tools/clang/lib/Frontend/CreateInvocationFromCommandLine.cpp new file mode 100644 index 0000000..78f39d4 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/CreateInvocationFromCommandLine.cpp @@ -0,0 +1,89 @@ +//===--- CreateInvocationFromCommandLine.cpp - CompilerInvocation from Args ==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Construct a compiler invocation object for command line driver arguments +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/Utils.h" +#include "clang/Basic/DiagnosticOptions.h" +#include "clang/Driver/Compilation.h" +#include "clang/Driver/Driver.h" +#include "clang/Driver/Options.h" +#include "clang/Driver/Tool.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Support/Host.h" +using namespace clang; +using namespace llvm::opt; + +/// createInvocationFromCommandLine - Construct a compiler invocation object for +/// a command line argument vector. +/// +/// \return A CompilerInvocation, or 0 if none was built for the given +/// argument vector. +CompilerInvocation * +clang::createInvocationFromCommandLine(ArrayRef<const char *> ArgList, + IntrusiveRefCntPtr<DiagnosticsEngine> Diags) { + if (!Diags.getPtr()) { + // No diagnostics engine was provided, so create our own diagnostics object + // with the default options. + Diags = CompilerInstance::createDiagnostics(new DiagnosticOptions); + } + + SmallVector<const char *, 16> Args; + Args.push_back("<clang>"); // FIXME: Remove dummy argument. + Args.insert(Args.end(), ArgList.begin(), ArgList.end()); + + // FIXME: Find a cleaner way to force the driver into restricted modes. + Args.push_back("-fsyntax-only"); + + // FIXME: We shouldn't have to pass in the path info. + driver::Driver TheDriver("clang", llvm::sys::getDefaultTargetTriple(), + "a.out", *Diags); + + // Don't check that inputs exist, they may have been remapped. + TheDriver.setCheckInputsExist(false); + + OwningPtr<driver::Compilation> C(TheDriver.BuildCompilation(Args)); + + // Just print the cc1 options if -### was present. + if (C->getArgs().hasArg(driver::options::OPT__HASH_HASH_HASH)) { + C->getJobs().Print(llvm::errs(), "\n", true); + return 0; + } + + // 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())) { + SmallString<256> Msg; + llvm::raw_svector_ostream OS(Msg); + Jobs.Print(OS, "; ", true); + Diags->Report(diag::err_fe_expected_compiler_job) << OS.str(); + return 0; + } + + const driver::Command *Cmd = cast<driver::Command>(*Jobs.begin()); + if (StringRef(Cmd->getCreator().getName()) != "clang") { + Diags->Report(diag::err_fe_expected_clang_command); + return 0; + } + + const ArgStringList &CCArgs = Cmd->getArguments(); + OwningPtr<CompilerInvocation> CI(new CompilerInvocation()); + if (!CompilerInvocation::CreateFromArgs(*CI, + const_cast<const char **>(CCArgs.data()), + const_cast<const char **>(CCArgs.data()) + + CCArgs.size(), + *Diags)) + return 0; + return CI.take(); +} 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..4037af9 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/DependencyFile.cpp @@ -0,0 +1,237 @@ +//===--- 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/SourceManager.h" +#include "clang/Frontend/DependencyOutputOptions.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Lex/DirectoryLookup.h" +#include "clang/Lex/LexDiagnostic.h" +#include "clang/Lex/PPCallbacks.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; + +namespace { +class DependencyFileCallback : public PPCallbacks { + std::vector<std::string> Files; + llvm::StringSet<> FilesSet; + const Preprocessor *PP; + std::string OutputFile; + std::vector<std::string> Targets; + bool IncludeSystemHeaders; + bool PhonyTarget; + bool AddMissingHeaderDeps; + bool SeenMissingHeader; +private: + bool FileMatchesDepCriteria(const char *Filename, + SrcMgr::CharacteristicKind FileType); + void AddFilename(StringRef Filename); + void OutputDependencyFile(); + +public: + DependencyFileCallback(const Preprocessor *_PP, + const DependencyOutputOptions &Opts) + : PP(_PP), OutputFile(Opts.OutputFile), Targets(Opts.Targets), + IncludeSystemHeaders(Opts.IncludeSystemHeaders), + PhonyTarget(Opts.UsePhonyTargets), + AddMissingHeaderDeps(Opts.AddMissingHeaderDeps), + SeenMissingHeader(false) {} + + virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, + FileID PrevFID); + virtual void InclusionDirective(SourceLocation HashLoc, + const Token &IncludeTok, + StringRef FileName, + bool IsAngled, + CharSourceRange FilenameRange, + const FileEntry *File, + StringRef SearchPath, + StringRef RelativePath, + const Module *Imported); + + virtual void EndOfMainFile() { + OutputDependencyFile(); + } +}; +} + +void clang::AttachDependencyFileGen(Preprocessor &PP, + const DependencyOutputOptions &Opts) { + if (Opts.Targets.empty()) { + PP.getDiagnostics().Report(diag::err_fe_dependency_file_requires_MT); + return; + } + + // Disable the "file not found" diagnostic if the -MG option was given. + if (Opts.AddMissingHeaderDeps) + PP.SetSuppressIncludeNotFoundError(true); + + PP.addPPCallbacks(new DependencyFileCallback(&PP, 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, + FileID PrevFID) { + 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.getExpansionLoc(Loc))); + if (FE == 0) return; + + StringRef Filename = FE->getName(); + if (!FileMatchesDepCriteria(Filename.data(), FileType)) + return; + + // Remove leading "./" (or ".//" or "././" etc.) + while (Filename.size() > 2 && Filename[0] == '.' && + llvm::sys::path::is_separator(Filename[1])) { + Filename = Filename.substr(1); + while (llvm::sys::path::is_separator(Filename[0])) + Filename = Filename.substr(1); + } + + AddFilename(Filename); +} + +void DependencyFileCallback::InclusionDirective(SourceLocation HashLoc, + const Token &IncludeTok, + StringRef FileName, + bool IsAngled, + CharSourceRange FilenameRange, + const FileEntry *File, + StringRef SearchPath, + StringRef RelativePath, + const Module *Imported) { + if (!File) { + if (AddMissingHeaderDeps) + AddFilename(FileName); + else + SeenMissingHeader = true; + } +} + +void DependencyFileCallback::AddFilename(StringRef Filename) { + if (FilesSet.insert(Filename)) + Files.push_back(Filename); +} + +/// PrintFilename - GCC escapes spaces, # and $, but apparently not ' or " or +/// other scary characters. +static void PrintFilename(raw_ostream &OS, StringRef Filename) { + for (unsigned i = 0, e = Filename.size(); i != e; ++i) { + if (Filename[i] == ' ' || Filename[i] == '#') + OS << '\\'; + else if (Filename[i] == '$') // $ is escaped by $$. + OS << '$'; + OS << Filename[i]; + } +} + +void DependencyFileCallback::OutputDependencyFile() { + if (SeenMissingHeader) { + bool existed; + llvm::sys::fs::remove(OutputFile, existed); + return; + } + + std::string Err; + llvm::raw_fd_ostream OS(OutputFile.c_str(), Err); + if (!Err.empty()) { + PP->getDiagnostics().Report(diag::err_fe_error_opening) + << OutputFile << Err; + return; + } + + // 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; + } else if (Columns + N + 2 > MaxColumns) { + Columns = N + 2; + OS << " \\\n "; + } else { + Columns += N + 1; + OS << ' '; + } + // Targets already quoted as needed. + 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 << ' '; + PrintFilename(OS, *I); + Columns += N + 1; + } + OS << '\n'; + + // Create phony targets if requested. + if (PhonyTarget && !Files.empty()) { + // 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'; + PrintFilename(OS, *I); + OS << ":\n"; + } + } +} + diff --git a/contrib/llvm/tools/clang/lib/Frontend/DependencyGraph.cpp b/contrib/llvm/tools/clang/lib/Frontend/DependencyGraph.cpp new file mode 100644 index 0000000..e128d91 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/DependencyGraph.cpp @@ -0,0 +1,141 @@ +//===--- DependencyGraph.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 a header dependency graph in DOT format, for use +// with, e.g., GraphViz. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/Utils.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Lex/PPCallbacks.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/Support/GraphWriter.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +namespace DOT = llvm::DOT; + +namespace { +class DependencyGraphCallback : public PPCallbacks { + const Preprocessor *PP; + std::string OutputFile; + std::string SysRoot; + llvm::SetVector<const FileEntry *> AllFiles; + typedef llvm::DenseMap<const FileEntry *, + SmallVector<const FileEntry *, 2> > DependencyMap; + + DependencyMap Dependencies; + +private: + raw_ostream &writeNodeReference(raw_ostream &OS, + const FileEntry *Node); + void OutputGraphFile(); + +public: + DependencyGraphCallback(const Preprocessor *_PP, StringRef OutputFile, + StringRef SysRoot) + : PP(_PP), OutputFile(OutputFile.str()), SysRoot(SysRoot.str()) { } + + virtual void InclusionDirective(SourceLocation HashLoc, + const Token &IncludeTok, + StringRef FileName, + bool IsAngled, + CharSourceRange FilenameRange, + const FileEntry *File, + StringRef SearchPath, + StringRef RelativePath, + const Module *Imported); + + virtual void EndOfMainFile() { + OutputGraphFile(); + } + +}; +} + +void clang::AttachDependencyGraphGen(Preprocessor &PP, StringRef OutputFile, + StringRef SysRoot) { + PP.addPPCallbacks(new DependencyGraphCallback(&PP, OutputFile, SysRoot)); +} + +void DependencyGraphCallback::InclusionDirective(SourceLocation HashLoc, + const Token &IncludeTok, + StringRef FileName, + bool IsAngled, + CharSourceRange FilenameRange, + const FileEntry *File, + StringRef SearchPath, + StringRef RelativePath, + const Module *Imported) { + if (!File) + return; + + SourceManager &SM = PP->getSourceManager(); + const FileEntry *FromFile + = SM.getFileEntryForID(SM.getFileID(SM.getExpansionLoc(HashLoc))); + if (FromFile == 0) + return; + + Dependencies[FromFile].push_back(File); + + AllFiles.insert(File); + AllFiles.insert(FromFile); +} + +raw_ostream & +DependencyGraphCallback::writeNodeReference(raw_ostream &OS, + const FileEntry *Node) { + OS << "header_" << Node->getUID(); + return OS; +} + +void DependencyGraphCallback::OutputGraphFile() { + std::string Err; + llvm::raw_fd_ostream OS(OutputFile.c_str(), Err); + if (!Err.empty()) { + PP->getDiagnostics().Report(diag::err_fe_error_opening) + << OutputFile << Err; + return; + } + + OS << "digraph \"dependencies\" {\n"; + + // Write the nodes + for (unsigned I = 0, N = AllFiles.size(); I != N; ++I) { + // Write the node itself. + OS.indent(2); + writeNodeReference(OS, AllFiles[I]); + OS << " [ shape=\"box\", label=\""; + StringRef FileName = AllFiles[I]->getName(); + if (FileName.startswith(SysRoot)) + FileName = FileName.substr(SysRoot.size()); + + OS << DOT::EscapeString(FileName) + << "\"];\n"; + } + + // Write the edges + for (DependencyMap::iterator F = Dependencies.begin(), + FEnd = Dependencies.end(); + F != FEnd; ++F) { + for (unsigned I = 0, N = F->second.size(); I != N; ++I) { + OS.indent(2); + writeNodeReference(OS, F->first); + OS << " -> "; + writeNodeReference(OS, F->second[I]); + OS << ";\n"; + } + } + OS << "}\n"; +} + diff --git a/contrib/llvm/tools/clang/lib/Frontend/DiagnosticRenderer.cpp b/contrib/llvm/tools/clang/lib/Frontend/DiagnosticRenderer.cpp new file mode 100644 index 0000000..4eee595 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/DiagnosticRenderer.cpp @@ -0,0 +1,510 @@ +//===--- DiagnosticRenderer.cpp - Diagnostic Pretty-Printing --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/DiagnosticRenderer.h" +#include "clang/Basic/DiagnosticOptions.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Edit/Commit.h" +#include "clang/Edit/EditedSource.h" +#include "clang/Edit/EditsReceiver.h" +#include "clang/Lex/Lexer.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +using namespace clang; + +/// \brief Retrieve the name of the immediate macro expansion. +/// +/// This routine starts from a source location, and finds the name of the macro +/// responsible for its immediate expansion. It looks through any intervening +/// macro argument expansions to compute this. It returns a StringRef which +/// refers to the SourceManager-owned buffer of the source where that macro +/// name is spelled. Thus, the result shouldn't out-live that SourceManager. +/// +/// This differs from Lexer::getImmediateMacroName in that any macro argument +/// location will result in the topmost function macro that accepted it. +/// e.g. +/// \code +/// MAC1( MAC2(foo) ) +/// \endcode +/// for location of 'foo' token, this function will return "MAC1" while +/// Lexer::getImmediateMacroName will return "MAC2". +static StringRef getImmediateMacroName(SourceLocation Loc, + const SourceManager &SM, + const LangOptions &LangOpts) { + assert(Loc.isMacroID() && "Only reasonble to call this on macros"); + // Walk past macro argument expanions. + while (SM.isMacroArgExpansion(Loc)) + Loc = SM.getImmediateExpansionRange(Loc).first; + + // If the macro's spelling has no FileID, then it's actually a token paste + // or stringization (or similar) and not a macro at all. + if (!SM.getFileEntryForID(SM.getFileID(SM.getSpellingLoc(Loc)))) + return StringRef(); + + // Find the spelling location of the start of the non-argument expansion + // range. This is where the macro name was spelled in order to begin + // expanding this macro. + Loc = SM.getSpellingLoc(SM.getImmediateExpansionRange(Loc).first); + + // Dig out the buffer where the macro name was spelled and the extents of the + // name so that we can render it into the expansion note. + std::pair<FileID, unsigned> ExpansionInfo = SM.getDecomposedLoc(Loc); + unsigned MacroTokenLength = Lexer::MeasureTokenLength(Loc, SM, LangOpts); + StringRef ExpansionBuffer = SM.getBufferData(ExpansionInfo.first); + return ExpansionBuffer.substr(ExpansionInfo.second, MacroTokenLength); +} + +DiagnosticRenderer::DiagnosticRenderer(const LangOptions &LangOpts, + DiagnosticOptions *DiagOpts) + : LangOpts(LangOpts), DiagOpts(DiagOpts), LastLevel() {} + +DiagnosticRenderer::~DiagnosticRenderer() {} + +namespace { + +class FixitReceiver : public edit::EditsReceiver { + SmallVectorImpl<FixItHint> &MergedFixits; + +public: + FixitReceiver(SmallVectorImpl<FixItHint> &MergedFixits) + : MergedFixits(MergedFixits) { } + virtual void insert(SourceLocation loc, StringRef text) { + MergedFixits.push_back(FixItHint::CreateInsertion(loc, text)); + } + virtual void replace(CharSourceRange range, StringRef text) { + MergedFixits.push_back(FixItHint::CreateReplacement(range, text)); + } +}; + +} + +static void mergeFixits(ArrayRef<FixItHint> FixItHints, + const SourceManager &SM, const LangOptions &LangOpts, + SmallVectorImpl<FixItHint> &MergedFixits) { + edit::Commit commit(SM, LangOpts); + for (ArrayRef<FixItHint>::const_iterator + I = FixItHints.begin(), E = FixItHints.end(); I != E; ++I) { + const FixItHint &Hint = *I; + if (Hint.CodeToInsert.empty()) { + if (Hint.InsertFromRange.isValid()) + commit.insertFromRange(Hint.RemoveRange.getBegin(), + Hint.InsertFromRange, /*afterToken=*/false, + Hint.BeforePreviousInsertions); + else + commit.remove(Hint.RemoveRange); + } else { + if (Hint.RemoveRange.isTokenRange() || + Hint.RemoveRange.getBegin() != Hint.RemoveRange.getEnd()) + commit.replace(Hint.RemoveRange, Hint.CodeToInsert); + else + commit.insert(Hint.RemoveRange.getBegin(), Hint.CodeToInsert, + /*afterToken=*/false, Hint.BeforePreviousInsertions); + } + } + + edit::EditedSource Editor(SM, LangOpts); + if (Editor.commit(commit)) { + FixitReceiver Rec(MergedFixits); + Editor.applyRewrites(Rec); + } +} + +void DiagnosticRenderer::emitDiagnostic(SourceLocation Loc, + DiagnosticsEngine::Level Level, + StringRef Message, + ArrayRef<CharSourceRange> Ranges, + ArrayRef<FixItHint> FixItHints, + const SourceManager *SM, + DiagOrStoredDiag D) { + assert(SM || Loc.isInvalid()); + + beginDiagnostic(D, Level); + + if (!Loc.isValid()) + // If we have no source location, just emit the diagnostic message. + emitDiagnosticMessage(Loc, PresumedLoc(), Level, Message, Ranges, SM, D); + else { + // Get the ranges into a local array we can hack on. + SmallVector<CharSourceRange, 20> MutableRanges(Ranges.begin(), + Ranges.end()); + + SmallVector<FixItHint, 8> MergedFixits; + if (!FixItHints.empty()) { + mergeFixits(FixItHints, *SM, LangOpts, MergedFixits); + FixItHints = MergedFixits; + } + + for (ArrayRef<FixItHint>::const_iterator I = FixItHints.begin(), + E = FixItHints.end(); + I != E; ++I) + if (I->RemoveRange.isValid()) + MutableRanges.push_back(I->RemoveRange); + + SourceLocation UnexpandedLoc = Loc; + + // Find the ultimate expansion location for the diagnostic. + Loc = SM->getFileLoc(Loc); + + PresumedLoc PLoc = SM->getPresumedLoc(Loc, DiagOpts->ShowPresumedLoc); + + // First, if this diagnostic is not in the main file, print out the + // "included from" lines. + emitIncludeStack(Loc, PLoc, Level, *SM); + + // Next, emit the actual diagnostic message and caret. + emitDiagnosticMessage(Loc, PLoc, Level, Message, Ranges, SM, D); + emitCaret(Loc, Level, MutableRanges, FixItHints, *SM); + + // If this location is within a macro, walk from UnexpandedLoc up to Loc + // and produce a macro backtrace. + if (UnexpandedLoc.isValid() && UnexpandedLoc.isMacroID()) { + unsigned MacroDepth = 0; + emitMacroExpansions(UnexpandedLoc, Level, MutableRanges, FixItHints, *SM, + MacroDepth); + } + } + + LastLoc = Loc; + LastLevel = Level; + + endDiagnostic(D, Level); +} + + +void DiagnosticRenderer::emitStoredDiagnostic(StoredDiagnostic &Diag) { + emitDiagnostic(Diag.getLocation(), Diag.getLevel(), Diag.getMessage(), + Diag.getRanges(), Diag.getFixIts(), + Diag.getLocation().isValid() ? &Diag.getLocation().getManager() + : 0, + &Diag); +} + +/// \brief Prints an include stack when appropriate for a particular +/// diagnostic level and location. +/// +/// This routine handles all the logic of suppressing particular include +/// stacks (such as those for notes) and duplicate include stacks when +/// repeated warnings occur within the same file. It also handles the logic +/// of customizing the formatting and display of the include stack. +/// +/// \param Loc The diagnostic location. +/// \param PLoc The presumed location of the diagnostic location. +/// \param Level The diagnostic level of the message this stack pertains to. +void DiagnosticRenderer::emitIncludeStack(SourceLocation Loc, + PresumedLoc PLoc, + DiagnosticsEngine::Level Level, + const SourceManager &SM) { + SourceLocation IncludeLoc = PLoc.getIncludeLoc(); + + // Skip redundant include stacks altogether. + if (LastIncludeLoc == IncludeLoc) + return; + + LastIncludeLoc = IncludeLoc; + + if (!DiagOpts->ShowNoteIncludeStack && Level == DiagnosticsEngine::Note) + return; + + if (IncludeLoc.isValid()) + emitIncludeStackRecursively(IncludeLoc, SM); + else { + emitModuleBuildStack(SM); + emitImportStack(Loc, SM); + } +} + +/// \brief Helper to recursivly walk up the include stack and print each layer +/// on the way back down. +void DiagnosticRenderer::emitIncludeStackRecursively(SourceLocation Loc, + const SourceManager &SM) { + if (Loc.isInvalid()) { + emitModuleBuildStack(SM); + return; + } + + PresumedLoc PLoc = SM.getPresumedLoc(Loc, DiagOpts->ShowPresumedLoc); + if (PLoc.isInvalid()) + return; + + // If this source location was imported from a module, print the module + // import stack rather than the + // FIXME: We want submodule granularity here. + std::pair<SourceLocation, StringRef> Imported = SM.getModuleImportLoc(Loc); + if (Imported.first.isValid()) { + // This location was imported by a module. Emit the module import stack. + emitImportStackRecursively(Imported.first, Imported.second, SM); + return; + } + + // Emit the other include frames first. + emitIncludeStackRecursively(PLoc.getIncludeLoc(), SM); + + // Emit the inclusion text/note. + emitIncludeLocation(Loc, PLoc, SM); +} + +/// \brief Emit the module import stack associated with the current location. +void DiagnosticRenderer::emitImportStack(SourceLocation Loc, + const SourceManager &SM) { + if (Loc.isInvalid()) { + emitModuleBuildStack(SM); + return; + } + + std::pair<SourceLocation, StringRef> NextImportLoc + = SM.getModuleImportLoc(Loc); + emitImportStackRecursively(NextImportLoc.first, NextImportLoc.second, SM); +} + +/// \brief Helper to recursivly walk up the import stack and print each layer +/// on the way back down. +void DiagnosticRenderer::emitImportStackRecursively(SourceLocation Loc, + StringRef ModuleName, + const SourceManager &SM) { + if (Loc.isInvalid()) { + return; + } + + PresumedLoc PLoc = SM.getPresumedLoc(Loc, DiagOpts->ShowPresumedLoc); + if (PLoc.isInvalid()) + return; + + // Emit the other import frames first. + std::pair<SourceLocation, StringRef> NextImportLoc + = SM.getModuleImportLoc(Loc); + emitImportStackRecursively(NextImportLoc.first, NextImportLoc.second, SM); + + // Emit the inclusion text/note. + emitImportLocation(Loc, PLoc, ModuleName, SM); +} + +/// \brief Emit the module build stack, for cases where a module is (re-)built +/// on demand. +void DiagnosticRenderer::emitModuleBuildStack(const SourceManager &SM) { + ModuleBuildStack Stack = SM.getModuleBuildStack(); + for (unsigned I = 0, N = Stack.size(); I != N; ++I) { + const SourceManager &CurSM = Stack[I].second.getManager(); + SourceLocation CurLoc = Stack[I].second; + emitBuildingModuleLocation(CurLoc, + CurSM.getPresumedLoc(CurLoc, + DiagOpts->ShowPresumedLoc), + Stack[I].first, + CurSM); + } +} + +// Helper function to fix up source ranges. It takes in an array of ranges, +// and outputs an array of ranges where we want to draw the range highlighting +// around the location specified by CaretLoc. +// +// To find locations which correspond to the caret, we crawl the macro caller +// chain for the beginning and end of each range. If the caret location +// is in a macro expansion, we search each chain for a location +// in the same expansion as the caret; otherwise, we crawl to the top of +// each chain. Two locations are part of the same macro expansion +// iff the FileID is the same. +static void mapDiagnosticRanges( + SourceLocation CaretLoc, + ArrayRef<CharSourceRange> Ranges, + SmallVectorImpl<CharSourceRange> &SpellingRanges, + const SourceManager *SM) { + FileID CaretLocFileID = SM->getFileID(CaretLoc); + + for (ArrayRef<CharSourceRange>::const_iterator I = Ranges.begin(), + E = Ranges.end(); + I != E; ++I) { + SourceLocation Begin = I->getBegin(), End = I->getEnd(); + bool IsTokenRange = I->isTokenRange(); + + FileID BeginFileID = SM->getFileID(Begin); + FileID EndFileID = SM->getFileID(End); + + // Find the common parent for the beginning and end of the range. + + // First, crawl the expansion chain for the beginning of the range. + llvm::SmallDenseMap<FileID, SourceLocation> BeginLocsMap; + while (Begin.isMacroID() && BeginFileID != EndFileID) { + BeginLocsMap[BeginFileID] = Begin; + Begin = SM->getImmediateExpansionRange(Begin).first; + BeginFileID = SM->getFileID(Begin); + } + + // Then, crawl the expansion chain for the end of the range. + if (BeginFileID != EndFileID) { + while (End.isMacroID() && !BeginLocsMap.count(EndFileID)) { + End = SM->getImmediateExpansionRange(End).second; + EndFileID = SM->getFileID(End); + } + if (End.isMacroID()) { + Begin = BeginLocsMap[EndFileID]; + BeginFileID = EndFileID; + } + } + + while (Begin.isMacroID() && BeginFileID != CaretLocFileID) { + if (SM->isMacroArgExpansion(Begin)) { + Begin = SM->getImmediateSpellingLoc(Begin); + End = SM->getImmediateSpellingLoc(End); + } else { + Begin = SM->getImmediateExpansionRange(Begin).first; + End = SM->getImmediateExpansionRange(End).second; + } + BeginFileID = SM->getFileID(Begin); + if (BeginFileID != SM->getFileID(End)) { + // FIXME: Ugly hack to stop a crash; this code is making bad + // assumptions and it's too complicated for me to reason + // about. + Begin = End = SourceLocation(); + break; + } + } + + // Return the spelling location of the beginning and end of the range. + Begin = SM->getSpellingLoc(Begin); + End = SM->getSpellingLoc(End); + SpellingRanges.push_back(CharSourceRange(SourceRange(Begin, End), + IsTokenRange)); + } +} + +void DiagnosticRenderer::emitCaret(SourceLocation Loc, + DiagnosticsEngine::Level Level, + ArrayRef<CharSourceRange> Ranges, + ArrayRef<FixItHint> Hints, + const SourceManager &SM) { + SmallVector<CharSourceRange, 4> SpellingRanges; + mapDiagnosticRanges(Loc, Ranges, SpellingRanges, &SM); + emitCodeContext(Loc, Level, SpellingRanges, Hints, SM); +} + +/// \brief Recursively emit notes for each macro expansion and caret +/// diagnostics where appropriate. +/// +/// Walks up the macro expansion stack printing expansion notes, the code +/// snippet, caret, underlines and FixItHint display as appropriate at each +/// level. +/// +/// \param Loc The location for this caret. +/// \param Level The diagnostic level currently being emitted. +/// \param Ranges The underlined ranges for this code snippet. +/// \param Hints The FixIt hints active for this diagnostic. +/// \param OnMacroInst The current depth of the macro expansion stack. +void DiagnosticRenderer::emitMacroExpansions(SourceLocation Loc, + DiagnosticsEngine::Level Level, + ArrayRef<CharSourceRange> Ranges, + ArrayRef<FixItHint> Hints, + const SourceManager &SM, + unsigned &MacroDepth, + unsigned OnMacroInst) { + assert(!Loc.isInvalid() && "must have a valid source location here"); + + // Walk up to the caller of this macro, and produce a backtrace down to there. + SourceLocation OneLevelUp = SM.getImmediateMacroCallerLoc(Loc); + if (OneLevelUp.isMacroID()) + emitMacroExpansions(OneLevelUp, Level, Ranges, Hints, SM, + MacroDepth, OnMacroInst + 1); + else + MacroDepth = OnMacroInst + 1; + + unsigned MacroSkipStart = 0, MacroSkipEnd = 0; + if (MacroDepth > DiagOpts->MacroBacktraceLimit && + DiagOpts->MacroBacktraceLimit != 0) { + MacroSkipStart = DiagOpts->MacroBacktraceLimit / 2 + + DiagOpts->MacroBacktraceLimit % 2; + MacroSkipEnd = MacroDepth - DiagOpts->MacroBacktraceLimit / 2; + } + + // Whether to suppress printing this macro expansion. + bool Suppressed = (OnMacroInst >= MacroSkipStart && + OnMacroInst < MacroSkipEnd); + + if (Suppressed) { + // Tell the user that we've skipped contexts. + if (OnMacroInst == MacroSkipStart) { + SmallString<200> MessageStorage; + llvm::raw_svector_ostream Message(MessageStorage); + Message << "(skipping " << (MacroSkipEnd - MacroSkipStart) + << " expansions in backtrace; use -fmacro-backtrace-limit=0 to " + "see all)"; + emitBasicNote(Message.str()); + } + return; + } + + // Find the spelling location for the macro definition. We must use the + // spelling location here to avoid emitting a macro bactrace for the note. + SourceLocation SpellingLoc = Loc; + // If this is the expansion of a macro argument, point the caret at the + // use of the argument in the definition of the macro, not the expansion. + if (SM.isMacroArgExpansion(Loc)) + SpellingLoc = SM.getImmediateExpansionRange(Loc).first; + SpellingLoc = SM.getSpellingLoc(SpellingLoc); + + // Map the ranges into the FileID of the diagnostic location. + SmallVector<CharSourceRange, 4> SpellingRanges; + mapDiagnosticRanges(Loc, Ranges, SpellingRanges, &SM); + + SmallString<100> MessageStorage; + llvm::raw_svector_ostream Message(MessageStorage); + StringRef MacroName = getImmediateMacroName(Loc, SM, LangOpts); + if (MacroName.empty()) + Message << "expanded from here"; + else + Message << "expanded from macro '" << MacroName << "'"; + emitDiagnostic(SpellingLoc, DiagnosticsEngine::Note, Message.str(), + SpellingRanges, None, &SM); +} + +DiagnosticNoteRenderer::~DiagnosticNoteRenderer() {} + +void DiagnosticNoteRenderer::emitIncludeLocation(SourceLocation Loc, + PresumedLoc PLoc, + const SourceManager &SM) { + // Generate a note indicating the include location. + SmallString<200> MessageStorage; + llvm::raw_svector_ostream Message(MessageStorage); + Message << "in file included from " << PLoc.getFilename() << ':' + << PLoc.getLine() << ":"; + emitNote(Loc, Message.str(), &SM); +} + +void DiagnosticNoteRenderer::emitImportLocation(SourceLocation Loc, + PresumedLoc PLoc, + StringRef ModuleName, + const SourceManager &SM) { + // Generate a note indicating the include location. + SmallString<200> MessageStorage; + llvm::raw_svector_ostream Message(MessageStorage); + Message << "in module '" << ModuleName << "' imported from " + << PLoc.getFilename() << ':' << PLoc.getLine() << ":"; + emitNote(Loc, Message.str(), &SM); +} + +void +DiagnosticNoteRenderer::emitBuildingModuleLocation(SourceLocation Loc, + PresumedLoc PLoc, + StringRef ModuleName, + const SourceManager &SM) { + // Generate a note indicating the include location. + SmallString<200> MessageStorage; + llvm::raw_svector_ostream Message(MessageStorage); + Message << "while building module '" << ModuleName << "' imported from " + << PLoc.getFilename() << ':' << PLoc.getLine() << ":"; + emitNote(Loc, Message.str(), &SM); +} + + +void DiagnosticNoteRenderer::emitBasicNote(StringRef Message) { + emitNote(SourceLocation(), Message, 0); +} 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..075fe93 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/FrontendAction.cpp @@ -0,0 +1,528 @@ +//===--- 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/AST/DeclGroup.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/ChainedIncludesSource.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "clang/Frontend/LayoutOverrideSource.h" +#include "clang/Frontend/MultiplexConsumer.h" +#include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Parse/ParseAST.h" +#include "clang/Serialization/ASTDeserializationListener.h" +#include "clang/Serialization/ASTReader.h" +#include "clang/Serialization/GlobalModuleIndex.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/system_error.h" +using namespace clang; + +namespace { + +class DelegatingDeserializationListener : public ASTDeserializationListener { + ASTDeserializationListener *Previous; + +public: + explicit DelegatingDeserializationListener( + ASTDeserializationListener *Previous) + : Previous(Previous) { } + + virtual void ReaderInitialized(ASTReader *Reader) { + if (Previous) + Previous->ReaderInitialized(Reader); + } + virtual void IdentifierRead(serialization::IdentID ID, + IdentifierInfo *II) { + if (Previous) + Previous->IdentifierRead(ID, II); + } + virtual void TypeRead(serialization::TypeIdx Idx, QualType T) { + if (Previous) + Previous->TypeRead(Idx, T); + } + virtual void DeclRead(serialization::DeclID ID, const Decl *D) { + if (Previous) + Previous->DeclRead(ID, D); + } + virtual void SelectorRead(serialization::SelectorID ID, Selector Sel) { + if (Previous) + Previous->SelectorRead(ID, Sel); + } + virtual void MacroDefinitionRead(serialization::PreprocessedEntityID PPID, + MacroDefinition *MD) { + if (Previous) + Previous->MacroDefinitionRead(PPID, MD); + } +}; + +/// \brief Dumps deserialized declarations. +class DeserializedDeclsDumper : public DelegatingDeserializationListener { +public: + explicit DeserializedDeclsDumper(ASTDeserializationListener *Previous) + : DelegatingDeserializationListener(Previous) { } + + virtual void DeclRead(serialization::DeclID ID, const Decl *D) { + llvm::outs() << "PCH DECL: " << D->getDeclKindName(); + if (const NamedDecl *ND = dyn_cast<NamedDecl>(D)) + llvm::outs() << " - " << *ND; + llvm::outs() << "\n"; + + DelegatingDeserializationListener::DeclRead(ID, D); + } +}; + +/// \brief Checks deserialized declarations and emits error if a name +/// matches one given in command-line using -error-on-deserialized-decl. +class DeserializedDeclsChecker : public DelegatingDeserializationListener { + ASTContext &Ctx; + std::set<std::string> NamesToCheck; + +public: + DeserializedDeclsChecker(ASTContext &Ctx, + const std::set<std::string> &NamesToCheck, + ASTDeserializationListener *Previous) + : DelegatingDeserializationListener(Previous), + Ctx(Ctx), NamesToCheck(NamesToCheck) { } + + virtual void DeclRead(serialization::DeclID ID, const Decl *D) { + if (const NamedDecl *ND = dyn_cast<NamedDecl>(D)) + if (NamesToCheck.find(ND->getNameAsString()) != NamesToCheck.end()) { + unsigned DiagID + = Ctx.getDiagnostics().getCustomDiagID(DiagnosticsEngine::Error, + "%0 was deserialized"); + Ctx.getDiagnostics().Report(Ctx.getFullLoc(D->getLocation()), DiagID) + << ND->getNameAsString(); + } + + DelegatingDeserializationListener::DeclRead(ID, D); + } +}; + +} // end anonymous namespace + +FrontendAction::FrontendAction() : Instance(0) {} + +FrontendAction::~FrontendAction() {} + +void FrontendAction::setCurrentInput(const FrontendInputFile &CurrentInput, + ASTUnit *AST) { + this->CurrentInput = CurrentInput; + CurrentASTUnit.reset(AST); +} + +ASTConsumer* FrontendAction::CreateWrappedASTConsumer(CompilerInstance &CI, + StringRef InFile) { + ASTConsumer* Consumer = CreateASTConsumer(CI, InFile); + if (!Consumer) + return 0; + + if (CI.getFrontendOpts().AddPluginActions.size() == 0) + return Consumer; + + // Make sure the non-plugin consumer is first, so that plugins can't + // modifiy the AST. + std::vector<ASTConsumer*> Consumers(1, Consumer); + + for (size_t i = 0, e = CI.getFrontendOpts().AddPluginActions.size(); + i != e; ++i) { + // This is O(|plugins| * |add_plugins|), but since both numbers are + // way below 50 in practice, that's ok. + for (FrontendPluginRegistry::iterator + it = FrontendPluginRegistry::begin(), + ie = FrontendPluginRegistry::end(); + it != ie; ++it) { + if (it->getName() == CI.getFrontendOpts().AddPluginActions[i]) { + OwningPtr<PluginASTAction> P(it->instantiate()); + FrontendAction* c = P.get(); + if (P->ParseArgs(CI, CI.getFrontendOpts().AddPluginArgs[i])) + Consumers.push_back(c->CreateASTConsumer(CI, InFile)); + } + } + } + + return new MultiplexConsumer(Consumers); +} + + +bool FrontendAction::BeginSourceFile(CompilerInstance &CI, + const FrontendInputFile &Input) { + assert(!Instance && "Already processing a source file!"); + assert(!Input.isEmpty() && "Unexpected empty filename!"); + setCurrentInput(Input); + setCompilerInstance(&CI); + + StringRef InputFile = Input.getFile(); + bool HasBegunSourceFile = false; + if (!BeginInvocation(CI)) + goto failure; + + // AST files follow a very different path, since they share objects via the + // AST unit. + if (Input.getKind() == IK_AST) { + assert(!usesPreprocessorOnly() && + "Attempt to pass AST file to preprocessor only action!"); + assert(hasASTFileSupport() && + "This action does not have AST file support!"); + + IntrusiveRefCntPtr<DiagnosticsEngine> Diags(&CI.getDiagnostics()); + std::string Error; + ASTUnit *AST = ASTUnit::LoadFromASTFile(InputFile, Diags, + CI.getFileSystemOpts()); + if (!AST) + goto failure; + + setCurrentInput(Input, AST); + + // Inform the diagnostic client we are processing a source file. + CI.getDiagnosticClient().BeginSourceFile(CI.getLangOpts(), 0); + HasBegunSourceFile = true; + + // 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, InputFile)) + goto failure; + + // Create the AST consumer. + CI.setASTConsumer(CreateWrappedASTConsumer(CI, InputFile)); + 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(CI.getFileManager()); + + // IR files bypass the rest of initialization. + if (Input.getKind() == 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); + HasBegunSourceFile = true; + + // Initialize the action. + if (!BeginSourceFileAction(CI, InputFile)) + goto failure; + + return true; + } + + // If the implicit PCH include is actually a directory, rather than + // a single file, search for a suitable PCH file in that directory. + if (!CI.getPreprocessorOpts().ImplicitPCHInclude.empty()) { + FileManager &FileMgr = CI.getFileManager(); + PreprocessorOptions &PPOpts = CI.getPreprocessorOpts(); + StringRef PCHInclude = PPOpts.ImplicitPCHInclude; + if (const DirectoryEntry *PCHDir = FileMgr.getDirectory(PCHInclude)) { + llvm::error_code EC; + SmallString<128> DirNative; + llvm::sys::path::native(PCHDir->getName(), DirNative); + bool Found = false; + for (llvm::sys::fs::directory_iterator Dir(DirNative.str(), EC), DirEnd; + Dir != DirEnd && !EC; Dir.increment(EC)) { + // Check whether this is an acceptable AST file. + if (ASTReader::isAcceptableASTFile(Dir->path(), FileMgr, + CI.getLangOpts(), + CI.getTargetOpts(), + CI.getPreprocessorOpts())) { + PPOpts.ImplicitPCHInclude = Dir->path(); + Found = true; + break; + } + } + + if (!Found) { + CI.getDiagnostics().Report(diag::err_fe_no_pch_in_dir) << PCHInclude; + 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()); + HasBegunSourceFile = true; + + // Initialize the action. + if (!BeginSourceFileAction(CI, InputFile)) + goto failure; + + // Create the AST context and consumer unless this is a preprocessor only + // action. + if (!usesPreprocessorOnly()) { + CI.createASTContext(); + + OwningPtr<ASTConsumer> Consumer( + CreateWrappedASTConsumer(CI, InputFile)); + if (!Consumer) + goto failure; + + CI.getASTContext().setASTMutationListener(Consumer->GetASTMutationListener()); + + if (!CI.getPreprocessorOpts().ChainedIncludes.empty()) { + // Convert headers to PCH and chain them. + OwningPtr<ExternalASTSource> source; + source.reset(ChainedIncludesSource::create(CI)); + if (!source) + goto failure; + CI.setModuleManager(static_cast<ASTReader*>( + &static_cast<ChainedIncludesSource*>(source.get())->getFinalReader())); + CI.getASTContext().setExternalSource(source); + + } else if (!CI.getPreprocessorOpts().ImplicitPCHInclude.empty()) { + // Use PCH. + assert(hasPCHSupport() && "This action does not have PCH support!"); + ASTDeserializationListener *DeserialListener = + Consumer->GetASTDeserializationListener(); + if (CI.getPreprocessorOpts().DumpDeserializedPCHDecls) + DeserialListener = new DeserializedDeclsDumper(DeserialListener); + if (!CI.getPreprocessorOpts().DeserializedPCHDeclsToErrorOn.empty()) + DeserialListener = new DeserializedDeclsChecker(CI.getASTContext(), + CI.getPreprocessorOpts().DeserializedPCHDeclsToErrorOn, + DeserialListener); + CI.createPCHExternalASTSource( + CI.getPreprocessorOpts().ImplicitPCHInclude, + CI.getPreprocessorOpts().DisablePCHValidation, + CI.getPreprocessorOpts().AllowPCHWithCompilerErrors, + DeserialListener); + if (!CI.getASTContext().getExternalSource()) + goto failure; + } + + CI.setASTConsumer(Consumer.take()); + if (!CI.hasASTConsumer()) + goto failure; + } + + // Initialize built-in 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.getLangOpts()); + } + + // If there is a layout overrides file, attach an external AST source that + // provides the layouts from that file. + if (!CI.getFrontendOpts().OverrideRecordLayoutsFile.empty() && + CI.hasASTContext() && !CI.getASTContext().getExternalSource()) { + OwningPtr<ExternalASTSource> + Override(new LayoutOverrideSource( + CI.getFrontendOpts().OverrideRecordLayoutsFile)); + CI.getASTContext().setExternalSource(Override); + } + + return true; + + // If we failed, reset state since the client will not end up calling the + // matching EndSourceFile(). + failure: + if (isCurrentFileAST()) { + CI.setASTContext(0); + CI.setPreprocessor(0); + CI.setSourceManager(0); + CI.setFileManager(0); + } + + if (HasBegunSourceFile) + CI.getDiagnosticClient().EndSourceFile(); + CI.clearOutputFiles(/*EraseFiles=*/true); + setCurrentInput(FrontendInputFile()); + setCompilerInstance(0); + return false; +} + +bool FrontendAction::Execute() { + CompilerInstance &CI = getCompilerInstance(); + + // Initialize the main file entry. This needs to be delayed until after PCH + // has loaded. + if (!isCurrentFileAST()) { + if (!CI.InitializeSourceManager(getCurrentInput())) + return false; + } + + if (CI.hasFrontendTimer()) { + llvm::TimeRegion Timer(CI.getFrontendTimer()); + ExecuteAction(); + } + else ExecuteAction(); + + // If we are supposed to rebuild the global module index, do so now unless + // there were any module-build failures. + if (CI.shouldBuildGlobalModuleIndex() && CI.hasFileManager() && + CI.hasPreprocessor()) { + GlobalModuleIndex::writeIndex( + CI.getFileManager(), + CI.getPreprocessor().getHeaderSearchInfo().getModuleCachePath()); + } + + return true; +} + +void FrontendAction::EndSourceFile() { + CompilerInstance &CI = getCompilerInstance(); + + // Inform the diagnostic client we are done with this source file. + CI.getDiagnosticClient().EndSourceFile(); + + // 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.resetAndLeakASTContext(); + } + } 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 instructed by the + // FrontendAction. + CI.clearOutputFiles(/*EraseFiles=*/shouldEraseOutputFiles()); + + if (isCurrentFileAST()) { + CI.takeSema(); + CI.resetAndLeakASTContext(); + CI.resetAndLeakPreprocessor(); + CI.resetAndLeakSourceManager(); + CI.resetAndLeakFileManager(); + } + + setCompilerInstance(0); + setCurrentInput(FrontendInputFile()); +} + +bool FrontendAction::shouldEraseOutputFiles() { + return getCompilerInstance().getDiagnostics().hasErrorOccurred(); +} + +//===----------------------------------------------------------------------===// +// Utility Actions +//===----------------------------------------------------------------------===// + +void ASTFrontendAction::ExecuteAction() { + CompilerInstance &CI = getCompilerInstance(); + if (!CI.hasPreprocessor()) + return; + + // 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(getTranslationUnitKind(), CompletionConsumer); + + ParseAST(CI.getSema(), CI.getFrontendOpts().ShowStats, + CI.getFrontendOpts().SkipFunctionBodies); +} + +void PluginASTAction::anchor() { } + +ASTConsumer * +PreprocessorFrontendAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + llvm_unreachable("Invalid CreateASTConsumer on preprocessor action!"); +} + +ASTConsumer *WrapperFrontendAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + return WrappedAction->CreateASTConsumer(CI, InFile); +} +bool WrapperFrontendAction::BeginInvocation(CompilerInstance &CI) { + return WrappedAction->BeginInvocation(CI); +} +bool WrapperFrontendAction::BeginSourceFileAction(CompilerInstance &CI, + StringRef Filename) { + WrappedAction->setCurrentInput(getCurrentInput()); + WrappedAction->setCompilerInstance(&CI); + return WrappedAction->BeginSourceFileAction(CI, Filename); +} +void WrapperFrontendAction::ExecuteAction() { + WrappedAction->ExecuteAction(); +} +void WrapperFrontendAction::EndSourceFileAction() { + WrappedAction->EndSourceFileAction(); +} + +bool WrapperFrontendAction::usesPreprocessorOnly() const { + return WrappedAction->usesPreprocessorOnly(); +} +TranslationUnitKind WrapperFrontendAction::getTranslationUnitKind() { + return WrappedAction->getTranslationUnitKind(); +} +bool WrapperFrontendAction::hasPCHSupport() const { + return WrappedAction->hasPCHSupport(); +} +bool WrapperFrontendAction::hasASTFileSupport() const { + return WrappedAction->hasASTFileSupport(); +} +bool WrapperFrontendAction::hasIRSupport() const { + return WrappedAction->hasIRSupport(); +} +bool WrapperFrontendAction::hasCodeCompletionSupport() const { + return WrappedAction->hasCodeCompletionSupport(); +} + +WrapperFrontendAction::WrapperFrontendAction(FrontendAction *WrappedAction) + : WrappedAction(WrappedAction) {} + 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..a3ab1be --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/FrontendActions.cpp @@ -0,0 +1,578 @@ +//===--- 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/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/Lex/HeaderSearch.h" +#include "clang/Lex/Pragma.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Parse/Parser.h" +#include "clang/Serialization/ASTReader.h" +#include "clang/Serialization/ASTWriter.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/system_error.h" + +using namespace clang; + +//===----------------------------------------------------------------------===// +// Custom Actions +//===----------------------------------------------------------------------===// + +ASTConsumer *InitOnlyAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + return new ASTConsumer(); +} + +void InitOnlyAction::ExecuteAction() { +} + +//===----------------------------------------------------------------------===// +// AST Consumer Actions +//===----------------------------------------------------------------------===// + +ASTConsumer *ASTPrintAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + if (raw_ostream *OS = CI.createDefaultOutputFile(false, InFile)) + return CreateASTPrinter(OS, CI.getFrontendOpts().ASTDumpFilter); + return 0; +} + +ASTConsumer *ASTDumpAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + return CreateASTDumper(CI.getFrontendOpts().ASTDumpFilter, + CI.getFrontendOpts().ASTDumpLookups); +} + +ASTConsumer *ASTDeclListAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + return CreateASTDeclNodeLister(); +} + +ASTConsumer *ASTViewAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + return CreateASTViewer(); +} + +ASTConsumer *DeclContextPrintAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + return CreateDeclContextPrinter(); +} + +ASTConsumer *GeneratePCHAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + std::string Sysroot; + std::string OutputFile; + raw_ostream *OS = 0; + if (ComputeASTConsumerArguments(CI, InFile, Sysroot, OutputFile, OS)) + return 0; + + if (!CI.getFrontendOpts().RelocatablePCH) + Sysroot.clear(); + return new PCHGenerator(CI.getPreprocessor(), OutputFile, 0, Sysroot, OS); +} + +bool GeneratePCHAction::ComputeASTConsumerArguments(CompilerInstance &CI, + StringRef InFile, + std::string &Sysroot, + std::string &OutputFile, + raw_ostream *&OS) { + Sysroot = CI.getHeaderSearchOpts().Sysroot; + if (CI.getFrontendOpts().RelocatablePCH && Sysroot.empty()) { + CI.getDiagnostics().Report(diag::err_relocatable_without_isysroot); + return true; + } + + // We use createOutputFile here because this is exposed via libclang, and we + // must disable the RemoveFileOnSignal behavior. + // We use a temporary to avoid race conditions. + OS = CI.createOutputFile(CI.getFrontendOpts().OutputFile, /*Binary=*/true, + /*RemoveFileOnSignal=*/false, InFile, + /*Extension=*/"", /*useTemporary=*/true); + if (!OS) + return true; + + OutputFile = CI.getFrontendOpts().OutputFile; + return false; +} + +ASTConsumer *GenerateModuleAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + std::string Sysroot; + std::string OutputFile; + raw_ostream *OS = 0; + if (ComputeASTConsumerArguments(CI, InFile, Sysroot, OutputFile, OS)) + return 0; + + return new PCHGenerator(CI.getPreprocessor(), OutputFile, Module, + Sysroot, OS); +} + +static SmallVectorImpl<char> & +operator+=(SmallVectorImpl<char> &Includes, StringRef RHS) { + Includes.append(RHS.begin(), RHS.end()); + return Includes; +} + +static void addHeaderInclude(StringRef HeaderName, + SmallVectorImpl<char> &Includes, + const LangOptions &LangOpts) { + if (LangOpts.ObjC1) + Includes += "#import \""; + else + Includes += "#include \""; + Includes += HeaderName; + Includes += "\"\n"; +} + +static void addHeaderInclude(const FileEntry *Header, + SmallVectorImpl<char> &Includes, + const LangOptions &LangOpts) { + addHeaderInclude(Header->getName(), Includes, LangOpts); +} + +/// \brief Collect the set of header includes needed to construct the given +/// module and update the TopHeaders file set of the module. +/// +/// \param Module The module we're collecting includes from. +/// +/// \param Includes Will be augmented with the set of \#includes or \#imports +/// needed to load all of the named headers. +static void collectModuleHeaderIncludes(const LangOptions &LangOpts, + FileManager &FileMgr, + ModuleMap &ModMap, + clang::Module *Module, + SmallVectorImpl<char> &Includes) { + // Don't collect any headers for unavailable modules. + if (!Module->isAvailable()) + return; + + // Add includes for each of these headers. + for (unsigned I = 0, N = Module->NormalHeaders.size(); I != N; ++I) { + const FileEntry *Header = Module->NormalHeaders[I]; + Module->addTopHeader(Header); + addHeaderInclude(Header, Includes, LangOpts); + } + // Note that Module->PrivateHeaders will not be a TopHeader. + + if (const FileEntry *UmbrellaHeader = Module->getUmbrellaHeader()) { + Module->addTopHeader(UmbrellaHeader); + if (Module->Parent) { + // Include the umbrella header for submodules. + addHeaderInclude(UmbrellaHeader, Includes, LangOpts); + } + } else if (const DirectoryEntry *UmbrellaDir = Module->getUmbrellaDir()) { + // Add all of the headers we find in this subdirectory. + llvm::error_code EC; + SmallString<128> DirNative; + llvm::sys::path::native(UmbrellaDir->getName(), DirNative); + for (llvm::sys::fs::recursive_directory_iterator Dir(DirNative.str(), EC), + DirEnd; + Dir != DirEnd && !EC; Dir.increment(EC)) { + // Check whether this entry has an extension typically associated with + // headers. + if (!llvm::StringSwitch<bool>(llvm::sys::path::extension(Dir->path())) + .Cases(".h", ".H", ".hh", ".hpp", true) + .Default(false)) + continue; + + // If this header is marked 'unavailable' in this module, don't include + // it. + if (const FileEntry *Header = FileMgr.getFile(Dir->path())) { + if (ModMap.isHeaderInUnavailableModule(Header)) + continue; + Module->addTopHeader(Header); + } + + // Include this header umbrella header for submodules. + addHeaderInclude(Dir->path(), Includes, LangOpts); + } + } + + // Recurse into submodules. + for (clang::Module::submodule_iterator Sub = Module->submodule_begin(), + SubEnd = Module->submodule_end(); + Sub != SubEnd; ++Sub) + collectModuleHeaderIncludes(LangOpts, FileMgr, ModMap, *Sub, Includes); +} + +bool GenerateModuleAction::BeginSourceFileAction(CompilerInstance &CI, + StringRef Filename) { + // Find the module map file. + const FileEntry *ModuleMap = CI.getFileManager().getFile(Filename); + if (!ModuleMap) { + CI.getDiagnostics().Report(diag::err_module_map_not_found) + << Filename; + return false; + } + + // Parse the module map file. + HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo(); + if (HS.loadModuleMapFile(ModuleMap, IsSystem)) + return false; + + if (CI.getLangOpts().CurrentModule.empty()) { + CI.getDiagnostics().Report(diag::err_missing_module_name); + + // FIXME: Eventually, we could consider asking whether there was just + // a single module described in the module map, and use that as a + // default. Then it would be fairly trivial to just "compile" a module + // map with a single module (the common case). + return false; + } + + // Dig out the module definition. + Module = HS.lookupModule(CI.getLangOpts().CurrentModule, + /*AllowSearch=*/false); + if (!Module) { + CI.getDiagnostics().Report(diag::err_missing_module) + << CI.getLangOpts().CurrentModule << Filename; + + return false; + } + + // Check whether we can build this module at all. + clang::Module::Requirement Requirement; + if (!Module->isAvailable(CI.getLangOpts(), CI.getTarget(), Requirement)) { + CI.getDiagnostics().Report(diag::err_module_unavailable) + << Module->getFullModuleName() + << Requirement.second << Requirement.first; + + return false; + } + + FileManager &FileMgr = CI.getFileManager(); + + // Collect the set of #includes we need to build the module. + SmallString<256> HeaderContents; + if (const FileEntry *UmbrellaHeader = Module->getUmbrellaHeader()) + addHeaderInclude(UmbrellaHeader, HeaderContents, CI.getLangOpts()); + collectModuleHeaderIncludes(CI.getLangOpts(), FileMgr, + CI.getPreprocessor().getHeaderSearchInfo().getModuleMap(), + Module, HeaderContents); + + llvm::MemoryBuffer *InputBuffer = + llvm::MemoryBuffer::getMemBufferCopy(HeaderContents, + Module::getModuleInputBufferName()); + // Ownership of InputBuffer will be transfered to the SourceManager. + setCurrentInput(FrontendInputFile(InputBuffer, getCurrentFileKind(), + Module->IsSystem)); + return true; +} + +bool GenerateModuleAction::ComputeASTConsumerArguments(CompilerInstance &CI, + StringRef InFile, + std::string &Sysroot, + std::string &OutputFile, + raw_ostream *&OS) { + // If no output file was provided, figure out where this module would go + // in the module cache. + if (CI.getFrontendOpts().OutputFile.empty()) { + HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo(); + SmallString<256> ModuleFileName(HS.getModuleCachePath()); + llvm::sys::path::append(ModuleFileName, + CI.getLangOpts().CurrentModule + ".pcm"); + CI.getFrontendOpts().OutputFile = ModuleFileName.str(); + } + + // We use createOutputFile here because this is exposed via libclang, and we + // must disable the RemoveFileOnSignal behavior. + // We use a temporary to avoid race conditions. + OS = CI.createOutputFile(CI.getFrontendOpts().OutputFile, /*Binary=*/true, + /*RemoveFileOnSignal=*/false, InFile, + /*Extension=*/"", /*useTemporary=*/true, + /*CreateMissingDirectories=*/true); + if (!OS) + return true; + + OutputFile = CI.getFrontendOpts().OutputFile; + return false; +} + +ASTConsumer *SyntaxOnlyAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + return new ASTConsumer(); +} + +ASTConsumer *DumpModuleInfoAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + return new ASTConsumer(); +} + +namespace { + /// \brief AST reader listener that dumps module information for a module + /// file. + class DumpModuleInfoListener : public ASTReaderListener { + llvm::raw_ostream &Out; + + public: + DumpModuleInfoListener(llvm::raw_ostream &Out) : Out(Out) { } + +#define DUMP_BOOLEAN(Value, Text) \ + Out.indent(4) << Text << ": " << (Value? "Yes" : "No") << "\n" + + virtual bool ReadFullVersionInformation(StringRef FullVersion) { + Out.indent(2) + << "Generated by " + << (FullVersion == getClangFullRepositoryVersion()? "this" + : "a different") + << " Clang: " << FullVersion << "\n"; + return ASTReaderListener::ReadFullVersionInformation(FullVersion); + } + + virtual bool ReadLanguageOptions(const LangOptions &LangOpts, + bool Complain) { + Out.indent(2) << "Language options:\n"; +#define LANGOPT(Name, Bits, Default, Description) \ + DUMP_BOOLEAN(LangOpts.Name, Description); +#define ENUM_LANGOPT(Name, Type, Bits, Default, Description) \ + Out.indent(4) << Description << ": " \ + << static_cast<unsigned>(LangOpts.get##Name()) << "\n"; +#define VALUE_LANGOPT(Name, Bits, Default, Description) \ + Out.indent(4) << Description << ": " << LangOpts.Name << "\n"; +#define BENIGN_LANGOPT(Name, Bits, Default, Description) +#define BENIGN_ENUM_LANGOPT(Name, Type, Bits, Default, Description) +#include "clang/Basic/LangOptions.def" + return false; + } + + virtual bool ReadTargetOptions(const TargetOptions &TargetOpts, + bool Complain) { + Out.indent(2) << "Target options:\n"; + Out.indent(4) << " Triple: " << TargetOpts.Triple << "\n"; + Out.indent(4) << " CPU: " << TargetOpts.CPU << "\n"; + Out.indent(4) << " ABI: " << TargetOpts.ABI << "\n"; + Out.indent(4) << " C++ ABI: " << TargetOpts.CXXABI << "\n"; + Out.indent(4) << " Linker version: " << TargetOpts.LinkerVersion << "\n"; + + if (!TargetOpts.FeaturesAsWritten.empty()) { + Out.indent(4) << "Target features:\n"; + for (unsigned I = 0, N = TargetOpts.FeaturesAsWritten.size(); + I != N; ++I) { + Out.indent(6) << TargetOpts.FeaturesAsWritten[I] << "\n"; + } + } + + return false; + } + + virtual bool ReadHeaderSearchOptions(const HeaderSearchOptions &HSOpts, + bool Complain) { + Out.indent(2) << "Header search options:\n"; + Out.indent(4) << "System root [-isysroot=]: '" << HSOpts.Sysroot << "'\n"; + DUMP_BOOLEAN(HSOpts.UseBuiltinIncludes, + "Use builtin include directories [-nobuiltininc]"); + DUMP_BOOLEAN(HSOpts.UseStandardSystemIncludes, + "Use standard system include directories [-nostdinc]"); + DUMP_BOOLEAN(HSOpts.UseStandardCXXIncludes, + "Use standard C++ include directories [-nostdinc++]"); + DUMP_BOOLEAN(HSOpts.UseLibcxx, + "Use libc++ (rather than libstdc++) [-stdlib=]"); + return false; + } + + virtual bool ReadPreprocessorOptions(const PreprocessorOptions &PPOpts, + bool Complain, + std::string &SuggestedPredefines) { + Out.indent(2) << "Preprocessor options:\n"; + DUMP_BOOLEAN(PPOpts.UsePredefines, + "Uses compiler/target-specific predefines [-undef]"); + DUMP_BOOLEAN(PPOpts.DetailedRecord, + "Uses detailed preprocessing record (for indexing)"); + + if (!PPOpts.Macros.empty()) { + Out.indent(4) << "Predefined macros:\n"; + } + + for (std::vector<std::pair<std::string, bool/*isUndef*/> >::const_iterator + I = PPOpts.Macros.begin(), IEnd = PPOpts.Macros.end(); + I != IEnd; ++I) { + Out.indent(6); + if (I->second) + Out << "-U"; + else + Out << "-D"; + Out << I->first << "\n"; + } + return false; + } +#undef DUMP_BOOLEAN + }; +} + +void DumpModuleInfoAction::ExecuteAction() { + // Set up the output file. + llvm::OwningPtr<llvm::raw_fd_ostream> OutFile; + StringRef OutputFileName = getCompilerInstance().getFrontendOpts().OutputFile; + if (!OutputFileName.empty() && OutputFileName != "-") { + std::string ErrorInfo; + OutFile.reset(new llvm::raw_fd_ostream(OutputFileName.str().c_str(), + ErrorInfo)); + } + llvm::raw_ostream &Out = OutFile.get()? *OutFile.get() : llvm::outs(); + + Out << "Information for module file '" << getCurrentFile() << "':\n"; + DumpModuleInfoListener Listener(Out); + ASTReader::readASTFileControlBlock(getCurrentFile(), + getCompilerInstance().getFileManager(), + Listener); +} + +//===----------------------------------------------------------------------===// +// 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.getLangOpts()); + 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 may need to be set to 'Binary', to avoid converting Unix style + // line feeds (<LF>) to Microsoft style line feeds (<CR><LF>). + // + // Look to see what type of line endings the file uses. If there's a + // CRLF, then we won't open the file up in binary mode. If there is + // just an LF or CR, then we will open the file up in binary mode. + // In this fashion, the output format should match the input format, unless + // the input format has inconsistent line endings. + // + // This should be a relatively fast operation since most files won't have + // all of their source code on a single line. However, that is still a + // concern, so if we scan for too long, we'll just assume the file should + // be opened in binary mode. + bool BinaryMode = true; + bool InvalidFile = false; + const SourceManager& SM = CI.getSourceManager(); + const llvm::MemoryBuffer *Buffer = SM.getBuffer(SM.getMainFileID(), + &InvalidFile); + if (!InvalidFile) { + const char *cur = Buffer->getBufferStart(); + const char *end = Buffer->getBufferEnd(); + const char *next = (cur != end) ? cur + 1 : end; + + // Limit ourselves to only scanning 256 characters into the source + // file. This is mostly a sanity check in case the file has no + // newlines whatsoever. + if (end - cur > 256) end = cur + 256; + + while (next < end) { + if (*cur == 0x0D) { // CR + if (*next == 0x0A) // CRLF + BinaryMode = false; + + break; + } else if (*cur == 0x0A) // LF + break; + + ++cur, ++next; + } + } + + raw_ostream *OS = CI.createDefaultOutputFile(BinaryMode, 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: + case IK_CUDA: + 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; + } + + CompilerInstance &CI = getCompilerInstance(); + llvm::MemoryBuffer *Buffer + = CI.getFileManager().getBufferForFile(getCurrentFile()); + if (Buffer) { + unsigned Preamble = Lexer::ComputePreamble(Buffer, CI.getLangOpts()).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..1869d0c --- /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(StringRef Extension) { + return llvm::StringSwitch<InputKind>(Extension) + .Cases("ast", "pcm", 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) + .Cases("C", "cc", "cp", IK_CXX) + .Cases("cpp", "CPP", "c++", "cxx", "hpp", IK_CXX) + .Case("cl", IK_OpenCL) + .Case("cu", IK_CUDA) + .Cases("ll", "bc", IK_LLVM_IR) + .Default(IK_C); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/HeaderIncludeGen.cpp b/contrib/llvm/tools/clang/lib/Frontend/HeaderIncludeGen.cpp new file mode 100644 index 0000000..237e5b1 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/HeaderIncludeGen.cpp @@ -0,0 +1,134 @@ +//===--- HeaderIncludes.cpp - Generate Header Includes --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/Utils.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" +using namespace clang; + +namespace { +class HeaderIncludesCallback : public PPCallbacks { + SourceManager &SM; + raw_ostream *OutputFile; + unsigned CurrentIncludeDepth; + bool HasProcessedPredefines; + bool OwnsOutputFile; + bool ShowAllHeaders; + bool ShowDepth; + bool MSStyle; + +public: + HeaderIncludesCallback(const Preprocessor *PP, bool ShowAllHeaders_, + raw_ostream *OutputFile_, bool OwnsOutputFile_, + bool ShowDepth_, bool MSStyle_) + : SM(PP->getSourceManager()), OutputFile(OutputFile_), + CurrentIncludeDepth(0), HasProcessedPredefines(false), + OwnsOutputFile(OwnsOutputFile_), ShowAllHeaders(ShowAllHeaders_), + ShowDepth(ShowDepth_), MSStyle(MSStyle_) {} + + ~HeaderIncludesCallback() { + if (OwnsOutputFile) + delete OutputFile; + } + + virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, + FileID PrevFID); +}; +} + +void clang::AttachHeaderIncludeGen(Preprocessor &PP, bool ShowAllHeaders, + StringRef OutputPath, bool ShowDepth, + bool MSStyle) { + raw_ostream *OutputFile = &llvm::errs(); + bool OwnsOutputFile = false; + + // Open the output file, if used. + if (!OutputPath.empty()) { + std::string Error; + llvm::raw_fd_ostream *OS = new llvm::raw_fd_ostream( + OutputPath.str().c_str(), Error, llvm::sys::fs::F_Append); + if (!Error.empty()) { + PP.getDiagnostics().Report( + clang::diag::warn_fe_cc_print_header_failure) << Error; + delete OS; + } else { + OS->SetUnbuffered(); + OS->SetUseAtomicWrites(true); + OutputFile = OS; + OwnsOutputFile = true; + } + } + + PP.addPPCallbacks(new HeaderIncludesCallback(&PP, ShowAllHeaders, + OutputFile, OwnsOutputFile, + ShowDepth, MSStyle)); +} + +void HeaderIncludesCallback::FileChanged(SourceLocation Loc, + FileChangeReason Reason, + SrcMgr::CharacteristicKind NewFileType, + FileID PrevFID) { + // Unless we are exiting a #include, make sure to skip ahead to the line the + // #include directive was at. + PresumedLoc UserLoc = SM.getPresumedLoc(Loc); + if (UserLoc.isInvalid()) + return; + + // Adjust the current include depth. + if (Reason == PPCallbacks::EnterFile) { + ++CurrentIncludeDepth; + } else if (Reason == PPCallbacks::ExitFile) { + 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 1. + if (CurrentIncludeDepth == 1 && !HasProcessedPredefines) + HasProcessedPredefines = true; + + return; + } else + return; + + // Show the header if we are (a) past the predefines, or (b) showing all + // headers and in the predefines at a depth past the initial file and command + // line buffers. + bool ShowHeader = (HasProcessedPredefines || + (ShowAllHeaders && CurrentIncludeDepth > 2)); + + // Dump the header include information we are past the predefines buffer or + // are showing all headers. + if (ShowHeader && Reason == PPCallbacks::EnterFile) { + // Write to a temporary string to avoid unnecessary flushing on errs(). + SmallString<512> Filename(UserLoc.getFilename()); + if (!MSStyle) + Lexer::Stringify(Filename); + + SmallString<256> Msg; + if (MSStyle) + Msg += "Note: including file:"; + + if (ShowDepth) { + // The main source file is at depth 1, so skip one dot. + for (unsigned i = 1; i != CurrentIncludeDepth; ++i) + Msg += MSStyle ? ' ' : '.'; + + if (!MSStyle) + Msg += ' '; + } + Msg += Filename; + Msg += '\n'; + + OutputFile->write(Msg.data(), Msg.size()); + } +} 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..9371cbc --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/InitHeaderSearch.cpp @@ -0,0 +1,712 @@ +//===--- 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/Config/config.h" // C_INCLUDE_DIRS +#include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/HeaderSearchOptions.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/Triple.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +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<std::pair<IncludeDirGroup, DirectoryLookup> > IncludePath; + typedef std::vector<std::pair<IncludeDirGroup, + DirectoryLookup> >::const_iterator path_iterator; + std::vector<std::pair<std::string, bool> > SystemHeaderPrefixes; + HeaderSearch &Headers; + bool Verbose; + std::string IncludeSysroot; + bool HasSysroot; + +public: + + InitHeaderSearch(HeaderSearch &HS, bool verbose, StringRef sysroot) + : Headers(HS), Verbose(verbose), IncludeSysroot(sysroot), + HasSysroot(!(sysroot.empty() || sysroot == "/")) { + } + + /// AddPath - Add the specified path to the specified group list, prefixing + /// the sysroot if used. + void AddPath(const Twine &Path, IncludeDirGroup Group, bool isFramework); + + /// AddUnmappedPath - Add the specified path to the specified group list, + /// without performing any sysroot remapping. + void AddUnmappedPath(const Twine &Path, IncludeDirGroup Group, + bool isFramework); + + /// AddSystemHeaderPrefix - Add the specified prefix to the system header + /// prefix list. + void AddSystemHeaderPrefix(StringRef Prefix, bool IsSystemHeader) { + SystemHeaderPrefixes.push_back(std::make_pair(Prefix, IsSystemHeader)); + } + + /// AddGnuCPlusPlusIncludePaths - Add the necessary paths to support a gnu + /// libstdc++. + void AddGnuCPlusPlusIncludePaths(StringRef Base, + StringRef ArchDir, + StringRef Dir32, + StringRef Dir64, + const llvm::Triple &triple); + + /// AddMinGWCPlusPlusIncludePaths - Add the necessary paths to support a MinGW + /// libstdc++. + void AddMinGWCPlusPlusIncludePaths(StringRef Base, + StringRef Arch, + StringRef Version); + + /// AddMinGW64CXXPaths - Add the necessary paths to support + /// libstdc++ of x86_64-w64-mingw32 aka mingw-w64. + void AddMinGW64CXXPaths(StringRef Base, + StringRef Version); + + // 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, + const HeaderSearchOptions &HSOpts); + + /// AddDefaultSystemIncludePaths - Adds the default system include paths so + /// that e.g. stdio.h is found. + void AddDefaultIncludePaths(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(const LangOptions &Lang); +}; + +} // end anonymous namespace. + +static bool CanPrefixSysroot(StringRef Path) { +#if defined(_WIN32) + return !Path.empty() && llvm::sys::path::is_separator(Path[0]); +#else + return llvm::sys::path::is_absolute(Path); +#endif +} + +void InitHeaderSearch::AddPath(const Twine &Path, IncludeDirGroup Group, + bool isFramework) { + // Add the path with sysroot prepended, if desired and this is a system header + // group. + if (HasSysroot) { + SmallString<256> MappedPathStorage; + StringRef MappedPathStr = Path.toStringRef(MappedPathStorage); + if (CanPrefixSysroot(MappedPathStr)) { + AddUnmappedPath(IncludeSysroot + Path, Group, isFramework); + return; + } + } + + AddUnmappedPath(Path, Group, isFramework); +} + +void InitHeaderSearch::AddUnmappedPath(const Twine &Path, IncludeDirGroup Group, + bool isFramework) { + assert(!Path.isTriviallyEmpty() && "can't handle empty path here"); + + FileManager &FM = Headers.getFileMgr(); + SmallString<256> MappedPathStorage; + StringRef MappedPathStr = Path.toStringRef(MappedPathStorage); + + // Compute the DirectoryLookup type. + SrcMgr::CharacteristicKind Type; + if (Group == Quoted || Group == Angled || Group == IndexHeaderMap) { + Type = SrcMgr::C_User; + } else if (Group == ExternCSystem) { + Type = SrcMgr::C_ExternCSystem; + } else { + Type = SrcMgr::C_System; + } + + // If the directory exists, add it. + if (const DirectoryEntry *DE = FM.getDirectory(MappedPathStr)) { + IncludePath.push_back( + std::make_pair(Group, DirectoryLookup(DE, Type, 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(MappedPathStr)) { + if (const HeaderMap *HM = Headers.CreateHeaderMap(FE)) { + // It is a headermap, add it to the search path. + IncludePath.push_back( + std::make_pair(Group, + DirectoryLookup(HM, Type, Group == IndexHeaderMap))); + return; + } + } + } + + if (Verbose) + llvm::errs() << "ignoring nonexistent directory \"" + << MappedPathStr << "\"\n"; +} + +void InitHeaderSearch::AddGnuCPlusPlusIncludePaths(StringRef Base, + StringRef ArchDir, + StringRef Dir32, + StringRef Dir64, + const llvm::Triple &triple) { + // Add the base dir + AddPath(Base, CXXSystem, 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, CXXSystem, false); + else + AddPath(Base + "/" + ArchDir + "/" + Dir32, CXXSystem, false); + + // Add the backward dir + AddPath(Base + "/backward", CXXSystem, false); +} + +void InitHeaderSearch::AddMinGWCPlusPlusIncludePaths(StringRef Base, + StringRef Arch, + StringRef Version) { + AddPath(Base + "/" + Arch + "/" + Version + "/include/c++", + CXXSystem, false); + AddPath(Base + "/" + Arch + "/" + Version + "/include/c++/" + Arch, + CXXSystem, false); + AddPath(Base + "/" + Arch + "/" + Version + "/include/c++/backward", + CXXSystem, false); +} + +void InitHeaderSearch::AddMinGW64CXXPaths(StringRef Base, + StringRef Version) { + // Assumes Base is HeaderSearchOpts' ResourceDir + AddPath(Base + "/../../../include/c++/" + Version, + CXXSystem, false); + AddPath(Base + "/../../../include/c++/" + Version + "/x86_64-w64-mingw32", + CXXSystem, false); + AddPath(Base + "/../../../include/c++/" + Version + "/i686-w64-mingw32", + CXXSystem, false); + AddPath(Base + "/../../../include/c++/" + Version + "/backward", + CXXSystem, false); +} + +void InitHeaderSearch::AddDefaultCIncludePaths(const llvm::Triple &triple, + const HeaderSearchOptions &HSOpts) { + llvm::Triple::OSType os = triple.getOS(); + + if (HSOpts.UseStandardSystemIncludes) { + switch (os) { + case llvm::Triple::FreeBSD: + case llvm::Triple::NetBSD: + case llvm::Triple::OpenBSD: + case llvm::Triple::Bitrig: + break; + default: + // FIXME: temporary hack: hard-coded paths. + AddPath("/usr/local/include", System, false); + break; + } + } + + // 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. + SmallString<128> P = StringRef(HSOpts.ResourceDir); + llvm::sys::path::append(P, "include"); + AddUnmappedPath(P.str(), ExternCSystem, false); + } + + // All remaining additions are for system include directories, early exit if + // we aren't using them. + if (!HSOpts.UseStandardSystemIncludes) + return; + + // Add dirs specified via 'configure --with-c-include-dirs'. + StringRef CIncludeDirs(C_INCLUDE_DIRS); + if (CIncludeDirs != "") { + SmallVector<StringRef, 5> dirs; + CIncludeDirs.split(dirs, ":"); + for (SmallVectorImpl<StringRef>::iterator i = dirs.begin(); + i != dirs.end(); + ++i) + AddPath(*i, ExternCSystem, false); + return; + } + + switch (os) { + case llvm::Triple::Linux: + case llvm::Triple::Win32: + llvm_unreachable("Include management is handled in the driver."); + + case llvm::Triple::Haiku: + AddPath("/boot/common/include", System, false); + AddPath("/boot/develop/headers/os", System, false); + AddPath("/boot/develop/headers/os/app", System, false); + AddPath("/boot/develop/headers/os/arch", System, false); + AddPath("/boot/develop/headers/os/device", System, false); + AddPath("/boot/develop/headers/os/drivers", System, false); + AddPath("/boot/develop/headers/os/game", System, false); + AddPath("/boot/develop/headers/os/interface", System, false); + AddPath("/boot/develop/headers/os/kernel", System, false); + AddPath("/boot/develop/headers/os/locale", System, false); + AddPath("/boot/develop/headers/os/mail", System, false); + AddPath("/boot/develop/headers/os/media", System, false); + AddPath("/boot/develop/headers/os/midi", System, false); + AddPath("/boot/develop/headers/os/midi2", System, false); + AddPath("/boot/develop/headers/os/net", System, false); + AddPath("/boot/develop/headers/os/storage", System, false); + AddPath("/boot/develop/headers/os/support", System, false); + AddPath("/boot/develop/headers/os/translation", System, false); + AddPath("/boot/develop/headers/os/add-ons/graphics", System, false); + AddPath("/boot/develop/headers/os/add-ons/input_server", System, false); + AddPath("/boot/develop/headers/os/add-ons/screen_saver", System, false); + AddPath("/boot/develop/headers/os/add-ons/tracker", System, false); + AddPath("/boot/develop/headers/os/be_apps/Deskbar", System, false); + AddPath("/boot/develop/headers/os/be_apps/NetPositive", System, false); + AddPath("/boot/develop/headers/os/be_apps/Tracker", System, false); + AddPath("/boot/develop/headers/cpp", System, false); + AddPath("/boot/develop/headers/cpp/i586-pc-haiku", System, false); + AddPath("/boot/develop/headers/3rdparty", System, false); + AddPath("/boot/develop/headers/bsd", System, false); + AddPath("/boot/develop/headers/glibc", System, false); + AddPath("/boot/develop/headers/posix", System, false); + AddPath("/boot/develop/headers", System, false); + break; + case llvm::Triple::RTEMS: + break; + case llvm::Triple::Cygwin: + AddPath("/usr/include/w32api", System, false); + break; + case llvm::Triple::MinGW32: { + // mingw-w64 crt include paths + // <sysroot>/i686-w64-mingw32/include + SmallString<128> P = StringRef(HSOpts.ResourceDir); + llvm::sys::path::append(P, "../../../i686-w64-mingw32/include"); + AddPath(P.str(), System, false); + + // <sysroot>/x86_64-w64-mingw32/include + P.resize(HSOpts.ResourceDir.size()); + llvm::sys::path::append(P, "../../../x86_64-w64-mingw32/include"); + AddPath(P.str(), System, false); + + // mingw.org crt include paths + // <sysroot>/include + P.resize(HSOpts.ResourceDir.size()); + llvm::sys::path::append(P, "../../../include"); + AddPath(P.str(), System, false); + AddPath("/mingw/include", System, false); +#if defined(_WIN32) + AddPath("c:/mingw/include", System, false); +#endif + } + break; + case llvm::Triple::FreeBSD: + AddPath("/usr/include/clang/" CLANG_VERSION_STRING, System, false); + break; + + default: + break; + } + + if ( os != llvm::Triple::RTEMS ) + AddPath("/usr/include", ExternCSystem, false); +} + +void InitHeaderSearch:: +AddDefaultCPlusPlusIncludePaths(const llvm::Triple &triple, const HeaderSearchOptions &HSOpts) { + llvm::Triple::OSType os = triple.getOS(); + // FIXME: temporary hack: hard-coded paths. + + if (triple.isOSDarwin()) { + 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; + } + return; + } + + switch (os) { + case llvm::Triple::Linux: + case llvm::Triple::Win32: + llvm_unreachable("Include management is handled in the driver."); + + case llvm::Triple::Cygwin: + // Cygwin-1.7 + AddMinGWCPlusPlusIncludePaths("/usr/lib/gcc", "i686-pc-cygwin", "4.7.3"); + AddMinGWCPlusPlusIncludePaths("/usr/lib/gcc", "i686-pc-cygwin", "4.5.3"); + AddMinGWCPlusPlusIncludePaths("/usr/lib/gcc", "i686-pc-cygwin", "4.3.4"); + // g++-4 / Cygwin-1.5 + AddMinGWCPlusPlusIncludePaths("/usr/lib/gcc", "i686-pc-cygwin", "4.3.2"); + break; + case llvm::Triple::MinGW32: + // mingw-w64 C++ include paths (i686-w64-mingw32 and x86_64-w64-mingw32) + AddMinGW64CXXPaths(HSOpts.ResourceDir, "4.5.0"); + AddMinGW64CXXPaths(HSOpts.ResourceDir, "4.5.1"); + AddMinGW64CXXPaths(HSOpts.ResourceDir, "4.5.2"); + AddMinGW64CXXPaths(HSOpts.ResourceDir, "4.5.3"); + AddMinGW64CXXPaths(HSOpts.ResourceDir, "4.5.4"); + AddMinGW64CXXPaths(HSOpts.ResourceDir, "4.6.0"); + AddMinGW64CXXPaths(HSOpts.ResourceDir, "4.6.1"); + AddMinGW64CXXPaths(HSOpts.ResourceDir, "4.6.2"); + AddMinGW64CXXPaths(HSOpts.ResourceDir, "4.6.3"); + AddMinGW64CXXPaths(HSOpts.ResourceDir, "4.7.0"); + // mingw.org C++ include paths + AddMinGWCPlusPlusIncludePaths("/mingw/lib/gcc", "mingw32", "4.5.2"); //MSYS +#if defined(_WIN32) + AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw32", "4.8.1"); + AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw32", "4.6.2"); + AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw32", "4.6.1"); + AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw32", "4.5.2"); + AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw32", "4.5.0"); + AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw32", "4.4.0"); + AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw32", "4.3.0"); +#endif + break; + case llvm::Triple::DragonFly: + if (llvm::sys::fs::exists("/usr/lib/gcc47")) + AddPath("/usr/include/c++/4.7", CXXSystem, false); + else + AddPath("/usr/include/c++/4.4", CXXSystem, false); + break; + case llvm::Triple::FreeBSD: + // FreeBSD 8.0 + // FreeBSD 7.3 + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.2", + "", "", "", triple); + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.2/backward", + "", "", "", 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: + AddGnuCPlusPlusIncludePaths("/usr/gcc/4.5/include/c++/4.5.2/", + "i386-pc-solaris2.11", "", "", triple); + // 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::AddDefaultIncludePaths(const LangOptions &Lang, + const llvm::Triple &triple, + const HeaderSearchOptions &HSOpts) { + // NB: This code path is going away. All of the logic is moving into the + // driver which has the information necessary to do target-specific + // selections of default include paths. Each target which moves there will be + // exempted from this logic here until we can delete the entire pile of code. + switch (triple.getOS()) { + default: + break; // Everything else continues to use this routine's logic. + + case llvm::Triple::Linux: + case llvm::Triple::Win32: + return; + } + + if (Lang.CPlusPlus && HSOpts.UseStandardCXXIncludes && + HSOpts.UseStandardSystemIncludes) { + if (HSOpts.UseLibcxx) { + if (triple.isOSDarwin()) { + // On Darwin, libc++ may be installed alongside the compiler in + // include/c++/v1. + if (!HSOpts.ResourceDir.empty()) { + // Remove version from foo/lib/clang/version + StringRef NoVer = llvm::sys::path::parent_path(HSOpts.ResourceDir); + // Remove clang from foo/lib/clang + StringRef Lib = llvm::sys::path::parent_path(NoVer); + // Remove lib from foo/lib + SmallString<128> P = llvm::sys::path::parent_path(Lib); + + // Get foo/include/c++/v1 + llvm::sys::path::append(P, "include", "c++", "v1"); + AddUnmappedPath(P.str(), CXXSystem, false); + } + } + // On Solaris, include the support directory for things like xlocale and + // fudged system headers. + if (triple.getOS() == llvm::Triple::Solaris) + AddPath("/usr/include/c++/v1/support/solaris", CXXSystem, false); + + AddPath("/usr/include/c++/v1", CXXSystem, false); + } else { + AddDefaultCPlusPlusIncludePaths(triple, HSOpts); + } + } + + AddDefaultCIncludePaths(triple, HSOpts); + + // Add the default framework include paths on Darwin. + if (HSOpts.UseStandardSystemIncludes) { + if (triple.isOSDarwin()) { + AddPath("/System/Library/Frameworks", System, true); + AddPath("/Library/Frameworks", System, true); + } + } +} + +/// RemoveDuplicates - If there are duplicate directory entries in the specified +/// search list, remove the later (dead) ones. Returns the number of non-system +/// headers removed, which is used to update NumAngled. +static unsigned RemoveDuplicates(std::vector<DirectoryLookup> &SearchList, + unsigned First, bool Verbose) { + llvm::SmallPtrSet<const DirectoryEntry *, 8> SeenDirs; + llvm::SmallPtrSet<const DirectoryEntry *, 8> SeenFrameworkDirs; + llvm::SmallPtrSet<const HeaderMap *, 8> SeenHeaderMaps; + unsigned NonSystemRemoved = 0; + for (unsigned i = First; 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"; + } + if (DirToRemove != i) + ++NonSystemRemoved; + + // This is reached if the current entry is a duplicate. Remove the + // DirToRemove (usually the current dir). + SearchList.erase(SearchList.begin()+DirToRemove); + --i; + } + return NonSystemRemoved; +} + + +void InitHeaderSearch::Realize(const LangOptions &Lang) { + // Concatenate ANGLE+SYSTEM+AFTER chains together into SearchList. + std::vector<DirectoryLookup> SearchList; + SearchList.reserve(IncludePath.size()); + + // Quoted arguments go first. + for (path_iterator it = IncludePath.begin(), ie = IncludePath.end(); + it != ie; ++it) { + if (it->first == Quoted) + SearchList.push_back(it->second); + } + // Deduplicate and remember index. + RemoveDuplicates(SearchList, 0, Verbose); + unsigned NumQuoted = SearchList.size(); + + for (path_iterator it = IncludePath.begin(), ie = IncludePath.end(); + it != ie; ++it) { + if (it->first == Angled || it->first == IndexHeaderMap) + SearchList.push_back(it->second); + } + + RemoveDuplicates(SearchList, NumQuoted, Verbose); + unsigned NumAngled = SearchList.size(); + + for (path_iterator it = IncludePath.begin(), ie = IncludePath.end(); + it != ie; ++it) { + if (it->first == System || it->first == ExternCSystem || + (!Lang.ObjC1 && !Lang.CPlusPlus && it->first == CSystem) || + (/*FIXME !Lang.ObjC1 && */Lang.CPlusPlus && it->first == CXXSystem) || + (Lang.ObjC1 && !Lang.CPlusPlus && it->first == ObjCSystem) || + (Lang.ObjC1 && Lang.CPlusPlus && it->first == ObjCXXSystem)) + SearchList.push_back(it->second); + } + + for (path_iterator it = IncludePath.begin(), ie = IncludePath.end(); + it != ie; ++it) { + if (it->first == After) + SearchList.push_back(it->second); + } + + // Remove duplicates across both the Angled and System directories. GCC does + // this and failing to remove duplicates across these two groups breaks + // #include_next. + unsigned NonSystemRemoved = RemoveDuplicates(SearchList, NumQuoted, Verbose); + NumAngled -= NonSystemRemoved; + + bool DontSearchCurDir = false; // TODO: set to true if -I- is set? + Headers.SetSearchPaths(SearchList, NumQuoted, NumAngled, DontSearchCurDir); + + Headers.SetSystemHeaderPrefixes(SystemHeaderPrefixes); + + // If verbose, print the list of directories that will be searched. + if (Verbose) { + llvm::errs() << "#include \"...\" search starts here:\n"; + for (unsigned i = 0, e = SearchList.size(); i != e; ++i) { + if (i == NumQuoted) + 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]; + if (E.IgnoreSysRoot) { + Init.AddUnmappedPath(E.Path, E.Group, E.IsFramework); + } else { + Init.AddPath(E.Path, E.Group, E.IsFramework); + } + } + + Init.AddDefaultIncludePaths(Lang, Triple, HSOpts); + + for (unsigned i = 0, e = HSOpts.SystemHeaderPrefixes.size(); i != e; ++i) + Init.AddSystemHeaderPrefix(HSOpts.SystemHeaderPrefixes[i].Prefix, + HSOpts.SystemHeaderPrefixes[i].IsSystemHeader); + + if (HSOpts.UseBuiltinIncludes) { + // Set up the builtin include directory in the module map. + SmallString<128> P = StringRef(HSOpts.ResourceDir); + llvm::sys::path::append(P, "include"); + if (const DirectoryEntry *Dir = HS.getFileMgr().getDirectory(P.str())) + HS.getModuleMap().setBuiltinIncludeDir(Dir); + } + + Init.Realize(Lang); +} 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..c7d2550 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/InitPreprocessor.cpp @@ -0,0 +1,888 @@ +//===--- 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/Frontend/Utils.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/MacroBuilder.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Basic/Version.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/FrontendOptions.h" +#include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Lex/PreprocessorOptions.h" +#include "clang/Serialization/ASTReader.h" +#include "llvm/ADT/APFloat.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +using namespace clang; + +static bool MacroBodyEndsInBackslash(StringRef MacroBody) { + while (!MacroBody.empty() && isWhitespace(MacroBody.back())) + MacroBody = MacroBody.drop_back(); + return !MacroBody.empty() && MacroBody.back() == '\\'; +} + +// 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, StringRef Macro, + DiagnosticsEngine &Diags) { + std::pair<StringRef, StringRef> MacroPair = Macro.split('='); + StringRef MacroName = MacroPair.first; + StringRef MacroBody = MacroPair.second; + if (MacroName.size() != Macro.size()) { + // Per GCC -D semantics, the macro ends at \n if it exists. + StringRef::size_type End = MacroBody.find_first_of("\n\r"); + if (End != StringRef::npos) + Diags.Report(diag::warn_fe_macro_contains_embedded_newline) + << MacroName; + MacroBody = MacroBody.substr(0, End); + // We handle macro bodies which end in a backslash by appending an extra + // backslash+newline. This makes sure we don't accidentally treat the + // backslash as a line continuation marker. + if (MacroBodyEndsInBackslash(MacroBody)) + Builder.defineMacro(MacroName, Twine(MacroBody) + "\\\n"); + else + Builder.defineMacro(MacroName, MacroBody); + } else { + // Push "macroname 1". + Builder.defineMacro(Macro); + } +} + +/// AddImplicitInclude - Add an implicit \#include of the specified file to the +/// predefines buffer. +static void AddImplicitInclude(MacroBuilder &Builder, StringRef File, + FileManager &FileMgr) { + Builder.append(Twine("#include \"") + + HeaderSearch::NormalizeDashIncludePath(File, FileMgr) + "\""); +} + +static void AddImplicitIncludeMacros(MacroBuilder &Builder, + StringRef File, + FileManager &FileMgr) { + Builder.append(Twine("#__include_macros \"") + + HeaderSearch::NormalizeDashIncludePath(File, FileMgr) + "\""); + // 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, + 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, PP.getFileManager()); +} + +/// \brief Add an implicit \#include using the original file used to generate +/// a PCH file. +static void AddImplicitIncludePCH(MacroBuilder &Builder, Preprocessor &PP, + StringRef ImplicitIncludePCH) { + std::string OriginalFile = + ASTReader::getOriginalSourceFile(ImplicitIncludePCH, PP.getFileManager(), + PP.getDiagnostics()); + if (OriginalFile.empty()) + return; + + AddImplicitInclude(Builder, OriginalFile, PP.getFileManager()); +} + +/// 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, StringRef Prefix, + const llvm::fltSemantics *Sem, StringRef Ext) { + const char *DenormMin, *Epsilon, *Max, *Min; + DenormMin = PickFP(Sem, "1.40129846e-45", "4.9406564584124654e-324", + "3.64519953188247460253e-4951", + "4.94065645841246544176568792868221e-324", + "6.47517511943802511092443895822764655e-4966"); + int Digits = PickFP(Sem, 6, 15, 18, 31, 33); + Epsilon = PickFP(Sem, "1.19209290e-7", "2.2204460492503131e-16", + "1.08420217248550443401e-19", + "4.94065645841246544176568792868221e-324", + "1.92592994438723585305597794258492732e-34"); + 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-38", "2.2250738585072014e-308", + "3.36210314311209350626e-4932", + "2.00416836000897277799610805135016e-292", + "3.36210314311209350626267781732175260e-4932"); + Max = PickFP(Sem, "3.40282347e+38", "1.7976931348623157e+308", + "1.18973149535723176502e+4932", + "1.79769313486231580793728971405301e+308", + "1.18973149535723176508575932662800702e+4932"); + + SmallString<32> DefPrefix; + DefPrefix = "__"; + DefPrefix += Prefix; + DefPrefix += "_"; + + Builder.defineMacro(DefPrefix + "DENORM_MIN__", Twine(DenormMin)+Ext); + Builder.defineMacro(DefPrefix + "HAS_DENORM__"); + Builder.defineMacro(DefPrefix + "DIG__", Twine(Digits)); + Builder.defineMacro(DefPrefix + "EPSILON__", Twine(Epsilon)+Ext); + Builder.defineMacro(DefPrefix + "HAS_INFINITY__"); + Builder.defineMacro(DefPrefix + "HAS_QUIET_NAN__"); + Builder.defineMacro(DefPrefix + "MANT_DIG__", Twine(MantissaDigits)); + + Builder.defineMacro(DefPrefix + "MAX_10_EXP__", Twine(Max10Exp)); + Builder.defineMacro(DefPrefix + "MAX_EXP__", Twine(MaxExp)); + Builder.defineMacro(DefPrefix + "MAX__", Twine(Max)+Ext); + + Builder.defineMacro(DefPrefix + "MIN_10_EXP__","("+Twine(Min10Exp)+")"); + Builder.defineMacro(DefPrefix + "MIN_EXP__", "("+Twine(MinExp)+")"); + Builder.defineMacro(DefPrefix + "MIN__", Twine(Min)+Ext); +} + + +/// 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(StringRef MacroName, unsigned TypeWidth, + StringRef ValSuffix, bool isSigned, + MacroBuilder &Builder) { + llvm::APInt MaxVal = isSigned ? llvm::APInt::getSignedMaxValue(TypeWidth) + : llvm::APInt::getMaxValue(TypeWidth); + Builder.defineMacro(MacroName, MaxVal.toString(10, isSigned) + ValSuffix); +} + +/// DefineTypeSize - An overloaded helper that uses TargetInfo to determine +/// the width, suffix, and signedness of the given type +static void DefineTypeSize(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 Twine &MacroName, TargetInfo::IntType Ty, + MacroBuilder &Builder) { + Builder.defineMacro(MacroName, TargetInfo::getTypeName(Ty)); +} + +static void DefineTypeWidth(StringRef MacroName, TargetInfo::IntType Ty, + const TargetInfo &TI, MacroBuilder &Builder) { + Builder.defineMacro(MacroName, Twine(TI.getTypeWidth(Ty))); +} + +static void DefineTypeSizeof(StringRef MacroName, unsigned BitWidth, + const TargetInfo &TI, MacroBuilder &Builder) { + Builder.defineMacro(MacroName, + 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" + Twine(TypeWidth) + "_TYPE__", Ty, Builder); + + StringRef ConstSuffix(TargetInfo::getTypeConstantSuffix(Ty)); + if (!ConstSuffix.empty()) + Builder.defineMacro("__INT" + Twine(TypeWidth) + "_C_SUFFIX__", + ConstSuffix); +} + +/// Get the value the ATOMIC_*_LOCK_FREE macro should have for a type with +/// the specified properties. +static const char *getLockFreeValue(unsigned TypeWidth, unsigned TypeAlign, + unsigned InlineWidth) { + // Fully-aligned, power-of-2 sizes no larger than the inline + // width will be inlined as lock-free operations. + if (TypeWidth == TypeAlign && (TypeWidth & (TypeWidth - 1)) == 0 && + TypeWidth <= InlineWidth) + return "2"; // "always lock free" + // We cannot be certain what operations the lib calls might be + // able to implement as lock-free on future processors. + return "1"; // "sometimes lock free" +} + +/// \brief Add definitions required for a smooth interaction between +/// Objective-C++ automated reference counting and libstdc++ (4.2). +static void AddObjCXXARCLibstdcxxDefines(const LangOptions &LangOpts, + MacroBuilder &Builder) { + Builder.defineMacro("_GLIBCXX_PREDEFINED_OBJC_ARC_IS_SCALAR"); + + std::string Result; + { + // Provide specializations for the __is_scalar type trait so that + // lifetime-qualified objects are not considered "scalar" types, which + // libstdc++ uses as an indicator of the presence of trivial copy, assign, + // default-construct, and destruct semantics (none of which hold for + // lifetime-qualified objects in ARC). + llvm::raw_string_ostream Out(Result); + + Out << "namespace std {\n" + << "\n" + << "struct __true_type;\n" + << "struct __false_type;\n" + << "\n"; + + Out << "template<typename _Tp> struct __is_scalar;\n" + << "\n"; + + Out << "template<typename _Tp>\n" + << "struct __is_scalar<__attribute__((objc_ownership(strong))) _Tp> {\n" + << " enum { __value = 0 };\n" + << " typedef __false_type __type;\n" + << "};\n" + << "\n"; + + if (LangOpts.ObjCARCWeak) { + Out << "template<typename _Tp>\n" + << "struct __is_scalar<__attribute__((objc_ownership(weak))) _Tp> {\n" + << " enum { __value = 0 };\n" + << " typedef __false_type __type;\n" + << "};\n" + << "\n"; + } + + Out << "template<typename _Tp>\n" + << "struct __is_scalar<__attribute__((objc_ownership(autoreleasing)))" + << " _Tp> {\n" + << " enum { __value = 0 };\n" + << " typedef __false_type __type;\n" + << "};\n" + << "\n"; + + Out << "}\n"; + } + Builder.append(Result); +} + +static void InitializeStandardPredefinedMacros(const TargetInfo &TI, + const LangOptions &LangOpts, + const FrontendOptions &FEOpts, + MacroBuilder &Builder) { + if (!LangOpts.MicrosoftMode && !LangOpts.TraditionalCPP) + Builder.defineMacro("__STDC__"); + if (LangOpts.Freestanding) + Builder.defineMacro("__STDC_HOSTED__", "0"); + else + Builder.defineMacro("__STDC_HOSTED__"); + + if (!LangOpts.CPlusPlus) { + if (LangOpts.C11) + Builder.defineMacro("__STDC_VERSION__", "201112L"); + else if (LangOpts.C99) + Builder.defineMacro("__STDC_VERSION__", "199901L"); + else if (!LangOpts.GNUMode && LangOpts.Digraphs) + Builder.defineMacro("__STDC_VERSION__", "199409L"); + } else { + // FIXME: Use the right value for __cplusplus for C++1y once one is chosen. + if (LangOpts.CPlusPlus1y) + Builder.defineMacro("__cplusplus", "201305L"); + // C++11 [cpp.predefined]p1: + // The name __cplusplus is defined to the value 201103L when compiling a + // C++ translation unit. + else if (LangOpts.CPlusPlus11) + Builder.defineMacro("__cplusplus", "201103L"); + // C++03 [cpp.predefined]p1: + // The name __cplusplus is defined to the value 199711L when compiling a + // C++ translation unit. + else + Builder.defineMacro("__cplusplus", "199711L"); + } + + // In C11 these are environment macros. In C++11 they are only defined + // as part of <cuchar>. To prevent breakage when mixing C and C++ + // code, define these macros unconditionally. We can define them + // unconditionally, as Clang always uses UTF-16 and UTF-32 for 16-bit + // and 32-bit character literals. + Builder.defineMacro("__STDC_UTF_16__", "1"); + Builder.defineMacro("__STDC_UTF_32__", "1"); + + if (LangOpts.ObjC1) + Builder.defineMacro("__OBJC__"); + + // Not "standard" per se, but available even with the -undef flag. + if (LangOpts.AsmPreprocessor) + Builder.defineMacro("__ASSEMBLER__"); +} + +/// Initialize the predefined C++ language feature test macros defined in +/// ISO/IEC JTC1/SC22/WG21 (C++) SD-6: "SG10 Feature Test Recommendations". +static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts, + MacroBuilder &Builder) { + // C++11 features. + if (LangOpts.CPlusPlus11) { + Builder.defineMacro("__cpp_unicode_characters", "200704"); + Builder.defineMacro("__cpp_raw_strings", "200710"); + Builder.defineMacro("__cpp_unicode_literals", "200710"); + Builder.defineMacro("__cpp_user_defined_literals", "200809"); + Builder.defineMacro("__cpp_lambdas", "200907"); + Builder.defineMacro("__cpp_constexpr", + LangOpts.CPlusPlus1y ? "201304" : "200704"); + Builder.defineMacro("__cpp_static_assert", "200410"); + Builder.defineMacro("__cpp_decltype", "200707"); + Builder.defineMacro("__cpp_attributes", "200809"); + Builder.defineMacro("__cpp_rvalue_references", "200610"); + Builder.defineMacro("__cpp_variadic_templates", "200704"); + } + + // C++14 features. + if (LangOpts.CPlusPlus1y) { + Builder.defineMacro("__cpp_binary_literals", "201304"); + Builder.defineMacro("__cpp_init_captures", "201304"); + Builder.defineMacro("__cpp_generic_lambdas", "201304"); + Builder.defineMacro("__cpp_decltype_auto", "201304"); + Builder.defineMacro("__cpp_return_type_deduction", "201304"); + Builder.defineMacro("__cpp_aggregate_nsdmi", "201304"); + Builder.defineMacro("__cpp_variable_templates", "201304"); + } +} + +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 + if (!LangOpts.MicrosoftMode) { + // Currently claim to be compatible with GCC 4.2.1-5621, but only if we're + // not compiling for MSVC compatibility + Builder.defineMacro("__GNUC_MINOR__", "2"); + Builder.defineMacro("__GNUC_PATCHLEVEL__", "1"); + Builder.defineMacro("__GNUC__", "4"); + Builder.defineMacro("__GXX_ABI_VERSION", "1002"); + } + + // Define macros for the C11 / C++11 memory orderings + Builder.defineMacro("__ATOMIC_RELAXED", "0"); + Builder.defineMacro("__ATOMIC_CONSUME", "1"); + Builder.defineMacro("__ATOMIC_ACQUIRE", "2"); + Builder.defineMacro("__ATOMIC_RELEASE", "3"); + Builder.defineMacro("__ATOMIC_ACQ_REL", "4"); + Builder.defineMacro("__ATOMIC_SEQ_CST", "5"); + + // Support for #pragma redefine_extname (Sun compatibility) + Builder.defineMacro("__PRAGMA_REDEFINE_EXTNAME", "1"); + + // As sad as it is, enough software depends on the __VERSION__ for version + // checks that it is necessary to report 4.2.1 (the base GCC version we claim + // compatibility with) first. + Builder.defineMacro("__VERSION__", "\"4.2.1 Compatible " + + Twine(getClangFullCPPVersion()) + "\""); + + // Initialize language-specific preprocessor defines. + + // Standard conforming mode? + if (!LangOpts.GNUMode) + Builder.defineMacro("__STRICT_ANSI__"); + + if (LangOpts.CPlusPlus11) + Builder.defineMacro("__GXX_EXPERIMENTAL_CXX0X__"); + + if (LangOpts.ObjC1) { + if (LangOpts.ObjCRuntime.isNonFragile()) { + Builder.defineMacro("__OBJC2__"); + + if (LangOpts.ObjCExceptions) + Builder.defineMacro("OBJC_ZEROCOST_EXCEPTIONS"); + } + + if (LangOpts.getGC() != LangOptions::NonGC) + Builder.defineMacro("__OBJC_GC__"); + + if (LangOpts.ObjCRuntime.isNeXTFamily()) + Builder.defineMacro("__NEXT_RUNTIME__"); + + if (LangOpts.ObjCRuntime.getKind() == ObjCRuntime::ObjFW) { + VersionTuple tuple = LangOpts.ObjCRuntime.getVersion(); + + unsigned minor = 0; + if (tuple.getMinor().hasValue()) + minor = tuple.getMinor().getValue(); + + unsigned subminor = 0; + if (tuple.getSubminor().hasValue()) + subminor = tuple.getSubminor().getValue(); + + Builder.defineMacro("__OBJFW_RUNTIME_ABI__", + Twine(tuple.getMajor() * 10000 + minor * 100 + + subminor)); + } + + Builder.defineMacro("IBOutlet", "__attribute__((iboutlet))"); + Builder.defineMacro("IBOutletCollection(ClassName)", + "__attribute__((iboutletcollection(ClassName)))"); + Builder.defineMacro("IBAction", "void)__attribute__((ibaction)"); + } + + if (LangOpts.CPlusPlus) + InitializeCPlusPlusFeatureTestMacros(LangOpts, Builder); + + // darwin_constant_cfstrings controls this. This is also dependent + // on other things like the runtime I believe. This is set even for C code. + if (!LangOpts.NoConstantCFStrings) + 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.CXXExceptions) + Builder.defineMacro("__EXCEPTIONS"); + if (LangOpts.RTTI) + Builder.defineMacro("__GXX_RTTI"); + if (LangOpts.SjLjExceptions) + Builder.defineMacro("__USING_SJLJ_EXCEPTIONS__"); + + if (LangOpts.Deprecated) + Builder.defineMacro("__DEPRECATED"); + + if (LangOpts.CPlusPlus) { + Builder.defineMacro("__GNUG__", "4"); + Builder.defineMacro("__GXX_WEAK__"); + Builder.defineMacro("__private_extern__", "extern"); + } + + if (LangOpts.MicrosoftExt) { + // 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++ headers. + if (LangOpts.WChar) { + // wchar_t supported as a keyword. + Builder.defineMacro("_WCHAR_T_DEFINED"); + Builder.defineMacro("_NATIVE_WCHAR_T_DEFINED"); + } + if (LangOpts.CPlusPlus) { + // FIXME: Support Microsoft's __identifier extension in the lexer. + Builder.append("#define __identifier(x) x"); + Builder.append("class type_info;"); + } + } + + if (LangOpts.Optimize) + Builder.defineMacro("__OPTIMIZE__"); + if (LangOpts.OptimizeSize) + Builder.defineMacro("__OPTIMIZE_SIZE__"); + + if (LangOpts.FastMath) + Builder.defineMacro("__FAST_MATH__"); + + // Initialize target-specific preprocessor defines. + + // __BYTE_ORDER__ was added in GCC 4.6. It's analogous + // to the macro __BYTE_ORDER (no trailing underscores) + // from glibc's <endian.h> header. + // We don't support the PDP-11 as a target, but include + // the define so it can still be compared against. + Builder.defineMacro("__ORDER_LITTLE_ENDIAN__", "1234"); + Builder.defineMacro("__ORDER_BIG_ENDIAN__", "4321"); + Builder.defineMacro("__ORDER_PDP_ENDIAN__", "3412"); + if (TI.isBigEndian()) + Builder.defineMacro("__BYTE_ORDER__", "__ORDER_BIG_ENDIAN__"); + else + Builder.defineMacro("__BYTE_ORDER__", "__ORDER_LITTLE_ENDIAN__"); + + + if (TI.getPointerWidth(0) == 64 && TI.getLongWidth() == 64 + && TI.getIntWidth() == 32) { + Builder.defineMacro("_LP64"); + Builder.defineMacro("__LP64__"); + } + + // 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__", TargetInfo::SignedChar, TI, 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); + DefineTypeSize("__SIZE_MAX__", TI.getSizeType(), 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); + if (TI.hasInt128Type()) + DefineTypeSizeof("__SIZEOF_INT128__", 128, 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(), "F"); + DefineFloatMacros(Builder, "DBL", &TI.getDoubleFormat(), ""); + DefineFloatMacros(Builder, "LDBL", &TI.getLongDoubleFormat(), "L"); + + // Define a __POINTER_WIDTH__ macro for stdint.h. + Builder.defineMacro("__POINTER_WIDTH__", + Twine((int)TI.getPointerWidth(0))); + + if (!LangOpts.CharIsSigned) + Builder.defineMacro("__CHAR_UNSIGNED__"); + + if (!TargetInfo::isTypeSigned(TI.getWCharType())) + Builder.defineMacro("__WCHAR_UNSIGNED__"); + + if (!TargetInfo::isTypeSigned(TI.getWIntType())) + Builder.defineMacro("__WINT_UNSIGNED__"); + + // Define exact-width integer types for stdint.h + Builder.defineMacro("__INT" + 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); + + if (const char *Prefix = TI.getUserLabelPrefix()) + Builder.defineMacro("__USER_LABEL_PREFIX__", Prefix); + + if (LangOpts.FastMath || LangOpts.FiniteMathOnly) + Builder.defineMacro("__FINITE_MATH_ONLY__", "1"); + else + Builder.defineMacro("__FINITE_MATH_ONLY__", "0"); + + if (LangOpts.GNUInline) + Builder.defineMacro("__GNUC_GNU_INLINE__"); + else + Builder.defineMacro("__GNUC_STDC_INLINE__"); + + // The value written by __atomic_test_and_set. + // FIXME: This is target-dependent. + Builder.defineMacro("__GCC_ATOMIC_TEST_AND_SET_TRUEVAL", "1"); + + // Used by libstdc++ to implement ATOMIC_<foo>_LOCK_FREE. + unsigned InlineWidthBits = TI.getMaxAtomicInlineWidth(); +#define DEFINE_LOCK_FREE_MACRO(TYPE, Type) \ + Builder.defineMacro("__GCC_ATOMIC_" #TYPE "_LOCK_FREE", \ + getLockFreeValue(TI.get##Type##Width(), \ + TI.get##Type##Align(), \ + InlineWidthBits)); + DEFINE_LOCK_FREE_MACRO(BOOL, Bool); + DEFINE_LOCK_FREE_MACRO(CHAR, Char); + DEFINE_LOCK_FREE_MACRO(CHAR16_T, Char16); + DEFINE_LOCK_FREE_MACRO(CHAR32_T, Char32); + DEFINE_LOCK_FREE_MACRO(WCHAR_T, WChar); + DEFINE_LOCK_FREE_MACRO(SHORT, Short); + DEFINE_LOCK_FREE_MACRO(INT, Int); + DEFINE_LOCK_FREE_MACRO(LONG, Long); + DEFINE_LOCK_FREE_MACRO(LLONG, LongLong); + Builder.defineMacro("__GCC_ATOMIC_POINTER_LOCK_FREE", + getLockFreeValue(TI.getPointerWidth(0), + TI.getPointerAlign(0), + InlineWidthBits)); +#undef DEFINE_LOCK_FREE_MACRO + + if (LangOpts.NoInlineDefine) + Builder.defineMacro("__NO_INLINE__"); + + if (unsigned PICLevel = LangOpts.PICLevel) { + Builder.defineMacro("__PIC__", Twine(PICLevel)); + Builder.defineMacro("__pic__", Twine(PICLevel)); + } + if (unsigned PIELevel = LangOpts.PIELevel) { + Builder.defineMacro("__PIE__", Twine(PIELevel)); + Builder.defineMacro("__pie__", Twine(PIELevel)); + } + + // Macros to control C99 numerics and <float.h> + Builder.defineMacro("__FLT_EVAL_METHOD__", Twine(TI.getFloatEvalMethod())); + Builder.defineMacro("__FLT_RADIX__", "2"); + int Dig = PickFP(&TI.getLongDoubleFormat(), -1/*FIXME*/, 17, 21, 33, 36); + Builder.defineMacro("__DECIMAL_DIG__", Twine(Dig)); + + if (LangOpts.getStackProtector() == LangOptions::SSPOn) + Builder.defineMacro("__SSP__"); + else if (LangOpts.getStackProtector() == 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__"); + + if (LangOpts.FastRelaxedMath) + Builder.defineMacro("__FAST_RELAXED_MATH__"); + + if (LangOpts.ObjCAutoRefCount) { + Builder.defineMacro("__weak", "__attribute__((objc_ownership(weak)))"); + Builder.defineMacro("__strong", "__attribute__((objc_ownership(strong)))"); + Builder.defineMacro("__autoreleasing", + "__attribute__((objc_ownership(autoreleasing)))"); + Builder.defineMacro("__unsafe_unretained", + "__attribute__((objc_ownership(none)))"); + } + + // OpenMP definition + if (LangOpts.OpenMP) { + // OpenMP 2.2: + // In implementations that support a preprocessor, the _OPENMP + // macro name is defined to have the decimal value yyyymm where + // yyyy and mm are the year and the month designations of the + // version of the OpenMP API that the implementation support. + Builder.defineMacro("_OPENMP", "201107"); + } + + // 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(DiagnosticsEngine &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; + } + + // Override the contents of the "from" file with the contents of + // the "to" file. + SourceMgr.overrideFileContents(FromFile, ToFile); + } + + SourceMgr.setOverridenFilesKeepOriginalName( + InitOpts.RemappedFilesKeepOriginalName); +} + +/// 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) { + const LangOptions &LangOpts = PP.getLangOpts(); + 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.getLangOpts().AsmPreprocessor) + Builder.append("# 1 \"<built-in>\" 3"); + + // Install things like __POWERPC__, __GNUC__, etc into the macro table. + if (InitOpts.UsePredefines) { + InitializePredefinedMacros(PP.getTargetInfo(), LangOpts, FEOpts, Builder); + + // Install definitions to make Objective-C++ ARC work well with various + // C++ Standard Library implementations. + if (LangOpts.ObjC1 && LangOpts.CPlusPlus && LangOpts.ObjCAutoRefCount) { + switch (InitOpts.ObjCXXARCStandardLibrary) { + case ARCXX_nolib: + case ARCXX_libcxx: + break; + + case ARCXX_libstdcxx: + AddObjCXXARCLibstdcxxDefines(LangOpts, Builder); + break; + } + } + } + + // Even with predefines off, some macros are still predefined. + // These should all be defined in the preprocessor according to the + // current language configuration. + InitializeStandardPredefinedMacros(PP.getTargetInfo(), PP.getLangOpts(), + 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.getLangOpts().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], + PP.getFileManager()); + + // Process -include-pch/-include-pth directives. + if (!InitOpts.ImplicitPCHInclude.empty()) + AddImplicitIncludePCH(Builder, PP, InitOpts.ImplicitPCHInclude); + if (!InitOpts.ImplicitPTHInclude.empty()) + AddImplicitIncludePTH(Builder, PP, InitOpts.ImplicitPTHInclude); + + // Process -include directives. + for (unsigned i = 0, e = InitOpts.Includes.size(); i != e; ++i) { + const std::string &Path = InitOpts.Includes[i]; + AddImplicitInclude(Builder, Path, PP.getFileManager()); + } + + // Exit the command line and go back to <built-in> (2 is LC_LEAVE). + if (!PP.getLangOpts().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.getLangOpts(), + 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..f86a574 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/LangStandards.cpp @@ -0,0 +1,43 @@ +//===--- 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) { + 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" + } + llvm_unreachable("Invalid language kind!"); +} + +const LangStandard *LangStandard::getLangStandardForName(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/LayoutOverrideSource.cpp b/contrib/llvm/tools/clang/lib/Frontend/LayoutOverrideSource.cpp new file mode 100644 index 0000000..924a640 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/LayoutOverrideSource.cpp @@ -0,0 +1,208 @@ +//===--- LayoutOverrideSource.cpp --Override Record Layouts ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "clang/Frontend/LayoutOverrideSource.h" +#include "clang/AST/Decl.h" +#include "clang/Basic/CharInfo.h" +#include "llvm/Support/raw_ostream.h" +#include <fstream> +#include <string> + +using namespace clang; + +/// \brief Parse a simple identifier. +static std::string parseName(StringRef S) { + if (S.empty() || !isIdentifierHead(S[0])) + return ""; + + unsigned Offset = 1; + while (Offset < S.size() && isIdentifierBody(S[Offset])) + ++Offset; + + return S.substr(0, Offset).str(); +} + +LayoutOverrideSource::LayoutOverrideSource(StringRef Filename) { + std::ifstream Input(Filename.str().c_str()); + if (!Input.is_open()) + return; + + // Parse the output of -fdump-record-layouts. + std::string CurrentType; + Layout CurrentLayout; + bool ExpectingType = false; + + while (Input.good()) { + std::string Line; + getline(Input, Line); + + StringRef LineStr(Line); + + // Determine whether the following line will start a + if (LineStr.find("*** Dumping AST Record Layout") != StringRef::npos) { + // Flush the last type/layout, if there is one. + if (!CurrentType.empty()) + Layouts[CurrentType] = CurrentLayout; + CurrentLayout = Layout(); + + ExpectingType = true; + continue; + } + + // If we're expecting a type, grab it. + if (ExpectingType) { + ExpectingType = false; + + StringRef::size_type Pos; + if ((Pos = LineStr.find("struct ")) != StringRef::npos) + LineStr = LineStr.substr(Pos + strlen("struct ")); + else if ((Pos = LineStr.find("class ")) != StringRef::npos) + LineStr = LineStr.substr(Pos + strlen("class ")); + else if ((Pos = LineStr.find("union ")) != StringRef::npos) + LineStr = LineStr.substr(Pos + strlen("union ")); + else + continue; + + // Find the name of the type. + CurrentType = parseName(LineStr); + CurrentLayout = Layout(); + continue; + } + + // Check for the size of the type. + StringRef::size_type Pos = LineStr.find(" Size:"); + if (Pos != StringRef::npos) { + // Skip past the " Size:" prefix. + LineStr = LineStr.substr(Pos + strlen(" Size:")); + + unsigned long long Size = 0; + (void)LineStr.getAsInteger(10, Size); + CurrentLayout.Size = Size; + continue; + } + + // Check for the alignment of the type. + Pos = LineStr.find("Alignment:"); + if (Pos != StringRef::npos) { + // Skip past the "Alignment:" prefix. + LineStr = LineStr.substr(Pos + strlen("Alignment:")); + + unsigned long long Alignment = 0; + (void)LineStr.getAsInteger(10, Alignment); + CurrentLayout.Align = Alignment; + continue; + } + + // Check for the size/alignment of the type. + Pos = LineStr.find("sizeof="); + if (Pos != StringRef::npos) { + /* Skip past the sizeof= prefix. */ + LineStr = LineStr.substr(Pos + strlen("sizeof=")); + + // Parse size. + unsigned long long Size = 0; + (void)LineStr.getAsInteger(10, Size); + CurrentLayout.Size = Size; + + Pos = LineStr.find("align="); + if (Pos != StringRef::npos) { + /* Skip past the align= prefix. */ + LineStr = LineStr.substr(Pos + strlen("align=")); + + // Parse alignment. + unsigned long long Alignment = 0; + (void)LineStr.getAsInteger(10, Alignment); + CurrentLayout.Align = Alignment; + } + + continue; + } + + // Check for the field offsets of the type. + Pos = LineStr.find("FieldOffsets: ["); + if (Pos == StringRef::npos) + continue; + + LineStr = LineStr.substr(Pos + strlen("FieldOffsets: [")); + while (!LineStr.empty() && isDigit(LineStr[0])) { + // Parse this offset. + unsigned Idx = 1; + while (Idx < LineStr.size() && isDigit(LineStr[Idx])) + ++Idx; + + unsigned long long Offset = 0; + (void)LineStr.substr(0, Idx).getAsInteger(10, Offset); + + CurrentLayout.FieldOffsets.push_back(Offset); + + // Skip over this offset, the following comma, and any spaces. + LineStr = LineStr.substr(Idx + 1); + while (!LineStr.empty() && isWhitespace(LineStr[0])) + LineStr = LineStr.substr(1); + } + } + + // Flush the last type/layout, if there is one. + if (!CurrentType.empty()) + Layouts[CurrentType] = CurrentLayout; +} + +bool +LayoutOverrideSource::layoutRecordType(const RecordDecl *Record, + uint64_t &Size, uint64_t &Alignment, + llvm::DenseMap<const FieldDecl *, uint64_t> &FieldOffsets, + llvm::DenseMap<const CXXRecordDecl *, CharUnits> &BaseOffsets, + llvm::DenseMap<const CXXRecordDecl *, CharUnits> &VirtualBaseOffsets) +{ + // We can't override unnamed declarations. + if (!Record->getIdentifier()) + return false; + + // Check whether we have a layout for this record. + llvm::StringMap<Layout>::iterator Known = Layouts.find(Record->getName()); + if (Known == Layouts.end()) + return false; + + // Provide field layouts. + unsigned NumFields = 0; + for (RecordDecl::field_iterator F = Record->field_begin(), + FEnd = Record->field_end(); + F != FEnd; ++F, ++NumFields) { + if (NumFields >= Known->second.FieldOffsets.size()) + continue; + + FieldOffsets[*F] = Known->second.FieldOffsets[NumFields]; + } + + // Wrong number of fields. + if (NumFields != Known->second.FieldOffsets.size()) + return false; + + Size = Known->second.Size; + Alignment = Known->second.Align; + return true; +} + +void LayoutOverrideSource::dump() { + raw_ostream &OS = llvm::errs(); + for (llvm::StringMap<Layout>::iterator L = Layouts.begin(), + LEnd = Layouts.end(); + L != LEnd; ++L) { + OS << "Type: blah " << L->first() << '\n'; + OS << " Size:" << L->second.Size << '\n'; + OS << " Alignment:" << L->second.Align << '\n'; + OS << " FieldOffsets: ["; + for (unsigned I = 0, N = L->second.FieldOffsets.size(); I != N; ++I) { + if (I) + OS << ", "; + OS << L->second.FieldOffsets[I]; + } + OS << "]\n"; + } +} + diff --git a/contrib/llvm/tools/clang/lib/Frontend/LogDiagnosticPrinter.cpp b/contrib/llvm/tools/clang/lib/Frontend/LogDiagnosticPrinter.cpp new file mode 100644 index 0000000..2189b86 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/LogDiagnosticPrinter.cpp @@ -0,0 +1,173 @@ +//===--- LogDiagnosticPrinter.cpp - Log Diagnostic Printer ----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/LogDiagnosticPrinter.h" +#include "clang/Basic/DiagnosticOptions.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +using namespace clang; + +LogDiagnosticPrinter::LogDiagnosticPrinter(raw_ostream &os, + DiagnosticOptions *diags, + bool _OwnsOutputStream) + : OS(os), LangOpts(0), DiagOpts(diags), + OwnsOutputStream(_OwnsOutputStream) { +} + +LogDiagnosticPrinter::~LogDiagnosticPrinter() { + if (OwnsOutputStream) + delete &OS; +} + +static StringRef getLevelName(DiagnosticsEngine::Level Level) { + switch (Level) { + case DiagnosticsEngine::Ignored: return "ignored"; + case DiagnosticsEngine::Note: return "note"; + case DiagnosticsEngine::Warning: return "warning"; + case DiagnosticsEngine::Error: return "error"; + case DiagnosticsEngine::Fatal: return "fatal error"; + } + llvm_unreachable("Invalid DiagnosticsEngine level!"); +} + +// Escape XML characters inside the raw string. +static void emitString(llvm::raw_svector_ostream &OS, const StringRef Raw) { + for (StringRef::iterator I = Raw.begin(), E = Raw.end(); I != E; ++I) { + char c = *I; + switch (c) { + default: OS << c; break; + case '&': OS << "&"; break; + case '<': OS << "<"; break; + case '>': OS << ">"; break; + case '\'': OS << "'"; break; + case '\"': OS << """; break; + } + } +} + +void LogDiagnosticPrinter::EndSourceFile() { + // We emit all the diagnostics in EndSourceFile. However, we don't emit any + // entry if no diagnostics were present. + // + // Note that DiagnosticConsumer has no "end-of-compilation" callback, so we + // will miss any diagnostics which are emitted after and outside the + // translation unit processing. + if (Entries.empty()) + return; + + // Write to a temporary string to ensure atomic write of diagnostic object. + SmallString<512> Msg; + llvm::raw_svector_ostream OS(Msg); + + OS << "<dict>\n"; + if (!MainFilename.empty()) { + OS << " <key>main-file</key>\n" + << " <string>"; + emitString(OS, MainFilename); + OS << "</string>\n"; + } + if (!DwarfDebugFlags.empty()) { + OS << " <key>dwarf-debug-flags</key>\n" + << " <string>"; + emitString(OS, DwarfDebugFlags); + OS << "</string>\n"; + } + OS << " <key>diagnostics</key>\n"; + OS << " <array>\n"; + for (unsigned i = 0, e = Entries.size(); i != e; ++i) { + DiagEntry &DE = Entries[i]; + + OS << " <dict>\n"; + OS << " <key>level</key>\n" + << " <string>"; + emitString(OS, getLevelName(DE.DiagnosticLevel)); + OS << "</string>\n"; + if (!DE.Filename.empty()) { + OS << " <key>filename</key>\n" + << " <string>"; + emitString(OS, DE.Filename); + OS << "</string>\n"; + } + if (DE.Line != 0) { + OS << " <key>line</key>\n" + << " <integer>" << DE.Line << "</integer>\n"; + } + if (DE.Column != 0) { + OS << " <key>column</key>\n" + << " <integer>" << DE.Column << "</integer>\n"; + } + if (!DE.Message.empty()) { + OS << " <key>message</key>\n" + << " <string>"; + emitString(OS, DE.Message); + OS << "</string>\n"; + } + OS << " </dict>\n"; + } + OS << " </array>\n"; + OS << "</dict>\n"; + + this->OS << OS.str(); +} + +void LogDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level, + const Diagnostic &Info) { + // Default implementation (Warnings/errors count). + DiagnosticConsumer::HandleDiagnostic(Level, Info); + + // Initialize the main file name, if we haven't already fetched it. + if (MainFilename.empty() && Info.hasSourceManager()) { + const SourceManager &SM = Info.getSourceManager(); + FileID FID = SM.getMainFileID(); + if (!FID.isInvalid()) { + const FileEntry *FE = SM.getFileEntryForID(FID); + if (FE && FE->getName()) + MainFilename = FE->getName(); + } + } + + // Create the diag entry. + DiagEntry DE; + DE.DiagnosticID = Info.getID(); + DE.DiagnosticLevel = Level; + + // Format the message. + SmallString<100> MessageStr; + Info.FormatDiagnostic(MessageStr); + DE.Message = MessageStr.str(); + + // Set the location information. + DE.Filename = ""; + DE.Line = DE.Column = 0; + if (Info.getLocation().isValid() && Info.hasSourceManager()) { + const SourceManager &SM = Info.getSourceManager(); + PresumedLoc PLoc = SM.getPresumedLoc(Info.getLocation()); + + if (PLoc.isInvalid()) { + // At least print the file name if available: + FileID FID = SM.getFileID(Info.getLocation()); + if (!FID.isInvalid()) { + const FileEntry *FE = SM.getFileEntryForID(FID); + if (FE && FE->getName()) + DE.Filename = FE->getName(); + } + } else { + DE.Filename = PLoc.getFilename(); + DE.Line = PLoc.getLine(); + DE.Column = PLoc.getColumn(); + } + } + + // Record the diagnostic entry. + Entries.push_back(DE); +} + diff --git a/contrib/llvm/tools/clang/lib/Frontend/MultiplexConsumer.cpp b/contrib/llvm/tools/clang/lib/Frontend/MultiplexConsumer.cpp new file mode 100644 index 0000000..9cf68a5 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/MultiplexConsumer.cpp @@ -0,0 +1,295 @@ +//===- MultiplexConsumer.cpp - AST Consumer for PCH Generation --*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the MultiplexConsumer class. It also declares and defines +// MultiplexASTDeserializationListener and MultiplexASTMutationListener, which +// are implementation details of MultiplexConsumer. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/MultiplexConsumer.h" +#include "clang/AST/ASTMutationListener.h" +#include "clang/AST/DeclGroup.h" +#include "clang/Serialization/ASTDeserializationListener.h" + +using namespace clang; + +namespace clang { + +// This ASTDeserializationListener forwards its notifications to a set of +// child listeners. +class MultiplexASTDeserializationListener + : public ASTDeserializationListener { +public: + // Does NOT take ownership of the elements in L. + MultiplexASTDeserializationListener( + const std::vector<ASTDeserializationListener*>& L); + virtual void ReaderInitialized(ASTReader *Reader); + virtual void IdentifierRead(serialization::IdentID ID, + IdentifierInfo *II); + virtual void TypeRead(serialization::TypeIdx Idx, QualType T); + virtual void DeclRead(serialization::DeclID ID, const Decl *D); + virtual void SelectorRead(serialization::SelectorID iD, Selector Sel); + virtual void MacroDefinitionRead(serialization::PreprocessedEntityID, + MacroDefinition *MD); +private: + std::vector<ASTDeserializationListener*> Listeners; +}; + +MultiplexASTDeserializationListener::MultiplexASTDeserializationListener( + const std::vector<ASTDeserializationListener*>& L) + : Listeners(L) { +} + +void MultiplexASTDeserializationListener::ReaderInitialized( + ASTReader *Reader) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->ReaderInitialized(Reader); +} + +void MultiplexASTDeserializationListener::IdentifierRead( + serialization::IdentID ID, IdentifierInfo *II) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->IdentifierRead(ID, II); +} + +void MultiplexASTDeserializationListener::TypeRead( + serialization::TypeIdx Idx, QualType T) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->TypeRead(Idx, T); +} + +void MultiplexASTDeserializationListener::DeclRead( + serialization::DeclID ID, const Decl *D) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->DeclRead(ID, D); +} + +void MultiplexASTDeserializationListener::SelectorRead( + serialization::SelectorID ID, Selector Sel) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->SelectorRead(ID, Sel); +} + +void MultiplexASTDeserializationListener::MacroDefinitionRead( + serialization::PreprocessedEntityID ID, MacroDefinition *MD) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->MacroDefinitionRead(ID, MD); +} + +// This ASTMutationListener forwards its notifications to a set of +// child listeners. +class MultiplexASTMutationListener : public ASTMutationListener { +public: + // Does NOT take ownership of the elements in L. + MultiplexASTMutationListener(ArrayRef<ASTMutationListener*> L); + virtual void CompletedTagDefinition(const TagDecl *D); + virtual void AddedVisibleDecl(const DeclContext *DC, const Decl *D); + virtual void AddedCXXImplicitMember(const CXXRecordDecl *RD, const Decl *D); + virtual void AddedCXXTemplateSpecialization(const ClassTemplateDecl *TD, + const ClassTemplateSpecializationDecl *D); + virtual void + AddedCXXTemplateSpecialization(const VarTemplateDecl *TD, + const VarTemplateSpecializationDecl *D); + virtual void AddedCXXTemplateSpecialization(const FunctionTemplateDecl *TD, + const FunctionDecl *D); + virtual void DeducedReturnType(const FunctionDecl *FD, QualType ReturnType); + virtual void CompletedImplicitDefinition(const FunctionDecl *D); + virtual void StaticDataMemberInstantiated(const VarDecl *D); + virtual void AddedObjCCategoryToInterface(const ObjCCategoryDecl *CatD, + const ObjCInterfaceDecl *IFD); + virtual void AddedObjCPropertyInClassExtension(const ObjCPropertyDecl *Prop, + const ObjCPropertyDecl *OrigProp, + const ObjCCategoryDecl *ClassExt); + void DeclarationMarkedUsed(const Decl *D) LLVM_OVERRIDE; + +private: + std::vector<ASTMutationListener*> Listeners; +}; + +MultiplexASTMutationListener::MultiplexASTMutationListener( + ArrayRef<ASTMutationListener*> L) + : Listeners(L.begin(), L.end()) { +} + +void MultiplexASTMutationListener::CompletedTagDefinition(const TagDecl *D) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->CompletedTagDefinition(D); +} + +void MultiplexASTMutationListener::AddedVisibleDecl( + const DeclContext *DC, const Decl *D) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->AddedVisibleDecl(DC, D); +} + +void MultiplexASTMutationListener::AddedCXXImplicitMember( + const CXXRecordDecl *RD, const Decl *D) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->AddedCXXImplicitMember(RD, D); +} +void MultiplexASTMutationListener::AddedCXXTemplateSpecialization( + const ClassTemplateDecl *TD, const ClassTemplateSpecializationDecl *D) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->AddedCXXTemplateSpecialization(TD, D); +} +void MultiplexASTMutationListener::AddedCXXTemplateSpecialization( + const VarTemplateDecl *TD, const VarTemplateSpecializationDecl *D) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->AddedCXXTemplateSpecialization(TD, D); +} +void MultiplexASTMutationListener::AddedCXXTemplateSpecialization( + const FunctionTemplateDecl *TD, const FunctionDecl *D) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->AddedCXXTemplateSpecialization(TD, D); +} +void MultiplexASTMutationListener::DeducedReturnType(const FunctionDecl *FD, + QualType ReturnType) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->DeducedReturnType(FD, ReturnType); +} +void MultiplexASTMutationListener::CompletedImplicitDefinition( + const FunctionDecl *D) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->CompletedImplicitDefinition(D); +} +void MultiplexASTMutationListener::StaticDataMemberInstantiated( + const VarDecl *D) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->StaticDataMemberInstantiated(D); +} +void MultiplexASTMutationListener::AddedObjCCategoryToInterface( + const ObjCCategoryDecl *CatD, + const ObjCInterfaceDecl *IFD) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->AddedObjCCategoryToInterface(CatD, IFD); +} +void MultiplexASTMutationListener::AddedObjCPropertyInClassExtension( + const ObjCPropertyDecl *Prop, + const ObjCPropertyDecl *OrigProp, + const ObjCCategoryDecl *ClassExt) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->AddedObjCPropertyInClassExtension(Prop, OrigProp, ClassExt); +} +void MultiplexASTMutationListener::DeclarationMarkedUsed(const Decl *D) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->DeclarationMarkedUsed(D); +} + +} // end namespace clang + + +MultiplexConsumer::MultiplexConsumer(ArrayRef<ASTConsumer*> C) + : Consumers(C.begin(), C.end()), + MutationListener(0), DeserializationListener(0) { + // Collect the mutation listeners and deserialization listeners of all + // children, and create a multiplex listener each if so. + std::vector<ASTMutationListener*> mutationListeners; + std::vector<ASTDeserializationListener*> serializationListeners; + for (size_t i = 0, e = Consumers.size(); i != e; ++i) { + ASTMutationListener* mutationListener = + Consumers[i]->GetASTMutationListener(); + if (mutationListener) + mutationListeners.push_back(mutationListener); + ASTDeserializationListener* serializationListener = + Consumers[i]->GetASTDeserializationListener(); + if (serializationListener) + serializationListeners.push_back(serializationListener); + } + if (mutationListeners.size()) { + MutationListener.reset(new MultiplexASTMutationListener(mutationListeners)); + } + if (serializationListeners.size()) { + DeserializationListener.reset( + new MultiplexASTDeserializationListener(serializationListeners)); + } +} + +MultiplexConsumer::~MultiplexConsumer() { + for (size_t i = 0, e = Consumers.size(); i != e; ++i) + delete Consumers[i]; +} + +void MultiplexConsumer::Initialize(ASTContext &Context) { + for (size_t i = 0, e = Consumers.size(); i != e; ++i) + Consumers[i]->Initialize(Context); +} + +bool MultiplexConsumer::HandleTopLevelDecl(DeclGroupRef D) { + bool Continue = true; + for (size_t i = 0, e = Consumers.size(); i != e; ++i) + Continue = Continue && Consumers[i]->HandleTopLevelDecl(D); + return Continue; +} + +void MultiplexConsumer::HandleCXXStaticMemberVarInstantiation(VarDecl *VD) { + for (size_t i = 0, e = Consumers.size(); i != e; ++i) + Consumers[i]->HandleCXXStaticMemberVarInstantiation(VD); +} + +void MultiplexConsumer::HandleInterestingDecl(DeclGroupRef D) { + for (size_t i = 0, e = Consumers.size(); i != e; ++i) + Consumers[i]->HandleInterestingDecl(D); +} + +void MultiplexConsumer::HandleTranslationUnit(ASTContext &Ctx) { + for (size_t i = 0, e = Consumers.size(); i != e; ++i) + Consumers[i]->HandleTranslationUnit(Ctx); +} + +void MultiplexConsumer::HandleTagDeclDefinition(TagDecl *D) { + for (size_t i = 0, e = Consumers.size(); i != e; ++i) + Consumers[i]->HandleTagDeclDefinition(D); +} + +void MultiplexConsumer::HandleCXXImplicitFunctionInstantiation(FunctionDecl *D){ + for (size_t i = 0, e = Consumers.size(); i != e; ++i) + Consumers[i]->HandleCXXImplicitFunctionInstantiation(D); +} + +void MultiplexConsumer::HandleTopLevelDeclInObjCContainer(DeclGroupRef D) { + for (size_t i = 0, e = Consumers.size(); i != e; ++i) + Consumers[i]->HandleTopLevelDeclInObjCContainer(D); +} + +void MultiplexConsumer::CompleteTentativeDefinition(VarDecl *D) { + for (size_t i = 0, e = Consumers.size(); i != e; ++i) + Consumers[i]->CompleteTentativeDefinition(D); +} + +void MultiplexConsumer::HandleVTable( + CXXRecordDecl *RD, bool DefinitionRequired) { + for (size_t i = 0, e = Consumers.size(); i != e; ++i) + Consumers[i]->HandleVTable(RD, DefinitionRequired); +} + +ASTMutationListener *MultiplexConsumer::GetASTMutationListener() { + return MutationListener.get(); +} + +ASTDeserializationListener *MultiplexConsumer::GetASTDeserializationListener() { + return DeserializationListener.get(); +} + +void MultiplexConsumer::PrintStats() { + for (size_t i = 0, e = Consumers.size(); i != e; ++i) + Consumers[i]->PrintStats(); +} + +void MultiplexConsumer::InitializeSema(Sema &S) { + for (size_t i = 0, e = Consumers.size(); i != e; ++i) + if (SemaConsumer *SC = dyn_cast<SemaConsumer>(Consumers[i])) + SC->InitializeSema(S); +} + +void MultiplexConsumer::ForgetSema() { + for (size_t i = 0, e = Consumers.size(); i != e; ++i) + if (SemaConsumer *SC = dyn_cast<SemaConsumer>(Consumers[i])) + SC->ForgetSema(); +} 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..55a66d8 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/PrintPreprocessedOutput.cpp @@ -0,0 +1,782 @@ +//===--- 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/CharInfo.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/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/ErrorHandling.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, 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 << ' '; + + 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: + raw_ostream &OS; +private: + unsigned CurLine; + + bool EmittedTokensOnThisLine; + bool EmittedDirectiveOnThisLine; + SrcMgr::CharacteristicKind FileType; + SmallString<512> CurFilename; + bool Initialized; + bool DisableLineMarkers; + bool DumpDefines; + bool UseLineDirective; + bool IsFirstFileEntered; +public: + PrintPPOutputPPCallbacks(Preprocessor &pp, raw_ostream &os, + bool lineMarkers, bool defines) + : PP(pp), SM(PP.getSourceManager()), + ConcatInfo(PP), OS(os), DisableLineMarkers(lineMarkers), + DumpDefines(defines) { + CurLine = 0; + CurFilename += "<uninit>"; + EmittedTokensOnThisLine = false; + EmittedDirectiveOnThisLine = false; + FileType = SrcMgr::C_User; + Initialized = false; + IsFirstFileEntered = false; + + // If we're in microsoft mode, use normal #line instead of line markers. + UseLineDirective = PP.getLangOpts().MicrosoftExt; + } + + void setEmittedTokensOnThisLine() { EmittedTokensOnThisLine = true; } + bool hasEmittedTokensOnThisLine() const { return EmittedTokensOnThisLine; } + + void setEmittedDirectiveOnThisLine() { EmittedDirectiveOnThisLine = true; } + bool hasEmittedDirectiveOnThisLine() const { + return EmittedDirectiveOnThisLine; + } + + bool startNewLineIfNeeded(bool ShouldUpdateCurrentLine = true); + + virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, + FileID PrevFID); + virtual void InclusionDirective(SourceLocation HashLoc, + const Token &IncludeTok, + StringRef FileName, + bool IsAngled, + CharSourceRange FilenameRange, + const FileEntry *File, + StringRef SearchPath, + StringRef RelativePath, + const Module *Imported); + virtual void Ident(SourceLocation Loc, const std::string &str); + virtual void PragmaCaptured(SourceLocation Loc, StringRef Str); + virtual void PragmaComment(SourceLocation Loc, const IdentifierInfo *Kind, + const std::string &Str); + virtual void PragmaDetectMismatch(SourceLocation Loc, + const std::string &Name, + const std::string &Value); + virtual void PragmaMessage(SourceLocation Loc, StringRef Namespace, + PragmaMessageKind Kind, StringRef Str); + virtual void PragmaDebug(SourceLocation Loc, StringRef DebugType); + virtual void PragmaDiagnosticPush(SourceLocation Loc, + StringRef Namespace); + virtual void PragmaDiagnosticPop(SourceLocation Loc, + StringRef Namespace); + virtual void PragmaDiagnostic(SourceLocation Loc, StringRef Namespace, + diag::Mapping Map, StringRef Str); + virtual void PragmaWarning(SourceLocation Loc, StringRef WarningSpec, + ArrayRef<int> Ids); + virtual void PragmaWarningPush(SourceLocation Loc, int Level); + virtual void PragmaWarningPop(SourceLocation Loc); + + bool HandleFirstTokOnLine(Token &Tok); + + /// Move to the line of the provided source location. This will + /// return true if the output stream required adjustment or if + /// the requested location is on the first line. + bool MoveToLine(SourceLocation Loc) { + PresumedLoc PLoc = SM.getPresumedLoc(Loc); + if (PLoc.isInvalid()) + return false; + return MoveToLine(PLoc.getLine()) || (PLoc.getLine() == 1); + } + 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); + bool LineMarkersAreDisabled() const { return DisableLineMarkers; } + void HandleNewlinesInToken(const char *TokStr, unsigned Len); + + /// MacroDefined - This hook is called whenever a macro definition is seen. + void MacroDefined(const Token &MacroNameTok, const MacroDirective *MD); + + /// MacroUndefined - This hook is called whenever a macro #undef is seen. + void MacroUndefined(const Token &MacroNameTok, const MacroDirective *MD); +}; +} // end anonymous namespace + +void PrintPPOutputPPCallbacks::WriteLineInfo(unsigned LineNo, + const char *Extra, + unsigned ExtraLen) { + startNewLineIfNeeded(/*ShouldUpdateCurrentLine=*/false); + + // Emit #line directives or GNU line markers depending on what mode we're in. + if (UseLineDirective) { + OS << "#line" << ' ' << LineNo << ' ' << '"'; + OS.write_escaped(CurFilename); + OS << '"'; + } else { + OS << '#' << ' ' << LineNo << ' ' << '"'; + OS.write_escaped(CurFilename); + 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 expansion 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. + startNewLineIfNeeded(/*ShouldUpdateCurrentLine=*/false); + } + + CurLine = LineNo; + return true; +} + +bool +PrintPPOutputPPCallbacks::startNewLineIfNeeded(bool ShouldUpdateCurrentLine) { + if (EmittedTokensOnThisLine || EmittedDirectiveOnThisLine) { + OS << '\n'; + EmittedTokensOnThisLine = false; + EmittedDirectiveOnThisLine = false; + if (ShouldUpdateCurrentLine) + ++CurLine; + return true; + } + + return false; +} + +/// 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, + FileID PrevFID) { + // 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); + if (UserLoc.isInvalid()) + return; + + unsigned NewLine = UserLoc.getLine(); + + if (Reason == PPCallbacks::EnterFile) { + SourceLocation IncludeLoc = UserLoc.getIncludeLoc(); + if (IncludeLoc.isValid()) + MoveToLine(IncludeLoc); + } else if (Reason == PPCallbacks::SystemHeaderPragma) { + // GCC emits the # directive for this directive on the line AFTER the + // directive and emits a bunch of spaces that aren't needed. This is because + // otherwise we will emit a line marker for THIS line, which requires an + // extra blank line after the directive to avoid making all following lines + // off by one. We can do better by simply incrementing NewLine here. + NewLine += 1; + } + + CurLine = NewLine; + + CurFilename.clear(); + CurFilename += UserLoc.getFilename(); + FileType = NewFileType; + + if (DisableLineMarkers) { + startNewLineIfNeeded(/*ShouldUpdateCurrentLine=*/false); + return; + } + + if (!Initialized) { + WriteLineInfo(CurLine); + Initialized = true; + } + + // Do not emit an enter marker for the main file (which we expect is the first + // entered file). This matches gcc, and improves compatibility with some tools + // which track the # line markers as a way to determine when the preprocessed + // output is in the context of the main file. + if (Reason == PPCallbacks::EnterFile && !IsFirstFileEntered) { + IsFirstFileEntered = true; + return; + } + + 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; + } +} + +void PrintPPOutputPPCallbacks::InclusionDirective(SourceLocation HashLoc, + const Token &IncludeTok, + StringRef FileName, + bool IsAngled, + CharSourceRange FilenameRange, + const FileEntry *File, + StringRef SearchPath, + StringRef RelativePath, + const Module *Imported) { + // When preprocessing, turn implicit imports into @imports. + // FIXME: This is a stop-gap until a more comprehensive "preprocessing with + // modules" solution is introduced. + if (Imported) { + startNewLineIfNeeded(); + MoveToLine(HashLoc); + OS << "@import " << Imported->getFullModuleName() << ";" + << " /* clang -E: implicit import for \"" << File->getName() << "\" */"; + EmittedTokensOnThisLine = true; + } +} + +/// 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; +} + +void PrintPPOutputPPCallbacks::PragmaCaptured(SourceLocation Loc, + StringRef Str) { + startNewLineIfNeeded(); + MoveToLine(Loc); + OS << "#pragma captured"; + + setEmittedDirectiveOnThisLine(); +} + +/// MacroDefined - This hook is called whenever a macro definition is seen. +void PrintPPOutputPPCallbacks::MacroDefined(const Token &MacroNameTok, + const MacroDirective *MD) { + const MacroInfo *MI = MD->getMacroInfo(); + // Only print out macro definitions in -dD mode. + if (!DumpDefines || + // Ignore __FILE__ etc. + MI->isBuiltinMacro()) return; + + MoveToLine(MI->getDefinitionLoc()); + PrintMacroDefinition(*MacroNameTok.getIdentifierInfo(), *MI, PP, OS); + setEmittedDirectiveOnThisLine(); +} + +void PrintPPOutputPPCallbacks::MacroUndefined(const Token &MacroNameTok, + const MacroDirective *MD) { + // Only print out macro definitions in -dD mode. + if (!DumpDefines) return; + + MoveToLine(MacroNameTok.getLocation()); + OS << "#undef " << MacroNameTok.getIdentifierInfo()->getName(); + setEmittedDirectiveOnThisLine(); +} + +static void outputPrintable(llvm::raw_ostream& OS, + const std::string &Str) { + for (unsigned i = 0, e = Str.size(); i != e; ++i) { + unsigned char Char = Str[i]; + if (isPrintable(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)); + } +} + +void PrintPPOutputPPCallbacks::PragmaComment(SourceLocation Loc, + const IdentifierInfo *Kind, + const std::string &Str) { + startNewLineIfNeeded(); + MoveToLine(Loc); + OS << "#pragma comment(" << Kind->getName(); + + if (!Str.empty()) { + OS << ", \""; + outputPrintable(OS, Str); + OS << '"'; + } + + OS << ')'; + setEmittedDirectiveOnThisLine(); +} + +void PrintPPOutputPPCallbacks::PragmaDetectMismatch(SourceLocation Loc, + const std::string &Name, + const std::string &Value) { + startNewLineIfNeeded(); + MoveToLine(Loc); + OS << "#pragma detect_mismatch(\"" << Name << '"'; + outputPrintable(OS, Name); + OS << "\", \""; + outputPrintable(OS, Value); + OS << "\")"; + setEmittedDirectiveOnThisLine(); +} + +void PrintPPOutputPPCallbacks::PragmaMessage(SourceLocation Loc, + StringRef Namespace, + PragmaMessageKind Kind, + StringRef Str) { + startNewLineIfNeeded(); + MoveToLine(Loc); + OS << "#pragma "; + if (!Namespace.empty()) + OS << Namespace << ' '; + switch (Kind) { + case PMK_Message: + OS << "message(\""; + break; + case PMK_Warning: + OS << "warning \""; + break; + case PMK_Error: + OS << "error \""; + break; + } + + outputPrintable(OS, Str); + OS << '"'; + if (Kind == PMK_Message) + OS << ')'; + setEmittedDirectiveOnThisLine(); +} + +void PrintPPOutputPPCallbacks::PragmaDebug(SourceLocation Loc, + StringRef DebugType) { + startNewLineIfNeeded(); + MoveToLine(Loc); + + OS << "#pragma clang __debug "; + OS << DebugType; + + setEmittedDirectiveOnThisLine(); +} + +void PrintPPOutputPPCallbacks:: +PragmaDiagnosticPush(SourceLocation Loc, StringRef Namespace) { + startNewLineIfNeeded(); + MoveToLine(Loc); + OS << "#pragma " << Namespace << " diagnostic push"; + setEmittedDirectiveOnThisLine(); +} + +void PrintPPOutputPPCallbacks:: +PragmaDiagnosticPop(SourceLocation Loc, StringRef Namespace) { + startNewLineIfNeeded(); + MoveToLine(Loc); + OS << "#pragma " << Namespace << " diagnostic pop"; + setEmittedDirectiveOnThisLine(); +} + +void PrintPPOutputPPCallbacks:: +PragmaDiagnostic(SourceLocation Loc, StringRef Namespace, + diag::Mapping Map, StringRef Str) { + startNewLineIfNeeded(); + MoveToLine(Loc); + OS << "#pragma " << Namespace << " diagnostic "; + switch (Map) { + case diag::MAP_WARNING: + OS << "warning"; + break; + case diag::MAP_ERROR: + OS << "error"; + break; + case diag::MAP_IGNORE: + OS << "ignored"; + break; + case diag::MAP_FATAL: + OS << "fatal"; + break; + } + OS << " \"" << Str << '"'; + setEmittedDirectiveOnThisLine(); +} + +void PrintPPOutputPPCallbacks::PragmaWarning(SourceLocation Loc, + StringRef WarningSpec, + ArrayRef<int> Ids) { + startNewLineIfNeeded(); + MoveToLine(Loc); + OS << "#pragma warning(" << WarningSpec << ':'; + for (ArrayRef<int>::iterator I = Ids.begin(), E = Ids.end(); I != E; ++I) + OS << ' ' << *I; + OS << ')'; + setEmittedDirectiveOnThisLine(); +} + +void PrintPPOutputPPCallbacks::PragmaWarningPush(SourceLocation Loc, + int Level) { + startNewLineIfNeeded(); + MoveToLine(Loc); + OS << "#pragma warning(push"; + if (Level >= 0) + OS << ", " << Level; + OS << ')'; + setEmittedDirectiveOnThisLine(); +} + +void PrintPPOutputPPCallbacks::PragmaWarningPop(SourceLocation Loc) { + startNewLineIfNeeded(); + MoveToLine(Loc); + OS << "#pragma warning(pop)"; + setEmittedDirectiveOnThisLine(); +} + +/// 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 expansion 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.getExpansionColumnNumber(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, PragmaIntroducerKind Introducer, + Token &PragmaTok) { + // Figure out what line we went to and insert the appropriate number of + // newline characters. + Callbacks->startNewLineIfNeeded(); + Callbacks->MoveToLine(PragmaTok.getLocation()); + Callbacks->OS.write(Prefix, strlen(Prefix)); + // Read and print all of the pragma tokens. + while (PragmaTok.isNot(tok::eod)) { + if (PragmaTok.hasLeadingSpace()) + Callbacks->OS << ' '; + std::string TokSpell = PP.getSpelling(PragmaTok); + Callbacks->OS.write(&TokSpell[0], TokSpell.size()); + PP.LexUnexpandedToken(PragmaTok); + } + Callbacks->setEmittedDirectiveOnThisLine(); + } +}; +} // end anonymous namespace + + +static void PrintPreprocessedTokens(Preprocessor &PP, Token &Tok, + PrintPPOutputPPCallbacks *Callbacks, + raw_ostream &OS) { + bool DropComments = PP.getLangOpts().TraditionalCPP && + !PP.getCommentRetentionState(); + + char Buffer[256]; + Token PrevPrevTok, PrevTok; + PrevPrevTok.startToken(); + PrevTok.startToken(); + while (1) { + if (Callbacks->hasEmittedDirectiveOnThisLine()) { + Callbacks->startNewLineIfNeeded(); + Callbacks->MoveToLine(Tok.getLocation()); + } + + // 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 (DropComments && Tok.is(tok::comment)) { + // Skip comments. Normally the preprocessor does not generate + // tok::comment nodes at all when not keeping comments, but under + // -traditional-cpp the lexer keeps /all/ whitespace, including comments. + SourceLocation StartLoc = Tok.getLocation(); + Callbacks->MoveToLine(StartLoc.getLocWithOffset(Tok.getLength())); + } else if (Tok.is(tok::annot_module_include)) { + // PrintPPOutputPPCallbacks::InclusionDirective handles producing + // appropriate output here. Ignore this token entirely. + PP.Lex(Tok); + continue; + } else 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 || Tok.getKind() == tok::unknown) + 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 || Tok.getKind() == tok::unknown) + Callbacks->HandleNewlinesInToken(&S[0], S.size()); + } + Callbacks->setEmittedTokensOnThisLine(); + + if (Tok.is(tok::eof)) break; + + PrevPrevTok = PrevTok; + PrevTok = Tok; + PP.Lex(Tok); + } +} + +typedef std::pair<const IdentifierInfo *, MacroInfo *> id_macro_pair; +static int MacroIDCompare(const id_macro_pair *LHS, const id_macro_pair *RHS) { + return LHS->first->getName().compare(RHS->first->getName()); +} + +static void DoPrintMacros(Preprocessor &PP, 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)); + + SmallVector<id_macro_pair, 128> MacrosByID; + for (Preprocessor::macro_iterator I = PP.macro_begin(), E = PP.macro_end(); + I != E; ++I) { + if (I->first->hasMacroDefinition()) + MacrosByID.push_back(id_macro_pair(I->first, I->second->getMacroInfo())); + } + 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, 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); + PP.AddPragmaHandler(new UnknownPragmaHandler("#pragma", Callbacks)); + PP.AddPragmaHandler("GCC", new UnknownPragmaHandler("#pragma GCC",Callbacks)); + PP.AddPragmaHandler("clang", + new UnknownPragmaHandler("#pragma clang", 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); + if (Tok.is(tok::eof) || !Tok.getLocation().isFileID()) + break; + + PresumedLoc PLoc = SourceMgr.getPresumedLoc(Tok.getLocation()); + if (PLoc.isInvalid()) + break; + + if (strcmp(PLoc.getFilename(), "<built-in>")) + break; + } while (true); + + // 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/SerializedDiagnosticPrinter.cpp b/contrib/llvm/tools/clang/lib/Frontend/SerializedDiagnosticPrinter.cpp new file mode 100644 index 0000000..6514321 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/SerializedDiagnosticPrinter.cpp @@ -0,0 +1,692 @@ +//===--- SerializedDiagnosticPrinter.cpp - Serializer for diagnostics -----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/SerializedDiagnosticPrinter.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticOptions.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/Version.h" +#include "clang/Frontend/DiagnosticRenderer.h" +#include "clang/Lex/Lexer.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" +#include <vector> + +using namespace clang; +using namespace clang::serialized_diags; + +namespace { + +class AbbreviationMap { + llvm::DenseMap<unsigned, unsigned> Abbrevs; +public: + AbbreviationMap() {} + + void set(unsigned recordID, unsigned abbrevID) { + assert(Abbrevs.find(recordID) == Abbrevs.end() + && "Abbreviation already set."); + Abbrevs[recordID] = abbrevID; + } + + unsigned get(unsigned recordID) { + assert(Abbrevs.find(recordID) != Abbrevs.end() && + "Abbreviation not set."); + return Abbrevs[recordID]; + } +}; + +typedef SmallVector<uint64_t, 64> RecordData; +typedef SmallVectorImpl<uint64_t> RecordDataImpl; + +class SDiagsWriter; + +class SDiagsRenderer : public DiagnosticNoteRenderer { + SDiagsWriter &Writer; +public: + SDiagsRenderer(SDiagsWriter &Writer, const LangOptions &LangOpts, + DiagnosticOptions *DiagOpts) + : DiagnosticNoteRenderer(LangOpts, DiagOpts), Writer(Writer) {} + + virtual ~SDiagsRenderer() {} + +protected: + virtual void emitDiagnosticMessage(SourceLocation Loc, + PresumedLoc PLoc, + DiagnosticsEngine::Level Level, + StringRef Message, + ArrayRef<CharSourceRange> Ranges, + const SourceManager *SM, + DiagOrStoredDiag D); + + virtual void emitDiagnosticLoc(SourceLocation Loc, PresumedLoc PLoc, + DiagnosticsEngine::Level Level, + ArrayRef<CharSourceRange> Ranges, + const SourceManager &SM) {} + + virtual void emitNote(SourceLocation Loc, StringRef Message, + const SourceManager *SM); + + virtual void emitCodeContext(SourceLocation Loc, + DiagnosticsEngine::Level Level, + SmallVectorImpl<CharSourceRange>& Ranges, + ArrayRef<FixItHint> Hints, + const SourceManager &SM); + + virtual void beginDiagnostic(DiagOrStoredDiag D, + DiagnosticsEngine::Level Level); + virtual void endDiagnostic(DiagOrStoredDiag D, + DiagnosticsEngine::Level Level); +}; + +class SDiagsWriter : public DiagnosticConsumer { + friend class SDiagsRenderer; + + struct SharedState; + + explicit SDiagsWriter(IntrusiveRefCntPtr<SharedState> State) + : LangOpts(0), OriginalInstance(false), State(State) { } + +public: + SDiagsWriter(raw_ostream *os, DiagnosticOptions *diags) + : LangOpts(0), OriginalInstance(true), State(new SharedState(os, diags)) + { + EmitPreamble(); + } + + ~SDiagsWriter() {} + + void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, + const Diagnostic &Info); + + void BeginSourceFile(const LangOptions &LO, + const Preprocessor *PP) { + LangOpts = &LO; + } + + virtual void finish(); + +private: + /// \brief Emit the preamble for the serialized diagnostics. + void EmitPreamble(); + + /// \brief Emit the BLOCKINFO block. + void EmitBlockInfoBlock(); + + /// \brief Emit the META data block. + void EmitMetaBlock(); + + /// \brief Start a DIAG block. + void EnterDiagBlock(); + + /// \brief End a DIAG block. + void ExitDiagBlock(); + + /// \brief Emit a DIAG record. + void EmitDiagnosticMessage(SourceLocation Loc, + PresumedLoc PLoc, + DiagnosticsEngine::Level Level, + StringRef Message, + const SourceManager *SM, + DiagOrStoredDiag D); + + /// \brief Emit FIXIT and SOURCE_RANGE records for a diagnostic. + void EmitCodeContext(SmallVectorImpl<CharSourceRange> &Ranges, + ArrayRef<FixItHint> Hints, + const SourceManager &SM); + + /// \brief Emit a record for a CharSourceRange. + void EmitCharSourceRange(CharSourceRange R, const SourceManager &SM); + + /// \brief Emit the string information for the category. + unsigned getEmitCategory(unsigned category = 0); + + /// \brief Emit the string information for diagnostic flags. + unsigned getEmitDiagnosticFlag(DiagnosticsEngine::Level DiagLevel, + unsigned DiagID = 0); + + /// \brief Emit (lazily) the file string and retrieved the file identifier. + unsigned getEmitFile(const char *Filename); + + /// \brief Add SourceLocation information the specified record. + void AddLocToRecord(SourceLocation Loc, const SourceManager *SM, + PresumedLoc PLoc, RecordDataImpl &Record, + unsigned TokSize = 0); + + /// \brief Add SourceLocation information the specified record. + void AddLocToRecord(SourceLocation Loc, RecordDataImpl &Record, + const SourceManager *SM, + unsigned TokSize = 0) { + AddLocToRecord(Loc, SM, SM ? SM->getPresumedLoc(Loc) : PresumedLoc(), + Record, TokSize); + } + + /// \brief Add CharSourceRange information the specified record. + void AddCharSourceRangeToRecord(CharSourceRange R, RecordDataImpl &Record, + const SourceManager &SM); + + /// \brief The version of the diagnostics file. + enum { Version = 1 }; + + /// \brief Language options, which can differ from one clone of this client + /// to another. + const LangOptions *LangOpts; + + /// \brief Whether this is the original instance (rather than one of its + /// clones), responsible for writing the file at the end. + bool OriginalInstance; + + /// \brief State that is shared among the various clones of this diagnostic + /// consumer. + struct SharedState : RefCountedBase<SharedState> { + SharedState(raw_ostream *os, DiagnosticOptions *diags) + : DiagOpts(diags), Stream(Buffer), OS(os), EmittedAnyDiagBlocks(false) { } + + /// \brief Diagnostic options. + IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts; + + /// \brief The byte buffer for the serialized content. + SmallString<1024> Buffer; + + /// \brief The BitStreamWriter for the serialized diagnostics. + llvm::BitstreamWriter Stream; + + /// \brief The name of the diagnostics file. + OwningPtr<raw_ostream> OS; + + /// \brief The set of constructed record abbreviations. + AbbreviationMap Abbrevs; + + /// \brief A utility buffer for constructing record content. + RecordData Record; + + /// \brief A text buffer for rendering diagnostic text. + SmallString<256> diagBuf; + + /// \brief The collection of diagnostic categories used. + llvm::DenseSet<unsigned> Categories; + + /// \brief The collection of files used. + llvm::DenseMap<const char *, unsigned> Files; + + typedef llvm::DenseMap<const void *, std::pair<unsigned, StringRef> > + DiagFlagsTy; + + /// \brief Map for uniquing strings. + DiagFlagsTy DiagFlags; + + /// \brief Whether we have already started emission of any DIAG blocks. Once + /// this becomes \c true, we never close a DIAG block until we know that we're + /// starting another one or we're done. + bool EmittedAnyDiagBlocks; + }; + + /// \brief State shared among the various clones of this diagnostic consumer. + IntrusiveRefCntPtr<SharedState> State; +}; +} // end anonymous namespace + +namespace clang { +namespace serialized_diags { +DiagnosticConsumer *create(raw_ostream *OS, DiagnosticOptions *diags) { + return new SDiagsWriter(OS, diags); +} +} // end namespace serialized_diags +} // end namespace clang + +//===----------------------------------------------------------------------===// +// Serialization methods. +//===----------------------------------------------------------------------===// + +/// \brief Emits a block ID in the BLOCKINFO block. +static void EmitBlockID(unsigned ID, const char *Name, + llvm::BitstreamWriter &Stream, + RecordDataImpl &Record) { + Record.clear(); + Record.push_back(ID); + Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETBID, Record); + + // Emit the block name if present. + if (Name == 0 || Name[0] == 0) + return; + + Record.clear(); + + while (*Name) + Record.push_back(*Name++); + + Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_BLOCKNAME, Record); +} + +/// \brief Emits a record ID in the BLOCKINFO block. +static void EmitRecordID(unsigned ID, const char *Name, + llvm::BitstreamWriter &Stream, + RecordDataImpl &Record){ + Record.clear(); + Record.push_back(ID); + + while (*Name) + Record.push_back(*Name++); + + Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETRECORDNAME, Record); +} + +void SDiagsWriter::AddLocToRecord(SourceLocation Loc, + const SourceManager *SM, + PresumedLoc PLoc, + RecordDataImpl &Record, + unsigned TokSize) { + if (PLoc.isInvalid()) { + // Emit a "sentinel" location. + Record.push_back((unsigned)0); // File. + Record.push_back((unsigned)0); // Line. + Record.push_back((unsigned)0); // Column. + Record.push_back((unsigned)0); // Offset. + return; + } + + Record.push_back(getEmitFile(PLoc.getFilename())); + Record.push_back(PLoc.getLine()); + Record.push_back(PLoc.getColumn()+TokSize); + Record.push_back(SM->getFileOffset(Loc)); +} + +void SDiagsWriter::AddCharSourceRangeToRecord(CharSourceRange Range, + RecordDataImpl &Record, + const SourceManager &SM) { + AddLocToRecord(Range.getBegin(), Record, &SM); + unsigned TokSize = 0; + if (Range.isTokenRange()) + TokSize = Lexer::MeasureTokenLength(Range.getEnd(), + SM, *LangOpts); + + AddLocToRecord(Range.getEnd(), Record, &SM, TokSize); +} + +unsigned SDiagsWriter::getEmitFile(const char *FileName){ + if (!FileName) + return 0; + + unsigned &entry = State->Files[FileName]; + if (entry) + return entry; + + // Lazily generate the record for the file. + entry = State->Files.size(); + RecordData Record; + Record.push_back(RECORD_FILENAME); + Record.push_back(entry); + Record.push_back(0); // For legacy. + Record.push_back(0); // For legacy. + StringRef Name(FileName); + Record.push_back(Name.size()); + State->Stream.EmitRecordWithBlob(State->Abbrevs.get(RECORD_FILENAME), Record, + Name); + + return entry; +} + +void SDiagsWriter::EmitCharSourceRange(CharSourceRange R, + const SourceManager &SM) { + State->Record.clear(); + State->Record.push_back(RECORD_SOURCE_RANGE); + AddCharSourceRangeToRecord(R, State->Record, SM); + State->Stream.EmitRecordWithAbbrev(State->Abbrevs.get(RECORD_SOURCE_RANGE), + State->Record); +} + +/// \brief Emits the preamble of the diagnostics file. +void SDiagsWriter::EmitPreamble() { + // Emit the file header. + State->Stream.Emit((unsigned)'D', 8); + State->Stream.Emit((unsigned)'I', 8); + State->Stream.Emit((unsigned)'A', 8); + State->Stream.Emit((unsigned)'G', 8); + + EmitBlockInfoBlock(); + EmitMetaBlock(); +} + +static void AddSourceLocationAbbrev(llvm::BitCodeAbbrev *Abbrev) { + using namespace llvm; + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // File ID. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Line. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Column. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Offset; +} + +static void AddRangeLocationAbbrev(llvm::BitCodeAbbrev *Abbrev) { + AddSourceLocationAbbrev(Abbrev); + AddSourceLocationAbbrev(Abbrev); +} + +void SDiagsWriter::EmitBlockInfoBlock() { + State->Stream.EnterBlockInfoBlock(3); + + using namespace llvm; + llvm::BitstreamWriter &Stream = State->Stream; + RecordData &Record = State->Record; + AbbreviationMap &Abbrevs = State->Abbrevs; + + // ==---------------------------------------------------------------------==// + // The subsequent records and Abbrevs are for the "Meta" block. + // ==---------------------------------------------------------------------==// + + EmitBlockID(BLOCK_META, "Meta", Stream, Record); + EmitRecordID(RECORD_VERSION, "Version", Stream, Record); + BitCodeAbbrev *Abbrev = new BitCodeAbbrev(); + Abbrev->Add(BitCodeAbbrevOp(RECORD_VERSION)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); + Abbrevs.set(RECORD_VERSION, Stream.EmitBlockInfoAbbrev(BLOCK_META, Abbrev)); + + // ==---------------------------------------------------------------------==// + // The subsequent records and Abbrevs are for the "Diagnostic" block. + // ==---------------------------------------------------------------------==// + + EmitBlockID(BLOCK_DIAG, "Diag", Stream, Record); + EmitRecordID(RECORD_DIAG, "DiagInfo", Stream, Record); + EmitRecordID(RECORD_SOURCE_RANGE, "SrcRange", Stream, Record); + EmitRecordID(RECORD_CATEGORY, "CatName", Stream, Record); + EmitRecordID(RECORD_DIAG_FLAG, "DiagFlag", Stream, Record); + EmitRecordID(RECORD_FILENAME, "FileName", Stream, Record); + EmitRecordID(RECORD_FIXIT, "FixIt", Stream, Record); + + // Emit abbreviation for RECORD_DIAG. + Abbrev = new BitCodeAbbrev(); + Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Diag level. + AddSourceLocationAbbrev(Abbrev); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Category. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped Diag ID. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Diagnostc text. + Abbrevs.set(RECORD_DIAG, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev)); + + // Emit abbrevation for RECORD_CATEGORY. + Abbrev = new BitCodeAbbrev(); + Abbrev->Add(BitCodeAbbrevOp(RECORD_CATEGORY)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Category ID. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 8)); // Text size. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Category text. + Abbrevs.set(RECORD_CATEGORY, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev)); + + // Emit abbrevation for RECORD_SOURCE_RANGE. + Abbrev = new BitCodeAbbrev(); + Abbrev->Add(BitCodeAbbrevOp(RECORD_SOURCE_RANGE)); + AddRangeLocationAbbrev(Abbrev); + Abbrevs.set(RECORD_SOURCE_RANGE, + Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev)); + + // Emit the abbreviation for RECORD_DIAG_FLAG. + Abbrev = new BitCodeAbbrev(); + Abbrev->Add(BitCodeAbbrevOp(RECORD_DIAG_FLAG)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped Diag ID. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Flag name text. + Abbrevs.set(RECORD_DIAG_FLAG, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, + Abbrev)); + + // Emit the abbreviation for RECORD_FILENAME. + Abbrev = new BitCodeAbbrev(); + Abbrev->Add(BitCodeAbbrevOp(RECORD_FILENAME)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 10)); // Mapped file ID. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Size. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Modifcation time. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // File name text. + Abbrevs.set(RECORD_FILENAME, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, + Abbrev)); + + // Emit the abbreviation for RECORD_FIXIT. + Abbrev = new BitCodeAbbrev(); + Abbrev->Add(BitCodeAbbrevOp(RECORD_FIXIT)); + AddRangeLocationAbbrev(Abbrev); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 16)); // Text size. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // FixIt text. + Abbrevs.set(RECORD_FIXIT, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, + Abbrev)); + + Stream.ExitBlock(); +} + +void SDiagsWriter::EmitMetaBlock() { + llvm::BitstreamWriter &Stream = State->Stream; + RecordData &Record = State->Record; + AbbreviationMap &Abbrevs = State->Abbrevs; + + Stream.EnterSubblock(BLOCK_META, 3); + Record.clear(); + Record.push_back(RECORD_VERSION); + Record.push_back(Version); + Stream.EmitRecordWithAbbrev(Abbrevs.get(RECORD_VERSION), Record); + Stream.ExitBlock(); +} + +unsigned SDiagsWriter::getEmitCategory(unsigned int category) { + if (State->Categories.count(category)) + return category; + + State->Categories.insert(category); + + // We use a local version of 'Record' so that we can be generating + // another record when we lazily generate one for the category entry. + RecordData Record; + Record.push_back(RECORD_CATEGORY); + Record.push_back(category); + StringRef catName = DiagnosticIDs::getCategoryNameFromID(category); + Record.push_back(catName.size()); + State->Stream.EmitRecordWithBlob(State->Abbrevs.get(RECORD_CATEGORY), Record, + catName); + + return category; +} + +unsigned SDiagsWriter::getEmitDiagnosticFlag(DiagnosticsEngine::Level DiagLevel, + unsigned DiagID) { + if (DiagLevel == DiagnosticsEngine::Note) + return 0; // No flag for notes. + + StringRef FlagName = DiagnosticIDs::getWarningOptionForDiag(DiagID); + if (FlagName.empty()) + return 0; + + // Here we assume that FlagName points to static data whose pointer + // value is fixed. This allows us to unique by diagnostic groups. + const void *data = FlagName.data(); + std::pair<unsigned, StringRef> &entry = State->DiagFlags[data]; + if (entry.first == 0) { + entry.first = State->DiagFlags.size(); + entry.second = FlagName; + + // Lazily emit the string in a separate record. + RecordData Record; + Record.push_back(RECORD_DIAG_FLAG); + Record.push_back(entry.first); + Record.push_back(FlagName.size()); + State->Stream.EmitRecordWithBlob(State->Abbrevs.get(RECORD_DIAG_FLAG), + Record, FlagName); + } + + return entry.first; +} + +void SDiagsWriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, + const Diagnostic &Info) { + // Enter the block for a non-note diagnostic immediately, rather than waiting + // for beginDiagnostic, in case associated notes are emitted before we get + // there. + if (DiagLevel != DiagnosticsEngine::Note) { + if (State->EmittedAnyDiagBlocks) + ExitDiagBlock(); + + EnterDiagBlock(); + State->EmittedAnyDiagBlocks = true; + } + + // Compute the diagnostic text. + State->diagBuf.clear(); + Info.FormatDiagnostic(State->diagBuf); + + if (Info.getLocation().isInvalid()) { + // Special-case diagnostics with no location. We may not have entered a + // source file in this case, so we can't use the normal DiagnosticsRenderer + // machinery. + + // Make sure we bracket all notes as "sub-diagnostics". This matches + // the behavior in SDiagsRenderer::emitDiagnostic(). + if (DiagLevel == DiagnosticsEngine::Note) + EnterDiagBlock(); + + EmitDiagnosticMessage(SourceLocation(), PresumedLoc(), DiagLevel, + State->diagBuf, 0, &Info); + + if (DiagLevel == DiagnosticsEngine::Note) + ExitDiagBlock(); + + return; + } + + assert(Info.hasSourceManager() && LangOpts && + "Unexpected diagnostic with valid location outside of a source file"); + SDiagsRenderer Renderer(*this, *LangOpts, &*State->DiagOpts); + Renderer.emitDiagnostic(Info.getLocation(), DiagLevel, + State->diagBuf.str(), + Info.getRanges(), + llvm::makeArrayRef(Info.getFixItHints(), + Info.getNumFixItHints()), + &Info.getSourceManager(), + &Info); +} + +void SDiagsWriter::EmitDiagnosticMessage(SourceLocation Loc, + PresumedLoc PLoc, + DiagnosticsEngine::Level Level, + StringRef Message, + const SourceManager *SM, + DiagOrStoredDiag D) { + llvm::BitstreamWriter &Stream = State->Stream; + RecordData &Record = State->Record; + AbbreviationMap &Abbrevs = State->Abbrevs; + + // Emit the RECORD_DIAG record. + Record.clear(); + Record.push_back(RECORD_DIAG); + Record.push_back(Level); + AddLocToRecord(Loc, SM, PLoc, Record); + + if (const Diagnostic *Info = D.dyn_cast<const Diagnostic*>()) { + // Emit the category string lazily and get the category ID. + unsigned DiagID = DiagnosticIDs::getCategoryNumberForDiag(Info->getID()); + Record.push_back(getEmitCategory(DiagID)); + // Emit the diagnostic flag string lazily and get the mapped ID. + Record.push_back(getEmitDiagnosticFlag(Level, Info->getID())); + } else { + Record.push_back(getEmitCategory()); + Record.push_back(getEmitDiagnosticFlag(Level)); + } + + Record.push_back(Message.size()); + Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_DIAG), Record, Message); +} + +void +SDiagsRenderer::emitDiagnosticMessage(SourceLocation Loc, + PresumedLoc PLoc, + DiagnosticsEngine::Level Level, + StringRef Message, + ArrayRef<clang::CharSourceRange> Ranges, + const SourceManager *SM, + DiagOrStoredDiag D) { + Writer.EmitDiagnosticMessage(Loc, PLoc, Level, Message, SM, D); +} + +void SDiagsWriter::EnterDiagBlock() { + State->Stream.EnterSubblock(BLOCK_DIAG, 4); +} + +void SDiagsWriter::ExitDiagBlock() { + State->Stream.ExitBlock(); +} + +void SDiagsRenderer::beginDiagnostic(DiagOrStoredDiag D, + DiagnosticsEngine::Level Level) { + if (Level == DiagnosticsEngine::Note) + Writer.EnterDiagBlock(); +} + +void SDiagsRenderer::endDiagnostic(DiagOrStoredDiag D, + DiagnosticsEngine::Level Level) { + // Only end note diagnostics here, because we can't be sure when we've seen + // the last note associated with a non-note diagnostic. + if (Level == DiagnosticsEngine::Note) + Writer.ExitDiagBlock(); +} + +void SDiagsWriter::EmitCodeContext(SmallVectorImpl<CharSourceRange> &Ranges, + ArrayRef<FixItHint> Hints, + const SourceManager &SM) { + llvm::BitstreamWriter &Stream = State->Stream; + RecordData &Record = State->Record; + AbbreviationMap &Abbrevs = State->Abbrevs; + + // Emit Source Ranges. + for (ArrayRef<CharSourceRange>::iterator I = Ranges.begin(), E = Ranges.end(); + I != E; ++I) + if (I->isValid()) + EmitCharSourceRange(*I, SM); + + // Emit FixIts. + for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end(); + I != E; ++I) { + const FixItHint &Fix = *I; + if (Fix.isNull()) + continue; + Record.clear(); + Record.push_back(RECORD_FIXIT); + AddCharSourceRangeToRecord(Fix.RemoveRange, Record, SM); + Record.push_back(Fix.CodeToInsert.size()); + Stream.EmitRecordWithBlob(Abbrevs.get(RECORD_FIXIT), Record, + Fix.CodeToInsert); + } +} + +void SDiagsRenderer::emitCodeContext(SourceLocation Loc, + DiagnosticsEngine::Level Level, + SmallVectorImpl<CharSourceRange> &Ranges, + ArrayRef<FixItHint> Hints, + const SourceManager &SM) { + Writer.EmitCodeContext(Ranges, Hints, SM); +} + +void SDiagsRenderer::emitNote(SourceLocation Loc, StringRef Message, + const SourceManager *SM) { + Writer.EnterDiagBlock(); + PresumedLoc PLoc = SM ? SM->getPresumedLoc(Loc) : PresumedLoc(); + Writer.EmitDiagnosticMessage(Loc, PLoc, DiagnosticsEngine::Note, + Message, SM, DiagOrStoredDiag()); + Writer.ExitDiagBlock(); +} + +void SDiagsWriter::finish() { + // The original instance is responsible for writing the file. + if (!OriginalInstance) + return; + + // Finish off any diagnostic we were in the process of emitting. + if (State->EmittedAnyDiagBlocks) + ExitDiagBlock(); + + // Write the generated bitstream to "Out". + State->OS->write((char *)&State->Buffer.front(), State->Buffer.size()); + State->OS->flush(); + + State->OS.reset(0); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/TextDiagnostic.cpp b/contrib/llvm/tools/clang/lib/Frontend/TextDiagnostic.cpp new file mode 100644 index 0000000..a2dc953 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/TextDiagnostic.cpp @@ -0,0 +1,1271 @@ +//===--- TextDiagnostic.cpp - Text Diagnostic Pretty-Printing -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/TextDiagnostic.h" +#include "clang/Basic/CharInfo.h" +#include "clang/Basic/DiagnosticOptions.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/Lexer.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Locale.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> + +using namespace clang; + +static const enum raw_ostream::Colors noteColor = + raw_ostream::BLACK; +static const enum raw_ostream::Colors fixitColor = + raw_ostream::GREEN; +static const enum raw_ostream::Colors caretColor = + raw_ostream::GREEN; +static const enum raw_ostream::Colors warningColor = + raw_ostream::MAGENTA; +static const enum raw_ostream::Colors templateColor = + raw_ostream::CYAN; +static const enum raw_ostream::Colors errorColor = raw_ostream::RED; +static const enum raw_ostream::Colors fatalColor = raw_ostream::RED; +// Used for changing only the bold attribute. +static const enum raw_ostream::Colors savedColor = + raw_ostream::SAVEDCOLOR; + +/// \brief Add highlights to differences in template strings. +static void applyTemplateHighlighting(raw_ostream &OS, StringRef Str, + bool &Normal, bool Bold) { + while (1) { + size_t Pos = Str.find(ToggleHighlight); + OS << Str.slice(0, Pos); + if (Pos == StringRef::npos) + break; + + Str = Str.substr(Pos + 1); + if (Normal) + OS.changeColor(templateColor, true); + else { + OS.resetColor(); + if (Bold) + OS.changeColor(savedColor, true); + } + Normal = !Normal; + } +} + +/// \brief Number of spaces to indent when word-wrapping. +const unsigned WordWrapIndentation = 6; + +static int bytesSincePreviousTabOrLineBegin(StringRef SourceLine, size_t i) { + int bytes = 0; + while (0<i) { + if (SourceLine[--i]=='\t') + break; + ++bytes; + } + return bytes; +} + +/// \brief returns a printable representation of first item from input range +/// +/// This function returns a printable representation of the next item in a line +/// of source. If the next byte begins a valid and printable character, that +/// character is returned along with 'true'. +/// +/// Otherwise, if the next byte begins a valid, but unprintable character, a +/// printable, escaped representation of the character is returned, along with +/// 'false'. Otherwise a printable, escaped representation of the next byte +/// is returned along with 'false'. +/// +/// \note The index is updated to be used with a subsequent call to +/// printableTextForNextCharacter. +/// +/// \param SourceLine The line of source +/// \param i Pointer to byte index, +/// \param TabStop used to expand tabs +/// \return pair(printable text, 'true' iff original text was printable) +/// +static std::pair<SmallString<16>, bool> +printableTextForNextCharacter(StringRef SourceLine, size_t *i, + unsigned TabStop) { + assert(i && "i must not be null"); + assert(*i<SourceLine.size() && "must point to a valid index"); + + if (SourceLine[*i]=='\t') { + assert(0 < TabStop && TabStop <= DiagnosticOptions::MaxTabStop && + "Invalid -ftabstop value"); + unsigned col = bytesSincePreviousTabOrLineBegin(SourceLine, *i); + unsigned NumSpaces = TabStop - col%TabStop; + assert(0 < NumSpaces && NumSpaces <= TabStop + && "Invalid computation of space amt"); + ++(*i); + + SmallString<16> expandedTab; + expandedTab.assign(NumSpaces, ' '); + return std::make_pair(expandedTab, true); + } + + unsigned char const *begin, *end; + begin = reinterpret_cast<unsigned char const *>(&*(SourceLine.begin() + *i)); + end = begin + (SourceLine.size() - *i); + + if (isLegalUTF8Sequence(begin, end)) { + UTF32 c; + UTF32 *cptr = &c; + unsigned char const *original_begin = begin; + unsigned char const *cp_end = begin+getNumBytesForUTF8(SourceLine[*i]); + + ConversionResult res = ConvertUTF8toUTF32(&begin, cp_end, &cptr, cptr+1, + strictConversion); + (void)res; + assert(conversionOK==res); + assert(0 < begin-original_begin + && "we must be further along in the string now"); + *i += begin-original_begin; + + if (!llvm::sys::locale::isPrint(c)) { + // If next character is valid UTF-8, but not printable + SmallString<16> expandedCP("<U+>"); + while (c) { + expandedCP.insert(expandedCP.begin()+3, llvm::hexdigit(c%16)); + c/=16; + } + while (expandedCP.size() < 8) + expandedCP.insert(expandedCP.begin()+3, llvm::hexdigit(0)); + return std::make_pair(expandedCP, false); + } + + // If next character is valid UTF-8, and printable + return std::make_pair(SmallString<16>(original_begin, cp_end), true); + + } + + // If next byte is not valid UTF-8 (and therefore not printable) + SmallString<16> expandedByte("<XX>"); + unsigned char byte = SourceLine[*i]; + expandedByte[1] = llvm::hexdigit(byte / 16); + expandedByte[2] = llvm::hexdigit(byte % 16); + ++(*i); + return std::make_pair(expandedByte, false); +} + +static void expandTabs(std::string &SourceLine, unsigned TabStop) { + size_t i = SourceLine.size(); + while (i>0) { + i--; + if (SourceLine[i]!='\t') + continue; + size_t tmp_i = i; + std::pair<SmallString<16>,bool> res + = printableTextForNextCharacter(SourceLine, &tmp_i, TabStop); + SourceLine.replace(i, 1, res.first.c_str()); + } +} + +/// This function takes a raw source line and produces a mapping from the bytes +/// of the printable representation of the line to the columns those printable +/// characters will appear at (numbering the first column as 0). +/// +/// If a byte 'i' corresponds to muliple columns (e.g. the byte contains a tab +/// character) then the array will map that byte to the first column the +/// tab appears at and the next value in the map will have been incremented +/// more than once. +/// +/// If a byte is the first in a sequence of bytes that together map to a single +/// entity in the output, then the array will map that byte to the appropriate +/// column while the subsequent bytes will be -1. +/// +/// The last element in the array does not correspond to any byte in the input +/// and instead is the number of columns needed to display the source +/// +/// example: (given a tabstop of 8) +/// +/// "a \t \u3042" -> {0,1,2,8,9,-1,-1,11} +/// +/// (\\u3042 is represented in UTF-8 by three bytes and takes two columns to +/// display) +static void byteToColumn(StringRef SourceLine, unsigned TabStop, + SmallVectorImpl<int> &out) { + out.clear(); + + if (SourceLine.empty()) { + out.resize(1u,0); + return; + } + + out.resize(SourceLine.size()+1, -1); + + int columns = 0; + size_t i = 0; + while (i<SourceLine.size()) { + out[i] = columns; + std::pair<SmallString<16>,bool> res + = printableTextForNextCharacter(SourceLine, &i, TabStop); + columns += llvm::sys::locale::columnWidth(res.first); + } + out.back() = columns; +} + +/// This function takes a raw source line and produces a mapping from columns +/// to the byte of the source line that produced the character displaying at +/// that column. This is the inverse of the mapping produced by byteToColumn() +/// +/// The last element in the array is the number of bytes in the source string +/// +/// example: (given a tabstop of 8) +/// +/// "a \t \u3042" -> {0,1,2,-1,-1,-1,-1,-1,3,4,-1,7} +/// +/// (\\u3042 is represented in UTF-8 by three bytes and takes two columns to +/// display) +static void columnToByte(StringRef SourceLine, unsigned TabStop, + SmallVectorImpl<int> &out) { + out.clear(); + + if (SourceLine.empty()) { + out.resize(1u, 0); + return; + } + + int columns = 0; + size_t i = 0; + while (i<SourceLine.size()) { + out.resize(columns+1, -1); + out.back() = i; + std::pair<SmallString<16>,bool> res + = printableTextForNextCharacter(SourceLine, &i, TabStop); + columns += llvm::sys::locale::columnWidth(res.first); + } + out.resize(columns+1, -1); + out.back() = i; +} + +namespace { +struct SourceColumnMap { + SourceColumnMap(StringRef SourceLine, unsigned TabStop) + : m_SourceLine(SourceLine) { + + ::byteToColumn(SourceLine, TabStop, m_byteToColumn); + ::columnToByte(SourceLine, TabStop, m_columnToByte); + + assert(m_byteToColumn.size()==SourceLine.size()+1); + assert(0 < m_byteToColumn.size() && 0 < m_columnToByte.size()); + assert(m_byteToColumn.size() + == static_cast<unsigned>(m_columnToByte.back()+1)); + assert(static_cast<unsigned>(m_byteToColumn.back()+1) + == m_columnToByte.size()); + } + int columns() const { return m_byteToColumn.back(); } + int bytes() const { return m_columnToByte.back(); } + + /// \brief Map a byte to the column which it is at the start of, or return -1 + /// if it is not at the start of a column (for a UTF-8 trailing byte). + int byteToColumn(int n) const { + assert(0<=n && n<static_cast<int>(m_byteToColumn.size())); + return m_byteToColumn[n]; + } + + /// \brief Map a byte to the first column which contains it. + int byteToContainingColumn(int N) const { + assert(0 <= N && N < static_cast<int>(m_byteToColumn.size())); + while (m_byteToColumn[N] == -1) + --N; + return m_byteToColumn[N]; + } + + /// \brief Map a column to the byte which starts the column, or return -1 if + /// the column the second or subsequent column of an expanded tab or similar + /// multi-column entity. + int columnToByte(int n) const { + assert(0<=n && n<static_cast<int>(m_columnToByte.size())); + return m_columnToByte[n]; + } + + /// \brief Map from a byte index to the next byte which starts a column. + int startOfNextColumn(int N) const { + assert(0 <= N && N < static_cast<int>(m_columnToByte.size() - 1)); + while (byteToColumn(++N) == -1) {} + return N; + } + + /// \brief Map from a byte index to the previous byte which starts a column. + int startOfPreviousColumn(int N) const { + assert(0 < N && N < static_cast<int>(m_columnToByte.size())); + while (byteToColumn(--N) == -1) {} + return N; + } + + StringRef getSourceLine() const { + return m_SourceLine; + } + +private: + const std::string m_SourceLine; + SmallVector<int,200> m_byteToColumn; + SmallVector<int,200> m_columnToByte; +}; + +// used in assert in selectInterestingSourceRegion() +struct char_out_of_range { + const char lower,upper; + char_out_of_range(char lower, char upper) : + lower(lower), upper(upper) {} + bool operator()(char c) { return c < lower || upper < c; } +}; +} // end anonymous namespace + +/// \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 Columns, + const SourceColumnMap &map) { + unsigned MaxColumns = std::max<unsigned>(map.columns(), + std::max(CaretLine.size(), + FixItInsertionLine.size())); + // if the number of columns is less than the desired number we're done + if (MaxColumns <= Columns) + return; + + // No special characters are allowed in CaretLine. + assert(CaretLine.end() == + std::find_if(CaretLine.begin(), CaretLine.end(), + char_out_of_range(' ','~'))); + + // Find the slice that we need to display the full caret line + // correctly. + unsigned CaretStart = 0, CaretEnd = CaretLine.size(); + for (; CaretStart != CaretEnd; ++CaretStart) + if (!isWhitespace(CaretLine[CaretStart])) + break; + + for (; CaretEnd != CaretStart; --CaretEnd) + if (!isWhitespace(CaretLine[CaretEnd - 1])) + break; + + // caret has already been inserted into CaretLine so the above whitespace + // check is guaranteed to include the caret + + // 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 (!isWhitespace(FixItInsertionLine[FixItStart])) + break; + + for (; FixItEnd != FixItStart; --FixItEnd) + if (!isWhitespace(FixItInsertionLine[FixItEnd - 1])) + break; + + // We can safely use the byte offset FixItStart as the column offset + // because the characters up until FixItStart are all ASCII whitespace + // characters. + unsigned FixItStartCol = FixItStart; + unsigned FixItEndCol + = llvm::sys::locale::columnWidth(FixItInsertionLine.substr(0, FixItEnd)); + + CaretStart = std::min(FixItStartCol, CaretStart); + CaretEnd = std::max(FixItEndCol, CaretEnd); + } + + // CaretEnd may have been set at the middle of a character + // If it's not at a character's first column then advance it past the current + // character. + while (static_cast<int>(CaretEnd) < map.columns() && + -1 == map.columnToByte(CaretEnd)) + ++CaretEnd; + + assert((static_cast<int>(CaretStart) > map.columns() || + -1!=map.columnToByte(CaretStart)) && + "CaretStart must not point to a column in the middle of a source" + " line character"); + assert((static_cast<int>(CaretEnd) > map.columns() || + -1!=map.columnToByte(CaretEnd)) && + "CaretEnd must not point to a column in the middle of a source line" + " character"); + + // 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. + + unsigned SourceStart = map.columnToByte(std::min<unsigned>(CaretStart, + map.columns())); + unsigned SourceEnd = map.columnToByte(std::min<unsigned>(CaretEnd, + map.columns())); + + unsigned CaretColumnsOutsideSource = CaretEnd-CaretStart + - (map.byteToColumn(SourceEnd)-map.byteToColumn(SourceStart)); + + char const *front_ellipse = " ..."; + char const *front_space = " "; + char const *back_ellipse = "..."; + unsigned ellipses_space = strlen(front_ellipse) + strlen(back_ellipse); + + unsigned TargetColumns = Columns; + // Give us extra room for the ellipses + // and any of the caret line that extends past the source + if (TargetColumns > ellipses_space+CaretColumnsOutsideSource) + TargetColumns -= ellipses_space+CaretColumnsOutsideSource; + + while (SourceStart>0 || SourceEnd<SourceLine.size()) { + bool ExpandedRegion = false; + + if (SourceStart>0) { + unsigned NewStart = map.startOfPreviousColumn(SourceStart); + + // Skip over any whitespace we see here; we're looking for + // another bit of interesting text. + // FIXME: Detect non-ASCII whitespace characters too. + while (NewStart && isWhitespace(SourceLine[NewStart])) + NewStart = map.startOfPreviousColumn(NewStart); + + // Skip over this bit of "interesting" text. + while (NewStart) { + unsigned Prev = map.startOfPreviousColumn(NewStart); + if (isWhitespace(SourceLine[Prev])) + break; + NewStart = Prev; + } + + assert(map.byteToColumn(NewStart) != -1); + unsigned NewColumns = map.byteToColumn(SourceEnd) - + map.byteToColumn(NewStart); + if (NewColumns <= TargetColumns) { + SourceStart = NewStart; + ExpandedRegion = true; + } + } + + if (SourceEnd<SourceLine.size()) { + unsigned NewEnd = map.startOfNextColumn(SourceEnd); + + // Skip over any whitespace we see here; we're looking for + // another bit of interesting text. + // FIXME: Detect non-ASCII whitespace characters too. + while (NewEnd < SourceLine.size() && isWhitespace(SourceLine[NewEnd])) + NewEnd = map.startOfNextColumn(NewEnd); + + // Skip over this bit of "interesting" text. + while (NewEnd < SourceLine.size() && isWhitespace(SourceLine[NewEnd])) + NewEnd = map.startOfNextColumn(NewEnd); + + assert(map.byteToColumn(NewEnd) != -1); + unsigned NewColumns = map.byteToColumn(NewEnd) - + map.byteToColumn(SourceStart); + if (NewColumns <= TargetColumns) { + SourceEnd = NewEnd; + ExpandedRegion = true; + } + } + + if (!ExpandedRegion) + break; + } + + CaretStart = map.byteToColumn(SourceStart); + CaretEnd = map.byteToColumn(SourceEnd) + CaretColumnsOutsideSource; + + // [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. + + assert(CaretStart!=(unsigned)-1 && CaretEnd!=(unsigned)-1 && + SourceStart!=(unsigned)-1 && SourceEnd!=(unsigned)-1); + assert(SourceStart <= SourceEnd); + assert(CaretStart <= CaretEnd); + + unsigned BackColumnsRemoved + = map.byteToColumn(SourceLine.size())-map.byteToColumn(SourceEnd); + unsigned FrontColumnsRemoved = CaretStart; + unsigned ColumnsKept = CaretEnd-CaretStart; + + // We checked up front that the line needed truncation + assert(FrontColumnsRemoved+ColumnsKept+BackColumnsRemoved > Columns); + + // The line needs some trunctiona, and we'd prefer to keep the front + // if possible, so remove the back + if (BackColumnsRemoved > strlen(back_ellipse)) + SourceLine.replace(SourceEnd, std::string::npos, back_ellipse); + + // If that's enough then we're done + if (FrontColumnsRemoved+ColumnsKept <= Columns) + return; + + // Otherwise remove the front as well + if (FrontColumnsRemoved > strlen(front_ellipse)) { + SourceLine.replace(0, SourceStart, front_ellipse); + CaretLine.replace(0, CaretStart, front_space); + if (!FixItInsertionLine.empty()) + FixItInsertionLine.replace(0, CaretStart, front_space); + } +} + +/// \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, StringRef Str, unsigned Length) { + while (Idx < Length && isWhitespace(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, StringRef 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 && !isWhitespace(Str[End])) + ++End; + return End; + } + + // We have the start of a balanced punctuation sequence (quotes, + // parentheses, etc.). Determine the full sequence is. + 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 && !isWhitespace(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. +/// +/// \param OS the stream to which the word-wrapping string will be +/// emitted. +/// \param Str the string to word-wrap and output. +/// \param Columns the number of columns to word-wrap to. +/// \param 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. +/// \param Bold if the current text should be bold +/// \param 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(raw_ostream &OS, StringRef Str, + unsigned Columns, + unsigned Column = 0, + bool Bold = false, + unsigned Indentation = WordWrapIndentation) { + const unsigned Length = std::min(Str.find('\n'), Str.size()); + bool TextNormal = true; + + // The string used to indent each line. + 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; + } + applyTemplateHighlighting(OS, Str.substr(WordStart, WordLength), + TextNormal, Bold); + 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); + applyTemplateHighlighting(OS, Str.substr(WordStart, WordLength), + TextNormal, Bold); + Column = Indentation + WordLength; + Wrapped = true; + } + + // Append any remaning text from the message with its existing formatting. + applyTemplateHighlighting(OS, Str.substr(Length), TextNormal, Bold); + + assert(TextNormal && "Text highlighted at end of diagnostic message."); + + return Wrapped; +} + +TextDiagnostic::TextDiagnostic(raw_ostream &OS, + const LangOptions &LangOpts, + DiagnosticOptions *DiagOpts) + : DiagnosticRenderer(LangOpts, DiagOpts), OS(OS) {} + +TextDiagnostic::~TextDiagnostic() {} + +void +TextDiagnostic::emitDiagnosticMessage(SourceLocation Loc, + PresumedLoc PLoc, + DiagnosticsEngine::Level Level, + StringRef Message, + ArrayRef<clang::CharSourceRange> Ranges, + const SourceManager *SM, + DiagOrStoredDiag D) { + uint64_t StartOfLocationInfo = OS.tell(); + + // Emit the location of this particular diagnostic. + if (Loc.isValid()) + emitDiagnosticLoc(Loc, PLoc, Level, Ranges, *SM); + + if (DiagOpts->ShowColors) + OS.resetColor(); + + printDiagnosticLevel(OS, Level, DiagOpts->ShowColors, + DiagOpts->CLFallbackMode); + printDiagnosticMessage(OS, Level, Message, + OS.tell() - StartOfLocationInfo, + DiagOpts->MessageLength, DiagOpts->ShowColors); +} + +/*static*/ void +TextDiagnostic::printDiagnosticLevel(raw_ostream &OS, + DiagnosticsEngine::Level Level, + bool ShowColors, + bool CLFallbackMode) { + if (ShowColors) { + // Print diagnostic category in bold and color + switch (Level) { + case DiagnosticsEngine::Ignored: + llvm_unreachable("Invalid diagnostic type"); + case DiagnosticsEngine::Note: OS.changeColor(noteColor, true); break; + case DiagnosticsEngine::Warning: OS.changeColor(warningColor, true); break; + case DiagnosticsEngine::Error: OS.changeColor(errorColor, true); break; + case DiagnosticsEngine::Fatal: OS.changeColor(fatalColor, true); break; + } + } + + switch (Level) { + case DiagnosticsEngine::Ignored: + llvm_unreachable("Invalid diagnostic type"); + case DiagnosticsEngine::Note: OS << "note"; break; + case DiagnosticsEngine::Warning: OS << "warning"; break; + case DiagnosticsEngine::Error: OS << "error"; break; + case DiagnosticsEngine::Fatal: OS << "fatal error"; break; + } + + // In clang-cl /fallback mode, print diagnostics as "error(clang):". This + // makes it more clear whether a message is coming from clang or cl.exe, + // and it prevents MSBuild from concluding that the build failed just because + // there is an "error:" in the output. + if (CLFallbackMode) + OS << "(clang)"; + + OS << ": "; + + if (ShowColors) + OS.resetColor(); +} + +/*static*/ void +TextDiagnostic::printDiagnosticMessage(raw_ostream &OS, + DiagnosticsEngine::Level Level, + StringRef Message, + unsigned CurrentColumn, unsigned Columns, + bool ShowColors) { + bool Bold = false; + if (ShowColors) { + // Print warnings, errors and fatal errors in bold, no color + switch (Level) { + case DiagnosticsEngine::Warning: + case DiagnosticsEngine::Error: + case DiagnosticsEngine::Fatal: + OS.changeColor(savedColor, true); + Bold = true; + break; + default: break; //don't bold notes + } + } + + if (Columns) + printWordWrapped(OS, Message, Columns, CurrentColumn, Bold); + else { + bool Normal = true; + applyTemplateHighlighting(OS, Message, Normal, Bold); + assert(Normal && "Formatting should have returned to normal"); + } + + if (ShowColors) + OS.resetColor(); + OS << '\n'; +} + +/// \brief Print out the file/line/column information and include trace. +/// +/// This method handlen the emission of the diagnostic location information. +/// This includes extracting as much location information as is present for +/// the diagnostic and printing it, as well as any include stack or source +/// ranges necessary. +void TextDiagnostic::emitDiagnosticLoc(SourceLocation Loc, PresumedLoc PLoc, + DiagnosticsEngine::Level Level, + ArrayRef<CharSourceRange> Ranges, + const SourceManager &SM) { + if (PLoc.isInvalid()) { + // At least print the file name if available: + FileID FID = SM.getFileID(Loc); + if (!FID.isInvalid()) { + const FileEntry* FE = SM.getFileEntryForID(FID); + if (FE && FE->getName()) { + OS << FE->getName(); + if (FE->isInPCH()) + OS << " (in PCH)"; + OS << ": "; + } + } + return; + } + unsigned LineNo = PLoc.getLine(); + + if (!DiagOpts->ShowLocation) + return; + + if (DiagOpts->ShowColors) + OS.changeColor(savedColor, true); + + OS << PLoc.getFilename(); + switch (DiagOpts->getFormat()) { + case DiagnosticOptions::Clang: OS << ':' << LineNo; break; + case DiagnosticOptions::Msvc: OS << '(' << LineNo; break; + case DiagnosticOptions::Vi: OS << " +" << LineNo; break; + } + + if (DiagOpts->ShowColumn) + // Compute the column number. + if (unsigned ColNo = PLoc.getColumn()) { + if (DiagOpts->getFormat() == DiagnosticOptions::Msvc) { + OS << ','; + ColNo--; + } else + OS << ':'; + OS << ColNo; + } + switch (DiagOpts->getFormat()) { + case DiagnosticOptions::Clang: + case DiagnosticOptions::Vi: OS << ':'; break; + case DiagnosticOptions::Msvc: OS << ") : "; break; + } + + if (DiagOpts->ShowSourceRanges && !Ranges.empty()) { + FileID CaretFileID = + SM.getFileID(SM.getExpansionLoc(Loc)); + bool PrintedRange = false; + + for (ArrayRef<CharSourceRange>::const_iterator RI = Ranges.begin(), + RE = Ranges.end(); + RI != RE; ++RI) { + // Ignore invalid ranges. + if (!RI->isValid()) continue; + + SourceLocation B = SM.getExpansionLoc(RI->getBegin()); + SourceLocation E = SM.getExpansionLoc(RI->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 (B == E && RI->getEnd().isMacroID()) + E = SM.getExpansionRange(RI->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 (RI->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 << ' '; +} + +void TextDiagnostic::emitBasicNote(StringRef Message) { + // FIXME: Emit this as a real note diagnostic. + // FIXME: Format an actual diagnostic rather than a hard coded string. + OS << "note: " << Message << "\n"; +} + +void TextDiagnostic::emitIncludeLocation(SourceLocation Loc, + PresumedLoc PLoc, + const SourceManager &SM) { + if (DiagOpts->ShowLocation) + OS << "In file included from " << PLoc.getFilename() << ':' + << PLoc.getLine() << ":\n"; + else + OS << "In included file:\n"; +} + +void TextDiagnostic::emitImportLocation(SourceLocation Loc, PresumedLoc PLoc, + StringRef ModuleName, + const SourceManager &SM) { + if (DiagOpts->ShowLocation) + OS << "In module '" << ModuleName << "' imported from " + << PLoc.getFilename() << ':' << PLoc.getLine() << ":\n"; + else + OS << "In module " << ModuleName << "':\n"; +} + +void TextDiagnostic::emitBuildingModuleLocation(SourceLocation Loc, + PresumedLoc PLoc, + StringRef ModuleName, + const SourceManager &SM) { + if (DiagOpts->ShowLocation && PLoc.getFilename()) + OS << "While building module '" << ModuleName << "' imported from " + << PLoc.getFilename() << ':' << PLoc.getLine() << ":\n"; + else + OS << "While building module '" << ModuleName << "':\n"; +} + +/// \brief Highlight a SourceRange (with ~'s) for any characters on LineNo. +static void highlightRange(const CharSourceRange &R, + unsigned LineNo, FileID FID, + const SourceColumnMap &map, + std::string &CaretLine, + const SourceManager &SM, + const LangOptions &LangOpts) { + if (!R.isValid()) return; + + SourceLocation Begin = R.getBegin(); + SourceLocation End = R.getEnd(); + + unsigned StartLineNo = SM.getExpansionLineNumber(Begin); + if (StartLineNo > LineNo || SM.getFileID(Begin) != FID) + return; // No intersection. + + unsigned EndLineNo = SM.getExpansionLineNumber(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.getExpansionColumnNumber(Begin); + if (StartColNo) --StartColNo; // Zero base the col #. + } + + // Compute the column number of the end. + unsigned EndColNo = map.getSourceLine().size(); + if (EndLineNo == LineNo) { + EndColNo = SM.getExpansionColumnNumber(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 < map.getSourceLine().size() && + (map.getSourceLine()[StartColNo] == ' ' || + map.getSourceLine()[StartColNo] == '\t')) + StartColNo = map.startOfNextColumn(StartColNo); + + // Pick the last non-whitespace column. + if (EndColNo > map.getSourceLine().size()) + EndColNo = map.getSourceLine().size(); + while (EndColNo && + (map.getSourceLine()[EndColNo-1] == ' ' || + map.getSourceLine()[EndColNo-1] == '\t')) + EndColNo = map.startOfPreviousColumn(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??"); + } + + assert(StartColNo <= map.getSourceLine().size() && "Invalid range!"); + assert(EndColNo <= map.getSourceLine().size() && "Invalid range!"); + + // Fill the range with ~'s. + StartColNo = map.byteToContainingColumn(StartColNo); + EndColNo = map.byteToContainingColumn(EndColNo); + + assert(StartColNo <= EndColNo && "Invalid range!"); + if (CaretLine.size() < EndColNo) + CaretLine.resize(EndColNo,' '); + std::fill(CaretLine.begin()+StartColNo,CaretLine.begin()+EndColNo,'~'); +} + +static std::string buildFixItInsertionLine(unsigned LineNo, + const SourceColumnMap &map, + ArrayRef<FixItHint> Hints, + const SourceManager &SM, + const DiagnosticOptions *DiagOpts) { + std::string FixItInsertionLine; + if (Hints.empty() || !DiagOpts->ShowFixits) + return FixItInsertionLine; + unsigned PrevHintEndCol = 0; + + for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end(); + I != E; ++I) { + if (!I->CodeToInsert.empty()) { + // We have an insertion hint. Determine whether the inserted + // code contains no newlines and is on the same line as the caret. + std::pair<FileID, unsigned> HintLocInfo + = SM.getDecomposedExpansionLoc(I->RemoveRange.getBegin()); + if (LineNo == SM.getLineNumber(HintLocInfo.first, HintLocInfo.second) && + StringRef(I->CodeToInsert).find_first_of("\n\r") == StringRef::npos) { + // Insert the new code into the line just below the code + // that the user wrote. + // Note: When modifying this function, be very careful about what is a + // "column" (printed width, platform-dependent) and what is a + // "byte offset" (SourceManager "column"). + unsigned HintByteOffset + = SM.getColumnNumber(HintLocInfo.first, HintLocInfo.second) - 1; + + // The hint must start inside the source or right at the end + assert(HintByteOffset < static_cast<unsigned>(map.bytes())+1); + unsigned HintCol = map.byteToContainingColumn(HintByteOffset); + + // If we inserted a long previous hint, push this one forwards, and add + // an extra space to show that this is not part of the previous + // completion. This is sort of the best we can do when two hints appear + // to overlap. + // + // Note that if this hint is located immediately after the previous + // hint, no space will be added, since the location is more important. + if (HintCol < PrevHintEndCol) + HintCol = PrevHintEndCol + 1; + + // This should NOT use HintByteOffset, because the source might have + // Unicode characters in earlier columns. + unsigned NewFixItLineSize = FixItInsertionLine.size() + + (HintCol - PrevHintEndCol) + I->CodeToInsert.size(); + if (NewFixItLineSize > FixItInsertionLine.size()) + FixItInsertionLine.resize(NewFixItLineSize, ' '); + + std::copy(I->CodeToInsert.begin(), I->CodeToInsert.end(), + FixItInsertionLine.end() - I->CodeToInsert.size()); + + PrevHintEndCol = + HintCol + llvm::sys::locale::columnWidth(I->CodeToInsert); + } else { + FixItInsertionLine.clear(); + break; + } + } + } + + expandTabs(FixItInsertionLine, DiagOpts->TabStop); + + return FixItInsertionLine; +} + +/// \brief Emit a code snippet and caret line. +/// +/// This routine emits a single line's code snippet and caret line.. +/// +/// \param Loc The location for the caret. +/// \param Ranges The underlined ranges for this code snippet. +/// \param Hints The FixIt hints active for this diagnostic. +void TextDiagnostic::emitSnippetAndCaret( + SourceLocation Loc, DiagnosticsEngine::Level Level, + SmallVectorImpl<CharSourceRange>& Ranges, + ArrayRef<FixItHint> Hints, + const SourceManager &SM) { + assert(!Loc.isInvalid() && "must have a valid source location here"); + assert(Loc.isFileID() && "must have a file location here"); + + // 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) + return; + if (Loc == LastLoc && Ranges.empty() && Hints.empty() && + (LastLevel != DiagnosticsEngine::Note || Level == LastLevel)) + 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 LineNo = SM.getLineNumber(FID, FileOffset); + unsigned ColNo = SM.getColumnNumber(FID, FileOffset); + + // Arbitrarily stop showing snippets when the line is too long. + static const size_t MaxLineLengthToPrint = 4096; + if (ColNo > MaxLineLengthToPrint) + return; + + // 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; + + // Arbitrarily stop showing snippets when the line is too long. + if (size_t(LineEnd - LineStart) > MaxLineLengthToPrint) + return; + + // 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, ' '); + + const SourceColumnMap sourceColMap(SourceLine, DiagOpts->TabStop); + + // Highlight all of the characters covered by Ranges with ~ characters. + for (SmallVectorImpl<CharSourceRange>::iterator I = Ranges.begin(), + E = Ranges.end(); + I != E; ++I) + highlightRange(*I, LineNo, FID, sourceColMap, CaretLine, SM, LangOpts); + + // Next, insert the caret itself. + ColNo = sourceColMap.byteToContainingColumn(ColNo-1); + if (CaretLine.size()<ColNo+1) + CaretLine.resize(ColNo+1, ' '); + CaretLine[ColNo] = '^'; + + std::string FixItInsertionLine = buildFixItInsertionLine(LineNo, + sourceColMap, + Hints, SM, + DiagOpts.getPtr()); + + // If the source line is too long for our terminal, select only the + // "interesting" source region within that line. + unsigned Columns = DiagOpts->MessageLength; + if (Columns) + selectInterestingSourceRegion(SourceLine, CaretLine, FixItInsertionLine, + Columns, sourceColMap); + + // 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; + } + + // 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. + emitSnippet(SourceLine); + + 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(); + } + + // Print out any parseable fixit information requested by the options. + emitParseableFixits(Hints, SM); +} + +void TextDiagnostic::emitSnippet(StringRef line) { + if (line.empty()) + return; + + size_t i = 0; + + std::string to_print; + bool print_reversed = false; + + while (i<line.size()) { + std::pair<SmallString<16>,bool> res + = printableTextForNextCharacter(line, &i, DiagOpts->TabStop); + bool was_printable = res.second; + + if (DiagOpts->ShowColors && was_printable == print_reversed) { + if (print_reversed) + OS.reverseColor(); + OS << to_print; + to_print.clear(); + if (DiagOpts->ShowColors) + OS.resetColor(); + } + + print_reversed = !was_printable; + to_print += res.first.str(); + } + + if (print_reversed && DiagOpts->ShowColors) + OS.reverseColor(); + OS << to_print; + if (print_reversed && DiagOpts->ShowColors) + OS.resetColor(); + + OS << '\n'; +} + +void TextDiagnostic::emitParseableFixits(ArrayRef<FixItHint> Hints, + const SourceManager &SM) { + if (!DiagOpts->ShowParseableFixits) + return; + + // We follow FixItRewriter's example in not (yet) handling + // fix-its in macros. + for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end(); + I != E; ++I) { + if (I->RemoveRange.isInvalid() || + I->RemoveRange.getBegin().isMacroID() || + I->RemoveRange.getEnd().isMacroID()) + return; + } + + for (ArrayRef<FixItHint>::iterator I = Hints.begin(), E = Hints.end(); + I != E; ++I) { + SourceLocation BLoc = I->RemoveRange.getBegin(); + SourceLocation ELoc = I->RemoveRange.getEnd(); + + std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(BLoc); + std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(ELoc); + + // Adjust for token ranges. + if (I->RemoveRange.isTokenRange()) + EInfo.second += Lexer::MeasureTokenLength(ELoc, SM, LangOpts); + + // We specifically do not do word-wrapping or tab-expansion here, + // because this is supposed to be easy to parse. + PresumedLoc PLoc = SM.getPresumedLoc(BLoc); + if (PLoc.isInvalid()) + break; + + OS << "fix-it:\""; + OS.write_escaped(PLoc.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(I->CodeToInsert); + OS << "\"\n"; + } +} 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..5821436 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/TextDiagnosticBuffer.cpp @@ -0,0 +1,77 @@ +//===--- 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" +#include "llvm/Support/ErrorHandling.h" +using namespace clang; + +/// HandleDiagnostic - Store the errors, warnings, and notes that are +/// reported. +/// +void TextDiagnosticBuffer::HandleDiagnostic(DiagnosticsEngine::Level Level, + const Diagnostic &Info) { + // Default implementation (Warnings/errors count). + DiagnosticConsumer::HandleDiagnostic(Level, Info); + + SmallString<100> Buf; + Info.FormatDiagnostic(Buf); + switch (Level) { + default: llvm_unreachable( + "Diagnostic not handled during diagnostic buffering!"); + case DiagnosticsEngine::Note: + Notes.push_back(std::make_pair(Info.getLocation(), Buf.str())); + break; + case DiagnosticsEngine::Warning: + Warnings.push_back(std::make_pair(Info.getLocation(), Buf.str())); + break; + case DiagnosticsEngine::Error: + case DiagnosticsEngine::Fatal: + Errors.push_back(std::make_pair(Info.getLocation(), Buf.str())); + break; + } +} + +/// \brief Escape diagnostic texts to avoid problems when they are fed into the +/// diagnostic formatter a second time. +static StringRef escapeDiag(StringRef Str, SmallVectorImpl<char> &Buf) { + size_t Pos = Str.find('%'); + if (Pos == StringRef::npos) + return Str; + + // We found a '%'. Replace this and all following '%' with '%%'. + Buf.clear(); + Buf.append(Str.data(), Str.data() + Pos); + for (size_t I = Pos, E = Str.size(); I != E; ++I) { + if (Str[I] == '%') + Buf.push_back('%'); + Buf.push_back(Str[I]); + } + + return StringRef(Buf.data(), Buf.size()); +} + +void TextDiagnosticBuffer::FlushDiagnostics(DiagnosticsEngine &Diags) const { + SmallVector<char, 64> Buf; + // FIXME: Flush the diagnostics in order. + for (const_iterator it = err_begin(), ie = err_end(); it != ie; ++it) + Diags.Report(Diags.getCustomDiagID(DiagnosticsEngine::Error, + escapeDiag(it->second, Buf))); + for (const_iterator it = warn_begin(), ie = warn_end(); it != ie; ++it) + Diags.Report(Diags.getCustomDiagID(DiagnosticsEngine::Warning, + escapeDiag(it->second, Buf))); + for (const_iterator it = note_begin(), ie = note_end(); it != ie; ++it) + Diags.Report(Diags.getCustomDiagID(DiagnosticsEngine::Note, + escapeDiag(it->second, Buf))); +} + 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..994a8f7 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/TextDiagnosticPrinter.cpp @@ -0,0 +1,158 @@ +//===--- 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/DiagnosticOptions.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Frontend/TextDiagnostic.h" +#include "clang/Lex/Lexer.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +using namespace clang; + +TextDiagnosticPrinter::TextDiagnosticPrinter(raw_ostream &os, + DiagnosticOptions *diags, + bool _OwnsOutputStream) + : OS(os), DiagOpts(diags), + OwnsOutputStream(_OwnsOutputStream) { +} + +TextDiagnosticPrinter::~TextDiagnosticPrinter() { + if (OwnsOutputStream) + delete &OS; +} + +void TextDiagnosticPrinter::BeginSourceFile(const LangOptions &LO, + const Preprocessor *PP) { + // Build the TextDiagnostic utility. + TextDiag.reset(new TextDiagnostic(OS, LO, &*DiagOpts)); +} + +void TextDiagnosticPrinter::EndSourceFile() { + TextDiag.reset(0); +} + +/// \brief Print any diagnostic option information to a raw_ostream. +/// +/// This implements all of the logic for adding diagnostic options to a message +/// (via OS). Each relevant option is comma separated and all are enclosed in +/// the standard bracketing: " [...]". +static void printDiagnosticOptions(raw_ostream &OS, + DiagnosticsEngine::Level Level, + const Diagnostic &Info, + const DiagnosticOptions &DiagOpts) { + bool Started = false; + if (DiagOpts.ShowOptionNames) { + // Handle special cases for non-warnings early. + if (Info.getID() == diag::fatal_too_many_errors) { + OS << " [-ferror-limit=]"; + return; + } + + // The code below is somewhat fragile because we are essentially trying to + // report to the user what happened by inferring what the diagnostic engine + // did. Eventually it might make more sense to have the diagnostic engine + // include some "why" information in the diagnostic. + + // If this is a warning which has been mapped to an error by the user (as + // inferred by checking whether the default mapping is to an error) then + // flag it as such. Note that diagnostics could also have been mapped by a + // pragma, but we don't currently have a way to distinguish this. + if (Level == DiagnosticsEngine::Error && + DiagnosticIDs::isBuiltinWarningOrExtension(Info.getID()) && + !DiagnosticIDs::isDefaultMappingAsError(Info.getID())) { + OS << " [-Werror"; + Started = true; + } + + StringRef Opt = DiagnosticIDs::getWarningOptionForDiag(Info.getID()); + if (!Opt.empty()) { + OS << (Started ? "," : " [") << "-W" << Opt; + Started = true; + } + } + + // If the user wants to see category information, include it too. + if (DiagOpts.ShowCategories) { + unsigned DiagCategory = + DiagnosticIDs::getCategoryNumberForDiag(Info.getID()); + if (DiagCategory) { + OS << (Started ? "," : " ["); + Started = true; + if (DiagOpts.ShowCategories == 1) + OS << DiagCategory; + else { + assert(DiagOpts.ShowCategories == 2 && "Invalid ShowCategories value"); + OS << DiagnosticIDs::getCategoryNameFromID(DiagCategory); + } + } + } + if (Started) + OS << ']'; +} + +void TextDiagnosticPrinter::HandleDiagnostic(DiagnosticsEngine::Level Level, + const Diagnostic &Info) { + // Default implementation (Warnings/errors count). + DiagnosticConsumer::HandleDiagnostic(Level, Info); + + // Render the diagnostic message into a temporary buffer eagerly. We'll use + // this later as we print out the diagnostic to the terminal. + SmallString<100> OutStr; + Info.FormatDiagnostic(OutStr); + + llvm::raw_svector_ostream DiagMessageStream(OutStr); + printDiagnosticOptions(DiagMessageStream, Level, Info, *DiagOpts); + + // Keeps track of 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 << ": "; + + // Use a dedicated, simpler path for diagnostics without a valid location. + // This is important as if the location is missing, we may be emitting + // diagnostics in a context that lacks language options, a source manager, or + // other infrastructure necessary when emitting more rich diagnostics. + if (!Info.getLocation().isValid()) { + TextDiagnostic::printDiagnosticLevel(OS, Level, DiagOpts->ShowColors, + DiagOpts->CLFallbackMode); + TextDiagnostic::printDiagnosticMessage(OS, Level, DiagMessageStream.str(), + OS.tell() - StartOfLocationInfo, + DiagOpts->MessageLength, + DiagOpts->ShowColors); + OS.flush(); + return; + } + + // Assert that the rest of our infrastructure is setup properly. + assert(DiagOpts && "Unexpected diagnostic without options set"); + assert(Info.hasSourceManager() && + "Unexpected diagnostic with no source manager"); + assert(TextDiag && "Unexpected diagnostic outside source file processing"); + + TextDiag->emitDiagnostic(Info.getLocation(), Level, DiagMessageStream.str(), + Info.getRanges(), + llvm::makeArrayRef(Info.getFixItHints(), + Info.getNumFixItHints()), + &Info.getSourceManager()); + + OS.flush(); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp b/contrib/llvm/tools/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp new file mode 100644 index 0000000..045e60a --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp @@ -0,0 +1,829 @@ +//===---- VerifyDiagnosticConsumer.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/VerifyDiagnosticConsumer.h" +#include "clang/Basic/CharInfo.h" +#include "clang/Basic/FileManager.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/TextDiagnosticBuffer.h" +#include "clang/Lex/HeaderSearch.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; +typedef VerifyDiagnosticConsumer::Directive Directive; +typedef VerifyDiagnosticConsumer::DirectiveList DirectiveList; +typedef VerifyDiagnosticConsumer::ExpectedData ExpectedData; + +VerifyDiagnosticConsumer::VerifyDiagnosticConsumer(DiagnosticsEngine &_Diags) + : Diags(_Diags), + PrimaryClient(Diags.getClient()), OwnsPrimaryClient(Diags.ownsClient()), + Buffer(new TextDiagnosticBuffer()), CurrentPreprocessor(0), + LangOpts(0), SrcManager(0), ActiveSourceFiles(0), Status(HasNoDirectives) +{ + Diags.takeClient(); + if (Diags.hasSourceManager()) + setSourceManager(Diags.getSourceManager()); +} + +VerifyDiagnosticConsumer::~VerifyDiagnosticConsumer() { + assert(!ActiveSourceFiles && "Incomplete parsing of source files!"); + assert(!CurrentPreprocessor && "CurrentPreprocessor should be invalid!"); + SrcManager = 0; + CheckDiagnostics(); + Diags.takeClient(); + if (OwnsPrimaryClient) + delete PrimaryClient; +} + +#ifndef NDEBUG +namespace { +class VerifyFileTracker : public PPCallbacks { + VerifyDiagnosticConsumer &Verify; + SourceManager &SM; + +public: + VerifyFileTracker(VerifyDiagnosticConsumer &Verify, SourceManager &SM) + : Verify(Verify), SM(SM) { } + + /// \brief Hook into the preprocessor and update the list of parsed + /// files when the preprocessor indicates a new file is entered. + virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, + FileID PrevFID) { + Verify.UpdateParsedFileStatus(SM, SM.getFileID(Loc), + VerifyDiagnosticConsumer::IsParsed); + } +}; +} // End anonymous namespace. +#endif + +// DiagnosticConsumer interface. + +void VerifyDiagnosticConsumer::BeginSourceFile(const LangOptions &LangOpts, + const Preprocessor *PP) { + // Attach comment handler on first invocation. + if (++ActiveSourceFiles == 1) { + if (PP) { + CurrentPreprocessor = PP; + this->LangOpts = &LangOpts; + setSourceManager(PP->getSourceManager()); + const_cast<Preprocessor*>(PP)->addCommentHandler(this); +#ifndef NDEBUG + // Debug build tracks parsed files. + VerifyFileTracker *V = new VerifyFileTracker(*this, *SrcManager); + const_cast<Preprocessor*>(PP)->addPPCallbacks(V); +#endif + } + } + + assert((!PP || CurrentPreprocessor == PP) && "Preprocessor changed!"); + PrimaryClient->BeginSourceFile(LangOpts, PP); +} + +void VerifyDiagnosticConsumer::EndSourceFile() { + assert(ActiveSourceFiles && "No active source files!"); + PrimaryClient->EndSourceFile(); + + // Detach comment handler once last active source file completed. + if (--ActiveSourceFiles == 0) { + if (CurrentPreprocessor) + const_cast<Preprocessor*>(CurrentPreprocessor)->removeCommentHandler(this); + + // Check diagnostics once last file completed. + CheckDiagnostics(); + CurrentPreprocessor = 0; + LangOpts = 0; + } +} + +void VerifyDiagnosticConsumer::HandleDiagnostic( + DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) { + if (Info.hasSourceManager()) { + // If this diagnostic is for a different source manager, ignore it. + if (SrcManager && &Info.getSourceManager() != SrcManager) + return; + + setSourceManager(Info.getSourceManager()); + } + +#ifndef NDEBUG + // Debug build tracks unparsed files for possible + // unparsed expected-* directives. + if (SrcManager) { + SourceLocation Loc = Info.getLocation(); + if (Loc.isValid()) { + ParsedStatus PS = IsUnparsed; + + Loc = SrcManager->getExpansionLoc(Loc); + FileID FID = SrcManager->getFileID(Loc); + + const FileEntry *FE = SrcManager->getFileEntryForID(FID); + if (FE && CurrentPreprocessor && SrcManager->isLoadedFileID(FID)) { + // If the file is a modules header file it shall not be parsed + // for expected-* directives. + HeaderSearch &HS = CurrentPreprocessor->getHeaderSearchInfo(); + if (HS.findModuleForHeader(FE)) + PS = IsUnparsedNoDirectives; + } + + UpdateParsedFileStatus(*SrcManager, FID, PS); + } + } +#endif + + // 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); +} + +//===----------------------------------------------------------------------===// +// Checking diagnostics implementation. +//===----------------------------------------------------------------------===// + +typedef TextDiagnosticBuffer::DiagList DiagList; +typedef TextDiagnosticBuffer::const_iterator const_diag_iterator; + +namespace { + +/// StandardDirective - Directive with string matching. +/// +class StandardDirective : public Directive { +public: + StandardDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc, + StringRef Text, unsigned Min, unsigned Max) + : Directive(DirectiveLoc, DiagnosticLoc, Text, Min, Max) { } + + virtual bool isValid(std::string &Error) { + // all strings are considered valid; even empty ones + return true; + } + + virtual bool match(StringRef S) { + return S.find(Text) != StringRef::npos; + } +}; + +/// RegexDirective - Directive with regular-expression matching. +/// +class RegexDirective : public Directive { +public: + RegexDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc, + StringRef Text, unsigned Min, unsigned Max) + : Directive(DirectiveLoc, DiagnosticLoc, Text, Min, Max), Regex(Text) { } + + virtual bool isValid(std::string &Error) { + if (Regex.isValid(Error)) + return true; + return false; + } + + virtual bool match(StringRef S) { + return Regex.match(S); + } + +private: + llvm::Regex Regex; +}; + +class ParseHelper +{ +public: + ParseHelper(StringRef S) + : Begin(S.begin()), End(S.end()), C(Begin), P(Begin), PEnd(NULL) { } + + // Return true if string literal is next. + bool Next(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(StringRef S, bool EnsureStartOfWord = false) { + do { + P = std::search(C, End, S.begin(), S.end()); + PEnd = P + S.size(); + if (P == End) + break; + if (!EnsureStartOfWord + // Check if string literal starts a new word. + || P == Begin || isWhitespace(P[-1]) + // Or it could be preceeded by the start of a comment. + || (P > (Begin + 1) && (P[-1] == '/' || P[-1] == '*') + && P[-2] == '/')) + return true; + // Otherwise, skip and search again. + } while (Advance()); + return false; + } + + // 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 && isWhitespace(*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. +/// +/// Returns true if any valid directives were found. +static bool ParseDirective(StringRef S, ExpectedData *ED, SourceManager &SM, + Preprocessor *PP, SourceLocation Pos, + VerifyDiagnosticConsumer::DirectiveStatus &Status) { + DiagnosticsEngine &Diags = PP ? PP->getDiagnostics() : SM.getDiagnostics(); + + // A single comment may contain multiple directives. + bool FoundDirective = false; + for (ParseHelper PH(S); !PH.Done();) { + // Search for token: expected + if (!PH.Search("expected", true)) + 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 ? &ED->Errors : NULL; + else if (PH.Next("warning")) + DL = ED ? &ED->Warnings : NULL; + else if (PH.Next("note")) + DL = ED ? &ED->Notes : NULL; + else if (PH.Next("no-diagnostics")) { + if (Status == VerifyDiagnosticConsumer::HasOtherExpectedDirectives) + Diags.Report(Pos, diag::err_verify_invalid_no_diags) + << /*IsExpectedNoDiagnostics=*/true; + else + Status = VerifyDiagnosticConsumer::HasExpectedNoDiagnostics; + continue; + } else + continue; + PH.Advance(); + + if (Status == VerifyDiagnosticConsumer::HasExpectedNoDiagnostics) { + Diags.Report(Pos, diag::err_verify_invalid_no_diags) + << /*IsExpectedNoDiagnostics=*/false; + continue; + } + Status = VerifyDiagnosticConsumer::HasOtherExpectedDirectives; + + // If a directive has been found but we're not interested + // in storing the directive information, return now. + if (!DL) + return true; + + // Default directive kind. + bool RegexKind = false; + const char* KindStr = "string"; + + // Next optional token: - + if (PH.Next("-re")) { + PH.Advance(); + RegexKind = true; + KindStr = "regex"; + } + + // Next optional token: @ + SourceLocation ExpectedLoc; + if (!PH.Next("@")) { + ExpectedLoc = Pos; + } else { + PH.Advance(); + unsigned Line = 0; + bool FoundPlus = PH.Next("+"); + if (FoundPlus || PH.Next("-")) { + // Relative to current line. + PH.Advance(); + bool Invalid = false; + unsigned ExpectedLine = SM.getSpellingLineNumber(Pos, &Invalid); + if (!Invalid && PH.Next(Line) && (FoundPlus || Line < ExpectedLine)) { + if (FoundPlus) ExpectedLine += Line; + else ExpectedLine -= Line; + ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), ExpectedLine, 1); + } + } else if (PH.Next(Line)) { + // Absolute line number. + if (Line > 0) + ExpectedLoc = SM.translateLineCol(SM.getFileID(Pos), Line, 1); + } else if (PP && PH.Search(":")) { + // Specific source file. + StringRef Filename(PH.C, PH.P-PH.C); + PH.Advance(); + + // Lookup file via Preprocessor, like a #include. + const DirectoryLookup *CurDir; + const FileEntry *FE = PP->LookupFile(Pos, Filename, false, NULL, CurDir, + NULL, NULL, 0); + if (!FE) { + Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), + diag::err_verify_missing_file) << Filename << KindStr; + continue; + } + + if (SM.translateFile(FE).isInvalid()) + SM.createFileID(FE, Pos, SrcMgr::C_User); + + if (PH.Next(Line) && Line > 0) + ExpectedLoc = SM.translateFileLineCol(FE, Line, 1); + } + + if (ExpectedLoc.isInvalid()) { + Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), + diag::err_verify_missing_line) << KindStr; + continue; + } + PH.Advance(); + } + + // Skip optional whitespace. + PH.SkipWhitespace(); + + // Next optional token: positive integer or a '+'. + unsigned Min = 1; + unsigned Max = 1; + if (PH.Next(Min)) { + PH.Advance(); + // A positive integer can be followed by a '+' meaning min + // or more, or by a '-' meaning a range from min to max. + if (PH.Next("+")) { + Max = Directive::MaxCount; + PH.Advance(); + } else if (PH.Next("-")) { + PH.Advance(); + if (!PH.Next(Max) || Max < Min) { + Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), + diag::err_verify_invalid_range) << KindStr; + continue; + } + PH.Advance(); + } else { + Max = Min; + } + } else if (PH.Next("+")) { + // '+' on its own means "1 or more". + Max = Directive::MaxCount; + PH.Advance(); + } + + // Skip optional whitespace. + PH.SkipWhitespace(); + + // Next token: {{ + if (!PH.Next("{{")) { + Diags.Report(Pos.getLocWithOffset(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("}}")) { + Diags.Report(Pos.getLocWithOffset(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; + StringRef NewlineStr = "\\n"; + StringRef Content(ContentBegin, ContentEnd-ContentBegin); + size_t CPos = 0; + size_t FPos; + while ((FPos = Content.find(NewlineStr, CPos)) != 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, ExpectedLoc, Text, + Min, Max); + std::string Error; + if (D->isValid(Error)) { + DL->push_back(D); + FoundDirective = true; + } else { + Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin), + diag::err_verify_invalid_content) + << KindStr << Error; + } + } + + return FoundDirective; +} + +/// HandleComment - Hook into the preprocessor and extract comments containing +/// expected errors and warnings. +bool VerifyDiagnosticConsumer::HandleComment(Preprocessor &PP, + SourceRange Comment) { + SourceManager &SM = PP.getSourceManager(); + + // If this comment is for a different source manager, ignore it. + if (SrcManager && &SM != SrcManager) + return false; + + SourceLocation CommentBegin = Comment.getBegin(); + + const char *CommentRaw = SM.getCharacterData(CommentBegin); + StringRef C(CommentRaw, SM.getCharacterData(Comment.getEnd()) - CommentRaw); + + if (C.empty()) + return false; + + // Fold any "\<EOL>" sequences + size_t loc = C.find('\\'); + if (loc == StringRef::npos) { + ParseDirective(C, &ED, SM, &PP, CommentBegin, Status); + return false; + } + + std::string C2; + C2.reserve(C.size()); + + for (size_t last = 0;; loc = C.find('\\', last)) { + if (loc == StringRef::npos || loc == C.size()) { + C2 += C.substr(last); + break; + } + C2 += C.substr(last, loc-last); + last = loc + 1; + + if (C[last] == '\n' || C[last] == '\r') { + ++last; + + // Escape \r\n or \n\r, but not \n\n. + if (last < C.size()) + if (C[last] == '\n' || C[last] == '\r') + if (C[last] != C[last-1]) + ++last; + } else { + // This was just a normal backslash. + C2 += '\\'; + } + } + + if (!C2.empty()) + ParseDirective(C2, &ED, SM, &PP, CommentBegin, Status); + return false; +} + +#ifndef NDEBUG +/// \brief Lex the specified source file to determine whether it contains +/// any expected-* directives. As a Lexer is used rather than a full-blown +/// Preprocessor, directives inside skipped #if blocks will still be found. +/// +/// \return true if any directives were found. +static bool findDirectives(SourceManager &SM, FileID FID, + const LangOptions &LangOpts) { + // Create a raw lexer to pull all the comments out of FID. + if (FID.isInvalid()) + return false; + + // 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, LangOpts); + + // Return comments as tokens, this is how we find expected diagnostics. + RawLex.SetCommentRetentionState(true); + + Token Tok; + Tok.setKind(tok::comment); + VerifyDiagnosticConsumer::DirectiveStatus Status = + VerifyDiagnosticConsumer::HasNoDirectives; + while (Tok.isNot(tok::eof)) { + RawLex.LexFromRawLexer(Tok); + if (!Tok.is(tok::comment)) continue; + + std::string Comment = RawLex.getSpelling(Tok, SM, LangOpts); + if (Comment.empty()) continue; + + // Find first directive. + if (ParseDirective(Comment, 0, SM, 0, Tok.getLocation(), Status)) + return true; + } + return false; +} +#endif // !NDEBUG + +/// \brief Takes a list of diagnostics that have been generated but not matched +/// by an expected-* directive and produces a diagnostic to the user from this. +static unsigned PrintUnexpected(DiagnosticsEngine &Diags, SourceManager *SourceMgr, + const_diag_iterator diag_begin, + const_diag_iterator diag_end, + const char *Kind) { + if (diag_begin == diag_end) return 0; + + 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 "; + if (const FileEntry *File = SourceMgr->getFileEntryForID( + SourceMgr->getFileID(I->first))) + OS << " File " << File->getName(); + OS << " Line " << SourceMgr->getPresumedLineNumber(I->first); + } + OS << ": " << I->second; + } + + Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit() + << Kind << /*Unexpected=*/true << OS.str(); + return std::distance(diag_begin, diag_end); +} + +/// \brief Takes a list of diagnostics that were expected to have been generated +/// but were not and produces a diagnostic to the user from this. +static unsigned PrintExpected(DiagnosticsEngine &Diags, SourceManager &SourceMgr, + DirectiveList &DL, const char *Kind) { + if (DL.empty()) + return 0; + + SmallString<256> Fmt; + llvm::raw_svector_ostream OS(Fmt); + for (DirectiveList::iterator I = DL.begin(), E = DL.end(); I != E; ++I) { + Directive &D = **I; + OS << "\n File " << SourceMgr.getFilename(D.DiagnosticLoc) + << " Line " << SourceMgr.getPresumedLineNumber(D.DiagnosticLoc); + if (D.DirectiveLoc != D.DiagnosticLoc) + OS << " (directive at " + << SourceMgr.getFilename(D.DirectiveLoc) << ':' + << SourceMgr.getPresumedLineNumber(D.DirectiveLoc) << ')'; + OS << ": " << D.Text; + } + + Diags.Report(diag::err_verify_inconsistent_diags).setForceEmit() + << Kind << /*Unexpected=*/false << OS.str(); + return DL.size(); +} + +/// \brief Determine whether two source locations come from the same file. +static bool IsFromSameFile(SourceManager &SM, SourceLocation DirectiveLoc, + SourceLocation DiagnosticLoc) { + while (DiagnosticLoc.isMacroID()) + DiagnosticLoc = SM.getImmediateMacroCallerLoc(DiagnosticLoc); + + if (SM.isWrittenInSameFile(DirectiveLoc, DiagnosticLoc)) + return true; + + const FileEntry *DiagFile = SM.getFileEntryForID(SM.getFileID(DiagnosticLoc)); + if (!DiagFile && SM.isWrittenInMainFile(DirectiveLoc)) + return true; + + return (DiagFile == SM.getFileEntryForID(SM.getFileID(DirectiveLoc))); +} + +/// CheckLists - Compare expected to seen diagnostic lists and return the +/// the difference between them. +/// +static unsigned CheckLists(DiagnosticsEngine &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.getPresumedLineNumber(D.DiagnosticLoc); + + for (unsigned i = 0; i < D.Max; ++i) { + DiagList::iterator II, IE; + for (II = Right.begin(), IE = Right.end(); II != IE; ++II) { + unsigned LineNo2 = SourceMgr.getPresumedLineNumber(II->first); + if (LineNo1 != LineNo2) + continue; + + if (!IsFromSameFile(SourceMgr, D.DiagnosticLoc, II->first)) + continue; + + const std::string &RightText = II->second; + if (D.match(RightText)) + break; + } + if (II == IE) { + // Not found. + if (i >= D.Min) break; + 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. + unsigned num = PrintExpected(Diags, SourceMgr, LeftOnly, Label); + num += PrintUnexpected(Diags, &SourceMgr, Right.begin(), Right.end(), Label); + return num; +} + +/// 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(DiagnosticsEngine &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 VerifyDiagnosticConsumer::UpdateParsedFileStatus(SourceManager &SM, + FileID FID, + ParsedStatus PS) { + // Check SourceManager hasn't changed. + setSourceManager(SM); + +#ifndef NDEBUG + if (FID.isInvalid()) + return; + + const FileEntry *FE = SM.getFileEntryForID(FID); + + if (PS == IsParsed) { + // Move the FileID from the unparsed set to the parsed set. + UnparsedFiles.erase(FID); + ParsedFiles.insert(std::make_pair(FID, FE)); + } else if (!ParsedFiles.count(FID) && !UnparsedFiles.count(FID)) { + // Add the FileID to the unparsed set if we haven't seen it before. + + // Check for directives. + bool FoundDirectives; + if (PS == IsUnparsedNoDirectives) + FoundDirectives = false; + else + FoundDirectives = !LangOpts || findDirectives(SM, FID, *LangOpts); + + // Add the FileID to the unparsed set. + UnparsedFiles.insert(std::make_pair(FID, + UnparsedFileStatus(FE, FoundDirectives))); + } +#endif +} + +void VerifyDiagnosticConsumer::CheckDiagnostics() { + // Ensure any diagnostics go to the primary client. + bool OwnsCurClient = Diags.ownsClient(); + DiagnosticConsumer *CurClient = Diags.takeClient(); + Diags.setClient(PrimaryClient, false); + +#ifndef NDEBUG + // In a debug build, scan through any files that may have been missed + // during parsing and issue a fatal error if directives are contained + // within these files. If a fatal error occurs, this suggests that + // this file is being parsed separately from the main file, in which + // case consider moving the directives to the correct place, if this + // is applicable. + if (UnparsedFiles.size() > 0) { + // Generate a cache of parsed FileEntry pointers for alias lookups. + llvm::SmallPtrSet<const FileEntry *, 8> ParsedFileCache; + for (ParsedFilesMap::iterator I = ParsedFiles.begin(), + End = ParsedFiles.end(); I != End; ++I) { + if (const FileEntry *FE = I->second) + ParsedFileCache.insert(FE); + } + + // Iterate through list of unparsed files. + for (UnparsedFilesMap::iterator I = UnparsedFiles.begin(), + End = UnparsedFiles.end(); I != End; ++I) { + const UnparsedFileStatus &Status = I->second; + const FileEntry *FE = Status.getFile(); + + // Skip files that have been parsed via an alias. + if (FE && ParsedFileCache.count(FE)) + continue; + + // Report a fatal error if this file contained directives. + if (Status.foundDirectives()) { + llvm::report_fatal_error(Twine("-verify directives found after rather" + " than during normal parsing of ", + StringRef(FE ? FE->getName() : "(unknown)"))); + } + } + + // UnparsedFiles has been processed now, so clear it. + UnparsedFiles.clear(); + } +#endif // !NDEBUG + + if (SrcManager) { + // Produce an error if no expected-* directives could be found in the + // source file(s) processed. + if (Status == HasNoDirectives) { + Diags.Report(diag::err_verify_no_directives).setForceEmit(); + ++NumErrors; + Status = HasNoDirectivesReported; + } + + // Check that the expected diagnostics occurred. + NumErrors += CheckResults(Diags, *SrcManager, *Buffer, ED); + } else { + NumErrors += (PrintUnexpected(Diags, 0, Buffer->err_begin(), + Buffer->err_end(), "error") + + PrintUnexpected(Diags, 0, Buffer->warn_begin(), + Buffer->warn_end(), "warn") + + PrintUnexpected(Diags, 0, Buffer->note_begin(), + Buffer->note_end(), "note")); + } + + Diags.takeClient(); + Diags.setClient(CurClient, OwnsCurClient); + + // Reset the buffer, we have processed all the diagnostics in it. + Buffer.reset(new TextDiagnosticBuffer()); + ED.Errors.clear(); + ED.Warnings.clear(); + ED.Notes.clear(); +} + +Directive *Directive::create(bool RegexKind, SourceLocation DirectiveLoc, + SourceLocation DiagnosticLoc, StringRef Text, + unsigned Min, unsigned Max) { + if (RegexKind) + return new RegexDirective(DirectiveLoc, DiagnosticLoc, Text, Min, Max); + return new StandardDirective(DirectiveLoc, DiagnosticLoc, Text, Min, Max); +} 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..767096a --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/Warnings.cpp @@ -0,0 +1,203 @@ +//===--- 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/Basic/DiagnosticOptions.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Lex/LexDiagnostic.h" +#include "clang/Sema/SemaDiagnostic.h" +#include <algorithm> +#include <cstring> +#include <utility> +using namespace clang; + +// EmitUnknownDiagWarning - Emit a warning and typo hint for unknown warning +// opts +static void EmitUnknownDiagWarning(DiagnosticsEngine &Diags, + StringRef Prefix, StringRef Opt, + bool isPositive) { + StringRef Suggestion = DiagnosticIDs::getNearestWarningOption(Opt); + if (!Suggestion.empty()) + Diags.Report(isPositive? diag::warn_unknown_warning_option_suggest : + diag::warn_unknown_negative_warning_option_suggest) + << (Prefix.str() += Opt) << (Prefix.str() += Suggestion); + else + Diags.Report(isPositive? diag::warn_unknown_warning_option : + diag::warn_unknown_negative_warning_option) + << (Prefix.str() += Opt); +} + +void clang::ProcessWarningOptions(DiagnosticsEngine &Diags, + const DiagnosticOptions &Opts, + bool ReportDiags) { + Diags.setSuppressSystemWarnings(true); // Default to -Wno-system-headers + Diags.setIgnoreAllWarnings(Opts.IgnoreWarnings); + Diags.setShowOverloads(Opts.getShowOverloads()); + + Diags.setElideType(Opts.ElideType); + Diags.setPrintTemplateTree(Opts.ShowTemplateTree); + Diags.setShowColors(Opts.ShowColors); + + // Handle -ferror-limit + if (Opts.ErrorLimit) + Diags.setErrorLimit(Opts.ErrorLimit); + if (Opts.TemplateBacktraceLimit) + Diags.setTemplateBacktraceLimit(Opts.TemplateBacktraceLimit); + if (Opts.ConstexprBacktraceLimit) + Diags.setConstexprBacktraceLimit(Opts.ConstexprBacktraceLimit); + + // 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(DiagnosticsEngine::Ext_Error); + else if (Opts.Pedantic) + Diags.setExtensionHandlingBehavior(DiagnosticsEngine::Ext_Warn); + else + Diags.setExtensionHandlingBehavior(DiagnosticsEngine::Ext_Ignore); + + SmallVector<diag::kind, 10> _Diags; + const IntrusiveRefCntPtr< DiagnosticIDs > DiagIDs = + Diags.getDiagnosticIDs(); + // We parse the warning options twice. The first pass sets diagnostic state, + // while the second pass reports warnings/errors. This has the effect that + // we follow the more canonical "last option wins" paradigm when there are + // conflicting options. + for (unsigned Report = 0, ReportEnd = 2; Report != ReportEnd; ++Report) { + bool SetDiagnostic = (Report == 0); + + // If we've set the diagnostic state and are not reporting diagnostics then + // we're done. + if (!SetDiagnostic && !ReportDiags) + break; + + for (unsigned i = 0, e = Opts.Warnings.size(); i != e; ++i) { + StringRef Opt = Opts.Warnings[i]; + StringRef OrigOpt = Opts.Warnings[i]; + + // Treat -Wformat=0 as an alias for -Wno-format. + if (Opt == "format=0") + Opt = "no-format"; + + // Check to see if this warning starts with "no-", if so, this is a + // negative form of the option. + bool isPositive = true; + if (Opt.startswith("no-")) { + isPositive = false; + Opt = Opt.substr(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 (Opt == "system-headers") { + if (SetDiagnostic) + Diags.setSuppressSystemWarnings(!isPositive); + continue; + } + + // -Weverything is a special case as well. It implicitly enables all + // warnings, including ones not explicitly in a warning group. + if (Opt == "everything") { + if (SetDiagnostic) { + if (isPositive) { + Diags.setEnableAllWarnings(true); + } else { + Diags.setEnableAllWarnings(false); + Diags.setMappingToAllDiagnostics(diag::MAP_IGNORE); + } + } + 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 (Opt.startswith("error")) { + StringRef Specifier; + if (Opt.size() > 5) { // Specifier must be present. + if ((Opt[5] != '=' && Opt[5] != '-') || Opt.size() == 6) { + if (Report) + Diags.Report(diag::warn_unknown_warning_specifier) + << "-Werror" << ("-W" + OrigOpt.str()); + continue; + } + Specifier = Opt.substr(6); + } + + if (Specifier.empty()) { + if (SetDiagnostic) + Diags.setWarningsAsErrors(isPositive); + continue; + } + + if (SetDiagnostic) { + // Set the warning as error flag for this specifier. + Diags.setDiagnosticGroupWarningAsError(Specifier, isPositive); + } else if (DiagIDs->getDiagnosticsInGroup(Specifier, _Diags)) { + EmitUnknownDiagWarning(Diags, "-Werror=", Specifier, isPositive); + } + continue; + } + + // -Wfatal-errors is yet another special case. + if (Opt.startswith("fatal-errors")) { + StringRef Specifier; + if (Opt.size() != 12) { + if ((Opt[12] != '=' && Opt[12] != '-') || Opt.size() == 13) { + if (Report) + Diags.Report(diag::warn_unknown_warning_specifier) + << "-Wfatal-errors" << ("-W" + OrigOpt.str()); + continue; + } + Specifier = Opt.substr(13); + } + + if (Specifier.empty()) { + if (SetDiagnostic) + Diags.setErrorsAsFatal(isPositive); + continue; + } + + if (SetDiagnostic) { + // Set the error as fatal flag for this specifier. + Diags.setDiagnosticGroupErrorAsFatal(Specifier, isPositive); + } else if (DiagIDs->getDiagnosticsInGroup(Specifier, _Diags)) { + EmitUnknownDiagWarning(Diags, "-Wfatal-errors=", Specifier, + isPositive); + } + continue; + } + + if (Report) { + if (DiagIDs->getDiagnosticsInGroup(Opt, _Diags)) + EmitUnknownDiagWarning(Diags, isPositive ? "-W" : "-Wno-", Opt, + isPositive); + } else { + Diags.setDiagnosticGroupMapping(Opt, Mapping); + } + } + } +} |