diff options
Diffstat (limited to 'contrib/llvm/tools/clang/lib/Frontend')
41 files changed, 35970 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 000000000000..28834a2de8a2 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/ASTConsumers.cpp @@ -0,0 +1,195 @@ +//===--- 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/SourceManager.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: + enum Kind { DumpFull, Dump, Print, None }; + ASTPrinter(std::unique_ptr<raw_ostream> Out, Kind K, StringRef FilterString, + bool DumpLookups = false) + : Out(Out ? *Out : llvm::outs()), OwnedOut(std::move(Out)), + OutputKind(K), FilterString(FilterString), DumpLookups(DumpLookups) {} + + void HandleTranslationUnit(ASTContext &Context) override { + TranslationUnitDecl *D = Context.getTranslationUnitDecl(); + + if (FilterString.empty()) + return print(D); + + TraverseDecl(D); + } + + bool shouldWalkTypesOfTypeLocs() const { return false; } + + bool TraverseDecl(Decl *D) { + if (D && filterMatches(D)) { + bool ShowColors = Out.has_colors(); + if (ShowColors) + Out.changeColor(raw_ostream::BLUE); + Out << (OutputKind != Print ? "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)) { + if (DC == DC->getPrimaryContext()) + DC->dumpLookups(Out, OutputKind != None, OutputKind == DumpFull); + else + Out << "Lookup map is in primary DeclContext " + << DC->getPrimaryContext() << "\n"; + } else + Out << "Not a DeclContext\n"; + } else if (OutputKind == Print) { + PrintingPolicy Policy(D->getASTContext().getLangOpts()); + D->print(Out, Policy, /*Indentation=*/0, /*PrintInstantiation=*/true); + } else if (OutputKind != None) + D->dump(Out, OutputKind == DumpFull); + } + + raw_ostream &Out; + std::unique_ptr<raw_ostream> OwnedOut; + + /// How to output individual declarations. + Kind OutputKind; + + /// Which declarations or DeclContexts to display. + std::string FilterString; + + /// Whether the primary output is lookup results or declarations. Individual + /// results will be output with a format determined by OutputKind. This is + /// incompatible with OutputKind == Print. + bool DumpLookups; + }; + + class ASTDeclNodeLister : public ASTConsumer, + public RecursiveASTVisitor<ASTDeclNodeLister> { + public: + ASTDeclNodeLister(raw_ostream *Out = nullptr) + : Out(Out ? *Out : llvm::outs()) {} + + void HandleTranslationUnit(ASTContext &Context) override { + TraverseDecl(Context.getTranslationUnitDecl()); + } + + bool shouldWalkTypesOfTypeLocs() const { return false; } + + bool VisitNamedDecl(NamedDecl *D) { + D->printQualifiedName(Out); + Out << '\n'; + return true; + } + + private: + raw_ostream &Out; + }; +} // end anonymous namespace + +std::unique_ptr<ASTConsumer> +clang::CreateASTPrinter(std::unique_ptr<raw_ostream> Out, + StringRef FilterString) { + return llvm::make_unique<ASTPrinter>(std::move(Out), ASTPrinter::Print, + FilterString); +} + +std::unique_ptr<ASTConsumer> +clang::CreateASTDumper(std::unique_ptr<raw_ostream> Out, + StringRef FilterString, + bool DumpDecls, + bool Deserialize, + bool DumpLookups) { + assert((DumpDecls || Deserialize || DumpLookups) && "nothing to dump"); + return llvm::make_unique<ASTPrinter>(std::move(Out), + Deserialize ? ASTPrinter::DumpFull : + DumpDecls ? ASTPrinter::Dump : + ASTPrinter::None, + FilterString, DumpLookups); +} + +std::unique_ptr<ASTConsumer> clang::CreateASTDeclNodeLister() { + return llvm::make_unique<ASTDeclNodeLister>(nullptr); +} + +//===----------------------------------------------------------------------===// +/// ASTViewer - AST Visualization + +namespace { + class ASTViewer : public ASTConsumer { + ASTContext *Context; + public: + void Initialize(ASTContext &Context) override { + this->Context = &Context; + } + + bool HandleTopLevelDecl(DeclGroupRef D) override { + 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'; + } + } +} + +std::unique_ptr<ASTConsumer> clang::CreateASTViewer() { + return llvm::make_unique<ASTViewer>(); +} 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 000000000000..4f622da118c5 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/ASTMerge.cpp @@ -0,0 +1,113 @@ +//===-- ASTMerge.cpp - AST Merging Frontend 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/AST/ASTImporterLookupTable.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" + +using namespace clang; + +std::unique_ptr<ASTConsumer> +ASTMergeAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + return AdaptedAction->CreateASTConsumer(CI, InFile); +} + +bool ASTMergeAction::BeginSourceFileAction(CompilerInstance &CI) { + // 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); +} + +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()); + ASTImporterLookupTable LookupTable( + *CI.getASTContext().getTranslationUnitDecl()); + 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)); + std::unique_ptr<ASTUnit> Unit = ASTUnit::LoadFromASTFile( + ASTFiles[I], CI.getPCHContainerReader(), ASTUnit::LoadEverything, Diags, + CI.getFileSystemOpts(), false); + + if (!Unit) + continue; + + ASTImporter Importer(CI.getASTContext(), CI.getFileManager(), + Unit->getASTContext(), Unit->getFileManager(), + /*MinimalImport=*/false, &LookupTable); + + TranslationUnitDecl *TU = Unit->getASTContext().getTranslationUnitDecl(); + for (auto *D : TU->decls()) { + // Don't re-import __va_list_tag, __builtin_va_list. + if (const auto *ND = dyn_cast<NamedDecl>(D)) + if (IdentifierInfo *II = ND->getIdentifier()) + if (II->isStr("__va_list_tag") || II->isStr("__builtin_va_list")) + continue; + + Decl *ToD = Importer.Import(D); + + if (ToD) { + DeclGroupRef DGR(ToD); + CI.getASTConsumer().HandleTopLevelDecl(DGR); + } + } + } + + AdaptedAction->ExecuteAction(); + CI.getDiagnostics().getClient()->EndSourceFile(); +} + +void ASTMergeAction::EndSourceFileAction() { + return AdaptedAction->EndSourceFileAction(); +} + +ASTMergeAction::ASTMergeAction(std::unique_ptr<FrontendAction> adaptedAction, + ArrayRef<std::string> ASTFiles) +: AdaptedAction(std::move(adaptedAction)), ASTFiles(ASTFiles.begin(), ASTFiles.end()) { + assert(AdaptedAction && "ASTMergeAction needs an action to adapt"); +} + +ASTMergeAction::~ASTMergeAction() { +} + +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 000000000000..c7b2551cb8d7 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/ASTUnit.cpp @@ -0,0 +1,2690 @@ +//===- 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/CommentCommandTraits.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclBase.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclGroup.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/DeclarationName.h" +#include "clang/AST/ExternalASTSource.h" +#include "clang/AST/PrettyPrinter.h" +#include "clang/AST/Type.h" +#include "clang/AST/TypeOrdering.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/MemoryBufferCache.h" +#include "clang/Basic/Module.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Basic/TargetOptions.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/CompilerInvocation.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/FrontendOptions.h" +#include "clang/Frontend/MultiplexConsumer.h" +#include "clang/Frontend/PrecompiledPreamble.h" +#include "clang/Frontend/Utils.h" +#include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/HeaderSearchOptions.h" +#include "clang/Lex/Lexer.h" +#include "clang/Lex/PPCallbacks.h" +#include "clang/Lex/PreprocessingRecord.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Lex/PreprocessorOptions.h" +#include "clang/Lex/Token.h" +#include "clang/Sema/CodeCompleteConsumer.h" +#include "clang/Sema/CodeCompleteOptions.h" +#include "clang/Sema/Sema.h" +#include "clang/Serialization/ASTBitCodes.h" +#include "clang/Serialization/ASTReader.h" +#include "clang/Serialization/ASTWriter.h" +#include "clang/Serialization/ContinuousRangeMap.h" +#include "clang/Serialization/Module.h" +#include "clang/Serialization/PCHContainerOperations.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/ADT/Twine.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/Bitcode/BitstreamWriter.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/CrashRecoveryContext.h" +#include "llvm/Support/DJB.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Mutex.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/VirtualFileSystem.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <atomic> +#include <cassert> +#include <cstdint> +#include <cstdio> +#include <cstdlib> +#include <memory> +#include <string> +#include <tuple> +#include <utility> +#include <vector> + +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(); + } + + ~SimpleTimer() { + if (WantTiming) { + TimeRecord Elapsed = TimeRecord::getCurrentTime(); + Elapsed -= Start; + llvm::errs() << Output << ':'; + Elapsed.print(Elapsed, llvm::errs()); + llvm::errs() << '\n'; + } + } + + void setOutput(const Twine &Output) { + if (WantTiming) + this->Output = Output.str(); + } + }; + +} // namespace + +template <class T> +static std::unique_ptr<T> valueOrNull(llvm::ErrorOr<std::unique_ptr<T>> Val) { + if (!Val) + return nullptr; + return std::move(*Val); +} + +template <class T> +static bool moveOnNoError(llvm::ErrorOr<T> Val, T &Output) { + if (!Val) + return false; + Output = std::move(*Val); + return true; +} + +/// Get a source buffer for \p MainFilePath, handling all file-to-file +/// and file-to-buffer remappings inside \p Invocation. +static std::unique_ptr<llvm::MemoryBuffer> +getBufferForFileHandlingRemapping(const CompilerInvocation &Invocation, + llvm::vfs::FileSystem *VFS, + StringRef FilePath, bool isVolatile) { + const auto &PreprocessorOpts = Invocation.getPreprocessorOpts(); + + // 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 = nullptr; + std::unique_ptr<llvm::MemoryBuffer> BufferOwner; + auto FileStatus = VFS->status(FilePath); + if (FileStatus) { + llvm::sys::fs::UniqueID MainFileID = FileStatus->getUniqueID(); + + // Check whether there is a file-file remapping of the main file + for (const auto &RF : PreprocessorOpts.RemappedFiles) { + std::string MPath(RF.first); + auto MPathStatus = VFS->status(MPath); + if (MPathStatus) { + llvm::sys::fs::UniqueID MID = MPathStatus->getUniqueID(); + if (MainFileID == MID) { + // We found a remapping. Try to load the resulting, remapped source. + BufferOwner = valueOrNull(VFS->getBufferForFile(RF.second, -1, true, isVolatile)); + if (!BufferOwner) + return nullptr; + } + } + } + + // Check whether there is a file-buffer remapping. It supercedes the + // file-file remapping. + for (const auto &RB : PreprocessorOpts.RemappedFileBuffers) { + std::string MPath(RB.first); + auto MPathStatus = VFS->status(MPath); + if (MPathStatus) { + llvm::sys::fs::UniqueID MID = MPathStatus->getUniqueID(); + if (MainFileID == MID) { + // We found a remapping. + BufferOwner.reset(); + Buffer = const_cast<llvm::MemoryBuffer *>(RB.second); + } + } + } + } + + // If the main source file was not remapped, load it now. + if (!Buffer && !BufferOwner) { + BufferOwner = valueOrNull(VFS->getBufferForFile(FilePath, -1, true, isVolatile)); + if (!BufferOwner) + return nullptr; + } + + if (BufferOwner) + return BufferOwner; + if (!Buffer) + return nullptr; + return llvm::MemoryBuffer::getMemBufferCopy(Buffer->getBuffer(), FilePath); +} + +struct ASTUnit::ASTWriterData { + SmallString<128> Buffer; + llvm::BitstreamWriter Stream; + ASTWriter Writer; + + ASTWriterData(MemoryBufferCache &PCMCache) + : Stream(Buffer), Writer(Stream, Buffer, PCMCache, {}) {} +}; + +void ASTUnit::clearFileLevelDecls() { + llvm::DeleteContainerSeconds(FileDecls); +} + +/// 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; + +/// Tracks the number of ASTUnit objects that are currently active. +/// +/// Used for debugging purposes only. +static std::atomic<unsigned> ActiveASTUnitObjects; + +ASTUnit::ASTUnit(bool _MainFileIsAST) + : MainFileIsAST(_MainFileIsAST), WantTiming(getenv("LIBCLANG_TIMING")), + ShouldCacheCodeCompletionResults(false), + IncludeBriefCommentsInCodeCompletion(false), UserFilesAreVolatile(false), + UnsafeToFree(false) { + if (getenv("LIBCLANG_OBJTRACKING")) + fprintf(stderr, "+++ %u 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(); + + // 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 && OwnsRemappedFileBuffers) { + PreprocessorOptions &PPOpts = Invocation->getPreprocessorOpts(); + for (const auto &RB : PPOpts.RemappedFileBuffers) + delete RB.second; + } + + ClearCachedCompletionResults(); + + if (getenv("LIBCLANG_OBJTRACKING")) + fprintf(stderr, "--- %u translation units\n", --ActiveASTUnitObjects); +} + +void ASTUnit::setPreprocessor(std::shared_ptr<Preprocessor> PP) { + this->PP = std::move(PP); +} + +void ASTUnit::enableSourceFileDiagnostics() { + assert(getDiagnostics().getClient() && Ctx && + "Bad context for source file"); + getDiagnostics().getClient()->BeginSourceFile(Ctx->getLangOpts(), PP.get()); +} + +/// Determine the set of code-completion contexts in which this +/// declaration should be shown. +static uint64_t getDeclShowContexts(const NamedDecl *ND, + const LangOptions &LangOpts, + bool &IsNestedNameSpecifier) { + IsNestedNameSpecifier = false; + + if (isa<UsingShadowDecl>(ND)) + ND = ND->getUnderlyingDecl(); + if (!ND) + return 0; + + uint64_t Contexts = 0; + if (isa<TypeDecl>(ND) || isa<ObjCInterfaceDecl>(ND) || + isa<ClassTemplateDecl>(ND) || isa<TemplateTemplateParmDecl>(ND) || + isa<TypeAliasTemplateDecl>(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 (const auto *ID = dyn_cast<ObjCInterfaceDecl>(ND)) { + // Objective-C interfaces can be used in a class property expression. + if (ID->getDefinition()) + Contexts |= (1LL << CodeCompletionContext::CCC_Expression); + 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 auto *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. + using Result = CodeCompletionResult; + SmallVector<Result, 8> Results; + CachedCompletionAllocator = std::make_shared<GlobalCodeCompletionAllocator>(); + CodeCompletionTUInfo CCTUInfo(CachedCompletionAllocator); + TheSema->GatherGlobalCodeCompletions(*CachedCompletionAllocator, + CCTUInfo, Results); + + // Translate global code completions into cached completions. + llvm::DenseMap<CanQualType, unsigned> CompletionTypes; + CodeCompletionContext CCContext(CodeCompletionContext::CCC_TopLevel); + + for (auto &R : Results) { + switch (R.Kind) { + case Result::RK_Declaration: { + bool IsNestedNameSpecifier = false; + CachedCodeCompletionResult CachedResult; + CachedResult.Completion = R.CreateCodeCompletionString( + *TheSema, CCContext, *CachedCompletionAllocator, CCTUInfo, + IncludeBriefCommentsInCodeCompletion); + CachedResult.ShowInContexts = getDeclShowContexts( + R.Declaration, Ctx->getLangOpts(), IsNestedNameSpecifier); + CachedResult.Priority = R.Priority; + CachedResult.Kind = R.CursorKind; + CachedResult.Availability = R.Availability; + + // Keep track of the type of this completion in an ASTContext-agnostic + // way. + QualType UsageType = getDeclUsageType(*Ctx, R.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 && + !R.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_Symbol) + | (1LL << CodeCompletionContext::CCC_SymbolOrNewName) + | (1LL << CodeCompletionContext::CCC_ParenthesizedExpression); + + if (isa<NamespaceDecl>(R.Declaration) || + isa<NamespaceAliasDecl>(R.Declaration)) + NNSContexts |= (1LL << CodeCompletionContext::CCC_Namespace); + + if (uint64_t 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. + R.StartsNestedNameSpecifier = true; + CachedResult.Completion = R.CreateCodeCompletionString( + *TheSema, CCContext, *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 = R.CreateCodeCompletionString( + *TheSema, CCContext, *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 = R.Priority; + CachedResult.Kind = R.CursorKind; + CachedResult.Availability = R.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 = nullptr; +} + +namespace { + +/// Gathers information from ASTReader that will be used to initialize +/// a Preprocessor. +class ASTInfoCollector : public ASTReaderListener { + Preprocessor &PP; + ASTContext *Context; + HeaderSearchOptions &HSOpts; + PreprocessorOptions &PPOpts; + LangOptions &LangOpt; + std::shared_ptr<TargetOptions> &TargetOpts; + IntrusiveRefCntPtr<TargetInfo> &Target; + unsigned &Counter; + bool InitializedLanguage = false; + +public: + ASTInfoCollector(Preprocessor &PP, ASTContext *Context, + HeaderSearchOptions &HSOpts, PreprocessorOptions &PPOpts, + LangOptions &LangOpt, + std::shared_ptr<TargetOptions> &TargetOpts, + IntrusiveRefCntPtr<TargetInfo> &Target, unsigned &Counter) + : PP(PP), Context(Context), HSOpts(HSOpts), PPOpts(PPOpts), + LangOpt(LangOpt), TargetOpts(TargetOpts), Target(Target), + Counter(Counter) {} + + bool ReadLanguageOptions(const LangOptions &LangOpts, bool Complain, + bool AllowCompatibleDifferences) override { + if (InitializedLanguage) + return false; + + LangOpt = LangOpts; + InitializedLanguage = true; + + updated(); + return false; + } + + bool ReadHeaderSearchOptions(const HeaderSearchOptions &HSOpts, + StringRef SpecificModuleCachePath, + bool Complain) override { + this->HSOpts = HSOpts; + return false; + } + + bool ReadPreprocessorOptions(const PreprocessorOptions &PPOpts, bool Complain, + std::string &SuggestedPredefines) override { + this->PPOpts = PPOpts; + return false; + } + + bool ReadTargetOptions(const TargetOptions &TargetOpts, bool Complain, + bool AllowCompatibleDifferences) override { + // If we've already initialized the target, don't do it again. + if (Target) + return false; + + this->TargetOpts = std::make_shared<TargetOptions>(TargetOpts); + Target = + TargetInfo::CreateTargetInfo(PP.getDiagnostics(), this->TargetOpts); + + updated(); + return false; + } + + void ReadCounter(const serialization::ModuleFile &M, + unsigned Value) override { + 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->adjust(LangOpt); + + // Initialize the preprocessor. + PP.Initialize(*Target); + + if (!Context) + return; + + // Initialize the ASTContext + Context->InitBuiltinTypes(*Target); + + // Adjust printing policy based on language options. + Context->setPrintingPolicy(PrintingPolicy(LangOpt)); + + // We didn't have access to the comment options when the ASTContext was + // constructed, so register them now. + Context->getCommentCommandTraits().registerCommentOptions( + LangOpt.CommentOpts); + } +}; + +/// Diagnostic consumer that saves each diagnostic it is given. +class StoredDiagnosticConsumer : public DiagnosticConsumer { + SmallVectorImpl<StoredDiagnostic> *StoredDiags; + SmallVectorImpl<ASTUnit::StandaloneDiagnostic> *StandaloneDiags; + const LangOptions *LangOpts = nullptr; + SourceManager *SourceMgr = nullptr; + +public: + StoredDiagnosticConsumer( + SmallVectorImpl<StoredDiagnostic> *StoredDiags, + SmallVectorImpl<ASTUnit::StandaloneDiagnostic> *StandaloneDiags) + : StoredDiags(StoredDiags), StandaloneDiags(StandaloneDiags) { + assert((StoredDiags || StandaloneDiags) && + "No output collections were passed to StoredDiagnosticConsumer."); + } + + void BeginSourceFile(const LangOptions &LangOpts, + const Preprocessor *PP = nullptr) override { + this->LangOpts = &LangOpts; + if (PP) + SourceMgr = &PP->getSourceManager(); + } + + void HandleDiagnostic(DiagnosticsEngine::Level Level, + const Diagnostic &Info) override; +}; + +/// RAII object that optionally captures diagnostics, if +/// there is no diagnostic client to capture them already. +class CaptureDroppedDiagnostics { + DiagnosticsEngine &Diags; + StoredDiagnosticConsumer Client; + DiagnosticConsumer *PreviousClient = nullptr; + std::unique_ptr<DiagnosticConsumer> OwningPreviousClient; + +public: + CaptureDroppedDiagnostics( + bool RequestCapture, DiagnosticsEngine &Diags, + SmallVectorImpl<StoredDiagnostic> *StoredDiags, + SmallVectorImpl<ASTUnit::StandaloneDiagnostic> *StandaloneDiags) + : Diags(Diags), Client(StoredDiags, StandaloneDiags) { + if (RequestCapture || Diags.getClient() == nullptr) { + OwningPreviousClient = Diags.takeClient(); + PreviousClient = Diags.getClient(); + Diags.setClient(&Client, false); + } + } + + ~CaptureDroppedDiagnostics() { + if (Diags.getClient() == &Client) + Diags.setClient(PreviousClient, !!OwningPreviousClient.release()); + } +}; + +} // namespace + +static ASTUnit::StandaloneDiagnostic +makeStandaloneDiagnostic(const LangOptions &LangOpts, + const StoredDiagnostic &InDiag); + +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) { + StoredDiagnostic *ResultDiag = nullptr; + if (StoredDiags) { + StoredDiags->emplace_back(Level, Info); + ResultDiag = &StoredDiags->back(); + } + + if (StandaloneDiags) { + llvm::Optional<StoredDiagnostic> StoredDiag = None; + if (!ResultDiag) { + StoredDiag.emplace(Level, Info); + ResultDiag = StoredDiag.getPointer(); + } + StandaloneDiags->push_back( + makeStandaloneDiagnostic(*LangOpts, *ResultDiag)); + } + } +} + +IntrusiveRefCntPtr<ASTReader> ASTUnit::getASTReader() const { + return Reader; +} + +ASTMutationListener *ASTUnit::getASTMutationListener() { + if (WriterData) + return &WriterData->Writer; + return nullptr; +} + +ASTDeserializationListener *ASTUnit::getDeserializationListener() { + if (WriterData) + return &WriterData->Writer; + return nullptr; +} + +std::unique_ptr<llvm::MemoryBuffer> +ASTUnit::getBufferForFile(StringRef Filename, std::string *ErrorStr) { + assert(FileMgr); + auto Buffer = FileMgr->getBufferForFile(Filename, UserFilesAreVolatile); + if (Buffer) + return std::move(*Buffer); + if (ErrorStr) + *ErrorStr = Buffer.getError().message(); + return nullptr; +} + +/// Configure the diagnostics object for use with ASTUnit. +void ASTUnit::ConfigureDiags(IntrusiveRefCntPtr<DiagnosticsEngine> Diags, + ASTUnit &AST, bool CaptureDiagnostics) { + assert(Diags.get() && "no DiagnosticsEngine was provided"); + if (CaptureDiagnostics) + Diags->setClient(new StoredDiagnosticConsumer(&AST.StoredDiagnostics, nullptr)); +} + +std::unique_ptr<ASTUnit> ASTUnit::LoadFromASTFile( + const std::string &Filename, const PCHContainerReader &PCHContainerRdr, + WhatToLoad ToLoad, IntrusiveRefCntPtr<DiagnosticsEngine> Diags, + const FileSystemOptions &FileSystemOpts, bool UseDebugInfo, + bool OnlyLocalDecls, ArrayRef<RemappedFile> RemappedFiles, + bool CaptureDiagnostics, bool AllowPCHWithCompilerErrors, + bool UserFilesAreVolatile) { + std::unique_ptr<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.get()); + + ConfigureDiags(Diags, *AST, CaptureDiagnostics); + + AST->LangOpts = std::make_shared<LangOptions>(); + AST->OnlyLocalDecls = OnlyLocalDecls; + AST->CaptureDiagnostics = CaptureDiagnostics; + AST->Diagnostics = Diags; + IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS = + llvm::vfs::getRealFileSystem(); + AST->FileMgr = new FileManager(FileSystemOpts, VFS); + AST->UserFilesAreVolatile = UserFilesAreVolatile; + AST->SourceMgr = new SourceManager(AST->getDiagnostics(), + AST->getFileManager(), + UserFilesAreVolatile); + AST->PCMCache = new MemoryBufferCache; + AST->HSOpts = std::make_shared<HeaderSearchOptions>(); + AST->HSOpts->ModuleFormat = PCHContainerRdr.getFormat(); + AST->HeaderInfo.reset(new HeaderSearch(AST->HSOpts, + AST->getSourceManager(), + AST->getDiagnostics(), + AST->getLangOpts(), + /*Target=*/nullptr)); + AST->PPOpts = std::make_shared<PreprocessorOptions>(); + + for (const auto &RemappedFile : RemappedFiles) + AST->PPOpts->addRemappedFile(RemappedFile.first, RemappedFile.second); + + // Gather Info for preprocessor construction later on. + + HeaderSearch &HeaderInfo = *AST->HeaderInfo; + unsigned Counter; + + AST->PP = std::make_shared<Preprocessor>( + AST->PPOpts, AST->getDiagnostics(), *AST->LangOpts, + AST->getSourceManager(), *AST->PCMCache, HeaderInfo, AST->ModuleLoader, + /*IILookup=*/nullptr, + /*OwnsHeaderSearch=*/false); + Preprocessor &PP = *AST->PP; + + if (ToLoad >= LoadASTOnly) + AST->Ctx = new ASTContext(*AST->LangOpts, AST->getSourceManager(), + PP.getIdentifierTable(), PP.getSelectorTable(), + PP.getBuiltinInfo()); + + bool disableValid = false; + if (::getenv("LIBCLANG_DISABLE_PCH_VALIDATION")) + disableValid = true; + AST->Reader = new ASTReader(PP, AST->Ctx.get(), PCHContainerRdr, {}, + /*isysroot=*/"", + /*DisableValidation=*/disableValid, + AllowPCHWithCompilerErrors); + + AST->Reader->setListener(llvm::make_unique<ASTInfoCollector>( + *AST->PP, AST->Ctx.get(), *AST->HSOpts, *AST->PPOpts, *AST->LangOpts, + AST->TargetOpts, AST->Target, 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. + // We need the external source to be set up before we read the AST, because + // eagerly-deserialized declarations may use it. + if (AST->Ctx) + AST->Ctx->setExternalSource(AST->Reader); + + switch (AST->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 nullptr; + } + + AST->OriginalSourceFile = AST->Reader->getOriginalSourceFile(); + + PP.setCounterValue(Counter); + + // Create an AST consumer, even though it isn't used. + if (ToLoad >= LoadASTOnly) + AST->Consumer.reset(new ASTConsumer); + + // Create a semantic analysis object and tell the AST reader about it. + if (ToLoad >= LoadEverything) { + AST->TheSema.reset(new Sema(PP, *AST->Ctx, *AST->Consumer)); + AST->TheSema->Initialize(); + AST->Reader->InitializeSema(*AST->TheSema); + } + + // Tell the diagnostic client that we have started a source file. + AST->getDiagnostics().getClient()->BeginSourceFile(PP.getLangOpts(), &PP); + + return AST; +} + +/// Add the given macro to the hash of all top-level entities. +static void AddDefinedMacroToHash(const Token &MacroNameTok, unsigned &Hash) { + Hash = llvm::djbHash(MacroNameTok.getIdentifierInfo()->getName(), Hash); +} + +namespace { + +/// 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) {} + + void MacroDefined(const Token &MacroNameTok, + const MacroDirective *MD) override { + AddDefinedMacroToHash(MacroNameTok, Hash); + } +}; + +} // namespace + +/// Add the given declaration to the hash of all top-level entities. +static void AddTopLevelDeclarationToHash(Decl *D, unsigned &Hash) { + if (!D) + return; + + DeclContext *DC = D->getDeclContext(); + if (!DC) + return; + + if (!(DC->isTranslationUnit() || DC->getLookupParent()->isTranslationUnit())) + return; + + if (const auto *ND = dyn_cast<NamedDecl>(D)) { + if (const auto *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 (const auto *EI : EnumD->enumerators()) { + if (EI->getIdentifier()) + Hash = llvm::djbHash(EI->getIdentifier()->getName(), Hash); + } + } + } + + if (ND->getIdentifier()) + Hash = llvm::djbHash(ND->getIdentifier()->getName(), Hash); + else if (DeclarationName Name = ND->getDeclName()) { + std::string NameStr = Name.getAsString(); + Hash = llvm::djbHash(NameStr, Hash); + } + return; + } + + if (const auto *ImportD = dyn_cast<ImportDecl>(D)) { + if (const Module *Mod = ImportD->getImportedModule()) { + std::string ModName = Mod->getFullModuleName(); + Hash = llvm::djbHash(ModName, Hash); + } + return; + } +} + +namespace { + +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 (auto *NSD = dyn_cast<NamespaceDecl>(D)) { + for (auto *I : NSD->decls()) + handleFileLevelDecl(I); + } + } + + bool HandleTopLevelDecl(DeclGroupRef D) override { + for (auto *TopLevelDecl : D) + handleTopLevelDecl(TopLevelDecl); + return true; + } + + // We're not interested in "interesting" decls. + void HandleInterestingDecl(DeclGroupRef) override {} + + void HandleTopLevelDeclInObjCContainer(DeclGroupRef D) override { + for (auto *TopLevelDecl : D) + handleTopLevelDecl(TopLevelDecl); + } + + ASTMutationListener *GetASTMutationListener() override { + return Unit.getASTMutationListener(); + } + + ASTDeserializationListener *GetASTDeserializationListener() override { + return Unit.getDeserializationListener(); + } +}; + +class TopLevelDeclTrackerAction : public ASTFrontendAction { +public: + ASTUnit &Unit; + + std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) override { + CI.getPreprocessor().addPPCallbacks( + llvm::make_unique<MacroDefinitionTrackerPPCallbacks>( + Unit.getCurrentTopLevelHashValue())); + return llvm::make_unique<TopLevelDeclTrackerConsumer>( + Unit, Unit.getCurrentTopLevelHashValue()); + } + +public: + TopLevelDeclTrackerAction(ASTUnit &_Unit) : Unit(_Unit) {} + + bool hasCodeCompletionSupport() const override { return false; } + + TranslationUnitKind getTranslationUnitKind() override { + return Unit.getTranslationUnitKind(); + } +}; + +class ASTUnitPreambleCallbacks : public PreambleCallbacks { +public: + unsigned getHash() const { return Hash; } + + std::vector<Decl *> takeTopLevelDecls() { return std::move(TopLevelDecls); } + + std::vector<serialization::DeclID> takeTopLevelDeclIDs() { + return std::move(TopLevelDeclIDs); + } + + void AfterPCHEmitted(ASTWriter &Writer) override { + TopLevelDeclIDs.reserve(TopLevelDecls.size()); + for (const auto *D : TopLevelDecls) { + // Invalid top-level decls may not have been serialized. + if (D->isInvalidDecl()) + continue; + TopLevelDeclIDs.push_back(Writer.getDeclID(D)); + } + } + + void HandleTopLevelDecl(DeclGroupRef DG) override { + for (auto *D : DG) { + // 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); + } + } + + std::unique_ptr<PPCallbacks> createPPCallbacks() override { + return llvm::make_unique<MacroDefinitionTrackerPPCallbacks>(Hash); + } + +private: + unsigned Hash = 0; + std::vector<Decl *> TopLevelDecls; + std::vector<serialization::DeclID> TopLevelDeclIDs; + llvm::SmallVector<ASTUnit::StandaloneDiagnostic, 4> PreambleDiags; +}; + +} // namespace + +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 (auto &SD : StoredDiagnostics) { + if (SD.getLocation().isValid()) { + FullSourceLoc Loc(SD.getLocation(), SM); + SD.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(std::shared_ptr<PCHContainerOperations> PCHContainerOps, + std::unique_ptr<llvm::MemoryBuffer> OverrideMainBuffer, + IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) { + if (!Invocation) + return true; + + auto CCInvocation = std::make_shared<CompilerInvocation>(*Invocation); + if (OverrideMainBuffer) { + assert(Preamble && + "No preamble was built, but OverrideMainBuffer is not null"); + IntrusiveRefCntPtr<llvm::vfs::FileSystem> OldVFS = VFS; + Preamble->AddImplicitPreamble(*CCInvocation, VFS, OverrideMainBuffer.get()); + if (OldVFS != VFS && FileMgr) { + assert(OldVFS == FileMgr->getVirtualFileSystem() && + "VFS passed to Parse and VFS in FileMgr are different"); + FileMgr = new FileManager(FileMgr->getFileSystemOpts(), VFS); + } + } + + // Create the compiler instance to use for building the AST. + std::unique_ptr<CompilerInstance> Clang( + new CompilerInstance(std::move(PCHContainerOps))); + if (FileMgr && VFS) { + assert(VFS == FileMgr->getVirtualFileSystem() && + "VFS passed to Parse and VFS in FileMgr are different"); + } else if (VFS) { + Clang->setVirtualFileSystem(VFS); + } + + // 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 that would + // otherwise be dropped. + Clang->setDiagnostics(&getDiagnostics()); + + // Create the target instance. + Clang->setTarget(TargetInfo::CreateTargetInfo( + Clang->getDiagnostics(), Clang->getInvocation().TargetOpts)); + if (!Clang->hasTarget()) + 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().adjust(Clang->getLangOpts()); + + assert(Clang->getFrontendOpts().Inputs.size() == 1 && + "Invocation must have exactly one source file!"); + assert(Clang->getFrontendOpts().Inputs[0].getKind().getFormat() == + InputKind::Source && + "FIXME: AST inputs not yet supported here!"); + assert(Clang->getFrontendOpts().Inputs[0].getKind().getLanguage() != + InputKind::LLVM_IR && + "IR inputs not support here!"); + + // Configure the various subsystems. + LangOpts = Clang->getInvocation().LangOpts; + FileSystemOpts = Clang->getFileSystemOpts(); + if (!FileMgr) { + Clang->createFileManager(); + FileMgr = &Clang->getFileManager(); + } + + ResetForParse(); + + SourceMgr = new SourceManager(getDiagnostics(), *FileMgr, + UserFilesAreVolatile); + 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. + if (OverrideMainBuffer) { + // The stored diagnostic has the old source manager in it; update + // the locations to refer into the new source manager. Since we've + // been careful to make sure that the source manager's state + // before and after are identical, so that we can reuse the source + // location itself. + checkAndSanitizeDiags(StoredDiagnostics, getSourceManager()); + + // Keep track of the override buffer; + SavedMainFileBuffer = std::move(OverrideMainBuffer); + } + + std::unique_ptr<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 (SavedMainFileBuffer) + TranslateStoredDiagnostics(getFileManager(), getSourceManager(), + PreambleDiagnostics, StoredDiagnostics); + else + PreambleSrcLocCache.clear(); + + if (!Act->Execute()) + goto error; + + transferASTDataFromCompilerInstance(*Clang); + + Act->EndSourceFile(); + + FailedParseDiagnostics.clear(); + + return false; + +error: + // Remove the overridden buffer we used for the preamble. + SavedMainFileBuffer = nullptr; + + // 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; +} + +static std::pair<unsigned, unsigned> +makeStandaloneRange(CharSourceRange Range, const SourceManager &SM, + const LangOptions &LangOpts) { + CharSourceRange FileRange = Lexer::makeFileCharRange(Range, SM, LangOpts); + unsigned Offset = SM.getFileOffset(FileRange.getBegin()); + unsigned EndOffset = SM.getFileOffset(FileRange.getEnd()); + return std::make_pair(Offset, EndOffset); +} + +static ASTUnit::StandaloneFixIt makeStandaloneFixIt(const SourceManager &SM, + const LangOptions &LangOpts, + const FixItHint &InFix) { + ASTUnit::StandaloneFixIt OutFix; + OutFix.RemoveRange = makeStandaloneRange(InFix.RemoveRange, SM, LangOpts); + OutFix.InsertFromRange = makeStandaloneRange(InFix.InsertFromRange, SM, + LangOpts); + OutFix.CodeToInsert = InFix.CodeToInsert; + OutFix.BeforePreviousInsertions = InFix.BeforePreviousInsertions; + return OutFix; +} + +static ASTUnit::StandaloneDiagnostic +makeStandaloneDiagnostic(const LangOptions &LangOpts, + const StoredDiagnostic &InDiag) { + ASTUnit::StandaloneDiagnostic OutDiag; + OutDiag.ID = InDiag.getID(); + OutDiag.Level = InDiag.getLevel(); + OutDiag.Message = InDiag.getMessage(); + OutDiag.LocOffset = 0; + if (InDiag.getLocation().isInvalid()) + return OutDiag; + const SourceManager &SM = InDiag.getLocation().getManager(); + SourceLocation FileLoc = SM.getFileLoc(InDiag.getLocation()); + OutDiag.Filename = SM.getFilename(FileLoc); + if (OutDiag.Filename.empty()) + return OutDiag; + OutDiag.LocOffset = SM.getFileOffset(FileLoc); + for (const auto &Range : InDiag.getRanges()) + OutDiag.Ranges.push_back(makeStandaloneRange(Range, SM, LangOpts)); + for (const auto &FixIt : InDiag.getFixIts()) + OutDiag.FixIts.push_back(makeStandaloneFixIt(SM, LangOpts, FixIt)); + + return OutDiag; +} + +/// 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. +std::unique_ptr<llvm::MemoryBuffer> +ASTUnit::getMainBufferWithPrecompiledPreamble( + std::shared_ptr<PCHContainerOperations> PCHContainerOps, + CompilerInvocation &PreambleInvocationIn, + IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS, bool AllowRebuild, + unsigned MaxLines) { + auto MainFilePath = + PreambleInvocationIn.getFrontendOpts().Inputs[0].getFile(); + std::unique_ptr<llvm::MemoryBuffer> MainFileBuffer = + getBufferForFileHandlingRemapping(PreambleInvocationIn, VFS.get(), + MainFilePath, UserFilesAreVolatile); + if (!MainFileBuffer) + return nullptr; + + PreambleBounds Bounds = + ComputePreambleBounds(*PreambleInvocationIn.getLangOpts(), + MainFileBuffer.get(), MaxLines); + if (!Bounds.Size) + return nullptr; + + if (Preamble) { + if (Preamble->CanReuse(PreambleInvocationIn, MainFileBuffer.get(), Bounds, + VFS.get())) { + // 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(), + PreambleInvocationIn.getDiagnosticOpts()); + getDiagnostics().setNumWarnings(NumWarningsInPreamble); + + PreambleRebuildCounter = 1; + return MainFileBuffer; + } else { + Preamble.reset(); + PreambleDiagnostics.clear(); + TopLevelDeclsInPreamble.clear(); + PreambleSrcLocCache.clear(); + PreambleRebuildCounter = 1; + } + } + + // 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 nullptr; + } + + assert(!Preamble && "No Preamble should be stored at that point"); + // If we aren't allowed to rebuild the precompiled preamble, just + // return now. + if (!AllowRebuild) + return nullptr; + + SmallVector<StandaloneDiagnostic, 4> NewPreambleDiagsStandalone; + SmallVector<StoredDiagnostic, 4> NewPreambleDiags; + ASTUnitPreambleCallbacks Callbacks; + { + llvm::Optional<CaptureDroppedDiagnostics> Capture; + if (CaptureDiagnostics) + Capture.emplace(/*RequestCapture=*/true, *Diagnostics, &NewPreambleDiags, + &NewPreambleDiagsStandalone); + + // We did not previously compute a preamble, or it can't be reused anyway. + SimpleTimer PreambleTimer(WantTiming); + PreambleTimer.setOutput("Precompiling preamble"); + + const bool PreviousSkipFunctionBodies = + PreambleInvocationIn.getFrontendOpts().SkipFunctionBodies; + if (SkipFunctionBodies == SkipFunctionBodiesScope::Preamble) + PreambleInvocationIn.getFrontendOpts().SkipFunctionBodies = true; + + llvm::ErrorOr<PrecompiledPreamble> NewPreamble = PrecompiledPreamble::Build( + PreambleInvocationIn, MainFileBuffer.get(), Bounds, *Diagnostics, VFS, + PCHContainerOps, /*StoreInMemory=*/false, Callbacks); + + PreambleInvocationIn.getFrontendOpts().SkipFunctionBodies = + PreviousSkipFunctionBodies; + + if (NewPreamble) { + Preamble = std::move(*NewPreamble); + PreambleRebuildCounter = 1; + } else { + switch (static_cast<BuildPreambleError>(NewPreamble.getError().value())) { + case BuildPreambleError::CouldntCreateTempFile: + // Try again next time. + PreambleRebuildCounter = 1; + return nullptr; + case BuildPreambleError::CouldntCreateTargetInfo: + case BuildPreambleError::BeginSourceFileFailed: + case BuildPreambleError::CouldntEmitPCH: + // These erros are more likely to repeat, retry after some period. + PreambleRebuildCounter = DefaultPreambleRebuildInterval; + return nullptr; + } + llvm_unreachable("unexpected BuildPreambleError"); + } + } + + assert(Preamble && "Preamble wasn't built"); + + TopLevelDecls.clear(); + TopLevelDeclsInPreamble = Callbacks.takeTopLevelDeclIDs(); + PreambleTopLevelHashValue = Callbacks.getHash(); + + NumWarningsInPreamble = getDiagnostics().getNumWarnings(); + + checkAndRemoveNonDriverDiags(NewPreambleDiags); + StoredDiagnostics = std::move(NewPreambleDiags); + PreambleDiagnostics = std::move(NewPreambleDiagsStandalone); + + // 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 MainFileBuffer; +} + +void ASTUnit::RealizeTopLevelDeclsFromPreamble() { + assert(Preamble && "Should only be called when preamble was built"); + + std::vector<Decl *> Resolved; + Resolved.reserve(TopLevelDeclsInPreamble.size()); + ExternalASTSource &Source = *getASTContext().getExternalSource(); + for (const auto TopLevelDecl : TopLevelDeclsInPreamble) { + // Resolve the declaration ID to an actual declaration, possibly + // deserializing the declaration in the process. + if (Decl *D = Source.GetExternalDecl(TopLevelDecl)) + 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 if they have been + // created. + assert(CI.hasInvocation() && "missing invocation"); + LangOpts = CI.getInvocation().LangOpts; + TheSema = CI.takeSema(); + Consumer = CI.takeASTConsumer(); + if (CI.hasASTContext()) + Ctx = &CI.getASTContext(); + if (CI.hasPreprocessor()) + PP = CI.getPreprocessorPtr(); + CI.setSourceManager(nullptr); + CI.setFileManager(nullptr); + if (CI.hasTarget()) + 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 ASTUnit::getASTFileName() const { + if (!isMainFileAST()) + return {}; + + serialization::ModuleFile & + Mod = Reader->getModuleManager().getPrimaryModule(); + return Mod.FileName; +} + +std::unique_ptr<ASTUnit> +ASTUnit::create(std::shared_ptr<CompilerInvocation> CI, + IntrusiveRefCntPtr<DiagnosticsEngine> Diags, + bool CaptureDiagnostics, bool UserFilesAreVolatile) { + std::unique_ptr<ASTUnit> AST(new ASTUnit(false)); + ConfigureDiags(Diags, *AST, CaptureDiagnostics); + IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS = + createVFSFromCompilerInvocation(*CI, *Diags); + AST->Diagnostics = Diags; + AST->FileSystemOpts = CI->getFileSystemOpts(); + AST->Invocation = std::move(CI); + AST->FileMgr = new FileManager(AST->FileSystemOpts, VFS); + AST->UserFilesAreVolatile = UserFilesAreVolatile; + AST->SourceMgr = new SourceManager(AST->getDiagnostics(), *AST->FileMgr, + UserFilesAreVolatile); + AST->PCMCache = new MemoryBufferCache; + + return AST; +} + +ASTUnit *ASTUnit::LoadFromCompilerInvocationAction( + std::shared_ptr<CompilerInvocation> CI, + std::shared_ptr<PCHContainerOperations> PCHContainerOps, + IntrusiveRefCntPtr<DiagnosticsEngine> Diags, FrontendAction *Action, + ASTUnit *Unit, bool Persistent, StringRef ResourceFilesPath, + bool OnlyLocalDecls, bool CaptureDiagnostics, + unsigned PrecompilePreambleAfterNParses, bool CacheCodeCompletionResults, + bool IncludeBriefCommentsInCodeCompletion, bool UserFilesAreVolatile, + std::unique_ptr<ASTUnit> *ErrAST) { + assert(CI && "A CompilerInvocation is required"); + + std::unique_ptr<ASTUnit> OwnAST; + ASTUnit *AST = Unit; + if (!AST) { + // Create the AST unit. + OwnAST = create(CI, Diags, CaptureDiagnostics, UserFilesAreVolatile); + AST = OwnAST.get(); + if (!AST) + return nullptr; + } + + if (!ResourceFilesPath.empty()) { + // Override the resources path. + CI->getHeaderSearchOpts().ResourceDir = ResourceFilesPath; + } + AST->OnlyLocalDecls = OnlyLocalDecls; + AST->CaptureDiagnostics = CaptureDiagnostics; + if (PrecompilePreambleAfterNParses > 0) + AST->PreambleRebuildCounter = PrecompilePreambleAfterNParses; + 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.get()); + + // 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. + std::unique_ptr<CompilerInstance> Clang( + new CompilerInstance(std::move(PCHContainerOps))); + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<CompilerInstance> + CICleanup(Clang.get()); + + Clang->setInvocation(std::move(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->getInvocation().TargetOpts)); + if (!Clang->hasTarget()) + return nullptr; + + // 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().adjust(Clang->getLangOpts()); + + assert(Clang->getFrontendOpts().Inputs.size() == 1 && + "Invocation must have exactly one source file!"); + assert(Clang->getFrontendOpts().Inputs[0].getKind().getFormat() == + InputKind::Source && + "FIXME: AST inputs not yet supported here!"); + assert(Clang->getFrontendOpts().Inputs[0].getKind().getLanguage() != + InputKind::LLVM_IR && + "IR inputs not support here!"); + + // Configure the various subsystems. + AST->TheSema.reset(); + AST->Ctx = nullptr; + AST->PP = nullptr; + AST->Reader = nullptr; + + // 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()); + + FrontendAction *Act = Action; + + std::unique_ptr<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 nullptr; + } + + if (Persistent && !TrackerAct) { + Clang->getPreprocessor().addPPCallbacks( + llvm::make_unique<MacroDefinitionTrackerPPCallbacks>( + AST->getCurrentTopLevelHashValue())); + std::vector<std::unique_ptr<ASTConsumer>> Consumers; + if (Clang->hasASTConsumer()) + Consumers.push_back(Clang->takeASTConsumer()); + Consumers.push_back(llvm::make_unique<TopLevelDeclTrackerConsumer>( + *AST, AST->getCurrentTopLevelHashValue())); + Clang->setASTConsumer( + llvm::make_unique<MultiplexConsumer>(std::move(Consumers))); + } + if (!Act->Execute()) { + AST->transferASTDataFromCompilerInstance(*Clang); + if (OwnAST && ErrAST) + ErrAST->swap(OwnAST); + + return nullptr; + } + + // Steal the created target, context, and preprocessor. + AST->transferASTDataFromCompilerInstance(*Clang); + + Act->EndSourceFile(); + + if (OwnAST) + return OwnAST.release(); + else + return AST; +} + +bool ASTUnit::LoadFromCompilerInvocation( + std::shared_ptr<PCHContainerOperations> PCHContainerOps, + unsigned PrecompilePreambleAfterNParses, + IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) { + if (!Invocation) + return true; + + assert(VFS && "VFS is null"); + + // We'll manage file buffers ourselves. + Invocation->getPreprocessorOpts().RetainRemappedFileBuffers = true; + Invocation->getFrontendOpts().DisableFree = false; + getDiagnostics().Reset(); + ProcessWarningOptions(getDiagnostics(), Invocation->getDiagnosticOpts()); + + std::unique_ptr<llvm::MemoryBuffer> OverrideMainBuffer; + if (PrecompilePreambleAfterNParses > 0) { + PreambleRebuildCounter = PrecompilePreambleAfterNParses; + OverrideMainBuffer = + getMainBufferWithPrecompiledPreamble(PCHContainerOps, *Invocation, VFS); + getDiagnostics().Reset(); + ProcessWarningOptions(getDiagnostics(), Invocation->getDiagnosticOpts()); + } + + SimpleTimer ParsingTimer(WantTiming); + ParsingTimer.setOutput("Parsing " + getMainFileName()); + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<llvm::MemoryBuffer> + MemBufferCleanup(OverrideMainBuffer.get()); + + return Parse(std::move(PCHContainerOps), std::move(OverrideMainBuffer), VFS); +} + +std::unique_ptr<ASTUnit> ASTUnit::LoadFromCompilerInvocation( + std::shared_ptr<CompilerInvocation> CI, + std::shared_ptr<PCHContainerOperations> PCHContainerOps, + IntrusiveRefCntPtr<DiagnosticsEngine> Diags, FileManager *FileMgr, + bool OnlyLocalDecls, bool CaptureDiagnostics, + unsigned PrecompilePreambleAfterNParses, TranslationUnitKind TUKind, + bool CacheCodeCompletionResults, bool IncludeBriefCommentsInCodeCompletion, + bool UserFilesAreVolatile) { + // Create the AST unit. + std::unique_ptr<ASTUnit> AST(new ASTUnit(false)); + ConfigureDiags(Diags, *AST, CaptureDiagnostics); + AST->Diagnostics = Diags; + AST->OnlyLocalDecls = OnlyLocalDecls; + AST->CaptureDiagnostics = CaptureDiagnostics; + AST->TUKind = TUKind; + AST->ShouldCacheCodeCompletionResults = CacheCodeCompletionResults; + AST->IncludeBriefCommentsInCodeCompletion + = IncludeBriefCommentsInCodeCompletion; + AST->Invocation = std::move(CI); + AST->FileSystemOpts = FileMgr->getFileSystemOpts(); + AST->FileMgr = FileMgr; + 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.get()); + + if (AST->LoadFromCompilerInvocation(std::move(PCHContainerOps), + PrecompilePreambleAfterNParses, + AST->FileMgr->getVirtualFileSystem())) + return nullptr; + return AST; +} + +ASTUnit *ASTUnit::LoadFromCommandLine( + const char **ArgBegin, const char **ArgEnd, + std::shared_ptr<PCHContainerOperations> PCHContainerOps, + IntrusiveRefCntPtr<DiagnosticsEngine> Diags, StringRef ResourceFilesPath, + bool OnlyLocalDecls, bool CaptureDiagnostics, + ArrayRef<RemappedFile> RemappedFiles, bool RemappedFilesKeepOriginalName, + unsigned PrecompilePreambleAfterNParses, TranslationUnitKind TUKind, + bool CacheCodeCompletionResults, bool IncludeBriefCommentsInCodeCompletion, + bool AllowPCHWithCompilerErrors, SkipFunctionBodiesScope SkipFunctionBodies, + bool SingleFileParse, bool UserFilesAreVolatile, bool ForSerialization, + llvm::Optional<StringRef> ModuleFormat, std::unique_ptr<ASTUnit> *ErrAST, + IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) { + assert(Diags.get() && "no DiagnosticsEngine was provided"); + + SmallVector<StoredDiagnostic, 4> StoredDiagnostics; + + std::shared_ptr<CompilerInvocation> CI; + + { + CaptureDroppedDiagnostics Capture(CaptureDiagnostics, *Diags, + &StoredDiagnostics, nullptr); + + CI = createInvocationFromCommandLine( + llvm::makeArrayRef(ArgBegin, ArgEnd), Diags, VFS); + if (!CI) + return nullptr; + } + + // Override any files that need remapping + for (const auto &RemappedFile : RemappedFiles) { + CI->getPreprocessorOpts().addRemappedFile(RemappedFile.first, + RemappedFile.second); + } + PreprocessorOptions &PPOpts = CI->getPreprocessorOpts(); + PPOpts.RemappedFilesKeepOriginalName = RemappedFilesKeepOriginalName; + PPOpts.AllowPCHWithCompilerErrors = AllowPCHWithCompilerErrors; + PPOpts.SingleFileParseMode = SingleFileParse; + + // Override the resources path. + CI->getHeaderSearchOpts().ResourceDir = ResourceFilesPath; + + CI->getFrontendOpts().SkipFunctionBodies = + SkipFunctionBodies == SkipFunctionBodiesScope::PreambleAndMainFile; + + if (ModuleFormat) + CI->getHeaderSearchOpts().ModuleFormat = ModuleFormat.getValue(); + + // Create the AST unit. + std::unique_ptr<ASTUnit> AST; + AST.reset(new ASTUnit(false)); + AST->NumStoredDiagnosticsFromDriver = StoredDiagnostics.size(); + AST->StoredDiagnostics.swap(StoredDiagnostics); + ConfigureDiags(Diags, *AST, CaptureDiagnostics); + AST->Diagnostics = Diags; + AST->FileSystemOpts = CI->getFileSystemOpts(); + if (!VFS) + VFS = llvm::vfs::getRealFileSystem(); + VFS = createVFSFromCompilerInvocation(*CI, *Diags, VFS); + AST->FileMgr = new FileManager(AST->FileSystemOpts, VFS); + AST->PCMCache = new MemoryBufferCache; + AST->OnlyLocalDecls = OnlyLocalDecls; + AST->CaptureDiagnostics = CaptureDiagnostics; + AST->TUKind = TUKind; + AST->ShouldCacheCodeCompletionResults = CacheCodeCompletionResults; + AST->IncludeBriefCommentsInCodeCompletion + = IncludeBriefCommentsInCodeCompletion; + AST->UserFilesAreVolatile = UserFilesAreVolatile; + AST->Invocation = CI; + AST->SkipFunctionBodies = SkipFunctionBodies; + if (ForSerialization) + AST->WriterData.reset(new ASTWriterData(*AST->PCMCache)); + // Zero out now to ease cleanup during crash recovery. + CI = nullptr; + Diags = nullptr; + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<ASTUnit> + ASTUnitCleanup(AST.get()); + + if (AST->LoadFromCompilerInvocation(std::move(PCHContainerOps), + PrecompilePreambleAfterNParses, + VFS)) { + // Some error occurred, if caller wants to examine diagnostics, pass it the + // ASTUnit. + if (ErrAST) { + AST->StoredDiagnostics.swap(AST->FailedParseDiagnostics); + ErrAST->swap(AST); + } + return nullptr; + } + + return AST.release(); +} + +bool ASTUnit::Reparse(std::shared_ptr<PCHContainerOperations> PCHContainerOps, + ArrayRef<RemappedFile> RemappedFiles, + IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) { + if (!Invocation) + return true; + + if (!VFS) { + assert(FileMgr && "FileMgr is null on Reparse call"); + VFS = FileMgr->getVirtualFileSystem(); + } + + clearFileLevelDecls(); + + SimpleTimer ParsingTimer(WantTiming); + ParsingTimer.setOutput("Reparsing " + getMainFileName()); + + // Remap files. + PreprocessorOptions &PPOpts = Invocation->getPreprocessorOpts(); + for (const auto &RB : PPOpts.RemappedFileBuffers) + delete RB.second; + + Invocation->getPreprocessorOpts().clearRemappedFiles(); + for (const auto &RemappedFile : RemappedFiles) { + Invocation->getPreprocessorOpts().addRemappedFile(RemappedFile.first, + RemappedFile.second); + } + + // If we have a preamble file lying around, or if we might try to + // build a precompiled preamble, do so now. + std::unique_ptr<llvm::MemoryBuffer> OverrideMainBuffer; + if (Preamble || PreambleRebuildCounter > 0) + OverrideMainBuffer = + getMainBufferWithPrecompiledPreamble(PCHContainerOps, *Invocation, VFS); + + // Clear out the diagnostics state. + FileMgr.reset(); + getDiagnostics().Reset(); + ProcessWarningOptions(getDiagnostics(), Invocation->getDiagnosticOpts()); + if (OverrideMainBuffer) + getDiagnostics().setNumWarnings(NumWarningsInPreamble); + + // Parse the sources + bool Result = + Parse(std::move(PCHContainerOps), std::move(OverrideMainBuffer), VFS); + + // 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; +} + +void ASTUnit::ResetForParse() { + SavedMainFileBuffer.reset(); + + SourceMgr.reset(); + TheSema.reset(); + Ctx.reset(); + PP.reset(); + Reader.reset(); + + TopLevelDecls.clear(); + clearFileLevelDecls(); +} + +//----------------------------------------------------------------------------// +// Code completion +//----------------------------------------------------------------------------// + +namespace { + + /// 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); + } + + void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context, + CodeCompletionResult *Results, + unsigned NumResults) override; + + void ProcessOverloadCandidates(Sema &S, unsigned CurrentArg, + OverloadCandidate *Candidates, + unsigned NumCandidates, + SourceLocation OpenParLoc) override { + Next.ProcessOverloadCandidates(S, CurrentArg, Candidates, NumCandidates, + OpenParLoc); + } + + CodeCompletionAllocator &getAllocator() override { + return Next.getAllocator(); + } + + CodeCompletionTUInfo &getCodeCompletionTUInfo() override { + return Next.getCodeCompletionTUInfo(); + } + }; + +} // namespace + +/// 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_Symbol: + case CodeCompletionContext::CCC_SymbolOrNewName: + 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: + case CodeCompletionContext::CCC_IncludedFile: + case CodeCompletionContext::CCC_NewName: + // We're looking for nothing, or we're looking for names that cannot + // be hidden. + return; + } + + using Result = CodeCompletionResult; + 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; + using Result = CodeCompletionResult; + 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, + ArrayRef<RemappedFile> RemappedFiles, bool IncludeMacros, + bool IncludeCodePatterns, bool IncludeBriefComments, + CodeCompleteConsumer &Consumer, + std::shared_ptr<PCHContainerOperations> PCHContainerOps, + 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)); + + auto CCInvocation = std::make_shared<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; + CodeCompleteOpts.LoadExternal = Consumer.loadExternal(); + CodeCompleteOpts.IncludeFixIts = Consumer.includeFixIts(); + + assert(IncludeBriefComments == this->IncludeBriefCommentsInCodeCompletion); + + FrontendOpts.CodeCompletionAt.FileName = File; + FrontendOpts.CodeCompletionAt.Line = Line; + FrontendOpts.CodeCompletionAt.Column = Column; + + // Set the language options appropriately. + LangOpts = *CCInvocation->getLangOpts(); + + // Spell-checking and warnings are wasteful during code-completion. + LangOpts.SpellChecking = false; + CCInvocation->getDiagnosticOpts().IgnoreWarnings = true; + + std::unique_ptr<CompilerInstance> Clang( + new CompilerInstance(PCHContainerOps)); + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<CompilerInstance> + CICleanup(Clang.get()); + + auto &Inv = *CCInvocation; + Clang->setInvocation(std::move(CCInvocation)); + OriginalSourceFile = Clang->getFrontendOpts().Inputs[0].getFile(); + + // Set up diagnostics, capturing any diagnostics produced. + Clang->setDiagnostics(&Diag); + CaptureDroppedDiagnostics Capture(true, + Clang->getDiagnostics(), + &StoredDiagnostics, nullptr); + ProcessWarningOptions(Diag, Inv.getDiagnosticOpts()); + + // Create the target instance. + Clang->setTarget(TargetInfo::CreateTargetInfo( + Clang->getDiagnostics(), Clang->getInvocation().TargetOpts)); + if (!Clang->hasTarget()) { + Clang->setInvocation(nullptr); + 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().adjust(Clang->getLangOpts()); + + assert(Clang->getFrontendOpts().Inputs.size() == 1 && + "Invocation must have exactly one source file!"); + assert(Clang->getFrontendOpts().Inputs[0].getKind().getFormat() == + InputKind::Source && + "FIXME: AST inputs not yet supported here!"); + assert(Clang->getFrontendOpts().Inputs[0].getKind().getLanguage() != + InputKind::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 (const auto &RemappedFile : RemappedFiles) { + PreprocessorOpts.addRemappedFile(RemappedFile.first, RemappedFile.second); + OwnedBuffers.push_back(RemappedFile.second); + } + + // 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. + std::unique_ptr<llvm::MemoryBuffer> OverrideMainBuffer; + if (Preamble) { + std::string CompleteFilePath(File); + + auto VFS = FileMgr.getVirtualFileSystem(); + auto CompleteFileStatus = VFS->status(CompleteFilePath); + if (CompleteFileStatus) { + llvm::sys::fs::UniqueID CompleteFileID = CompleteFileStatus->getUniqueID(); + + std::string MainPath(OriginalSourceFile); + auto MainStatus = VFS->status(MainPath); + if (MainStatus) { + llvm::sys::fs::UniqueID MainID = MainStatus->getUniqueID(); + if (CompleteFileID == MainID && Line > 1) + OverrideMainBuffer = getMainBufferWithPrecompiledPreamble( + PCHContainerOps, Inv, VFS, 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) { + assert(Preamble && + "No preamble was built, but OverrideMainBuffer is not null"); + + auto VFS = FileMgr.getVirtualFileSystem(); + Preamble->AddImplicitPreamble(Clang->getInvocation(), VFS, + OverrideMainBuffer.get()); + // FIXME: there is no way to update VFS if it was changed by + // AddImplicitPreamble as FileMgr is accepted as a parameter by this method. + // We use on-disk preambles instead and rely on FileMgr's VFS to ensure the + // PCH files are always readable. + OwnedBuffers.push_back(OverrideMainBuffer.release()); + } 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; + + std::unique_ptr<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, 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, File)) { + llvm::sys::fs::remove(TempPath); + return true; + } + + return false; +} + +static bool serializeUnit(ASTWriter &Writer, + SmallVectorImpl<char> &Buffer, + Sema &S, + bool hasErrors, + raw_ostream &OS) { + Writer.WriteAST(S, std::string(), nullptr, "", hasErrors); + + // Write the generated bitstream to "Out". + if (!Buffer.empty()) + OS.write(Buffer.data(), Buffer.size()); + + return false; +} + +bool ASTUnit::serialize(raw_ostream &OS) { + // For serialization we are lenient if the errors were only warn-as-error kind. + bool hasErrors = getDiagnostics().hasUncompilableErrorOccurred(); + + if (WriterData) + return serializeUnit(WriterData->Writer, WriterData->Buffer, + getSema(), hasErrors, OS); + + SmallString<128> Buffer; + llvm::BitstreamWriter Stream(Buffer); + MemoryBufferCache PCMCache; + ASTWriter Writer(Stream, Buffer, PCMCache, {}); + return serializeUnit(Writer, Buffer, getSema(), hasErrors, OS); +} + +using SLocRemap = ContinuousRangeMap<unsigned, int, 2>; + +void ASTUnit::TranslateStoredDiagnostics( + FileManager &FileMgr, + SourceManager &SrcMgr, + const SmallVectorImpl<StandaloneDiagnostic> &Diags, + SmallVectorImpl<StoredDiagnostic> &Out) { + // Map the standalone diagnostic 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()); + + for (const auto &SD : Diags) { + // Rebuild the StoredDiagnostic. + if (SD.Filename.empty()) + continue; + const FileEntry *FE = FileMgr.getFile(SD.Filename); + if (!FE) + continue; + SourceLocation FileLoc; + auto ItFileID = PreambleSrcLocCache.find(SD.Filename); + if (ItFileID == PreambleSrcLocCache.end()) { + FileID FID = SrcMgr.translateFile(FE); + FileLoc = SrcMgr.getLocForStartOfFile(FID); + PreambleSrcLocCache[SD.Filename] = FileLoc; + } else { + FileLoc = ItFileID->getValue(); + } + + if (FileLoc.isInvalid()) + continue; + SourceLocation L = FileLoc.getLocWithOffset(SD.LocOffset); + FullSourceLoc Loc(L, SrcMgr); + + SmallVector<CharSourceRange, 4> Ranges; + Ranges.reserve(SD.Ranges.size()); + for (const auto &Range : SD.Ranges) { + SourceLocation BL = FileLoc.getLocWithOffset(Range.first); + SourceLocation EL = FileLoc.getLocWithOffset(Range.second); + Ranges.push_back(CharSourceRange::getCharRange(BL, EL)); + } + + SmallVector<FixItHint, 2> FixIts; + FixIts.reserve(SD.FixIts.size()); + for (const auto &FixIt : SD.FixIts) { + FixIts.push_back(FixItHint()); + FixItHint &FH = FixIts.back(); + FH.CodeToInsert = FixIt.CodeToInsert; + SourceLocation BL = FileLoc.getLocWithOffset(FixIt.RemoveRange.first); + SourceLocation EL = FileLoc.getLocWithOffset(FixIt.RemoveRange.second); + FH.RemoveRange = CharSourceRange::getCharRange(BL, EL); + } + + Result.push_back(StoredDiagnostic(SD.Level, SD.ID, + SD.Message, 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; + std::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 *)nullptr), + 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 *)nullptr), 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)); +} + +/// 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) const { + FileID PreambleID; + if (SourceMgr) + PreambleID = SourceMgr->getPreambleFileID(); + + if (Loc.isInvalid() || !Preamble || PreambleID.isInvalid()) + return Loc; + + unsigned Offs; + if (SourceMgr->isInFileID(Loc, PreambleID, &Offs) && Offs < Preamble->getBounds().Size) { + SourceLocation FileLoc + = SourceMgr->getLocForStartOfFile(SourceMgr->getMainFileID()); + return FileLoc.getLocWithOffset(Offs); + } + + return Loc; +} + +/// 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) const { + FileID PreambleID; + if (SourceMgr) + PreambleID = SourceMgr->getPreambleFileID(); + + if (Loc.isInvalid() || !Preamble || PreambleID.isInvalid()) + return Loc; + + unsigned Offs; + if (SourceMgr->isInFileID(Loc, SourceMgr->getMainFileID(), &Offs) && + Offs < Preamble->getBounds().Size) { + SourceLocation FileLoc = SourceMgr->getLocForStartOfFile(PreambleID); + return FileLoc.getLocWithOffset(Offs); + } + + return Loc; +} + +bool ASTUnit::isInPreambleFileID(SourceLocation Loc) const { + FileID FID; + if (SourceMgr) + FID = SourceMgr->getPreambleFileID(); + + if (Loc.isInvalid() || FID.isInvalid()) + return false; + + return SourceMgr->isInFileID(Loc, FID); +} + +bool ASTUnit::isInMainFileID(SourceLocation Loc) const { + FileID FID; + if (SourceMgr) + FID = SourceMgr->getMainFileID(); + + if (Loc.isInvalid() || FID.isInvalid()) + return false; + + return SourceMgr->isInFileID(Loc, FID); +} + +SourceLocation ASTUnit::getEndOfPreambleFileID() const { + FileID FID; + if (SourceMgr) + FID = SourceMgr->getPreambleFileID(); + + if (FID.isInvalid()) + return {}; + + return SourceMgr->getLocForEndOfFile(FID); +} + +SourceLocation ASTUnit::getStartOfMainFileID() const { + FileID FID; + if (SourceMgr) + FID = SourceMgr->getMainFileID(); + + if (FID.isInvalid()) + return {}; + + return SourceMgr->getLocForStartOfFile(FID); +} + +llvm::iterator_range<PreprocessingRecord::iterator> +ASTUnit::getLocalPreprocessingEntities() const { + if (isMainFileAST()) { + serialization::ModuleFile & + Mod = Reader->getModuleManager().getPrimaryModule(); + return Reader->getModulePreprocessedEntities(Mod); + } + + if (PreprocessingRecord *PPRec = PP->getPreprocessingRecord()) + return llvm::make_range(PPRec->local_begin(), PPRec->local_end()); + + return llvm::make_range(PreprocessingRecord::iterator(), + PreprocessingRecord::iterator()); +} + +bool ASTUnit::visitLocalTopLevelDecls(void *context, DeclVisitorFn Fn) { + if (isMainFileAST()) { + serialization::ModuleFile & + Mod = Reader->getModuleManager().getPrimaryModule(); + for (const auto *D : Reader->getModuleFileLevelDecls(Mod)) { + if (!Fn(context, D)) + 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; +} + +const FileEntry *ASTUnit::getPCHFile() { + if (!Reader) + return nullptr; + + serialization::ModuleFile *Mod = nullptr; + Reader->getModuleManager().visit([&Mod](serialization::ModuleFile &M) { + switch (M.Kind) { + case serialization::MK_ImplicitModule: + case serialization::MK_ExplicitModule: + case serialization::MK_PrebuiltModule: + return true; // skip dependencies. + case serialization::MK_PCH: + 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; + }); + if (Mod) + return Mod->File; + + return nullptr; +} + +bool ASTUnit::isModuleFile() const { + return isMainFileAST() && getLangOpts().isCompilingModule(); +} + +InputKind ASTUnit::getInputKind() const { + auto &LangOpts = getLangOpts(); + + InputKind::Language Lang; + if (LangOpts.OpenCL) + Lang = InputKind::OpenCL; + else if (LangOpts.CUDA) + Lang = InputKind::CUDA; + else if (LangOpts.RenderScript) + Lang = InputKind::RenderScript; + else if (LangOpts.CPlusPlus) + Lang = LangOpts.ObjC ? InputKind::ObjCXX : InputKind::CXX; + else + Lang = LangOpts.ObjC ? InputKind::ObjC : InputKind::C; + + InputKind::Format Fmt = InputKind::Source; + if (LangOpts.getCompilingModule() == LangOptions::CMK_ModuleMap) + Fmt = InputKind::ModuleMap; + + // We don't know if input was preprocessed. Assume not. + bool PP = false; + + return InputKind(Lang, Fmt, PP); +} + +#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() { Mutex = nullptr; } +ASTUnit::ConcurrencyState::~ConcurrencyState() {} +void ASTUnit::ConcurrencyState::start() {} +void ASTUnit::ConcurrencyState::finish() {} + +#endif // NDEBUG 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 000000000000..d77fd180ea0d --- /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 000000000000..1bfc25c4c778 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/ChainedIncludesSource.cpp @@ -0,0 +1,218 @@ +//===- 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/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/Lex/PreprocessorOptions.h" +#include "clang/Parse/ParseAST.h" +#include "clang/Sema/MultiplexExternalSemaSource.h" +#include "clang/Serialization/ASTReader.h" +#include "clang/Serialization/ASTWriter.h" +#include "llvm/Support/MemoryBuffer.h" + +using namespace clang; + +namespace { +class ChainedIncludesSourceImpl : public ExternalSemaSource { +public: + ChainedIncludesSourceImpl(std::vector<std::unique_ptr<CompilerInstance>> CIs) + : CIs(std::move(CIs)) {} + +protected: + //===----------------------------------------------------------------------===// + // ExternalASTSource interface. + //===----------------------------------------------------------------------===// + + /// Return the amount of memory used by memory buffers, breaking down + /// by heap-backed versus mmap'ed memory. + void getMemoryBufferSizes(MemoryBufferSizes &sizes) const override { + for (unsigned i = 0, e = CIs.size(); i != e; ++i) { + if (const ExternalASTSource *eSrc = + CIs[i]->getASTContext().getExternalSource()) { + eSrc->getMemoryBufferSizes(sizes); + } + } + } + +private: + std::vector<std::unique_ptr<CompilerInstance>> CIs; +}; + +/// Members of ChainedIncludesSource, factored out so we can initialize +/// them before we initialize the ExternalSemaSource base class. +struct ChainedIncludesSourceMembers { + ChainedIncludesSourceMembers( + std::vector<std::unique_ptr<CompilerInstance>> CIs, + IntrusiveRefCntPtr<ExternalSemaSource> FinalReader) + : Impl(std::move(CIs)), FinalReader(std::move(FinalReader)) {} + ChainedIncludesSourceImpl Impl; + IntrusiveRefCntPtr<ExternalSemaSource> FinalReader; +}; + +/// Use MultiplexExternalSemaSource to dispatch all ExternalSemaSource +/// calls to the final reader. +class ChainedIncludesSource + : private ChainedIncludesSourceMembers, + public MultiplexExternalSemaSource { +public: + ChainedIncludesSource(std::vector<std::unique_ptr<CompilerInstance>> CIs, + IntrusiveRefCntPtr<ExternalSemaSource> FinalReader) + : ChainedIncludesSourceMembers(std::move(CIs), std::move(FinalReader)), + MultiplexExternalSemaSource(Impl, *this->FinalReader) {} +}; +} + +static ASTReader * +createASTReader(CompilerInstance &CI, StringRef pchFile, + SmallVectorImpl<std::unique_ptr<llvm::MemoryBuffer>> &MemBufs, + SmallVectorImpl<std::string> &bufNames, + ASTDeserializationListener *deserialListener = nullptr) { + Preprocessor &PP = CI.getPreprocessor(); + std::unique_ptr<ASTReader> Reader; + Reader.reset(new ASTReader(PP, &CI.getASTContext(), + CI.getPCHContainerReader(), + /*Extensions=*/{ }, + /*isysroot=*/"", /*DisableValidation=*/true)); + for (unsigned ti = 0; ti < bufNames.size(); ++ti) { + StringRef sr(bufNames[ti]); + Reader->addInMemoryBuffer(sr, std::move(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.release(); + + case ASTReader::Failure: + case ASTReader::Missing: + case ASTReader::OutOfDate: + case ASTReader::VersionMismatch: + case ASTReader::ConfigurationMismatch: + case ASTReader::HadErrors: + break; + } + return nullptr; +} + +IntrusiveRefCntPtr<ExternalSemaSource> clang::createChainedIncludesSource( + CompilerInstance &CI, IntrusiveRefCntPtr<ExternalSemaSource> &Reader) { + + std::vector<std::string> &includes = CI.getPreprocessorOpts().ChainedIncludes; + assert(!includes.empty() && "No '-chain-include' in options!"); + + std::vector<std::unique_ptr<CompilerInstance>> CIs; + InputKind IK = CI.getFrontendOpts().Inputs[0].getKind(); + + SmallVector<std::unique_ptr<llvm::MemoryBuffer>, 4> SerialBufs; + SmallVector<std::string, 4> serialBufNames; + + for (unsigned i = 0, e = includes.size(); i != e; ++i) { + bool firstInclude = (i == 0); + std::unique_ptr<CompilerInvocation> CInvok; + CInvok.reset(new CompilerInvocation(CI.getInvocation())); + + CInvok->getPreprocessorOpts().ChainedIncludes.clear(); + CInvok->getPreprocessorOpts().ImplicitPCHInclude.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)); + + std::unique_ptr<CompilerInstance> Clang( + new CompilerInstance(CI.getPCHContainerOperations())); + Clang->setInvocation(std::move(CInvok)); + Clang->setDiagnostics(Diags.get()); + Clang->setTarget(TargetInfo::CreateTargetInfo( + Clang->getDiagnostics(), Clang->getInvocation().TargetOpts)); + Clang->createFileManager(); + Clang->createSourceManager(Clang->getFileManager()); + Clang->createPreprocessor(TU_Prefix); + Clang->getDiagnosticClient().BeginSourceFile(Clang->getLangOpts(), + &Clang->getPreprocessor()); + Clang->createASTContext(); + + auto Buffer = std::make_shared<PCHBuffer>(); + ArrayRef<std::shared_ptr<ModuleFileExtension>> Extensions; + auto consumer = llvm::make_unique<PCHGenerator>( + Clang->getPreprocessor(), "-", /*isysroot=*/"", Buffer, + Extensions, /*AllowASTWithErrors=*/true); + Clang->getASTContext().setASTMutationListener( + consumer->GetASTMutationListener()); + Clang->setASTConsumer(std::move(consumer)); + Clang->createSema(TU_Prefix, nullptr); + + if (firstInclude) { + Preprocessor &PP = Clang->getPreprocessor(); + PP.getBuiltinInfo().initializeBuiltins(PP.getIdentifierTable(), + PP.getLangOpts()); + } else { + assert(!SerialBufs.empty()); + SmallVector<std::unique_ptr<llvm::MemoryBuffer>, 4> Bufs; + // TODO: Pass through the existing MemoryBuffer instances instead of + // allocating new ones. + for (auto &SB : SerialBufs) + Bufs.push_back(llvm::MemoryBuffer::getMemBuffer(SB->getBuffer())); + std::string pchName = includes[i-1]; + llvm::raw_string_ostream os(pchName); + os << ".pch" << i-1; + serialBufNames.push_back(os.str()); + + IntrusiveRefCntPtr<ASTReader> Reader; + Reader = createASTReader( + *Clang, pchName, Bufs, serialBufNames, + Clang->getASTConsumer().GetASTDeserializationListener()); + if (!Reader) + return nullptr; + Clang->setModuleManager(Reader); + Clang->getASTContext().setExternalSource(Reader); + } + + if (!Clang->InitializeSourceManager(InputFile)) + return nullptr; + + ParseAST(Clang->getSema()); + Clang->getDiagnosticClient().EndSourceFile(); + assert(Buffer->IsComplete && "serialization did not complete"); + auto &serialAST = Buffer->Data; + SerialBufs.push_back(llvm::MemoryBuffer::getMemBufferCopy( + StringRef(serialAST.data(), serialAST.size()))); + serialAST.clear(); + CIs.push_back(std::move(Clang)); + } + + assert(!SerialBufs.empty()); + std::string pchName = includes.back() + ".pch-final"; + serialBufNames.push_back(pchName); + Reader = createASTReader(CI, pchName, SerialBufs, serialBufNames); + if (!Reader) + return nullptr; + + return IntrusiveRefCntPtr<ChainedIncludesSource>( + new ChainedIncludesSource(std::move(CIs), Reader)); +} 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 000000000000..f66674535423 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/CompilerInstance.cpp @@ -0,0 +1,2128 @@ +//===--- 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/CharInfo.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/MemoryBufferCache.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/Stack.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Basic/Version.h" +#include "clang/Config/config.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/Preprocessor.h" +#include "clang/Lex/PreprocessorOptions.h" +#include "clang/Sema/CodeCompleteConsumer.h" +#include "clang/Sema/Sema.h" +#include "clang/Serialization/ASTReader.h" +#include "clang/Serialization/GlobalModuleIndex.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/Support/BuryPointer.h" +#include "llvm/Support/CrashRecoveryContext.h" +#include "llvm/Support/Errc.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 <sys/stat.h> +#include <system_error> +#include <time.h> +#include <utility> + +using namespace clang; + +CompilerInstance::CompilerInstance( + std::shared_ptr<PCHContainerOperations> PCHContainerOps, + MemoryBufferCache *SharedPCMCache) + : ModuleLoader(/* BuildingModule = */ SharedPCMCache), + Invocation(new CompilerInvocation()), + PCMCache(SharedPCMCache ? SharedPCMCache : new MemoryBufferCache), + ThePCHContainerOperations(std::move(PCHContainerOps)) { + // Don't allow this to invalidate buffers in use by others. + if (SharedPCMCache) + getPCMCache().finalizeCurrentBuffers(); +} + +CompilerInstance::~CompilerInstance() { + assert(OutputFiles.empty() && "Still output files in flight?"); +} + +void CompilerInstance::setInvocation( + std::shared_ptr<CompilerInvocation> Value) { + Invocation = std::move(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::setAuxTarget(TargetInfo *Value) { AuxTarget = Value; } + +void CompilerInstance::setFileManager(FileManager *Value) { + FileMgr = Value; + if (Value) + VirtualFileSystem = Value->getVirtualFileSystem(); + else + VirtualFileSystem.reset(); +} + +void CompilerInstance::setSourceManager(SourceManager *Value) { + SourceMgr = Value; +} + +void CompilerInstance::setPreprocessor(std::shared_ptr<Preprocessor> Value) { + PP = std::move(Value); +} + +void CompilerInstance::setASTContext(ASTContext *Value) { + Context = Value; + + if (Context && Consumer) + getASTConsumer().Initialize(getASTContext()); +} + +void CompilerInstance::setSema(Sema *S) { + TheSema.reset(S); +} + +void CompilerInstance::setASTConsumer(std::unique_ptr<ASTConsumer> Value) { + Consumer = std::move(Value); + + if (Context && Consumer) + getASTConsumer().Initialize(getASTContext()); +} + +void CompilerInstance::setCodeCompletionConsumer(CodeCompleteConsumer *Value) { + CompletionConsumer.reset(Value); +} + +std::unique_ptr<Sema> CompilerInstance::takeSema() { + return std::move(TheSema); +} + +IntrusiveRefCntPtr<ASTReader> CompilerInstance::getModuleManager() const { + return ModuleManager; +} +void CompilerInstance::setModuleManager(IntrusiveRefCntPtr<ASTReader> Reader) { + assert(PCMCache.get() == &Reader->getModuleManager().getPCMCache() && + "Expected ASTReader to use the same PCM cache"); + ModuleManager = std::move(Reader); +} + +std::shared_ptr<ModuleDependencyCollector> +CompilerInstance::getModuleDepCollector() const { + return ModuleDepCollector; +} + +void CompilerInstance::setModuleDepCollector( + std::shared_ptr<ModuleDependencyCollector> Collector) { + ModuleDepCollector = std::move(Collector); +} + +static void collectHeaderMaps(const HeaderSearch &HS, + std::shared_ptr<ModuleDependencyCollector> MDC) { + SmallVector<std::string, 4> HeaderMapFileNames; + HS.getHeaderMapFileNames(HeaderMapFileNames); + for (auto &Name : HeaderMapFileNames) + MDC->addFile(Name); +} + +static void collectIncludePCH(CompilerInstance &CI, + std::shared_ptr<ModuleDependencyCollector> MDC) { + const PreprocessorOptions &PPOpts = CI.getPreprocessorOpts(); + if (PPOpts.ImplicitPCHInclude.empty()) + return; + + StringRef PCHInclude = PPOpts.ImplicitPCHInclude; + FileManager &FileMgr = CI.getFileManager(); + const DirectoryEntry *PCHDir = FileMgr.getDirectory(PCHInclude); + if (!PCHDir) { + MDC->addFile(PCHInclude); + return; + } + + std::error_code EC; + SmallString<128> DirNative; + llvm::sys::path::native(PCHDir->getName(), DirNative); + llvm::vfs::FileSystem &FS = *FileMgr.getVirtualFileSystem(); + SimpleASTReaderListener Validator(CI.getPreprocessor()); + for (llvm::vfs::directory_iterator Dir = FS.dir_begin(DirNative, EC), DirEnd; + Dir != DirEnd && !EC; Dir.increment(EC)) { + // Check whether this is an AST file. ASTReader::isAcceptableASTFile is not + // used here since we're not interested in validating the PCH at this time, + // but only to check whether this is a file containing an AST. + if (!ASTReader::readASTFileControlBlock( + Dir->path(), FileMgr, CI.getPCHContainerReader(), + /*FindModuleFileExtensions=*/false, Validator, + /*ValidateDiagnosticOptions=*/false)) + MDC->addFile(Dir->path()); + } +} + +static void collectVFSEntries(CompilerInstance &CI, + std::shared_ptr<ModuleDependencyCollector> MDC) { + if (CI.getHeaderSearchOpts().VFSOverlayFiles.empty()) + return; + + // Collect all VFS found. + SmallVector<llvm::vfs::YAMLVFSEntry, 16> VFSEntries; + for (const std::string &VFSFile : CI.getHeaderSearchOpts().VFSOverlayFiles) { + llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buffer = + llvm::MemoryBuffer::getFile(VFSFile); + if (!Buffer) + return; + llvm::vfs::collectVFSFromYAML(std::move(Buffer.get()), + /*DiagHandler*/ nullptr, VFSFile, VFSEntries); + } + + for (auto &E : VFSEntries) + MDC->addFile(E.VPath, E.RPath); +} + +// Diagnostics +static void SetUpDiagnosticLog(DiagnosticOptions *DiagOpts, + const CodeGenOptions *CodeGenOpts, + DiagnosticsEngine &Diags) { + std::error_code EC; + std::unique_ptr<raw_ostream> StreamOwner; + raw_ostream *OS = &llvm::errs(); + if (DiagOpts->DiagnosticLogFile != "-") { + // Create the output stream. + auto FileOS = llvm::make_unique<llvm::raw_fd_ostream>( + DiagOpts->DiagnosticLogFile, EC, + llvm::sys::fs::F_Append | llvm::sys::fs::F_Text); + if (EC) { + Diags.Report(diag::warn_fe_cc_log_diagnostics_failure) + << DiagOpts->DiagnosticLogFile << EC.message(); + } else { + FileOS->SetUnbuffered(); + OS = FileOS.get(); + StreamOwner = std::move(FileOS); + } + } + + // Chain in the diagnostic client which will log the diagnostics. + auto Logger = llvm::make_unique<LogDiagnosticPrinter>(*OS, DiagOpts, + std::move(StreamOwner)); + if (CodeGenOpts) + Logger->setDwarfDebugFlags(CodeGenOpts->DwarfDebugFlags); + assert(Diags.ownsClient()); + Diags.setClient( + new ChainedDiagnosticConsumer(Diags.takeClient(), std::move(Logger))); +} + +static void SetupSerializedDiagnostics(DiagnosticOptions *DiagOpts, + DiagnosticsEngine &Diags, + StringRef OutputFile) { + auto SerializedConsumer = + clang::serialized_diags::create(OutputFile, DiagOpts); + + if (Diags.ownsClient()) { + Diags.setClient(new ChainedDiagnosticConsumer( + Diags.takeClient(), std::move(SerializedConsumer))); + } else { + Diags.setClient(new ChainedDiagnosticConsumer( + Diags.getClient(), std::move(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 + +FileManager *CompilerInstance::createFileManager() { + if (!hasVirtualFileSystem()) { + IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS = + createVFSFromCompilerInvocation(getInvocation(), getDiagnostics()); + setVirtualFileSystem(VFS); + } + FileMgr = new FileManager(getFileSystemOpts(), VirtualFileSystem); + return FileMgr.get(); +} + +// Source Manager + +void CompilerInstance::createSourceManager(FileManager &FileMgr) { + SourceMgr = new SourceManager(getDiagnostics(), FileMgr); +} + +// 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 (const auto &RB : InitOpts.RemappedFileBuffers) { + // Create the file entry for the file that we're mapping from. + const FileEntry *FromFile = + FileMgr.getVirtualFile(RB.first, RB.second->getBufferSize(), 0); + if (!FromFile) { + Diags.Report(diag::err_fe_remap_missing_from_file) << RB.first; + if (!InitOpts.RetainRemappedFileBuffers) + delete RB.second; + continue; + } + + // Override the contents of the "from" file with the contents of + // the "to" file. + SourceMgr.overrideFileContents(FromFile, RB.second, + InitOpts.RetainRemappedFileBuffers); + } + + // Remap files in the source manager (with other files). + for (const auto &RF : InitOpts.RemappedFiles) { + // Find the file that we're mapping to. + const FileEntry *ToFile = FileMgr.getFile(RF.second); + if (!ToFile) { + Diags.Report(diag::err_fe_remap_missing_to_file) << RF.first << RF.second; + continue; + } + + // Create the file entry for the file that we're mapping from. + const FileEntry *FromFile = + FileMgr.getVirtualFile(RF.first, ToFile->getSize(), 0); + if (!FromFile) { + Diags.Report(diag::err_fe_remap_missing_from_file) << RF.first; + continue; + } + + // Override the contents of the "from" file with the contents of + // the "to" file. + SourceMgr.overrideFileContents(FromFile, ToFile); + } + + SourceMgr.setOverridenFilesKeepOriginalName( + InitOpts.RemappedFilesKeepOriginalName); +} + +// Preprocessor + +void CompilerInstance::createPreprocessor(TranslationUnitKind TUKind) { + const PreprocessorOptions &PPOpts = getPreprocessorOpts(); + + // The module manager holds a reference to the old preprocessor (if any). + ModuleManager.reset(); + + // Create the Preprocessor. + HeaderSearch *HeaderInfo = + new HeaderSearch(getHeaderSearchOptsPtr(), getSourceManager(), + getDiagnostics(), getLangOpts(), &getTarget()); + PP = std::make_shared<Preprocessor>( + Invocation->getPreprocessorOptsPtr(), getDiagnostics(), getLangOpts(), + getSourceManager(), getPCMCache(), *HeaderInfo, *this, + /*IdentifierInfoLookup=*/nullptr, + /*OwnsHeaderSearch=*/true, TUKind); + getTarget().adjust(getLangOpts()); + PP->Initialize(getTarget(), getAuxTarget()); + + if (PPOpts.DetailedRecord) + PP->createPreprocessingRecord(); + + // Apply remappings to the source manager. + InitializeFileRemapping(PP->getDiagnostics(), PP->getSourceManager(), + PP->getFileManager(), PPOpts); + + // Predefine macros and configure the preprocessor. + InitializePreprocessor(*PP, PPOpts, getPCHContainerReader(), + getFrontendOpts()); + + // Initialize the header search object. In CUDA compilations, we use the aux + // triple (the host triple) to initialize our header search, since we need to + // find the host headers in order to compile the CUDA code. + const llvm::Triple *HeaderSearchTriple = &PP->getTargetInfo().getTriple(); + if (PP->getTargetInfo().getTriple().getOS() == llvm::Triple::CUDA && + PP->getAuxTargetInfo()) + HeaderSearchTriple = &PP->getAuxTargetInfo()->getTriple(); + + ApplyHeaderSearchOptions(PP->getHeaderSearchInfo(), getHeaderSearchOpts(), + PP->getLangOpts(), *HeaderSearchTriple); + + PP->setPreprocessedOutput(getPreprocessorOutputOpts().ShowCPP); + + if (PP->getLangOpts().Modules && PP->getLangOpts().ImplicitModules) + PP->getHeaderSearchInfo().setModuleCachePath(getSpecificModuleCachePath()); + + // Handle generating dependencies, if requested. + const DependencyOutputOptions &DepOpts = getDependencyOutputOpts(); + if (!DepOpts.OutputFile.empty()) + TheDependencyFileGenerator.reset( + DependencyFileGenerator::CreateAndAttachToPreprocessor(*PP, DepOpts)); + if (!DepOpts.DOTOutputFile.empty()) + AttachDependencyGraphGen(*PP, DepOpts.DOTOutputFile, + getHeaderSearchOpts().Sysroot); + + // If we don't have a collector, but we are collecting module dependencies, + // then we're the top level compiler instance and need to create one. + if (!ModuleDepCollector && !DepOpts.ModuleDependencyOutputDir.empty()) { + ModuleDepCollector = std::make_shared<ModuleDependencyCollector>( + DepOpts.ModuleDependencyOutputDir); + } + + // If there is a module dep collector, register with other dep collectors + // and also (a) collect header maps and (b) TODO: input vfs overlay files. + if (ModuleDepCollector) { + addDependencyCollector(ModuleDepCollector); + collectHeaderMaps(PP->getHeaderSearchInfo(), ModuleDepCollector); + collectIncludePCH(*this, ModuleDepCollector); + collectVFSEntries(*this, ModuleDepCollector); + } + + for (auto &Listener : DependencyCollectors) + Listener->attachToPreprocessor(*PP); + + // Handle generating header include information, if requested. + if (DepOpts.ShowHeaderIncludes) + AttachHeaderIncludeGen(*PP, DepOpts); + if (!DepOpts.HeaderIncludeOutputFile.empty()) { + StringRef OutputPath = DepOpts.HeaderIncludeOutputFile; + if (OutputPath == "-") + OutputPath = ""; + AttachHeaderIncludeGen(*PP, DepOpts, + /*ShowAllHeaders=*/true, OutputPath, + /*ShowDepth=*/false); + } + + if (DepOpts.ShowIncludesDest != ShowIncludesDestination::None) { + AttachHeaderIncludeGen(*PP, DepOpts, + /*ShowAllHeaders=*/true, /*OutputPath=*/"", + /*ShowDepth=*/true, /*MSStyle=*/true); + } +} + +std::string CompilerInstance::getSpecificModuleCachePath() { + // Set up the module path, including the hash for the + // module-creation options. + SmallString<256> SpecificModuleCache(getHeaderSearchOpts().ModuleCachePath); + if (!SpecificModuleCache.empty() && !getHeaderSearchOpts().DisableModuleHash) + llvm::sys::path::append(SpecificModuleCache, + getInvocation().getModuleHash()); + return SpecificModuleCache.str(); +} + +// ASTContext + +void CompilerInstance::createASTContext() { + Preprocessor &PP = getPreprocessor(); + auto *Context = new ASTContext(getLangOpts(), PP.getSourceManager(), + PP.getIdentifierTable(), PP.getSelectorTable(), + PP.getBuiltinInfo()); + Context->InitBuiltinTypes(getTarget(), getAuxTarget()); + setASTContext(Context); +} + +// ExternalASTSource + +void CompilerInstance::createPCHExternalASTSource( + StringRef Path, bool DisablePCHValidation, bool AllowPCHWithCompilerErrors, + void *DeserializationListener, bool OwnDeserializationListener) { + bool Preamble = getPreprocessorOpts().PrecompiledPreambleBytes.first != 0; + ModuleManager = createPCHExternalASTSource( + Path, getHeaderSearchOpts().Sysroot, DisablePCHValidation, + AllowPCHWithCompilerErrors, getPreprocessor(), getASTContext(), + getPCHContainerReader(), + getFrontendOpts().ModuleFileExtensions, + TheDependencyFileGenerator.get(), + DependencyCollectors, + DeserializationListener, + OwnDeserializationListener, Preamble, + getFrontendOpts().UseGlobalModuleIndex); +} + +IntrusiveRefCntPtr<ASTReader> CompilerInstance::createPCHExternalASTSource( + StringRef Path, StringRef Sysroot, bool DisablePCHValidation, + bool AllowPCHWithCompilerErrors, Preprocessor &PP, ASTContext &Context, + const PCHContainerReader &PCHContainerRdr, + ArrayRef<std::shared_ptr<ModuleFileExtension>> Extensions, + DependencyFileGenerator *DependencyFile, + ArrayRef<std::shared_ptr<DependencyCollector>> DependencyCollectors, + void *DeserializationListener, bool OwnDeserializationListener, + bool Preamble, bool UseGlobalModuleIndex) { + HeaderSearchOptions &HSOpts = PP.getHeaderSearchInfo().getHeaderSearchOpts(); + + IntrusiveRefCntPtr<ASTReader> Reader(new ASTReader( + PP, &Context, PCHContainerRdr, Extensions, + Sysroot.empty() ? "" : Sysroot.data(), DisablePCHValidation, + AllowPCHWithCompilerErrors, /*AllowConfigurationMismatch*/ false, + HSOpts.ModulesValidateSystemHeaders, UseGlobalModuleIndex)); + + // We need the external source to be set up before we read the AST, because + // eagerly-deserialized declarations may use it. + Context.setExternalSource(Reader.get()); + + Reader->setDeserializationListener( + static_cast<ASTDeserializationListener *>(DeserializationListener), + /*TakeOwnership=*/OwnDeserializationListener); + + if (DependencyFile) + DependencyFile->AttachToASTReader(*Reader); + for (auto &Listener : DependencyCollectors) + Listener->attachToASTReader(*Reader); + + 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; + + 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; + } + + Context.setExternalSource(nullptr); + return nullptr; +} + +// Code Completion + +static bool EnableCodeCompletion(Preprocessor &PP, + StringRef 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(nullptr); + return; + } + + if (CompletionConsumer->isOutputBinary() && + llvm::sys::ChangeStdoutToBinary()) { + getPreprocessor().getDiagnostics().Report(diag::err_fe_stdout_binary); + setCodeCompletionConsumer(nullptr); + } +} + +void CompilerInstance::createFrontendTimer() { + FrontendTimerGroup.reset( + new llvm::TimerGroup("frontend", "Clang front-end time report")); + FrontendTimer.reset( + new llvm::Timer("frontend", "Clang front-end timer", + *FrontendTimerGroup)); +} + +CodeCompleteConsumer * +CompilerInstance::createCodeCompletionConsumer(Preprocessor &PP, + StringRef Filename, + unsigned Line, + unsigned Column, + const CodeCompleteOptions &Opts, + raw_ostream &OS) { + if (EnableCodeCompletion(PP, Filename, Line, Column)) + return nullptr; + + // 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)); + // Attach the external sema source if there is any. + if (ExternalSemaSrc) { + TheSema->addExternalSource(ExternalSemaSrc.get()); + ExternalSemaSrc->InitializeSema(*TheSema); + } +} + +// Output Files + +void CompilerInstance::addOutputFile(OutputFile &&OutFile) { + OutputFiles.push_back(std::move(OutFile)); +} + +void CompilerInstance::clearOutputFiles(bool EraseFiles) { + for (OutputFile &OF : OutputFiles) { + if (!OF.TempFilename.empty()) { + if (EraseFiles) { + llvm::sys::fs::remove(OF.TempFilename); + } else { + SmallString<128> NewOutFile(OF.Filename); + + // If '-working-directory' was passed, the output filename should be + // relative to that. + FileMgr->FixupRelativePath(NewOutFile); + if (std::error_code ec = + llvm::sys::fs::rename(OF.TempFilename, NewOutFile)) { + getDiagnostics().Report(diag::err_unable_to_rename_temp) + << OF.TempFilename << OF.Filename << ec.message(); + + llvm::sys::fs::remove(OF.TempFilename); + } + } + } else if (!OF.Filename.empty() && EraseFiles) + llvm::sys::fs::remove(OF.Filename); + } + OutputFiles.clear(); + if (DeleteBuiltModules) { + for (auto &Module : BuiltModules) + llvm::sys::fs::remove(Module.second); + BuiltModules.clear(); + } + NonSeekStream.reset(); +} + +std::unique_ptr<raw_pwrite_stream> +CompilerInstance::createDefaultOutputFile(bool Binary, StringRef InFile, + StringRef Extension) { + return createOutputFile(getFrontendOpts().OutputFile, Binary, + /*RemoveFileOnSignal=*/true, InFile, Extension, + /*UseTemporary=*/true); +} + +std::unique_ptr<raw_pwrite_stream> CompilerInstance::createNullOutputFile() { + return llvm::make_unique<llvm::raw_null_ostream>(); +} + +std::unique_ptr<raw_pwrite_stream> +CompilerInstance::createOutputFile(StringRef OutputPath, bool Binary, + bool RemoveFileOnSignal, StringRef InFile, + StringRef Extension, bool UseTemporary, + bool CreateMissingDirectories) { + std::string OutputPathName, TempPathName; + std::error_code EC; + std::unique_ptr<raw_pwrite_stream> OS = createOutputFile( + OutputPath, EC, Binary, RemoveFileOnSignal, InFile, Extension, + UseTemporary, CreateMissingDirectories, &OutputPathName, &TempPathName); + if (!OS) { + getDiagnostics().Report(diag::err_fe_unable_to_open_output) << OutputPath + << EC.message(); + return nullptr; + } + + // Add the output file -- but don't try to remove "-", since this means we are + // using stdin. + addOutputFile( + OutputFile((OutputPathName != "-") ? OutputPathName : "", TempPathName)); + + return OS; +} + +std::unique_ptr<llvm::raw_pwrite_stream> CompilerInstance::createOutputFile( + StringRef OutputPath, std::error_code &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 = "-"; + } + + std::unique_ptr<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)) { + Error = make_error_code(llvm::errc::operation_not_permitted); + return nullptr; + } + + // 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. + // Insert -%%%%%%%% before the extension (if any), and because some tools + // (noticeable, clang's own GlobalModuleIndex.cpp) glob for build + // artifacts, also append .tmp. + StringRef OutputExtension = llvm::sys::path::extension(OutFile); + SmallString<128> TempPath = + StringRef(OutFile).drop_back(OutputExtension.size()); + TempPath += "-%%%%%%%%"; + TempPath += OutputExtension; + TempPath += ".tmp"; + int fd; + std::error_code EC = + llvm::sys::fs::createUniqueFile(TempPath, 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, 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, Error, + (Binary ? llvm::sys::fs::F_None : llvm::sys::fs::F_Text))); + if (Error) + return nullptr; + } + + // 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; + + if (!Binary || OS->supportsSeeking()) + return std::move(OS); + + auto B = llvm::make_unique<llvm::buffer_ostream>(*OS); + assert(!NonSeekStream); + NonSeekStream = std::move(OS); + return std::move(B); +} + +// Initialization Utilities + +bool CompilerInstance::InitializeSourceManager(const FrontendInputFile &Input){ + return InitializeSourceManager( + Input, getDiagnostics(), getFileManager(), getSourceManager(), + hasPreprocessor() ? &getPreprocessor().getHeaderSearchInfo() : nullptr, + getDependencyOutputOpts(), getFrontendOpts()); +} + +// static +bool CompilerInstance::InitializeSourceManager( + const FrontendInputFile &Input, DiagnosticsEngine &Diags, + FileManager &FileMgr, SourceManager &SourceMgr, HeaderSearch *HS, + DependencyOutputOptions &DepOpts, const FrontendOptions &Opts) { + SrcMgr::CharacteristicKind Kind = + Input.getKind().getFormat() == InputKind::ModuleMap + ? Input.isSystem() ? SrcMgr::C_System_ModuleMap + : SrcMgr::C_User_ModuleMap + : Input.isSystem() ? SrcMgr::C_System : SrcMgr::C_User; + + if (Input.isBuffer()) { + SourceMgr.setMainFileID(SourceMgr.createFileID(SourceManager::Unowned, + Input.getBuffer(), Kind)); + assert(SourceMgr.getMainFileID().isValid() && + "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()) { + auto MB = FileMgr.getBufferForFile(File, /*isVolatile=*/true); + if (MB) { + // Create a new virtual file that will have the correct size. + File = FileMgr.getVirtualFile(InputFile, (*MB)->getBufferSize(), 0); + SourceMgr.overrideFileContents(File, std::move(*MB)); + } else { + Diags.Report(diag::err_cannot_open_file) << InputFile + << MB.getError().message(); + return false; + } + } + + SourceMgr.setMainFileID( + SourceMgr.createFileID(File, SourceLocation(), Kind)); + } else { + llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> SBOrErr = + llvm::MemoryBuffer::getSTDIN(); + if (std::error_code EC = SBOrErr.getError()) { + Diags.Report(diag::err_fe_error_reading_stdin) << EC.message(); + return false; + } + std::unique_ptr<llvm::MemoryBuffer> SB = std::move(SBOrErr.get()); + + const FileEntry *File = FileMgr.getVirtualFile(SB->getBufferIdentifier(), + SB->getBufferSize(), 0); + SourceMgr.setMainFileID( + SourceMgr.createFileID(File, SourceLocation(), Kind)); + SourceMgr.overrideFileContents(File, std::move(SB)); + } + + assert(SourceMgr.getMainFileID().isValid() && + "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(); + + if (!Act.PrepareToExecute(*this)) + return false; + + // Create the target instance. + setTarget(TargetInfo::CreateTargetInfo(getDiagnostics(), + getInvocation().TargetOpts)); + if (!hasTarget()) + return false; + + // Create TargetInfo for the other side of CUDA and OpenMP compilation. + if ((getLangOpts().CUDA || getLangOpts().OpenMPIsDevice) && + !getFrontendOpts().AuxTriple.empty()) { + auto TO = std::make_shared<TargetOptions>(); + TO->Triple = llvm::Triple::normalize(getFrontendOpts().AuxTriple); + TO->HostTriple = getTarget().getTriple().str(); + setAuxTarget(TargetInfo::CreateTargetInfo(getDiagnostics(), TO)); + } + + // 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().adjust(getLangOpts()); + + // Adjust target options based on codegen options. + getTarget().adjustTargetOptions(getCodeGenOpts(), getTargetOpts()); + + // 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 " << BACKEND_PACKAGE_STRING + << " default target " << llvm::sys::getDefaultTargetTriple() << "\n"; + + if (getFrontendOpts().ShowTimers) + createFrontendTimer(); + + if (getFrontendOpts().ShowStats || !getFrontendOpts().StatsFile.empty()) + llvm::EnableStatistics(false); + + for (const FrontendInputFile &FIF : getFrontendOpts().Inputs) { + // Reset the ID tables if we are reusing the SourceManager and parsing + // regular files. + if (hasSourceManager() && !Act.isModelParsingAction()) + getSourceManager().clearIDTables(); + + if (Act.BeginSourceFile(*this, FIF)) { + 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"; + if (getLangOpts().CUDA) { + if (!getLangOpts().CUDAIsDevice) { + OS << " when compiling for host"; + } else { + OS << " when compiling for " << getTargetOpts().CPU; + } + } + OS << ".\n"; + } + } + + if (getFrontendOpts().ShowStats) { + if (hasFileManager()) { + getFileManager().PrintStats(); + OS << '\n'; + } + llvm::PrintStatistics(OS); + } + StringRef StatsFile = getFrontendOpts().StatsFile; + if (!StatsFile.empty()) { + std::error_code EC; + auto StatS = llvm::make_unique<llvm::raw_fd_ostream>(StatsFile, EC, + llvm::sys::fs::F_Text); + if (EC) { + getDiagnostics().Report(diag::warn_fe_unable_to_open_stats_file) + << StatsFile << EC.message(); + } else { + llvm::PrintStatisticsJSON(*StatS); + } + } + + return !getDiagnostics().getClient()->getNumErrors(); +} + +/// Determine the appropriate source input kind based on language +/// options. +static InputKind::Language getLanguageFromOptions(const LangOptions &LangOpts) { + if (LangOpts.OpenCL) + return InputKind::OpenCL; + if (LangOpts.CUDA) + return InputKind::CUDA; + if (LangOpts.ObjC) + return LangOpts.CPlusPlus ? InputKind::ObjCXX : InputKind::ObjC; + return LangOpts.CPlusPlus ? InputKind::CXX : InputKind::C; +} + +/// Compile a module file for the given module, using the options +/// provided by the importing compiler instance. Returns true if the module +/// was built without errors. +static bool +compileModuleImpl(CompilerInstance &ImportingInstance, SourceLocation ImportLoc, + StringRef ModuleName, FrontendInputFile Input, + StringRef OriginalModuleMapFile, StringRef ModuleFileName, + llvm::function_ref<void(CompilerInstance &)> PreBuildStep = + [](CompilerInstance &) {}, + llvm::function_ref<void(CompilerInstance &)> PostBuildStep = + [](CompilerInstance &) {}) { + // Construct a compiler invocation for creating this module. + auto Invocation = + std::make_shared<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. + HeaderSearchOptions &HSOpts = Invocation->getHeaderSearchOpts(); + PPOpts.Macros.erase( + std::remove_if(PPOpts.Macros.begin(), PPOpts.Macros.end(), + [&HSOpts](const std::pair<std::string, bool> &def) { + StringRef MacroDef = def.first; + return HSOpts.ModulesIgnoreMacros.count( + llvm::CachedHashString(MacroDef.split('=').first)) > 0; + }), + PPOpts.Macros.end()); + + // If the original compiler invocation had -fmodule-name, pass it through. + Invocation->getLangOpts()->ModuleName = + ImportingInstance.getInvocation().getLangOpts()->ModuleName; + + // Note the name of the module we're building. + Invocation->getLangOpts()->CurrentModule = ModuleName; + + // 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 = + std::make_shared<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.BuildingImplicitModule = true; + FrontendOpts.OriginalModuleMap = OriginalModuleMapFile; + // Force implicitly-built modules to hash the content of the module file. + HSOpts.ModulesHashContent = true; + FrontendOpts.Inputs = {Input}; + + // 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. Since we're sharing a PCMCache, + // CompilerInstance::CompilerInstance is responsible for finalizing the + // buffers to prevent use-after-frees. + CompilerInstance Instance(ImportingInstance.getPCHContainerOperations(), + &ImportingInstance.getPreprocessor().getPCMCache()); + auto &Inv = *Invocation; + Instance.setInvocation(std::move(Invocation)); + + Instance.createDiagnostics(new ForwardingDiagnosticConsumer( + ImportingInstance.getDiagnosticClient()), + /*ShouldOwnClient=*/true); + + Instance.setVirtualFileSystem(&ImportingInstance.getVirtualFileSystem()); + + // Note that this module is part of the module build stack, so that we + // can detect cycles in the module graph. + Instance.setFileManager(&ImportingInstance.getFileManager()); + Instance.createSourceManager(Instance.getFileManager()); + SourceManager &SourceMgr = Instance.getSourceManager(); + SourceMgr.setModuleBuildStack( + ImportingInstance.getSourceManager().getModuleBuildStack()); + SourceMgr.pushModuleBuildStack(ModuleName, + FullSourceLoc(ImportLoc, ImportingInstance.getSourceManager())); + + // If we're collecting module dependencies, we need to share a collector + // between all of the module CompilerInstances. Other than that, we don't + // want to produce any dependency output from the module build. + Instance.setModuleDepCollector(ImportingInstance.getModuleDepCollector()); + Inv.getDependencyOutputOpts() = DependencyOutputOptions(); + + ImportingInstance.getDiagnostics().Report(ImportLoc, + diag::remark_module_build) + << ModuleName << ModuleFileName; + + PreBuildStep(Instance); + + // Execute the action to actually build the module in-place. Use a separate + // thread so that we get a stack large enough. + llvm::CrashRecoveryContext CRC; + CRC.RunSafelyOnThread( + [&]() { + GenerateModuleFromModuleMapAction Action; + Instance.ExecuteAction(Action); + }, + DesiredStackSize); + + PostBuildStep(Instance); + + ImportingInstance.getDiagnostics().Report(ImportLoc, + diag::remark_module_build_done) + << ModuleName; + + // 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); + + return !Instance.getDiagnostics().hasErrorOccurred(); +} + +static const FileEntry *getPublicModuleMap(const FileEntry *File, + FileManager &FileMgr) { + StringRef Filename = llvm::sys::path::filename(File->getName()); + SmallString<128> PublicFilename(File->getDir()->getName()); + if (Filename == "module_private.map") + llvm::sys::path::append(PublicFilename, "module.map"); + else if (Filename == "module.private.modulemap") + llvm::sys::path::append(PublicFilename, "module.modulemap"); + else + return nullptr; + return FileMgr.getFile(PublicFilename); +} + +/// Compile a module file for the given module, using the options +/// provided by the importing compiler instance. Returns true if the module +/// was built without errors. +static bool compileModuleImpl(CompilerInstance &ImportingInstance, + SourceLocation ImportLoc, + Module *Module, + StringRef ModuleFileName) { + InputKind IK(getLanguageFromOptions(ImportingInstance.getLangOpts()), + InputKind::ModuleMap); + + // Get or create the module map that we'll use to build this module. + ModuleMap &ModMap + = ImportingInstance.getPreprocessor().getHeaderSearchInfo().getModuleMap(); + bool Result; + if (const FileEntry *ModuleMapFile = + ModMap.getContainingModuleMapFile(Module)) { + // Canonicalize compilation to start with the public module map. This is + // vital for submodules declarations in the private module maps to be + // correctly parsed when depending on a top level module in the public one. + if (const FileEntry *PublicMMFile = getPublicModuleMap( + ModuleMapFile, ImportingInstance.getFileManager())) + ModuleMapFile = PublicMMFile; + + // Use the module map where this module resides. + Result = compileModuleImpl( + ImportingInstance, ImportLoc, Module->getTopLevelModuleName(), + FrontendInputFile(ModuleMapFile->getName(), IK, +Module->IsSystem), + ModMap.getModuleMapFileForUniquing(Module)->getName(), + ModuleFileName); + } else { + // FIXME: We only need to fake up an input file here as a way of + // transporting the module's directory to the module map parser. We should + // be able to do that more directly, and parse from a memory buffer without + // inventing this file. + SmallString<128> FakeModuleMapFile(Module->Directory->getName()); + llvm::sys::path::append(FakeModuleMapFile, "__inferred_module.map"); + + std::string InferredModuleMapContent; + llvm::raw_string_ostream OS(InferredModuleMapContent); + Module->print(OS); + OS.flush(); + + Result = compileModuleImpl( + ImportingInstance, ImportLoc, Module->getTopLevelModuleName(), + FrontendInputFile(FakeModuleMapFile, IK, +Module->IsSystem), + ModMap.getModuleMapFileForUniquing(Module)->getName(), + ModuleFileName, + [&](CompilerInstance &Instance) { + std::unique_ptr<llvm::MemoryBuffer> ModuleMapBuffer = + llvm::MemoryBuffer::getMemBuffer(InferredModuleMapContent); + ModuleMapFile = Instance.getFileManager().getVirtualFile( + FakeModuleMapFile, InferredModuleMapContent.size(), 0); + Instance.getSourceManager().overrideFileContents( + ModuleMapFile, std::move(ModuleMapBuffer)); + }); + } + + // 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); + } + + return Result; +} + +static bool compileAndLoadModule(CompilerInstance &ImportingInstance, + SourceLocation ImportLoc, + SourceLocation ModuleNameLoc, Module *Module, + StringRef ModuleFileName) { + DiagnosticsEngine &Diags = ImportingInstance.getDiagnostics(); + + auto diagnoseBuildFailure = [&] { + Diags.Report(ModuleNameLoc, diag::err_module_not_built) + << Module->Name << SourceRange(ImportLoc, ModuleNameLoc); + }; + + // 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); + + while (1) { + unsigned ModuleLoadCapabilities = ASTReader::ARR_Missing; + llvm::LockFileManager Locked(ModuleFileName); + switch (Locked) { + case llvm::LockFileManager::LFS_Error: + // PCMCache takes care of correctness and locks are only necessary for + // performance. Fallback to building the module in case of any lock + // related errors. + Diags.Report(ModuleNameLoc, diag::remark_module_lock_failure) + << Module->Name << Locked.getErrorMessage(); + // Clear out any potential leftover. + Locked.unsafeRemoveLockFile(); + LLVM_FALLTHROUGH; + case llvm::LockFileManager::LFS_Owned: + // We're responsible for building the module ourselves. + if (!compileModuleImpl(ImportingInstance, ModuleNameLoc, Module, + ModuleFileName)) { + diagnoseBuildFailure(); + return false; + } + break; + + case llvm::LockFileManager::LFS_Shared: + // Someone else is responsible for building the module. Wait for them to + // finish. + switch (Locked.waitForUnlock()) { + case llvm::LockFileManager::Res_Success: + ModuleLoadCapabilities |= ASTReader::ARR_OutOfDate; + break; + case llvm::LockFileManager::Res_OwnerDied: + continue; // try again to get the lock. + case llvm::LockFileManager::Res_Timeout: + // Since PCMCache takes care of correctness, we try waiting for another + // process to complete the build so clang does not do it done twice. If + // case of timeout, build it ourselves. + Diags.Report(ModuleNameLoc, diag::remark_module_lock_timeout) + << Module->Name; + // Clear the lock file so that future invocations can make progress. + Locked.unsafeRemoveLockFile(); + continue; + } + break; + } + + // Try to read the module file, now that we've compiled it. + ASTReader::ASTReadResult ReadResult = + ImportingInstance.getModuleManager()->ReadAST( + ModuleFileName, serialization::MK_ImplicitModule, ImportLoc, + ModuleLoadCapabilities); + + if (ReadResult == ASTReader::OutOfDate && + Locked == llvm::LockFileManager::LFS_Shared) { + // The module may be out of date in the presence of file system races, + // or if one of its imports depends on header search paths that are not + // consistent with this ImportingInstance. Try again... + continue; + } else if (ReadResult == ASTReader::Missing) { + diagnoseBuildFailure(); + } else if (ReadResult != ASTReader::Success && + !Diags.hasErrorOccurred()) { + // The ASTReader didn't diagnose the error, so conservatively report it. + diagnoseBuildFailure(); + } + return ReadResult == ASTReader::Success; + } +} + +/// 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; + auto *LatestLocalMD = PP.getLocalMacroDirectiveHistory(Id); + + // Find the macro definition from the command line. + MacroInfo *CmdLineDefinition = nullptr; + for (auto *MD = LatestLocalMD; MD; MD = MD->getPrevious()) { + // We only care about the predefines buffer. + FileID FID = SourceMgr.getFileID(MD->getLocation()); + if (FID.isInvalid() || FID != PP.getPredefinesFileID()) + continue; + if (auto *DMD = dyn_cast<DefMacroDirective>(MD)) + CmdLineDefinition = DMD->getMacroInfo(); + break; + } + + auto *CurrentDefinition = PP.getMacroInfo(Id); + if (CurrentDefinition == CmdLineDefinition) { + // Macro matches. Nothing to do. + } else if (!CurrentDefinition) { + // 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(); + auto LatestDef = LatestLocalMD->getDefinition(); + assert(LatestDef.isUndefined() && + "predefined macro went away with no #undef?"); + PP.Diag(LatestDef.getUndefLocation(), diag::note_module_def_undef_here) + << true; + return; + } else if (!CmdLineDefinition) { + // There was no definition for this macro in the predefines buffer, + // but there was a local definition. Complain. + PP.Diag(ImportLoc, diag::warn_module_config_macro_undef) + << false << ConfigMacro << Mod->getFullModuleName(); + PP.Diag(CurrentDefinition->getDefinitionLoc(), + diag::note_module_def_undef_here) + << false; + } else if (!CurrentDefinition->isIdenticalTo(*CmdLineDefinition, PP, + /*Syntactically=*/true)) { + // The macro definitions differ. + PP.Diag(ImportLoc, diag::warn_module_config_macro_undef) + << false << ConfigMacro << Mod->getFullModuleName(); + PP.Diag(CurrentDefinition->getDefinitionLoc(), + diag::note_module_def_undef_here) + << false; + } +} + +/// Write a new timestamp file with the given path. +static void writeTimestampFile(StringRef TimestampFile) { + std::error_code EC; + llvm::raw_fd_ostream Out(TimestampFile.str(), EC, llvm::sys::fs::F_None); +} + +/// 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; + assert(!TimestampFile.empty()); + 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(nullptr); + 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. + std::error_code EC; + SmallString<128> ModuleCachePathNative; + llvm::sys::path::native(HSOpts.ModuleCachePath, ModuleCachePathNative); + for (llvm::sys::fs::directory_iterator Dir(ModuleCachePathNative, 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. + 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. + StringRef Extension = llvm::sys::path::extension(File->path()); + if (Extension != ".pcm" && Extension != ".timestamp" && + llvm::sys::path::filename(File->path()) != "modules.idx") + continue; + + // Look at this file. If we can't stat it, there's nothing interesting + // there. + if (::stat(File->path().c_str(), &StatBuf)) + 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)) { + continue; + } + + // Remove the file. + llvm::sys::fs::remove(File->path()); + + // Remove the timestamp file. + std::string TimpestampFilename = File->path() + ".timestamp"; + llvm::sys::fs::remove(TimpestampFilename); + } + + // If we removed all of the files in the directory, remove the directory + // itself. + if (llvm::sys::fs::directory_iterator(Dir->path(), EC) == + llvm::sys::fs::directory_iterator() && !EC) + llvm::sys::fs::remove(Dir->path()); + } +} + +void CompilerInstance::createModuleManager() { + if (!ModuleManager) { + if (!hasASTContext()) + createASTContext(); + + // If we're implicitly building modules but not currently recursively + // building a module, check whether we need to prune the module cache. + if (getSourceManager().getModuleBuildStack().empty() && + !getPreprocessor().getHeaderSearchInfo().getModuleCachePath().empty() && + getHeaderSearchOpts().ModuleCachePruneInterval > 0 && + getHeaderSearchOpts().ModuleCachePruneAfter > 0) { + pruneModuleCache(getHeaderSearchOpts()); + } + + HeaderSearchOptions &HSOpts = getHeaderSearchOpts(); + std::string Sysroot = HSOpts.Sysroot; + const PreprocessorOptions &PPOpts = getPreprocessorOpts(); + std::unique_ptr<llvm::Timer> ReadTimer; + if (FrontendTimerGroup) + ReadTimer = llvm::make_unique<llvm::Timer>("reading_modules", + "Reading modules", + *FrontendTimerGroup); + ModuleManager = new ASTReader( + getPreprocessor(), &getASTContext(), getPCHContainerReader(), + getFrontendOpts().ModuleFileExtensions, + Sysroot.empty() ? "" : Sysroot.c_str(), PPOpts.DisablePCHValidation, + /*AllowASTWithCompilerErrors=*/false, + /*AllowConfigurationMismatch=*/false, + HSOpts.ModulesValidateSystemHeaders, + getFrontendOpts().UseGlobalModuleIndex, + std::move(ReadTimer)); + if (hasASTConsumer()) { + ModuleManager->setDeserializationListener( + getASTConsumer().GetASTDeserializationListener()); + getASTContext().setASTMutationListener( + getASTConsumer().GetASTMutationListener()); + } + getASTContext().setExternalSource(ModuleManager); + if (hasSema()) + ModuleManager->InitializeSema(getSema()); + if (hasASTConsumer()) + ModuleManager->StartTranslationUnit(&getASTConsumer()); + + if (TheDependencyFileGenerator) + TheDependencyFileGenerator->AttachToASTReader(*ModuleManager); + for (auto &Listener : DependencyCollectors) + Listener->attachToASTReader(*ModuleManager); + } +} + +bool CompilerInstance::loadModuleFile(StringRef FileName) { + llvm::Timer Timer; + if (FrontendTimerGroup) + Timer.init("preloading." + FileName.str(), "Preloading " + FileName.str(), + *FrontendTimerGroup); + llvm::TimeRegion TimeLoading(FrontendTimerGroup ? &Timer : nullptr); + + // Helper to recursively read the module names for all modules we're adding. + // We mark these as known and redirect any attempt to load that module to + // the files we were handed. + struct ReadModuleNames : ASTReaderListener { + CompilerInstance &CI; + llvm::SmallVector<IdentifierInfo*, 8> LoadedModules; + + ReadModuleNames(CompilerInstance &CI) : CI(CI) {} + + void ReadModuleName(StringRef ModuleName) override { + LoadedModules.push_back( + CI.getPreprocessor().getIdentifierInfo(ModuleName)); + } + + void registerAll() { + for (auto *II : LoadedModules) { + CI.KnownModules[II] = CI.getPreprocessor() + .getHeaderSearchInfo() + .getModuleMap() + .findModule(II->getName()); + } + LoadedModules.clear(); + } + + void markAllUnavailable() { + for (auto *II : LoadedModules) { + if (Module *M = CI.getPreprocessor() + .getHeaderSearchInfo() + .getModuleMap() + .findModule(II->getName())) { + M->HasIncompatibleModuleFile = true; + + // Mark module as available if the only reason it was unavailable + // was missing headers. + SmallVector<Module *, 2> Stack; + Stack.push_back(M); + while (!Stack.empty()) { + Module *Current = Stack.pop_back_val(); + if (Current->IsMissingRequirement) continue; + Current->IsAvailable = true; + Stack.insert(Stack.end(), + Current->submodule_begin(), Current->submodule_end()); + } + } + } + LoadedModules.clear(); + } + }; + + // If we don't already have an ASTReader, create one now. + if (!ModuleManager) + createModuleManager(); + + // If -Wmodule-file-config-mismatch is mapped as an error or worse, allow the + // ASTReader to diagnose it, since it can produce better errors that we can. + bool ConfigMismatchIsRecoverable = + getDiagnostics().getDiagnosticLevel(diag::warn_module_config_mismatch, + SourceLocation()) + <= DiagnosticsEngine::Warning; + + auto Listener = llvm::make_unique<ReadModuleNames>(*this); + auto &ListenerRef = *Listener; + ASTReader::ListenerScope ReadModuleNamesListener(*ModuleManager, + std::move(Listener)); + + // Try to load the module file. + switch (ModuleManager->ReadAST( + FileName, serialization::MK_ExplicitModule, SourceLocation(), + ConfigMismatchIsRecoverable ? ASTReader::ARR_ConfigurationMismatch : 0)) { + case ASTReader::Success: + // We successfully loaded the module file; remember the set of provided + // modules so that we don't try to load implicit modules for them. + ListenerRef.registerAll(); + return true; + + case ASTReader::ConfigurationMismatch: + // Ignore unusable module files. + getDiagnostics().Report(SourceLocation(), diag::warn_module_config_mismatch) + << FileName; + // All modules provided by any files we tried and failed to load are now + // unavailable; includes of those modules should now be handled textually. + ListenerRef.markAllUnavailable(); + return true; + + default: + return false; + } +} + +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.isValid() && LastModuleImportLoc == ImportLoc) { + // Make the named module visible. + if (LastModuleImportResult && ModuleName != getLangOpts().CurrentModule) + ModuleManager->makeModuleVisible(LastModuleImportResult, Visibility, + ImportLoc); + return LastModuleImportResult; + } + + clang::Module *Module = nullptr; + + // 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().lookupModule( + ModuleName, /*AllowSearch*/ true, + /*AllowExtraModuleMapSearch*/ !IsInclusionDirective); + /// FIXME: perhaps we should (a) look for a module using the module name + // to file map (PrebuiltModuleFiles) and (b) diagnose if still not found? + //if (Module == nullptr) { + // getDiagnostics().Report(ModuleNameLoc, diag::err_module_not_found) + // << ModuleName; + // ModuleBuildFailed = true; + // return ModuleLoadResult(); + //} + 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, true, + !IsInclusionDirective); + HeaderSearchOptions &HSOpts = + PP->getHeaderSearchInfo().getHeaderSearchOpts(); + + std::string ModuleFileName; + enum ModuleSource { + ModuleNotFound, ModuleCache, PrebuiltModulePath, ModuleBuildPragma + } Source = ModuleNotFound; + + // Check to see if the module has been built as part of this compilation + // via a module build pragma. + auto BuiltModuleIt = BuiltModules.find(ModuleName); + if (BuiltModuleIt != BuiltModules.end()) { + ModuleFileName = BuiltModuleIt->second; + Source = ModuleBuildPragma; + } + + // Try to load the module from the prebuilt module path. + if (Source == ModuleNotFound && (!HSOpts.PrebuiltModuleFiles.empty() || + !HSOpts.PrebuiltModulePaths.empty())) { + ModuleFileName = + PP->getHeaderSearchInfo().getPrebuiltModuleFileName(ModuleName); + if (!ModuleFileName.empty()) + Source = PrebuiltModulePath; + } + + // Try to load the module from the module cache. + if (Source == ModuleNotFound && Module) { + ModuleFileName = PP->getHeaderSearchInfo().getCachedModuleFileName(Module); + Source = ModuleCache; + } + + if (Source == ModuleNotFound) { + // We can't find a module, error out here. + getDiagnostics().Report(ModuleNameLoc, diag::err_module_not_found) + << ModuleName << SourceRange(ImportLoc, ModuleNameLoc); + ModuleBuildFailed = true; + return ModuleLoadResult(); + } + + if (ModuleFileName.empty()) { + if (Module && Module->HasIncompatibleModuleFile) { + // We tried and failed to load a module file for this module. Fall + // back to textual inclusion for its headers. + return ModuleLoadResult::ConfigMismatch; + } + + getDiagnostics().Report(ModuleNameLoc, diag::err_module_build_disabled) + << ModuleName; + ModuleBuildFailed = true; + return ModuleLoadResult(); + } + + // If we don't already have an ASTReader, create one now. + if (!ModuleManager) + createModuleManager(); + + llvm::Timer Timer; + if (FrontendTimerGroup) + Timer.init("loading." + ModuleFileName, "Loading " + ModuleFileName, + *FrontendTimerGroup); + llvm::TimeRegion TimeLoading(FrontendTimerGroup ? &Timer : nullptr); + + // Try to load the module file. If we are not trying to load from the + // module cache, we don't know how to rebuild modules. + unsigned ARRFlags = Source == ModuleCache ? + ASTReader::ARR_OutOfDate | ASTReader::ARR_Missing : + Source == PrebuiltModulePath ? + 0 : + ASTReader::ARR_ConfigurationMismatch; + switch (ModuleManager->ReadAST(ModuleFileName, + Source == PrebuiltModulePath + ? serialization::MK_PrebuiltModule + : Source == ModuleBuildPragma + ? serialization::MK_ExplicitModule + : serialization::MK_ImplicitModule, + ImportLoc, ARRFlags)) { + case ASTReader::Success: { + if (Source != ModuleCache && !Module) { + Module = PP->getHeaderSearchInfo().lookupModule(ModuleName, true, + !IsInclusionDirective); + if (!Module || !Module->getASTFile() || + FileMgr->getFile(ModuleFileName) != Module->getASTFile()) { + // Error out if Module does not refer to the file in the prebuilt + // module path. + getDiagnostics().Report(ModuleNameLoc, diag::err_module_prebuilt) + << ModuleName; + ModuleBuildFailed = true; + KnownModules[Path[0].first] = nullptr; + return ModuleLoadResult(); + } + } + break; + } + + case ASTReader::OutOfDate: + case ASTReader::Missing: { + if (Source != ModuleCache) { + // We don't know the desired configuration for this module and don't + // necessarily even have a module map. Since ReadAST already produces + // diagnostics for these two cases, we simply error out here. + ModuleBuildFailed = true; + KnownModules[Path[0].first] = nullptr; + return ModuleLoadResult(); + } + + // The module file is missing or out-of-date. Build it. + assert(Module && "missing module file"); + // 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 and then load the module. + if (!compileAndLoadModule(*this, ImportLoc, ModuleNameLoc, Module, + ModuleFileName)) { + assert(getDiagnostics().hasErrorOccurred() && + "undiagnosed error in compileAndLoadModule"); + if (getPreprocessorOpts().FailedModules) + getPreprocessorOpts().FailedModules->addFailed(ModuleName); + KnownModules[Path[0].first] = nullptr; + ModuleBuildFailed = true; + return ModuleLoadResult(); + } + + // Okay, we've rebuilt and now loaded the module. + break; + } + + case ASTReader::ConfigurationMismatch: + if (Source == PrebuiltModulePath) + // FIXME: We shouldn't be setting HadFatalFailure below if we only + // produce a warning here! + getDiagnostics().Report(SourceLocation(), + diag::warn_module_config_mismatch) + << ModuleFileName; + // Fall through to error out. + LLVM_FALLTHROUGH; + case ASTReader::VersionMismatch: + case ASTReader::HadErrors: + ModuleLoader::HadFatalFailure = true; + // FIXME: The ASTReader will already have complained, but can we shoehorn + // that diagnostic information into a more useful form? + KnownModules[Path[0].first] = nullptr; + return ModuleLoadResult(); + + case ASTReader::Failure: + ModuleLoader::HadFatalFailure = true; + // Already complained, but note now that we failed. + KnownModules[Path[0].first] = nullptr; + ModuleBuildFailed = true; + return ModuleLoadResult(); + } + + // 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. + bool MapPrivateSubModToTopLevel = false; + 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 the user is requesting Foo.Private and it doesn't exist, try to + // match Foo_Private and emit a warning asking for the user to write + // @import Foo_Private instead. FIXME: remove this when existing clients + // migrate off of Foo.Private syntax. + if (!Sub && PP->getLangOpts().ImplicitModules && Name == "Private" && + Module == Module->getTopLevelModule()) { + SmallString<128> PrivateModule(Module->Name); + PrivateModule.append("_Private"); + + SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> PrivPath; + auto &II = PP->getIdentifierTable().get( + PrivateModule, PP->getIdentifierInfo(Module->Name)->getTokenID()); + PrivPath.push_back(std::make_pair(&II, Path[0].second)); + + if (PP->getHeaderSearchInfo().lookupModule(PrivateModule, true, + !IsInclusionDirective)) + Sub = + loadModule(ImportLoc, PrivPath, Visibility, IsInclusionDirective); + if (Sub) { + MapPrivateSubModToTopLevel = true; + if (!getDiagnostics().isIgnored( + diag::warn_no_priv_submodule_use_toplevel, ImportLoc)) { + getDiagnostics().Report(Path[I].second, + diag::warn_no_priv_submodule_use_toplevel) + << Path[I].first << Module->getFullModuleName() << PrivateModule + << SourceRange(Path[0].second, Path[I].second) + << FixItHint::CreateReplacement(SourceRange(Path[0].second), + PrivateModule); + getDiagnostics().Report(Sub->DefinitionLoc, + diag::note_private_top_level_defined); + } + } + } + + 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 && !MapPrivateSubModToTopLevel) { + // 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::MissingExpected; + } + + // Check whether this module is available. + if (Preprocessor::checkModuleIsAvailable(getLangOpts(), getTarget(), + getDiagnostics(), Module)) { + getDiagnostics().Report(ImportLoc, diag::note_module_import_here) + << SourceRange(Path.front().second, Path.back().second); + LastModuleImportLoc = ImportLoc; + LastModuleImportResult = ModuleLoadResult(); + return ModuleLoadResult(); + } + + ModuleManager->makeModuleVisible(Module, Visibility, ImportLoc); + } + + // 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); + } + + // Resolve any remaining module using export_as for this one. + getPreprocessor() + .getHeaderSearchInfo() + .getModuleMap() + .resolveLinkAsDependencies(TopModule); + + LastModuleImportLoc = ImportLoc; + LastModuleImportResult = ModuleLoadResult(Module); + return LastModuleImportResult; +} + +void CompilerInstance::loadModuleFromSource(SourceLocation ImportLoc, + StringRef ModuleName, + StringRef Source) { + // Avoid creating filenames with special characters. + SmallString<128> CleanModuleName(ModuleName); + for (auto &C : CleanModuleName) + if (!isAlphanumeric(C)) + C = '_'; + + // FIXME: Using a randomized filename here means that our intermediate .pcm + // output is nondeterministic (as .pcm files refer to each other by name). + // Can this affect the output in any way? + SmallString<128> ModuleFileName; + if (std::error_code EC = llvm::sys::fs::createTemporaryFile( + CleanModuleName, "pcm", ModuleFileName)) { + getDiagnostics().Report(ImportLoc, diag::err_fe_unable_to_open_output) + << ModuleFileName << EC.message(); + return; + } + std::string ModuleMapFileName = (CleanModuleName + ".map").str(); + + FrontendInputFile Input( + ModuleMapFileName, + InputKind(getLanguageFromOptions(*Invocation->getLangOpts()), + InputKind::ModuleMap, /*Preprocessed*/true)); + + std::string NullTerminatedSource(Source.str()); + + auto PreBuildStep = [&](CompilerInstance &Other) { + // Create a virtual file containing our desired source. + // FIXME: We shouldn't need to do this. + const FileEntry *ModuleMapFile = Other.getFileManager().getVirtualFile( + ModuleMapFileName, NullTerminatedSource.size(), 0); + Other.getSourceManager().overrideFileContents( + ModuleMapFile, + llvm::MemoryBuffer::getMemBuffer(NullTerminatedSource.c_str())); + + Other.BuiltModules = std::move(BuiltModules); + Other.DeleteBuiltModules = false; + }; + + auto PostBuildStep = [this](CompilerInstance &Other) { + BuiltModules = std::move(Other.BuiltModules); + }; + + // Build the module, inheriting any modules that we've built locally. + if (compileModuleImpl(*this, ImportLoc, ModuleName, Input, StringRef(), + ModuleFileName, PreBuildStep, PostBuildStep)) { + BuiltModules[ModuleName] = ModuleFileName.str(); + llvm::sys::RemoveFileOnSignal(ModuleFileName); + } +} + +void CompilerInstance::makeModuleVisible(Module *Mod, + Module::NameVisibilityKind Visibility, + SourceLocation ImportLoc) { + if (!ModuleManager) + createModuleManager(); + if (!ModuleManager) + return; + + ModuleManager->makeModuleVisible(Mod, Visibility, ImportLoc); +} + +GlobalModuleIndex *CompilerInstance::loadGlobalModuleIndex( + SourceLocation TriggerLoc) { + if (getPreprocessor().getHeaderSearchInfo().getModuleCachePath().empty()) + return nullptr; + if (!ModuleManager) + createModuleManager(); + // Can't do anything if we don't have the module manager. + if (!ModuleManager) + return nullptr; + // Get an existing global index. This loads it if not already + // loaded. + ModuleManager->loadGlobalIndex(); + GlobalModuleIndex *GlobalIndex = ModuleManager->getGlobalIndex(); + // If the global index doesn't exist, create it. + if (!GlobalIndex && shouldBuildGlobalModuleIndex() && hasFileManager() && + hasPreprocessor()) { + llvm::sys::fs::create_directories( + getPreprocessor().getHeaderSearchInfo().getModuleCachePath()); + GlobalModuleIndex::writeIndex( + getFileManager(), getPCHContainerReader(), + getPreprocessor().getHeaderSearchInfo().getModuleCachePath()); + ModuleManager->resetForReload(); + ModuleManager->loadGlobalIndex(); + GlobalIndex = ModuleManager->getGlobalIndex(); + } + // For finding modules needing to be imported for fixit messages, + // we need to make the global index cover all modules, so we do that here. + if (!HaveFullGlobalModuleIndex && GlobalIndex && !buildingModule()) { + ModuleMap &MMap = getPreprocessor().getHeaderSearchInfo().getModuleMap(); + bool RecreateIndex = false; + for (ModuleMap::module_iterator I = MMap.module_begin(), + E = MMap.module_end(); I != E; ++I) { + Module *TheModule = I->second; + const FileEntry *Entry = TheModule->getASTFile(); + if (!Entry) { + SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Path; + Path.push_back(std::make_pair( + getPreprocessor().getIdentifierInfo(TheModule->Name), TriggerLoc)); + std::reverse(Path.begin(), Path.end()); + // Load a module as hidden. This also adds it to the global index. + loadModule(TheModule->DefinitionLoc, Path, Module::Hidden, false); + RecreateIndex = true; + } + } + if (RecreateIndex) { + GlobalModuleIndex::writeIndex( + getFileManager(), getPCHContainerReader(), + getPreprocessor().getHeaderSearchInfo().getModuleCachePath()); + ModuleManager->resetForReload(); + ModuleManager->loadGlobalIndex(); + GlobalIndex = ModuleManager->getGlobalIndex(); + } + HaveFullGlobalModuleIndex = true; + } + return GlobalIndex; +} + +// Check global module index for missing imports. +bool +CompilerInstance::lookupMissingImports(StringRef Name, + SourceLocation TriggerLoc) { + // Look for the symbol in non-imported modules, but only if an error + // actually occurred. + if (!buildingModule()) { + // Load global module index, or retrieve a previously loaded one. + GlobalModuleIndex *GlobalIndex = loadGlobalModuleIndex( + TriggerLoc); + + // Only if we have a global index. + if (GlobalIndex) { + GlobalModuleIndex::HitSet FoundModules; + + // Find the modules that reference the identifier. + // Note that this only finds top-level modules. + // We'll let diagnoseTypo find the actual declaration module. + if (GlobalIndex->lookupIdentifier(Name, FoundModules)) + return true; + } + } + + return false; +} +void CompilerInstance::resetAndLeakSema() { llvm::BuryPointer(takeSema()); } + +void CompilerInstance::setExternalSemaSource( + IntrusiveRefCntPtr<ExternalSemaSource> ESS) { + ExternalSemaSrc = std::move(ESS); +} 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 000000000000..3e6528c25982 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/CompilerInvocation.cpp @@ -0,0 +1,3500 @@ +//===- 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 "TestModuleFileExtension.h" +#include "clang/Basic/Builtins.h" +#include "clang/Basic/CharInfo.h" +#include "clang/Basic/CodeGenOptions.h" +#include "clang/Basic/CommentOptions.h" +#include "clang/Basic/DebugInfoOptions.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticOptions.h" +#include "clang/Basic/FileSystemOptions.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/ObjCRuntime.h" +#include "clang/Basic/Sanitizers.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/TargetOptions.h" +#include "clang/Basic/Version.h" +#include "clang/Basic/Visibility.h" +#include "clang/Basic/XRayInstr.h" +#include "clang/Config/config.h" +#include "clang/Driver/DriverDiagnostic.h" +#include "clang/Driver/Options.h" +#include "clang/Frontend/CommandLineSourceLoc.h" +#include "clang/Frontend/DependencyOutputOptions.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/FrontendOptions.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "clang/Frontend/LangStandard.h" +#include "clang/Frontend/MigratorOptions.h" +#include "clang/Frontend/PreprocessorOutputOptions.h" +#include "clang/Frontend/Utils.h" +#include "clang/Lex/HeaderSearchOptions.h" +#include "clang/Lex/PreprocessorOptions.h" +#include "clang/Sema/CodeCompleteOptions.h" +#include "clang/Serialization/ModuleFileExtension.h" +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/CachedHashString.h" +#include "llvm/ADT/Hashing.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Triple.h" +#include "llvm/ADT/Twine.h" +#include "llvm/IR/DebugInfoMetadata.h" +#include "llvm/Linker/Linker.h" +#include "llvm/MC/MCTargetOptions.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/OptSpecifier.h" +#include "llvm/Option/OptTable.h" +#include "llvm/Option/Option.h" +#include "llvm/ProfileData/InstrProfReader.h" +#include "llvm/Support/CodeGen.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/Regex.h" +#include "llvm/Support/VersionTuple.h" +#include "llvm/Support/VirtualFileSystem.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Target/TargetOptions.h" +#include <algorithm> +#include <atomic> +#include <cassert> +#include <cstddef> +#include <cstring> +#include <memory> +#include <string> +#include <tuple> +#include <utility> +#include <vector> + +using namespace clang; +using namespace driver; +using namespace options; +using namespace llvm::opt; + +//===----------------------------------------------------------------------===// +// Initialization. +//===----------------------------------------------------------------------===// + +CompilerInvocationBase::CompilerInvocationBase() + : LangOpts(new LangOptions()), TargetOpts(new TargetOptions()), + DiagnosticOpts(new DiagnosticOptions()), + HeaderSearchOpts(new HeaderSearchOptions()), + PreprocessorOpts(new PreprocessorOptions()) {} + +CompilerInvocationBase::CompilerInvocationBase(const CompilerInvocationBase &X) + : 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())) {} + +CompilerInvocationBase::~CompilerInvocationBase() = default; + +//===----------------------------------------------------------------------===// +// Deserialization (from args) +//===----------------------------------------------------------------------===// + +static unsigned getOptimizationLevel(ArgList &Args, InputKind IK, + DiagnosticsEngine &Diags) { + unsigned DefaultOpt = llvm::CodeGenOpt::None; + if (IK.getLanguage() == InputKind::OpenCL && !Args.hasArg(OPT_cl_opt_disable)) + DefaultOpt = llvm::CodeGenOpt::Default; + + if (Arg *A = Args.getLastArg(options::OPT_O_Group)) { + if (A->getOption().matches(options::OPT_O0)) + return llvm::CodeGenOpt::None; + + if (A->getOption().matches(options::OPT_Ofast)) + return llvm::CodeGenOpt::Aggressive; + + assert(A->getOption().matches(options::OPT_O)); + + StringRef S(A->getValue()); + if (S == "s" || S == "z" || S.empty()) + return llvm::CodeGenOpt::Default; + + if (S == "g") + return llvm::CodeGenOpt::Less; + + 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 addDiagnosticArgs(ArgList &Args, OptSpecifier Group, + OptSpecifier GroupWithValue, + std::vector<std::string> &Diagnostics) { + for (auto *A : Args.filtered(Group)) { + if (A->getOption().getKind() == Option::FlagClass) { + // The argument is a pure flag (such as OPT_Wall or OPT_Wdeprecated). Add + // its name (minus the "W" or "R" at the beginning) to the warning list. + Diagnostics.push_back(A->getOption().getName().drop_front(1)); + } else if (A->getOption().matches(GroupWithValue)) { + // This is -Wfoo= or -Rfoo=, where foo is the name of the diagnostic group. + Diagnostics.push_back(A->getOption().getName().drop_front(1).rtrim("=-")); + } else { + // Otherwise, add its value (for OPT_W_Joined and similar). + for (const auto *Arg : A->getValues()) + Diagnostics.emplace_back(Arg); + } + } +} + +// Parse the Static Analyzer configuration. If \p Diags is set to nullptr, +// it won't verify the input. +static void parseAnalyzerConfigs(AnalyzerOptions &AnOpts, + DiagnosticsEngine *Diags); + +static void getAllNoBuiltinFuncValues(ArgList &Args, + std::vector<std::string> &Funcs) { + SmallVector<const char *, 8> Values; + for (const auto &Arg : Args) { + const Option &O = Arg->getOption(); + if (O.matches(options::OPT_fno_builtin_)) { + const char *FuncName = Arg->getValue(); + if (Builtin::Context::isBuiltinFunc(FuncName)) + Values.push_back(FuncName); + } + } + Funcs.insert(Funcs.end(), Values.begin(), Values.end()); +} + +static bool ParseAnalyzerArgs(AnalyzerOptions &Opts, ArgList &Args, + DiagnosticsEngine &Diags) { + 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.ShowConfigOptionsList = Args.hasArg(OPT_analyzer_config_help); + Opts.ShowEnabledCheckerList = Args.hasArg(OPT_analyzer_list_enabled_checkers); + Opts.ShouldEmitErrorsOnInvalidConfigValue = + /* negated */!llvm::StringSwitch<bool>( + Args.getLastArgValue(OPT_analyzer_config_compatibility_mode)) + .Case("true", true) + .Case("false", false) + .Default(false); + Opts.DisableAllChecks = Args.hasArg(OPT_analyzer_disable_all_checks); + + Opts.visualizeExplodedGraphWithGraphViz = + Args.hasArg(OPT_analyzer_viz_egraph_graphviz); + Opts.DumpExplodedGraphTo = Args.getLastArgValue(OPT_analyzer_dump_egraph); + 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.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 (const Arg *A : + Args.filtered(OPT_analyzer_checker, OPT_analyzer_disable_checker)) { + 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 (auto checker : checkers) + Opts.CheckersControlList.emplace_back(checker, enable); + } + + // Go through the analyzer configuration options. + for (const auto *A : Args.filtered(OPT_analyzer_config)) { + + // 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 (const auto &configVal : configVals) { + StringRef key, val; + std::tie(key, val) = configVal.split("="); + if (val.empty()) { + Diags.Report(SourceLocation(), + diag::err_analyzer_config_no_value) << configVal; + Success = false; + break; + } + if (val.find('=') != StringRef::npos) { + Diags.Report(SourceLocation(), + diag::err_analyzer_config_multiple_values) + << configVal; + Success = false; + break; + } + + // TODO: Check checker options too, possibly in CheckerRegistry. + // Leave unknown non-checker configs unclaimed. + if (!key.contains(":") && Opts.isUnknownAnalyzerConfig(key)) { + if (Opts.ShouldEmitErrorsOnInvalidConfigValue) + Diags.Report(diag::err_analyzer_config_unknown) << key; + continue; + } + + A->claim(); + Opts.Config[key] = val; + } + } + + if (Opts.ShouldEmitErrorsOnInvalidConfigValue) + parseAnalyzerConfigs(Opts, &Diags); + else + parseAnalyzerConfigs(Opts, nullptr); + + llvm::raw_string_ostream os(Opts.FullCompilerInvocation); + for (unsigned i = 0; i < Args.getNumInputArgStrings(); ++i) { + if (i != 0) + os << " "; + os << Args.getArgString(i); + } + os.flush(); + + return Success; +} + +static StringRef getStringOption(AnalyzerOptions::ConfigTable &Config, + StringRef OptionName, StringRef DefaultVal) { + return Config.insert({OptionName, DefaultVal}).first->second; +} + +static void initOption(AnalyzerOptions::ConfigTable &Config, + DiagnosticsEngine *Diags, + StringRef &OptionField, StringRef Name, + StringRef DefaultVal) { + // String options may be known to invalid (e.g. if the expected string is a + // file name, but the file does not exist), those will have to be checked in + // parseConfigs. + OptionField = getStringOption(Config, Name, DefaultVal); +} + +static void initOption(AnalyzerOptions::ConfigTable &Config, + DiagnosticsEngine *Diags, + bool &OptionField, StringRef Name, bool DefaultVal) { + auto PossiblyInvalidVal = llvm::StringSwitch<Optional<bool>>( + getStringOption(Config, Name, (DefaultVal ? "true" : "false"))) + .Case("true", true) + .Case("false", false) + .Default(None); + + if (!PossiblyInvalidVal) { + if (Diags) + Diags->Report(diag::err_analyzer_config_invalid_input) + << Name << "a boolean"; + else + OptionField = DefaultVal; + } else + OptionField = PossiblyInvalidVal.getValue(); +} + +static void initOption(AnalyzerOptions::ConfigTable &Config, + DiagnosticsEngine *Diags, + unsigned &OptionField, StringRef Name, + unsigned DefaultVal) { + + OptionField = DefaultVal; + bool HasFailed = getStringOption(Config, Name, std::to_string(DefaultVal)) + .getAsInteger(10, OptionField); + if (Diags && HasFailed) + Diags->Report(diag::err_analyzer_config_invalid_input) + << Name << "an unsigned"; +} + +static void parseAnalyzerConfigs(AnalyzerOptions &AnOpts, + DiagnosticsEngine *Diags) { + // TODO: There's no need to store the entire configtable, it'd be plenty + // enough tostore checker options. + +#define ANALYZER_OPTION(TYPE, NAME, CMDFLAG, DESC, DEFAULT_VAL) \ + initOption(AnOpts.Config, Diags, AnOpts.NAME, CMDFLAG, DEFAULT_VAL); + +#define ANALYZER_OPTION_DEPENDS_ON_USER_MODE(TYPE, NAME, CMDFLAG, DESC, \ + SHALLOW_VAL, DEEP_VAL) \ + switch (AnOpts.getUserMode()) { \ + case UMK_Shallow: \ + initOption(AnOpts.Config, Diags, AnOpts.NAME, CMDFLAG, SHALLOW_VAL); \ + break; \ + case UMK_Deep: \ + initOption(AnOpts.Config, Diags, AnOpts.NAME, CMDFLAG, DEEP_VAL); \ + break; \ + } \ + +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.def" +#undef ANALYZER_OPTION +#undef ANALYZER_OPTION_DEPENDS_ON_USER_MODE + + // At this point, AnalyzerOptions is configured. Let's validate some options. + + if (!Diags) + return; + + if (!AnOpts.CTUDir.empty() && !llvm::sys::fs::is_directory(AnOpts.CTUDir)) + Diags->Report(diag::err_analyzer_config_invalid_input) << "ctu-dir" + << "a filename"; + + if (!AnOpts.ModelPath.empty() && + !llvm::sys::fs::is_directory(AnOpts.ModelPath)) + Diags->Report(diag::err_analyzer_config_invalid_input) << "model-path" + << "a filename"; +} + +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 StringRef getCodeModel(ArgList &Args, DiagnosticsEngine &Diags) { + if (Arg *A = Args.getLastArg(OPT_mcode_model)) { + StringRef Value = A->getValue(); + if (Value == "small" || Value == "kernel" || Value == "medium" || + Value == "large" || Value == "tiny") + return Value; + Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Value; + } + return "default"; +} + +static llvm::Reloc::Model getRelocModel(ArgList &Args, + DiagnosticsEngine &Diags) { + if (Arg *A = Args.getLastArg(OPT_mrelocation_model)) { + StringRef Value = A->getValue(); + auto RM = llvm::StringSwitch<llvm::Optional<llvm::Reloc::Model>>(Value) + .Case("static", llvm::Reloc::Static) + .Case("pic", llvm::Reloc::PIC_) + .Case("ropi", llvm::Reloc::ROPI) + .Case("rwpi", llvm::Reloc::RWPI) + .Case("ropi-rwpi", llvm::Reloc::ROPI_RWPI) + .Case("dynamic-no-pic", llvm::Reloc::DynamicNoPIC) + .Default(None); + if (RM.hasValue()) + return *RM; + Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Value; + } + return llvm::Reloc::PIC_; +} + +/// Create a new Regex instance out of the string value in \p RpassArg. +/// It returns a pointer to the newly generated Regex instance. +static std::shared_ptr<llvm::Regex> +GenerateOptimizationRemarkRegex(DiagnosticsEngine &Diags, ArgList &Args, + Arg *RpassArg) { + StringRef Val = RpassArg->getValue(); + std::string RegexError; + std::shared_ptr<llvm::Regex> Pattern = std::make_shared<llvm::Regex>(Val); + if (!Pattern->isValid(RegexError)) { + Diags.Report(diag::err_drv_optimization_remark_pattern) + << RegexError << RpassArg->getAsString(Args); + Pattern.reset(); + } + return Pattern; +} + +static bool parseDiagnosticLevelMask(StringRef FlagName, + const std::vector<std::string> &Levels, + DiagnosticsEngine *Diags, + DiagnosticLevelMask &M) { + bool Success = true; + for (const auto &Level : Levels) { + DiagnosticLevelMask const PM = + llvm::StringSwitch<DiagnosticLevelMask>(Level) + .Case("note", DiagnosticLevelMask::Note) + .Case("remark", DiagnosticLevelMask::Remark) + .Case("warning", DiagnosticLevelMask::Warning) + .Case("error", DiagnosticLevelMask::Error) + .Default(DiagnosticLevelMask::None); + if (PM == DiagnosticLevelMask::None) { + Success = false; + if (Diags) + Diags->Report(diag::err_drv_invalid_value) << FlagName << Level; + } + M = M | PM; + } + return Success; +} + +static void parseSanitizerKinds(StringRef FlagName, + const std::vector<std::string> &Sanitizers, + DiagnosticsEngine &Diags, SanitizerSet &S) { + for (const auto &Sanitizer : Sanitizers) { + SanitizerMask K = parseSanitizerValue(Sanitizer, /*AllowGroups=*/false); + if (K == 0) + Diags.Report(diag::err_drv_invalid_value) << FlagName << Sanitizer; + else + S.set(K, true); + } +} + +static void parseXRayInstrumentationBundle(StringRef FlagName, StringRef Bundle, + ArgList &Args, DiagnosticsEngine &D, + XRayInstrSet &S) { + llvm::SmallVector<StringRef, 2> BundleParts; + llvm::SplitString(Bundle, BundleParts, ","); + for (const auto B : BundleParts) { + auto Mask = parseXRayInstrValue(B); + if (Mask == XRayInstrKind::None) + if (B != "none") + D.Report(diag::err_drv_invalid_value) << FlagName << Bundle; + else + S.Mask = Mask; + else if (Mask == XRayInstrKind::All) + S.Mask = Mask; + else + S.set(Mask, true); + } +} + +// Set the profile kind for fprofile-instrument. +static void setPGOInstrumentor(CodeGenOptions &Opts, ArgList &Args, + DiagnosticsEngine &Diags) { + Arg *A = Args.getLastArg(OPT_fprofile_instrument_EQ); + if (A == nullptr) + return; + StringRef S = A->getValue(); + unsigned I = llvm::StringSwitch<unsigned>(S) + .Case("none", CodeGenOptions::ProfileNone) + .Case("clang", CodeGenOptions::ProfileClangInstr) + .Case("llvm", CodeGenOptions::ProfileIRInstr) + .Default(~0U); + if (I == ~0U) { + Diags.Report(diag::err_drv_invalid_pgo_instrumentor) << A->getAsString(Args) + << S; + return; + } + auto Instrumentor = static_cast<CodeGenOptions::ProfileInstrKind>(I); + Opts.setProfileInstr(Instrumentor); +} + +// Set the profile kind using fprofile-instrument-use-path. +static void setPGOUseInstrumentor(CodeGenOptions &Opts, + const Twine &ProfileName) { + auto ReaderOrErr = llvm::IndexedInstrProfReader::create(ProfileName); + // In error, return silently and let Clang PGOUse report the error message. + if (auto E = ReaderOrErr.takeError()) { + llvm::consumeError(std::move(E)); + Opts.setProfileUse(CodeGenOptions::ProfileClangInstr); + return; + } + std::unique_ptr<llvm::IndexedInstrProfReader> PGOReader = + std::move(ReaderOrErr.get()); + if (PGOReader->isIRLevelProfile()) + Opts.setProfileUse(CodeGenOptions::ProfileIRInstr); + else + Opts.setProfileUse(CodeGenOptions::ProfileClangInstr); +} + +static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, + DiagnosticsEngine &Diags, + const TargetOptions &TargetOpts, + const FrontendOptions &FrontendOpts) { + bool Success = true; + llvm::Triple Triple = llvm::Triple(TargetOpts.Triple); + + unsigned OptimizationLevel = getOptimizationLevel(Args, IK, Diags); + // TODO: This could be done in Driver + unsigned MaxOptLevel = 3; + if (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; + OptimizationLevel = MaxOptLevel; + } + Opts.OptimizationLevel = OptimizationLevel; + + // At O0 we want to fully disable inlining outside of cases marked with + // 'alwaysinline' that are required for correctness. + Opts.setInlining((Opts.OptimizationLevel == 0) + ? CodeGenOptions::OnlyAlwaysInlining + : CodeGenOptions::NormalInlining); + // Explicit inlining flags can disable some or all inlining even at + // optimization levels above zero. + if (Arg *InlineArg = Args.getLastArg( + options::OPT_finline_functions, options::OPT_finline_hint_functions, + options::OPT_fno_inline_functions, options::OPT_fno_inline)) { + if (Opts.OptimizationLevel > 0) { + const Option &InlineOpt = InlineArg->getOption(); + if (InlineOpt.matches(options::OPT_finline_functions)) + Opts.setInlining(CodeGenOptions::NormalInlining); + else if (InlineOpt.matches(options::OPT_finline_hint_functions)) + Opts.setInlining(CodeGenOptions::OnlyHintInlining); + else + Opts.setInlining(CodeGenOptions::OnlyAlwaysInlining); + } + } + + Opts.ExperimentalNewPassManager = Args.hasFlag( + OPT_fexperimental_new_pass_manager, OPT_fno_experimental_new_pass_manager, + /* Default */ ENABLE_EXPERIMENTAL_NEW_PASS_MANAGER); + + Opts.DebugPassManager = + Args.hasFlag(OPT_fdebug_pass_manager, OPT_fno_debug_pass_manager, + /* Default */ false); + + if (Arg *A = Args.getLastArg(OPT_fveclib)) { + StringRef Name = A->getValue(); + if (Name == "Accelerate") + Opts.setVecLib(CodeGenOptions::Accelerate); + else if (Name == "SVML") + Opts.setVecLib(CodeGenOptions::SVML); + else if (Name == "none") + Opts.setVecLib(CodeGenOptions::NoLibrary); + else + Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Name; + } + + if (Arg *A = Args.getLastArg(OPT_debug_info_kind_EQ)) { + unsigned Val = + llvm::StringSwitch<unsigned>(A->getValue()) + .Case("line-tables-only", codegenoptions::DebugLineTablesOnly) + .Case("line-directives-only", codegenoptions::DebugDirectivesOnly) + .Case("limited", codegenoptions::LimitedDebugInfo) + .Case("standalone", codegenoptions::FullDebugInfo) + .Default(~0U); + if (Val == ~0U) + Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) + << A->getValue(); + else + Opts.setDebugInfo(static_cast<codegenoptions::DebugInfoKind>(Val)); + } + if (Arg *A = Args.getLastArg(OPT_debugger_tuning_EQ)) { + unsigned Val = llvm::StringSwitch<unsigned>(A->getValue()) + .Case("gdb", unsigned(llvm::DebuggerKind::GDB)) + .Case("lldb", unsigned(llvm::DebuggerKind::LLDB)) + .Case("sce", unsigned(llvm::DebuggerKind::SCE)) + .Default(~0U); + if (Val == ~0U) + Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) + << A->getValue(); + else + Opts.setDebuggerTuning(static_cast<llvm::DebuggerKind>(Val)); + } + Opts.DwarfVersion = getLastArgIntValue(Args, OPT_dwarf_version_EQ, 0, Diags); + Opts.DebugColumnInfo = Args.hasArg(OPT_dwarf_column_info); + Opts.EmitCodeView = Args.hasArg(OPT_gcodeview); + Opts.CodeViewGHash = Args.hasArg(OPT_gcodeview_ghash); + Opts.MacroDebugInfo = Args.hasArg(OPT_debug_info_macro); + Opts.WholeProgramVTables = Args.hasArg(OPT_fwhole_program_vtables); + Opts.LTOVisibilityPublicStd = Args.hasArg(OPT_flto_visibility_public_std); + Opts.SplitDwarfFile = Args.getLastArgValue(OPT_split_dwarf_file); + Opts.SplitDwarfInlining = !Args.hasArg(OPT_fno_split_dwarf_inlining); + + if (Arg *A = + Args.getLastArg(OPT_enable_split_dwarf, OPT_enable_split_dwarf_EQ)) { + if (A->getOption().matches(options::OPT_enable_split_dwarf)) { + Opts.setSplitDwarfMode(CodeGenOptions::SplitFileFission); + } else { + StringRef Name = A->getValue(); + if (Name == "single") + Opts.setSplitDwarfMode(CodeGenOptions::SingleFileFission); + else if (Name == "split") + Opts.setSplitDwarfMode(CodeGenOptions::SplitFileFission); + else + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << Name; + } + } + + Opts.DebugTypeExtRefs = Args.hasArg(OPT_dwarf_ext_refs); + Opts.DebugExplicitImport = Args.hasArg(OPT_dwarf_explicit_import); + Opts.DebugFwdTemplateParams = Args.hasArg(OPT_debug_forward_template_params); + Opts.EmbedSource = Args.hasArg(OPT_gembed_source); + + for (const auto &Arg : Args.getAllArgValues(OPT_fdebug_prefix_map_EQ)) + Opts.DebugPrefixMap.insert(StringRef(Arg).split('=')); + + if (const Arg *A = + Args.getLastArg(OPT_emit_llvm_uselists, OPT_no_emit_llvm_uselists)) + Opts.EmitLLVMUseLists = A->getOption().getID() == OPT_emit_llvm_uselists; + + Opts.DisableLLVMPasses = Args.hasArg(OPT_disable_llvm_passes); + Opts.DisableLifetimeMarkers = Args.hasArg(OPT_disable_lifetimemarkers); + Opts.DisableO0ImplyOptNone = Args.hasArg(OPT_disable_O0_optnone); + Opts.DisableRedZone = Args.hasArg(OPT_disable_red_zone); + Opts.IndirectTlsSegRefs = Args.hasArg(OPT_mno_tls_direct_seg_refs); + 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.NewStructPathTBAA = !Args.hasArg(OPT_no_struct_path_tbaa) && + Args.hasArg(OPT_new_struct_path_tbaa); + Opts.FineGrainedBitfieldAccesses = + Args.hasFlag(OPT_ffine_grained_bitfield_accesses, + OPT_fno_fine_grained_bitfield_accesses, false); + Opts.DwarfDebugFlags = Args.getLastArgValue(OPT_dwarf_debug_flags); + Opts.RecordCommandLine = Args.getLastArgValue(OPT_record_command_line); + Opts.MergeAllConstants = Args.hasArg(OPT_fmerge_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)); + if (Opts.SimplifyLibCalls) + getAllNoBuiltinFuncValues(Args, Opts.NoBuiltinFuncs); + Opts.UnrollLoops = + Args.hasFlag(OPT_funroll_loops, OPT_fno_unroll_loops, + (Opts.OptimizationLevel > 1)); + Opts.RerollLoops = Args.hasArg(OPT_freroll_loops); + + Opts.DisableIntegratedAS = Args.hasArg(OPT_fno_integrated_as); + Opts.Autolink = !Args.hasArg(OPT_fno_autolink); + Opts.SampleProfileFile = Args.getLastArgValue(OPT_fprofile_sample_use_EQ); + Opts.DebugInfoForProfiling = Args.hasFlag( + OPT_fdebug_info_for_profiling, OPT_fno_debug_info_for_profiling, false); + Opts.DebugNameTable = static_cast<unsigned>( + Args.hasArg(OPT_ggnu_pubnames) + ? llvm::DICompileUnit::DebugNameTableKind::GNU + : Args.hasArg(OPT_gpubnames) + ? llvm::DICompileUnit::DebugNameTableKind::Default + : llvm::DICompileUnit::DebugNameTableKind::None); + Opts.DebugRangesBaseAddress = Args.hasArg(OPT_fdebug_ranges_base_address); + + setPGOInstrumentor(Opts, Args, Diags); + Opts.InstrProfileOutput = + Args.getLastArgValue(OPT_fprofile_instrument_path_EQ); + Opts.ProfileInstrumentUsePath = + Args.getLastArgValue(OPT_fprofile_instrument_use_path_EQ); + if (!Opts.ProfileInstrumentUsePath.empty()) + setPGOUseInstrumentor(Opts, Opts.ProfileInstrumentUsePath); + Opts.ProfileRemappingFile = + Args.getLastArgValue(OPT_fprofile_remapping_file_EQ); + if (!Opts.ProfileRemappingFile.empty() && !Opts.ExperimentalNewPassManager) { + Diags.Report(diag::err_drv_argument_only_allowed_with) + << Args.getLastArg(OPT_fprofile_remapping_file_EQ)->getAsString(Args) + << "-fexperimental-new-pass-manager"; + } + + Opts.CoverageMapping = + Args.hasFlag(OPT_fcoverage_mapping, OPT_fno_coverage_mapping, false); + Opts.DumpCoverageMapping = Args.hasArg(OPT_dump_coverage_mapping); + Opts.AsmVerbose = Args.hasArg(OPT_masm_verbose); + Opts.PreserveAsmComments = !Args.hasArg(OPT_fno_preserve_as_comments); + Opts.AssumeSaneOperatorNew = !Args.hasArg(OPT_fno_assume_sane_operator_new); + Opts.ObjCAutoRefCountExceptions = Args.hasArg(OPT_fobjc_arc_exceptions); + Opts.CXAAtExit = !Args.hasArg(OPT_fno_use_cxa_atexit); + Opts.RegisterGlobalDtorsWithAtExit = + Args.hasArg(OPT_fregister_global_dtors_with_atexit); + Opts.CXXCtorDtorAliases = Args.hasArg(OPT_mconstructor_aliases); + Opts.CodeModel = TargetOpts.CodeModel; + Opts.DebugPass = Args.getLastArgValue(OPT_mdebug_pass); + Opts.DisableFPElim = + (Args.hasArg(OPT_mdisable_fp_elim) || Args.hasArg(OPT_pg)); + Opts.DisableFree = Args.hasArg(OPT_disable_free); + Opts.DiscardValueNames = Args.hasArg(OPT_discard_value_names); + Opts.DisableTailCalls = Args.hasArg(OPT_mdisable_tail_calls); + Opts.NoEscapingBlockTailCalls = + Args.hasArg(OPT_fno_escaping_block_tail_calls); + Opts.FloatABI = Args.getLastArgValue(OPT_mfloat_abi); + Opts.LessPreciseFPMAD = Args.hasArg(OPT_cl_mad_enable) || + Args.hasArg(OPT_cl_unsafe_math_optimizations) || + Args.hasArg(OPT_cl_fast_relaxed_math); + 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_unsafe_math_optimizations) || + Args.hasArg(OPT_cl_finite_math_only) || + Args.hasArg(OPT_cl_fast_relaxed_math)); + Opts.NoSignedZeros = (Args.hasArg(OPT_fno_signed_zeros) || + Args.hasArg(OPT_cl_no_signed_zeros) || + Args.hasArg(OPT_cl_unsafe_math_optimizations) || + Args.hasArg(OPT_cl_fast_relaxed_math)); + Opts.Reassociate = Args.hasArg(OPT_mreassociate); + Opts.FlushDenorm = Args.hasArg(OPT_cl_denorms_are_zero) || + (Args.hasArg(OPT_fcuda_is_device) && + Args.hasArg(OPT_fcuda_flush_denormals_to_zero)); + Opts.CorrectlyRoundedDivSqrt = + Args.hasArg(OPT_cl_fp32_correctly_rounded_divide_sqrt); + Opts.UniformWGSize = + Args.hasArg(OPT_cl_uniform_work_group_size); + Opts.Reciprocals = Args.getAllArgValues(OPT_mrecip_EQ); + Opts.ReciprocalMath = Args.hasArg(OPT_freciprocal_math); + Opts.NoTrappingMath = Args.hasArg(OPT_fno_trapping_math); + Opts.StrictFloatCastOverflow = + !Args.hasArg(OPT_fno_strict_float_cast_overflow); + + Opts.NoZeroInitializedInBSS = Args.hasArg(OPT_mno_zero_initialized_in_bss); + Opts.NumRegisterParameters = getLastArgIntValue(Args, OPT_mregparm, 0, Diags); + Opts.NoExecStack = Args.hasArg(OPT_mno_exec_stack); + Opts.FatalWarnings = Args.hasArg(OPT_massembler_fatal_warnings); + Opts.EnableSegmentedStacks = Args.hasArg(OPT_split_stacks); + Opts.RelaxAll = Args.hasArg(OPT_mrelax_all); + Opts.IncrementalLinkerCompatible = + Args.hasArg(OPT_mincremental_linker_compatible); + Opts.PIECopyRelocations = + Args.hasArg(OPT_mpie_copy_relocations); + Opts.NoPLT = Args.hasArg(OPT_fno_plt); + Opts.OmitLeafFramePointer = Args.hasArg(OPT_momit_leaf_frame_pointer); + Opts.SaveTempLabels = Args.hasArg(OPT_msave_temp_labels); + Opts.NoDwarfDirectoryAsm = Args.hasArg(OPT_fno_dwarf_directory_asm); + Opts.SoftFloat = Args.hasArg(OPT_msoft_float); + Opts.StrictEnums = Args.hasArg(OPT_fstrict_enums); + Opts.StrictReturn = !Args.hasArg(OPT_fno_strict_return); + Opts.StrictVTablePointers = Args.hasArg(OPT_fstrict_vtable_pointers); + Opts.ForceEmitVTables = Args.hasArg(OPT_fforce_emit_vtables); + 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 = getRelocModel(Args, Diags); + Opts.ThreadModel = Args.getLastArgValue(OPT_mthread_model, "posix"); + if (Opts.ThreadModel != "posix" && Opts.ThreadModel != "single") + Diags.Report(diag::err_drv_invalid_value) + << Args.getLastArg(OPT_mthread_model)->getAsString(Args) + << Opts.ThreadModel; + 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.StackSizeSection = + Args.hasFlag(OPT_fstack_size_section, OPT_fno_stack_size_section, false); + Opts.UniqueSectionNames = Args.hasFlag(OPT_funique_section_names, + OPT_fno_unique_section_names, true); + + Opts.MergeFunctions = Args.hasArg(OPT_fmerge_functions); + + Opts.NoUseJumpTables = Args.hasArg(OPT_fno_jump_tables); + + Opts.NullPointerIsValid = Args.hasArg(OPT_fno_delete_null_pointer_checks); + + Opts.ProfileSampleAccurate = Args.hasArg(OPT_fprofile_sample_accurate); + + Opts.PrepareForLTO = Args.hasArg(OPT_flto, OPT_flto_EQ); + Opts.PrepareForThinLTO = false; + if (Arg *A = Args.getLastArg(OPT_flto_EQ)) { + StringRef S = A->getValue(); + if (S == "thin") + Opts.PrepareForThinLTO = true; + else if (S != "full") + Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << S; + } + Opts.LTOUnit = Args.hasFlag(OPT_flto_unit, OPT_fno_lto_unit, false); + Opts.EnableSplitLTOUnit = Args.hasArg(OPT_fsplit_lto_unit); + if (Arg *A = Args.getLastArg(OPT_fthinlto_index_EQ)) { + if (IK.getLanguage() != InputKind::LLVM_IR) + Diags.Report(diag::err_drv_argument_only_allowed_with) + << A->getAsString(Args) << "-x ir"; + Opts.ThinLTOIndexFile = Args.getLastArgValue(OPT_fthinlto_index_EQ); + } + if (Arg *A = Args.getLastArg(OPT_save_temps_EQ)) + Opts.SaveTempsFilePrefix = + llvm::StringSwitch<std::string>(A->getValue()) + .Case("obj", FrontendOpts.OutputFile) + .Default(llvm::sys::path::filename(FrontendOpts.OutputFile).str()); + + Opts.ThinLinkBitcodeFile = Args.getLastArgValue(OPT_fthin_link_bitcode_EQ); + + Opts.MSVolatile = Args.hasArg(OPT_fms_volatile); + + Opts.VectorizeLoop = Args.hasArg(OPT_vectorize_loops); + Opts.VectorizeSLP = Args.hasArg(OPT_vectorize_slp); + + Opts.PreferVectorWidth = Args.getLastArgValue(OPT_mprefer_vector_width_EQ); + + Opts.MainFileName = Args.getLastArgValue(OPT_main_file_name); + Opts.VerifyModule = !Args.hasArg(OPT_disable_llvm_verifier); + + Opts.ControlFlowGuard = Args.hasArg(OPT_cfguard); + + 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.CoverageDataFile = Args.getLastArgValue(OPT_coverage_data_file); + Opts.CoverageNotesFile = Args.getLastArgValue(OPT_coverage_notes_file); + Opts.CoverageExtraChecksum = Args.hasArg(OPT_coverage_cfg_checksum); + Opts.CoverageNoFunctionNamesInData = + Args.hasArg(OPT_coverage_no_function_names_in_data); + Opts.ProfileFilterFiles = + Args.getLastArgValue(OPT_fprofile_filter_files_EQ); + Opts.ProfileExcludeFiles = + Args.getLastArgValue(OPT_fprofile_exclude_files_EQ); + Opts.CoverageExitBlockBeforeBody = + Args.hasArg(OPT_coverage_exit_block_before_body); + 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); + } + } + } + // Handle -fembed-bitcode option. + if (Arg *A = Args.getLastArg(OPT_fembed_bitcode_EQ)) { + StringRef Name = A->getValue(); + unsigned Model = llvm::StringSwitch<unsigned>(Name) + .Case("off", CodeGenOptions::Embed_Off) + .Case("all", CodeGenOptions::Embed_All) + .Case("bitcode", CodeGenOptions::Embed_Bitcode) + .Case("marker", CodeGenOptions::Embed_Marker) + .Default(~0U); + if (Model == ~0U) { + Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Name; + Success = false; + } else + Opts.setEmbedBitcode( + static_cast<CodeGenOptions::EmbedBitcodeKind>(Model)); + } + // FIXME: For backend options that are not yet recorded as function + // attributes in the IR, keep track of them so we can embed them in a + // separate data section and use them when building the bitcode. + if (Opts.getEmbedBitcode() == CodeGenOptions::Embed_All) { + for (const auto &A : Args) { + // Do not encode output and input. + if (A->getOption().getID() == options::OPT_o || + A->getOption().getID() == options::OPT_INPUT || + A->getOption().getID() == options::OPT_x || + A->getOption().getID() == options::OPT_fembed_bitcode || + (A->getOption().getGroup().isValid() && + A->getOption().getGroup().getID() == options::OPT_W_Group)) + continue; + ArgStringList ASL; + A->render(Args, ASL); + for (const auto &arg : ASL) { + StringRef ArgStr(arg); + Opts.CmdArgs.insert(Opts.CmdArgs.end(), ArgStr.begin(), ArgStr.end()); + // using \00 to separate each commandline options. + Opts.CmdArgs.push_back('\0'); + } + } + } + + Opts.PreserveVec3Type = Args.hasArg(OPT_fpreserve_vec3_type); + Opts.InstrumentFunctions = Args.hasArg(OPT_finstrument_functions); + Opts.InstrumentFunctionsAfterInlining = + Args.hasArg(OPT_finstrument_functions_after_inlining); + Opts.InstrumentFunctionEntryBare = + Args.hasArg(OPT_finstrument_function_entry_bare); + + Opts.XRayInstrumentFunctions = + Args.hasArg(OPT_fxray_instrument); + Opts.XRayAlwaysEmitCustomEvents = + Args.hasArg(OPT_fxray_always_emit_customevents); + Opts.XRayAlwaysEmitTypedEvents = + Args.hasArg(OPT_fxray_always_emit_typedevents); + Opts.XRayInstructionThreshold = + getLastArgIntValue(Args, OPT_fxray_instruction_threshold_EQ, 200, Diags); + + auto XRayInstrBundles = + Args.getAllArgValues(OPT_fxray_instrumentation_bundle); + if (XRayInstrBundles.empty()) + Opts.XRayInstrumentationBundle.Mask = XRayInstrKind::All; + else + for (const auto &A : XRayInstrBundles) + parseXRayInstrumentationBundle("-fxray-instrumentation-bundle=", A, Args, + Diags, Opts.XRayInstrumentationBundle); + + Opts.InstrumentForProfiling = Args.hasArg(OPT_pg); + Opts.CallFEntry = Args.hasArg(OPT_mfentry); + Opts.EmitOpenCLArgMetadata = Args.hasArg(OPT_cl_kernel_arg_info); + + if (const Arg *A = Args.getLastArg(OPT_fcf_protection_EQ)) { + StringRef Name = A->getValue(); + if (Name == "full") { + Opts.CFProtectionReturn = 1; + Opts.CFProtectionBranch = 1; + } else if (Name == "return") + Opts.CFProtectionReturn = 1; + else if (Name == "branch") + Opts.CFProtectionBranch = 1; + else if (Name != "none") { + Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Name; + Success = false; + } + } + + if (const Arg *A = Args.getLastArg(OPT_compress_debug_sections, + OPT_compress_debug_sections_EQ)) { + if (A->getOption().getID() == OPT_compress_debug_sections) { + // TODO: be more clever about the compression type auto-detection + Opts.setCompressDebugSections(llvm::DebugCompressionType::GNU); + } else { + auto DCT = llvm::StringSwitch<llvm::DebugCompressionType>(A->getValue()) + .Case("none", llvm::DebugCompressionType::None) + .Case("zlib", llvm::DebugCompressionType::Z) + .Case("zlib-gnu", llvm::DebugCompressionType::GNU) + .Default(llvm::DebugCompressionType::None); + Opts.setCompressDebugSections(DCT); + } + } + + Opts.RelaxELFRelocations = Args.hasArg(OPT_mrelax_relocations); + Opts.DebugCompilationDir = Args.getLastArgValue(OPT_fdebug_compilation_dir); + for (auto *A : + Args.filtered(OPT_mlink_bitcode_file, OPT_mlink_builtin_bitcode)) { + CodeGenOptions::BitcodeFileToLink F; + F.Filename = A->getValue(); + if (A->getOption().matches(OPT_mlink_builtin_bitcode)) { + F.LinkFlags = llvm::Linker::Flags::LinkOnlyNeeded; + // When linking CUDA bitcode, propagate function attributes so that + // e.g. libdevice gets fast-math attrs if we're building with fast-math. + F.PropagateAttrs = true; + F.Internalize = true; + } + Opts.LinkBitcodeFiles.push_back(F); + } + Opts.SanitizeCoverageType = + getLastArgIntValue(Args, OPT_fsanitize_coverage_type, 0, Diags); + Opts.SanitizeCoverageIndirectCalls = + Args.hasArg(OPT_fsanitize_coverage_indirect_calls); + Opts.SanitizeCoverageTraceBB = Args.hasArg(OPT_fsanitize_coverage_trace_bb); + Opts.SanitizeCoverageTraceCmp = Args.hasArg(OPT_fsanitize_coverage_trace_cmp); + Opts.SanitizeCoverageTraceDiv = Args.hasArg(OPT_fsanitize_coverage_trace_div); + Opts.SanitizeCoverageTraceGep = Args.hasArg(OPT_fsanitize_coverage_trace_gep); + Opts.SanitizeCoverage8bitCounters = + Args.hasArg(OPT_fsanitize_coverage_8bit_counters); + Opts.SanitizeCoverageTracePC = Args.hasArg(OPT_fsanitize_coverage_trace_pc); + Opts.SanitizeCoverageTracePCGuard = + Args.hasArg(OPT_fsanitize_coverage_trace_pc_guard); + Opts.SanitizeCoverageNoPrune = Args.hasArg(OPT_fsanitize_coverage_no_prune); + Opts.SanitizeCoverageInline8bitCounters = + Args.hasArg(OPT_fsanitize_coverage_inline_8bit_counters); + Opts.SanitizeCoveragePCTable = Args.hasArg(OPT_fsanitize_coverage_pc_table); + Opts.SanitizeCoverageStackDepth = + Args.hasArg(OPT_fsanitize_coverage_stack_depth); + Opts.SanitizeMemoryTrackOrigins = + getLastArgIntValue(Args, OPT_fsanitize_memory_track_origins_EQ, 0, Diags); + Opts.SanitizeMemoryUseAfterDtor = + Args.hasFlag(OPT_fsanitize_memory_use_after_dtor, + OPT_fno_sanitize_memory_use_after_dtor, + false); + Opts.SanitizeMinimalRuntime = Args.hasArg(OPT_fsanitize_minimal_runtime); + Opts.SanitizeCfiCrossDso = Args.hasArg(OPT_fsanitize_cfi_cross_dso); + Opts.SanitizeCfiICallGeneralizePointers = + Args.hasArg(OPT_fsanitize_cfi_icall_generalize_pointers); + Opts.SanitizeStats = Args.hasArg(OPT_fsanitize_stats); + if (Arg *A = Args.getLastArg( + OPT_fsanitize_address_poison_custom_array_cookie, + OPT_fno_sanitize_address_poison_custom_array_cookie)) { + Opts.SanitizeAddressPoisonCustomArrayCookie = + A->getOption().getID() == + OPT_fsanitize_address_poison_custom_array_cookie; + } + if (Arg *A = Args.getLastArg(OPT_fsanitize_address_use_after_scope, + OPT_fno_sanitize_address_use_after_scope)) { + Opts.SanitizeAddressUseAfterScope = + A->getOption().getID() == OPT_fsanitize_address_use_after_scope; + } + Opts.SanitizeAddressGlobalsDeadStripping = + Args.hasArg(OPT_fsanitize_address_globals_dead_stripping); + if (Arg *A = Args.getLastArg(OPT_fsanitize_address_use_odr_indicator, + OPT_fno_sanitize_address_use_odr_indicator)) { + Opts.SanitizeAddressUseOdrIndicator = + A->getOption().getID() == OPT_fsanitize_address_use_odr_indicator; + } + 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_mstack_probe_size)) { + StringRef Val = A->getValue(); + unsigned StackProbeSize = Opts.StackProbeSize; + Val.getAsInteger(0, StackProbeSize); + Opts.StackProbeSize = StackProbeSize; + } + + Opts.NoStackArgProbe = Args.hasArg(OPT_mno_stack_arg_probe); + + 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 (Args.hasArg(OPT_fno_objc_convert_messages_to_runtime_calls)) + Opts.ObjCConvertMessagesToRuntimeCalls = 0; + + if (Args.getLastArg(OPT_femulated_tls) || + Args.getLastArg(OPT_fno_emulated_tls)) { + Opts.ExplicitEmulatedTLS = true; + Opts.EmulatedTLS = + Args.hasFlag(OPT_femulated_tls, OPT_fno_emulated_tls, false); + } + + 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_fdenormal_fp_math_EQ)) { + StringRef Val = A->getValue(); + if (Val == "ieee") + Opts.FPDenormalMode = "ieee"; + else if (Val == "preserve-sign") + Opts.FPDenormalMode = "preserve-sign"; + else if (Val == "positive-zero") + Opts.FPDenormalMode = "positive-zero"; + 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); + Opts.LinkerOptions = Args.getAllArgValues(OPT_linker_option); + bool NeedLocTracking = false; + + Opts.OptRecordFile = Args.getLastArgValue(OPT_opt_record_file); + if (!Opts.OptRecordFile.empty()) + NeedLocTracking = true; + + if (Arg *A = Args.getLastArg(OPT_Rpass_EQ)) { + Opts.OptimizationRemarkPattern = + GenerateOptimizationRemarkRegex(Diags, Args, A); + NeedLocTracking = true; + } + + if (Arg *A = Args.getLastArg(OPT_Rpass_missed_EQ)) { + Opts.OptimizationRemarkMissedPattern = + GenerateOptimizationRemarkRegex(Diags, Args, A); + NeedLocTracking = true; + } + + if (Arg *A = Args.getLastArg(OPT_Rpass_analysis_EQ)) { + Opts.OptimizationRemarkAnalysisPattern = + GenerateOptimizationRemarkRegex(Diags, Args, A); + NeedLocTracking = true; + } + + Opts.DiagnosticsWithHotness = + Args.hasArg(options::OPT_fdiagnostics_show_hotness); + bool UsingSampleProfile = !Opts.SampleProfileFile.empty(); + bool UsingProfile = UsingSampleProfile || + (Opts.getProfileUse() != CodeGenOptions::ProfileNone); + + if (Opts.DiagnosticsWithHotness && !UsingProfile && + // An IR file will contain PGO as metadata + IK.getLanguage() != InputKind::LLVM_IR) + Diags.Report(diag::warn_drv_diagnostics_hotness_requires_pgo) + << "-fdiagnostics-show-hotness"; + + Opts.DiagnosticsHotnessThreshold = getLastArgUInt64Value( + Args, options::OPT_fdiagnostics_hotness_threshold_EQ, 0); + if (Opts.DiagnosticsHotnessThreshold > 0 && !UsingProfile) + Diags.Report(diag::warn_drv_diagnostics_hotness_requires_pgo) + << "-fdiagnostics-hotness-threshold="; + + // If the user requested to use a sample profile for PGO, then the + // backend will need to track source location information so the profile + // can be incorporated into the IR. + if (UsingSampleProfile) + NeedLocTracking = true; + + // If the user requested a flag that requires source locations available in + // the backend, make sure that the backend tracks source location information. + if (NeedLocTracking && Opts.getDebugInfo() == codegenoptions::NoDebugInfo) + Opts.setDebugInfo(codegenoptions::LocTrackingOnly); + + Opts.RewriteMapFiles = Args.getAllArgValues(OPT_frewrite_map_file); + + // Parse -fsanitize-recover= arguments. + // FIXME: Report unrecoverable sanitizers incorrectly specified here. + parseSanitizerKinds("-fsanitize-recover=", + Args.getAllArgValues(OPT_fsanitize_recover_EQ), Diags, + Opts.SanitizeRecover); + parseSanitizerKinds("-fsanitize-trap=", + Args.getAllArgValues(OPT_fsanitize_trap_EQ), Diags, + Opts.SanitizeTrap); + + Opts.CudaGpuBinaryFileName = + Args.getLastArgValue(OPT_fcuda_include_gpubinary); + + Opts.Backchain = Args.hasArg(OPT_mbackchain); + + Opts.EmitCheckPathComponentsToStrip = getLastArgIntValue( + Args, OPT_fsanitize_undefined_strip_path_components_EQ, 0, Diags); + + Opts.EmitVersionIdentMetadata = Args.hasFlag(OPT_Qy, OPT_Qn, true); + + Opts.Addrsig = Args.hasArg(OPT_faddrsig); + + if (Arg *A = Args.getLastArg(OPT_msign_return_address_EQ)) { + StringRef SignScope = A->getValue(); + + if (SignScope.equals_lower("none")) + Opts.setSignReturnAddress(CodeGenOptions::SignReturnAddressScope::None); + else if (SignScope.equals_lower("all")) + Opts.setSignReturnAddress(CodeGenOptions::SignReturnAddressScope::All); + else if (SignScope.equals_lower("non-leaf")) + Opts.setSignReturnAddress( + CodeGenOptions::SignReturnAddressScope::NonLeaf); + else + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << SignScope; + + if (Arg *A = Args.getLastArg(OPT_msign_return_address_key_EQ)) { + StringRef SignKey = A->getValue(); + if (!SignScope.empty() && !SignKey.empty()) { + if (SignKey.equals_lower("a_key")) + Opts.setSignReturnAddressKey( + CodeGenOptions::SignReturnAddressKeyValue::AKey); + else if (SignKey.equals_lower("b_key")) + Opts.setSignReturnAddressKey( + CodeGenOptions::SignReturnAddressKeyValue::BKey); + else + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << SignKey; + } + } + } + + Opts.BranchTargetEnforcement = Args.hasArg(OPT_mbranch_target_enforce); + + Opts.KeepStaticConsts = Args.hasArg(OPT_fkeep_static_consts); + + Opts.SpeculativeLoadHardening = Args.hasArg(OPT_mspeculative_load_hardening); + + Opts.DefaultFunctionAttrs = Args.getAllArgValues(OPT_default_function_attr); + + return Success; +} + +static void ParseDependencyOutputArgs(DependencyOutputOptions &Opts, + ArgList &Args) { + Opts.OutputFile = Args.getLastArgValue(OPT_dependency_file); + Opts.Targets = Args.getAllArgValues(OPT_MT); + Opts.IncludeSystemHeaders = Args.hasArg(OPT_sys_header_deps); + Opts.IncludeModuleFiles = Args.hasArg(OPT_module_file_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); + if (Args.hasArg(OPT_show_includes)) { + // Writing both /showIncludes and preprocessor output to stdout + // would produce interleaved output, so use stderr for /showIncludes. + // This behaves the same as cl.exe, when /E, /EP or /P are passed. + if (Args.hasArg(options::OPT_E) || Args.hasArg(options::OPT_P)) + Opts.ShowIncludesDest = ShowIncludesDestination::Stderr; + else + Opts.ShowIncludesDest = ShowIncludesDestination::Stdout; + } else { + Opts.ShowIncludesDest = ShowIncludesDestination::None; + } + Opts.DOTOutputFile = Args.getLastArgValue(OPT_dependency_dot); + Opts.ModuleDependencyOutputDir = + Args.getLastArgValue(OPT_module_dependency_dir); + if (Args.hasArg(OPT_MV)) + Opts.OutputFormat = DependencyOutputFormat::NMake; + // Add sanitizer blacklists as extra dependencies. + // They won't be discovered by the regular preprocessor, so + // we let make / ninja to know about this implicit dependency. + Opts.ExtraDeps = Args.getAllArgValues(OPT_fdepfile_entry); + // Only the -fmodule-file=<file> form. + for (const auto *A : Args.filtered(OPT_fmodule_file)) { + StringRef Val = A->getValue(); + if (Val.find('=') == StringRef::npos) + Opts.ExtraDeps.push_back(Val); + } +} + +static bool parseShowColorsArgs(const ArgList &Args, bool DefaultColor) { + // Color diagnostics default to auto ("on" if terminal supports) in the driver + // but default to off in cc1, needing an explicit OPT_fdiagnostics_color. + // Support both clang's -f[no-]color-diagnostics and gcc's + // -f[no-]diagnostics-colors[=never|always|auto]. + enum { + Colors_On, + Colors_Off, + Colors_Auto + } ShowColors = DefaultColor ? Colors_Auto : Colors_Off; + for (auto *A : Args) { + const Option &O = A->getOption(); + if (O.matches(options::OPT_fcolor_diagnostics) || + O.matches(options::OPT_fdiagnostics_color)) { + ShowColors = Colors_On; + } else if (O.matches(options::OPT_fno_color_diagnostics) || + O.matches(options::OPT_fno_diagnostics_color)) { + ShowColors = Colors_Off; + } else if (O.matches(options::OPT_fdiagnostics_color_EQ)) { + StringRef Value(A->getValue()); + if (Value == "always") + ShowColors = Colors_On; + else if (Value == "never") + ShowColors = Colors_Off; + else if (Value == "auto") + ShowColors = Colors_Auto; + } + } + return ShowColors == Colors_On || + (ShowColors == Colors_Auto && + llvm::sys::Process::StandardErrHasColors()); +} + +static bool checkVerifyPrefixes(const std::vector<std::string> &VerifyPrefixes, + DiagnosticsEngine *Diags) { + bool Success = true; + for (const auto &Prefix : VerifyPrefixes) { + // Every prefix must start with a letter and contain only alphanumeric + // characters, hyphens, and underscores. + auto BadChar = std::find_if(Prefix.begin(), Prefix.end(), + [](char C){return !isAlphanumeric(C) + && C != '-' && C != '_';}); + if (BadChar != Prefix.end() || !isLetter(Prefix[0])) { + Success = false; + if (Diags) { + Diags->Report(diag::err_drv_invalid_value) << "-verify=" << Prefix; + Diags->Report(diag::note_drv_verify_prefix_spelling); + } + } + } + return Success; +} + +bool clang::ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args, + DiagnosticsEngine *Diags, + bool DefaultDiagColor, bool DefaultShowOpt) { + bool Success = true; + + Opts.DiagnosticLogFile = Args.getLastArgValue(OPT_diagnostic_log_file); + if (Arg *A = + Args.getLastArg(OPT_diagnostic_serialized_file, OPT__serialize_diags)) + Opts.DiagnosticSerializationFile = A->getValue(); + 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 = parseShowColorsArgs(Args, DefaultDiagColor); + 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.AbsolutePath = Args.hasArg(OPT_fdiagnostics_absolute_paths); + Opts.ShowOptionNames = + Args.hasFlag(OPT_fdiagnostics_show_option, + OPT_fno_diagnostics_show_option, DefaultShowOpt); + + 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) || Args.hasArg(OPT_verify_EQ); + Opts.VerifyPrefixes = Args.getAllArgValues(OPT_verify_EQ); + if (Args.hasArg(OPT_verify)) + Opts.VerifyPrefixes.push_back("expected"); + // Keep VerifyPrefixes in its original order for the sake of diagnostics, and + // then sort it to prepare for fast lookup using std::binary_search. + if (!checkVerifyPrefixes(Opts.VerifyPrefixes, Diags)) { + Opts.VerifyDiagnostics = false; + Success = false; + } + else + llvm::sort(Opts.VerifyPrefixes); + DiagnosticLevelMask DiagMask = DiagnosticLevelMask::None; + Success &= parseDiagnosticLevelMask("-verify-ignore-unexpected=", + Args.getAllArgValues(OPT_verify_ignore_unexpected_EQ), + Diags, DiagMask); + if (Args.hasArg(OPT_verify_ignore_unexpected)) + DiagMask = DiagnosticLevelMask::All; + Opts.setVerifyIgnoreUnexpected(DiagMask); + 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.SpellCheckingLimit = getLastArgIntValue( + Args, OPT_fspell_checking_limit, + DiagnosticOptions::DefaultSpellCheckingLimit, Diags); + Opts.SnippetLineLimit = getLastArgIntValue( + Args, OPT_fcaret_diagnostics_max_lines, + DiagnosticOptions::DefaultSnippetLineLimit, 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); + addDiagnosticArgs(Args, OPT_W_Group, OPT_W_value_Group, Opts.Warnings); + addDiagnosticArgs(Args, OPT_R_Group, OPT_R_value_Group, Opts.Remarks); + + return Success; +} + +static void ParseFileSystemArgs(FileSystemOptions &Opts, ArgList &Args) { + Opts.WorkingDir = Args.getLastArgValue(OPT_working_directory); +} + +/// Parse the argument to the -ftest-module-file-extension +/// command-line argument. +/// +/// \returns true on error, false on success. +static bool parseTestModuleFileExtensionArg(StringRef Arg, + std::string &BlockName, + unsigned &MajorVersion, + unsigned &MinorVersion, + bool &Hashed, + std::string &UserInfo) { + SmallVector<StringRef, 5> Args; + Arg.split(Args, ':', 5); + if (Args.size() < 5) + return true; + + BlockName = Args[0]; + if (Args[1].getAsInteger(10, MajorVersion)) return true; + if (Args[2].getAsInteger(10, MinorVersion)) return true; + if (Args[3].getAsInteger(2, Hashed)) return true; + if (Args.size() > 4) + UserInfo = Args[4]; + return false; +} + +static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, + DiagnosticsEngine &Diags, + bool &IsHeaderFile) { + 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: + case OPT_ast_dump_all: + case OPT_ast_dump_lookups: + 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_compiler_options_dump: + Opts.ProgramAction = frontend::DumpCompilerOptions; 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(); + LLVM_FALLTHROUGH; + case OPT_fixit: + Opts.ProgramAction = frontend::FixIt; break; + case OPT_emit_module: + Opts.ProgramAction = frontend::GenerateModule; break; + case OPT_emit_module_interface: + Opts.ProgramAction = frontend::GenerateModuleInterface; break; + case OPT_emit_header_module: + Opts.ProgramAction = frontend::GenerateHeaderModule; break; + case OPT_emit_pch: + Opts.ProgramAction = frontend::GeneratePCH; 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_verify_pch: + Opts.ProgramAction = frontend::VerifyPCH; break; + case OPT_print_preamble: + Opts.ProgramAction = frontend::PrintPreamble; break; + case OPT_E: + Opts.ProgramAction = frontend::PrintPreprocessedInput; break; + case OPT_templight_dump: + Opts.ProgramAction = frontend::TemplightDump; 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.emplace_back(A->getValue(0)); + Opts.ProgramAction = frontend::PluginAction; + Opts.ActionName = A->getValue(); + } + Opts.AddPluginActions = Args.getAllArgValues(OPT_add_plugin); + for (const auto *AA : Args.filtered(OPT_plugin_arg)) + Opts.PluginArgs[AA->getValue(0)].emplace_back(AA->getValue(1)); + + for (const std::string &Arg : + Args.getAllArgValues(OPT_ftest_module_file_extension_EQ)) { + std::string BlockName; + unsigned MajorVersion; + unsigned MinorVersion; + bool Hashed; + std::string UserInfo; + if (parseTestModuleFileExtensionArg(Arg, BlockName, MajorVersion, + MinorVersion, Hashed, UserInfo)) { + Diags.Report(diag::err_test_module_file_extension_format) << Arg; + + continue; + } + + // Add the testing module file extension. + Opts.ModuleFileExtensions.push_back( + std::make_shared<TestModuleFileExtension>( + BlockName, MajorVersion, MinorVersion, Hashed, UserInfo)); + } + + 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.ASTDumpDecls = Args.hasArg(OPT_ast_dump); + Opts.ASTDumpAll = Args.hasArg(OPT_ast_dump_all); + 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.ModuleMapFiles = Args.getAllArgValues(OPT_fmodule_map_file); + // Only the -fmodule-file=<file> form. + for (const auto *A : Args.filtered(OPT_fmodule_file)) { + StringRef Val = A->getValue(); + if (Val.find('=') == StringRef::npos) + Opts.ModuleFiles.push_back(Val); + } + Opts.ModulesEmbedFiles = Args.getAllArgValues(OPT_fmodules_embed_file_EQ); + Opts.ModulesEmbedAllFiles = Args.hasArg(OPT_fmodules_embed_all_files); + Opts.IncludeTimestamps = !Args.hasArg(OPT_fno_pch_timestamp); + + 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.IncludeNamespaceLevelDecls + = !Args.hasArg(OPT_no_code_completion_ns_level_decls); + Opts.CodeCompleteOpts.IncludeBriefComments + = Args.hasArg(OPT_code_completion_brief_comments); + Opts.CodeCompleteOpts.IncludeFixIts + = Args.hasArg(OPT_code_completion_with_fixits); + + Opts.OverrideRecordLayoutsFile + = Args.getLastArgValue(OPT_foverride_record_layout_EQ); + Opts.AuxTriple = Args.getLastArgValue(OPT_aux_triple); + Opts.StatsFile = Args.getLastArgValue(OPT_stats_file); + + 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_dot_syntax)) + Opts.ObjCMTAction |= FrontendOptions::ObjCMT_PropertyDotSyntax; + 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_designated_init)) + Opts.ObjCMTAction |= FrontendOptions::ObjCMT_DesignatedInitializer; + if (Args.hasArg(OPT_objcmt_migrate_all)) + Opts.ObjCMTAction |= FrontendOptions::ObjCMT_MigrateDecls; + + Opts.ObjCMTWhiteListPath = Args.getLastArgValue(OPT_objcmt_whitelist_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(InputKind::Unknown); + if (const Arg *A = Args.getLastArg(OPT_x)) { + StringRef XValue = A->getValue(); + + // Parse suffixes: '<lang>(-header|[-module-map][-cpp-output])'. + // FIXME: Supporting '<lang>-header-cpp-output' would be useful. + bool Preprocessed = XValue.consume_back("-cpp-output"); + bool ModuleMap = XValue.consume_back("-module-map"); + IsHeaderFile = + !Preprocessed && !ModuleMap && XValue.consume_back("-header"); + + // Principal languages. + DashX = llvm::StringSwitch<InputKind>(XValue) + .Case("c", InputKind::C) + .Case("cl", InputKind::OpenCL) + .Case("cuda", InputKind::CUDA) + .Case("hip", InputKind::HIP) + .Case("c++", InputKind::CXX) + .Case("objective-c", InputKind::ObjC) + .Case("objective-c++", InputKind::ObjCXX) + .Case("renderscript", InputKind::RenderScript) + .Default(InputKind::Unknown); + + // "objc[++]-cpp-output" is an acceptable synonym for + // "objective-c[++]-cpp-output". + if (DashX.isUnknown() && Preprocessed && !IsHeaderFile && !ModuleMap) + DashX = llvm::StringSwitch<InputKind>(XValue) + .Case("objc", InputKind::ObjC) + .Case("objc++", InputKind::ObjCXX) + .Default(InputKind::Unknown); + + // Some special cases cannot be combined with suffixes. + if (DashX.isUnknown() && !Preprocessed && !ModuleMap && !IsHeaderFile) + DashX = llvm::StringSwitch<InputKind>(XValue) + .Case("cpp-output", InputKind(InputKind::C).getPreprocessed()) + .Case("assembler-with-cpp", InputKind::Asm) + .Cases("ast", "pcm", + InputKind(InputKind::Unknown, InputKind::Precompiled)) + .Case("ir", InputKind::LLVM_IR) + .Default(InputKind::Unknown); + + if (DashX.isUnknown()) + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << A->getValue(); + + if (Preprocessed) + DashX = DashX.getPreprocessed(); + if (ModuleMap) + DashX = DashX.withFormat(InputKind::ModuleMap); + } + + // '-' 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.isUnknown()) { + IK = FrontendOptions::getInputKindForExtension( + StringRef(Inputs[i]).rsplit('.').second); + // FIXME: Warn on this? + if (IK.isUnknown()) + IK = InputKind::C; + // FIXME: Remove this hack. + if (i == 0) + DashX = IK; + } + + // The -emit-module action implicitly takes a module map. + if (Opts.ProgramAction == frontend::GenerateModule && + IK.getFormat() == InputKind::Source) + IK = IK.withFormat(InputKind::ModuleMap); + + Opts.Inputs.emplace_back(std::move(Inputs[i]), IK); + } + + return DashX; +} + +std::string CompilerInvocation::GetResourcesPath(const char *Argv0, + void *MainAddr) { + std::string ClangExecutable = + llvm::sys::fs::getMainExecutable(Argv0, MainAddr); + StringRef Dir = llvm::sys::path::parent_path(ClangExecutable); + + // Compute the path to the resource directory. + StringRef ClangResourceDir(CLANG_RESOURCE_DIR); + SmallString<128> P(Dir); + if (ClangResourceDir != "") + llvm::sys::path::append(P, ClangResourceDir); + else + llvm::sys::path::append(P, "..", Twine("lib") + CLANG_LIBDIR_SUFFIX, + "clang", CLANG_VERSION_STRING); + + return P.str(); +} + +static void ParseHeaderSearchArgs(HeaderSearchOptions &Opts, ArgList &Args, + const std::string &WorkingDir) { + 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); + + // Canonicalize -fmodules-cache-path before storing it. + SmallString<128> P(Args.getLastArgValue(OPT_fmodules_cache_path)); + if (!(P.empty() || llvm::sys::path::is_absolute(P))) { + if (WorkingDir.empty()) + llvm::sys::fs::make_absolute(P); + else + llvm::sys::fs::make_absolute(WorkingDir, P); + } + llvm::sys::path::remove_dots(P); + Opts.ModuleCachePath = P.str(); + + Opts.ModuleUserBuildPath = Args.getLastArgValue(OPT_fmodules_user_build_path); + // Only the -fmodule-file=<name>=<file> form. + for (const auto *A : Args.filtered(OPT_fmodule_file)) { + StringRef Val = A->getValue(); + if (Val.find('=') != StringRef::npos) + Opts.PrebuiltModuleFiles.insert(Val.split('=')); + } + for (const auto *A : Args.filtered(OPT_fprebuilt_module_path)) + Opts.AddPrebuiltModulePath(A->getValue()); + Opts.DisableModuleHash = Args.hasArg(OPT_fdisable_module_hash); + Opts.ModulesHashContent = Args.hasArg(OPT_fmodules_hash_content); + Opts.ModulesValidateDiagnosticOptions = + !Args.hasArg(OPT_fmodules_disable_diagnostic_validation); + Opts.ImplicitModuleMaps = Args.hasArg(OPT_fimplicit_module_maps); + Opts.ModuleMapFileHomeIsCwd = Args.hasArg(OPT_fmodule_map_file_home_is_cwd); + Opts.ModuleCachePruneInterval = + getLastArgIntValue(Args, OPT_fmodules_prune_interval, 7 * 24 * 60 * 60); + Opts.ModuleCachePruneAfter = + getLastArgIntValue(Args, OPT_fmodules_prune_after, 31 * 24 * 60 * 60); + Opts.ModulesValidateOncePerBuildSession = + Args.hasArg(OPT_fmodules_validate_once_per_build_session); + Opts.BuildSessionTimestamp = + getLastArgUInt64Value(Args, OPT_fbuild_session_timestamp, 0); + Opts.ModulesValidateSystemHeaders = + Args.hasArg(OPT_fmodules_validate_system_headers); + if (const Arg *A = Args.getLastArg(OPT_fmodule_format_EQ)) + Opts.ModuleFormat = A->getValue(); + + for (const auto *A : Args.filtered(OPT_fmodules_ignore_macro)) { + StringRef MacroDef = A->getValue(); + Opts.ModulesIgnoreMacros.insert( + llvm::CachedHashString(MacroDef.split('=').first)); + } + + // Add -I..., -F..., and -index-header-map options in order. + bool IsIndexHeaderMap = false; + bool IsSysrootSpecified = + Args.hasArg(OPT__sysroot_EQ) || Args.hasArg(OPT_isysroot); + for (const auto *A : Args.filtered(OPT_I, OPT_F, OPT_index_header_map)) { + if (A->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; + + bool IsFramework = A->getOption().matches(OPT_F); + std::string Path = A->getValue(); + + if (IsSysrootSpecified && !IsFramework && A->getValue()[0] == '=') { + SmallString<32> Buffer; + llvm::sys::path::append(Buffer, Opts.Sysroot, + llvm::StringRef(A->getValue()).substr(1)); + Path = Buffer.str(); + } + + Opts.AddPath(Path, Group, IsFramework, + /*IgnoreSysroot*/ true); + IsIndexHeaderMap = false; + } + + // Add -iprefix/-iwithprefix/-iwithprefixbefore options. + StringRef Prefix = ""; // FIXME: This isn't the correct default prefix. + for (const auto *A : + Args.filtered(OPT_iprefix, OPT_iwithprefix, OPT_iwithprefixbefore)) { + 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 (const auto *A : Args.filtered(OPT_idirafter)) + Opts.AddPath(A->getValue(), frontend::After, false, true); + for (const auto *A : Args.filtered(OPT_iquote)) + Opts.AddPath(A->getValue(), frontend::Quoted, false, true); + for (const auto *A : Args.filtered(OPT_isystem, OPT_iwithsysroot)) + Opts.AddPath(A->getValue(), frontend::System, false, + !A->getOption().matches(OPT_iwithsysroot)); + for (const auto *A : Args.filtered(OPT_iframework)) + Opts.AddPath(A->getValue(), frontend::System, true, true); + for (const auto *A : Args.filtered(OPT_iframeworkwithsysroot)) + Opts.AddPath(A->getValue(), frontend::System, /*IsFramework=*/true, + /*IgnoreSysRoot=*/false); + + // Add the paths for the various language specific isystem flags. + for (const auto *A : Args.filtered(OPT_c_isystem)) + Opts.AddPath(A->getValue(), frontend::CSystem, false, true); + for (const auto *A : Args.filtered(OPT_cxx_isystem)) + Opts.AddPath(A->getValue(), frontend::CXXSystem, false, true); + for (const auto *A : Args.filtered(OPT_objc_isystem)) + Opts.AddPath(A->getValue(), frontend::ObjCSystem, false,true); + for (const auto *A : Args.filtered(OPT_objcxx_isystem)) + Opts.AddPath(A->getValue(), frontend::ObjCXXSystem, false, true); + + // Add the internal paths from a driver that detects standard include paths. + for (const auto *A : + Args.filtered(OPT_internal_isystem, OPT_internal_externc_isystem)) { + frontend::IncludeDirGroup Group = frontend::System; + if (A->getOption().matches(OPT_internal_externc_isystem)) + Group = frontend::ExternCSystem; + Opts.AddPath(A->getValue(), Group, false, true); + } + + // Add the path prefixes which are implicitly treated as being system headers. + for (const auto *A : + Args.filtered(OPT_system_header_prefix, OPT_no_system_header_prefix)) + Opts.AddSystemHeaderPrefix( + A->getValue(), A->getOption().matches(OPT_system_header_prefix)); + + for (const auto *A : Args.filtered(OPT_ivfsoverlay)) + Opts.AddVFSOverlayFile(A->getValue()); +} + +void CompilerInvocation::setLangDefaults(LangOptions &Opts, InputKind IK, + const llvm::Triple &T, + PreprocessorOptions &PPOpts, + 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. + // + // FIXME: Perhaps a better model would be for a single source file to have + // multiple language standards (C / C++ std, ObjC std, OpenCL std, OpenMP std) + // simultaneously active? + if (IK.getLanguage() == InputKind::Asm) { + Opts.AsmPreprocessor = 1; + } else if (IK.isObjectiveC()) { + Opts.ObjC = 1; + } + + if (LangStd == LangStandard::lang_unspecified) { + // Based on the base language, pick one. + switch (IK.getLanguage()) { + case InputKind::Unknown: + case InputKind::LLVM_IR: + llvm_unreachable("Invalid input kind!"); + case InputKind::OpenCL: + LangStd = LangStandard::lang_opencl10; + break; + case InputKind::CUDA: + LangStd = LangStandard::lang_cuda; + break; + case InputKind::Asm: + case InputKind::C: +#if defined(CLANG_DEFAULT_STD_C) + LangStd = CLANG_DEFAULT_STD_C; +#else + // The PS4 uses C99 as the default C standard. + if (T.isPS4()) + LangStd = LangStandard::lang_gnu99; + else + LangStd = LangStandard::lang_gnu11; +#endif + break; + case InputKind::ObjC: +#if defined(CLANG_DEFAULT_STD_C) + LangStd = CLANG_DEFAULT_STD_C; +#else + LangStd = LangStandard::lang_gnu11; +#endif + break; + case InputKind::CXX: + case InputKind::ObjCXX: +#if defined(CLANG_DEFAULT_STD_CXX) + LangStd = CLANG_DEFAULT_STD_CXX; +#else + LangStd = LangStandard::lang_gnucxx14; +#endif + break; + case InputKind::RenderScript: + LangStd = LangStandard::lang_c99; + break; + case InputKind::HIP: + LangStd = LangStandard::lang_hip; + break; + } + } + + const LangStandard &Std = LangStandard::getLangStandardForKind(LangStd); + Opts.LineComment = Std.hasLineComments(); + Opts.C99 = Std.isC99(); + Opts.C11 = Std.isC11(); + Opts.C17 = Std.isC17(); + Opts.CPlusPlus = Std.isCPlusPlus(); + Opts.CPlusPlus11 = Std.isCPlusPlus11(); + Opts.CPlusPlus14 = Std.isCPlusPlus14(); + Opts.CPlusPlus17 = Std.isCPlusPlus17(); + Opts.CPlusPlus2a = Std.isCPlusPlus2a(); + Opts.Digraphs = Std.hasDigraphs(); + Opts.GNUMode = Std.isGNUMode(); + Opts.GNUInline = !Opts.C99 && !Opts.CPlusPlus; + Opts.HexFloats = Std.hasHexFloats(); + Opts.ImplicitInt = Std.hasImplicitInt(); + + // Set OpenCL Version. + Opts.OpenCL = Std.isOpenCL(); + if (LangStd == LangStandard::lang_opencl10) + Opts.OpenCLVersion = 100; + else if (LangStd == LangStandard::lang_opencl11) + Opts.OpenCLVersion = 110; + else if (LangStd == LangStandard::lang_opencl12) + Opts.OpenCLVersion = 120; + else if (LangStd == LangStandard::lang_opencl20) + Opts.OpenCLVersion = 200; + else if (LangStd == LangStandard::lang_openclcpp) + Opts.OpenCLCPlusPlusVersion = 100; + + // OpenCL has some additional defaults. + if (Opts.OpenCL) { + Opts.AltiVec = 0; + Opts.ZVector = 0; + Opts.LaxVectorConversions = 0; + Opts.setDefaultFPContractMode(LangOptions::FPC_On); + Opts.NativeHalfType = 1; + Opts.NativeHalfArgsAndReturns = 1; + Opts.OpenCLCPlusPlus = Opts.CPlusPlus; + // Include default header file for OpenCL. + if (Opts.IncludeDefaultHeader) { + PPOpts.Includes.push_back("opencl-c.h"); + } + } + + Opts.HIP = IK.getLanguage() == InputKind::HIP; + Opts.CUDA = IK.getLanguage() == InputKind::CUDA || Opts.HIP; + if (Opts.CUDA) + // Set default FP_CONTRACT to FAST. + Opts.setDefaultFPContractMode(LangOptions::FPC_Fast); + + Opts.RenderScript = IK.getLanguage() == InputKind::RenderScript; + if (Opts.RenderScript) { + Opts.NativeHalfType = 1; + Opts.NativeHalfArgsAndReturns = 1; + } + + // OpenCL and C++ both have bool, true, false keywords. + Opts.Bool = Opts.OpenCL || Opts.CPlusPlus; + + // OpenCL has half keyword + Opts.Half = Opts.OpenCL; + + // C++ has wchar_t keyword. + Opts.WChar = Opts.CPlusPlus; + + Opts.GNUKeywords = Opts.GNUMode; + Opts.CXXOperatorNames = Opts.CPlusPlus; + + Opts.AlignedAllocation = Opts.CPlusPlus17; + + Opts.DollarIdents = !Opts.AsmPreprocessor; +} + +/// 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" || value == "internal") { + 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; +} + +/// Check if input file kind and language standard are compatible. +static bool IsInputCompatibleWithStandard(InputKind IK, + const LangStandard &S) { + switch (IK.getLanguage()) { + case InputKind::Unknown: + case InputKind::LLVM_IR: + llvm_unreachable("should not parse language flags for this input"); + + case InputKind::C: + case InputKind::ObjC: + case InputKind::RenderScript: + return S.getLanguage() == InputKind::C; + + case InputKind::OpenCL: + return S.getLanguage() == InputKind::OpenCL; + + case InputKind::CXX: + case InputKind::ObjCXX: + return S.getLanguage() == InputKind::CXX; + + case InputKind::CUDA: + // FIXME: What -std= values should be permitted for CUDA compilations? + return S.getLanguage() == InputKind::CUDA || + S.getLanguage() == InputKind::CXX; + + case InputKind::HIP: + return S.getLanguage() == InputKind::CXX || + S.getLanguage() == InputKind::HIP; + + case InputKind::Asm: + // Accept (and ignore) all -std= values. + // FIXME: The -std= value is not ignored; it affects the tokenization + // and preprocessing rules if we're preprocessing this asm input. + return true; + } + + llvm_unreachable("unexpected input language"); +} + +/// Get language name for given input kind. +static const StringRef GetInputKindName(InputKind IK) { + switch (IK.getLanguage()) { + case InputKind::C: + return "C"; + case InputKind::ObjC: + return "Objective-C"; + case InputKind::CXX: + return "C++"; + case InputKind::ObjCXX: + return "Objective-C++"; + case InputKind::OpenCL: + return "OpenCL"; + case InputKind::CUDA: + return "CUDA"; + case InputKind::RenderScript: + return "RenderScript"; + case InputKind::HIP: + return "HIP"; + + case InputKind::Asm: + return "Asm"; + case InputKind::LLVM_IR: + return "LLVM IR"; + + case InputKind::Unknown: + break; + } + llvm_unreachable("unknown input language"); +} + +static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, + const TargetOptions &TargetOpts, + PreprocessorOptions &PPOpts, + 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, lang, desc, features) \ + .Case(name, LangStandard::lang_##id) +#define LANGSTANDARD_ALIAS(id, alias) \ + .Case(alias, 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(); + // Report supported standards with short description. + for (unsigned KindValue = 0; + KindValue != LangStandard::lang_unspecified; + ++KindValue) { + const LangStandard &Std = LangStandard::getLangStandardForKind( + static_cast<LangStandard::Kind>(KindValue)); + if (IsInputCompatibleWithStandard(IK, Std)) { + auto Diag = Diags.Report(diag::note_drv_use_standard); + Diag << Std.getName() << Std.getDescription(); + unsigned NumAliases = 0; +#define LANGSTANDARD(id, name, lang, desc, features) +#define LANGSTANDARD_ALIAS(id, alias) \ + if (KindValue == LangStandard::lang_##id) ++NumAliases; +#define LANGSTANDARD_ALIAS_DEPR(id, alias) +#include "clang/Frontend/LangStandards.def" + Diag << NumAliases; +#define LANGSTANDARD(id, name, lang, desc, features) +#define LANGSTANDARD_ALIAS(id, alias) \ + if (KindValue == LangStandard::lang_##id) Diag << alias; +#define LANGSTANDARD_ALIAS_DEPR(id, alias) +#include "clang/Frontend/LangStandards.def" + } + } + } else { + // Valid standard, check to make sure language and standard are + // compatible. + const LangStandard &Std = LangStandard::getLangStandardForKind(LangStd); + if (!IsInputCompatibleWithStandard(IK, Std)) { + Diags.Report(diag::err_drv_argument_not_allowed_with) + << A->getAsString(Args) << GetInputKindName(IK); + } + } + } + + if (Args.hasArg(OPT_fno_dllexport_inlines)) + Opts.DllExportInlines = false; + + if (const Arg *A = Args.getLastArg(OPT_fcf_protection_EQ)) { + StringRef Name = A->getValue(); + if (Name == "full" || Name == "branch") { + Opts.CFProtectionBranch = 1; + } + } + // -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()) + .Cases("cl", "CL", LangStandard::lang_opencl10) + .Cases("cl1.1", "CL1.1", LangStandard::lang_opencl11) + .Cases("cl1.2", "CL1.2", LangStandard::lang_opencl12) + .Cases("cl2.0", "CL2.0", LangStandard::lang_opencl20) + .Case("c++", LangStandard::lang_openclcpp) + .Default(LangStandard::lang_unspecified); + + if (OpenCLLangStd == LangStandard::lang_unspecified) { + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << A->getValue(); + } + else + LangStd = OpenCLLangStd; + } + + Opts.IncludeDefaultHeader = Args.hasArg(OPT_finclude_default_header); + + llvm::Triple T(TargetOpts.Triple); + CompilerInvocation::setLangDefaults(Opts, IK, T, PPOpts, LangStd); + + // -cl-strict-aliasing needs to emit diagnostic in the case where CL > 1.0. + // This option should be deprecated for CL > 1.0 because + // this option was added for compatibility with OpenCL 1.0. + if (Args.getLastArg(OPT_cl_strict_aliasing) + && Opts.OpenCLVersion > 100) { + Diags.Report(diag::warn_option_invalid_ocl_version) + << Opts.getOpenCLVersionTuple().getAsString() + << Args.getLastArg(OPT_cl_strict_aliasing)->getAsString(Args); + } + + // 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); + + Opts.Digraphs = Args.hasFlag(OPT_fdigraphs, OPT_fno_digraphs, Opts.Digraphs); + + if (Args.hasArg(OPT_fno_operator_names)) + Opts.CXXOperatorNames = 0; + + if (Args.hasArg(OPT_fcuda_is_device)) + Opts.CUDAIsDevice = 1; + + if (Args.hasArg(OPT_fcuda_allow_variadic_functions)) + Opts.CUDAAllowVariadicFunctions = 1; + + if (Args.hasArg(OPT_fno_cuda_host_device_constexpr)) + Opts.CUDAHostDeviceConstexpr = 0; + + if (Opts.CUDAIsDevice && Args.hasArg(OPT_fcuda_approx_transcendentals)) + Opts.CUDADeviceApproxTranscendentals = 1; + + Opts.GPURelocatableDeviceCode = Args.hasArg(OPT_fgpu_rdc); + + if (Opts.ObjC) { + 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); + } + + // ObjCWeakRuntime tracks whether the runtime supports __weak, not + // whether the feature is actually enabled. This is predominantly + // determined by -fobjc-runtime, but we allow it to be overridden + // from the command line for testing purposes. + if (Args.hasArg(OPT_fobjc_runtime_has_weak)) + Opts.ObjCWeakRuntime = 1; + else + Opts.ObjCWeakRuntime = Opts.ObjCRuntime.allowsWeak(); + + // ObjCWeak determines whether __weak is actually enabled. + // Note that we allow -fno-objc-weak to disable this even in ARC mode. + if (auto weakArg = Args.getLastArg(OPT_fobjc_weak, OPT_fno_objc_weak)) { + if (!weakArg->getOption().matches(OPT_fobjc_weak)) { + assert(!Opts.ObjCWeak); + } else if (Opts.getGC() != LangOptions::NonGC) { + Diags.Report(diag::err_objc_weak_with_gc); + } else if (!Opts.ObjCWeakRuntime) { + Diags.Report(diag::err_objc_weak_unsupported); + } else { + Opts.ObjCWeak = 1; + } + } else if (Opts.ObjCAutoRefCount) { + Opts.ObjCWeak = Opts.ObjCWeakRuntime; + } + + 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)) { + if (Opts.CPlusPlus) + Diags.Report(diag::err_drv_argument_not_allowed_with) + << "-fgnu89-inline" << GetInputKindName(IK); + else + 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 (const auto *A = Args.getLastArg(OPT_fcf_runtime_abi_EQ)) + Opts.CFRuntime = + llvm::StringSwitch<LangOptions::CoreFoundationABI>(A->getValue()) + .Cases("unspecified", "standalone", "objc", + LangOptions::CoreFoundationABI::ObjectiveC) + .Cases("swift", "swift-5.0", + LangOptions::CoreFoundationABI::Swift5_0) + .Case("swift-4.2", LangOptions::CoreFoundationABI::Swift4_2) + .Case("swift-4.1", LangOptions::CoreFoundationABI::Swift4_1) + .Default(LangOptions::CoreFoundationABI::ObjectiveC); + + if (Args.hasArg(OPT_fzvector)) + Opts.ZVector = 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_fvisibility_global_new_delete_hidden)) + Opts.GlobalAllocationFunctionVisibilityHidden = 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); + + Opts.MSVCCompat = Args.hasArg(OPT_fms_compatibility); + Opts.MicrosoftExt = Opts.MSVCCompat || Args.hasArg(OPT_fms_extensions); + Opts.AsmBlocks = Args.hasArg(OPT_fasm_blocks) || Opts.MicrosoftExt; + Opts.MSCompatibilityVersion = 0; + if (const Arg *A = Args.getLastArg(OPT_fms_compatibility_version)) { + VersionTuple VT; + if (VT.tryParse(A->getValue())) + Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) + << A->getValue(); + Opts.MSCompatibilityVersion = VT.getMajor() * 10000000 + + VT.getMinor().getValueOr(0) * 100000 + + VT.getSubminor().getValueOr(0); + } + + // Mimicking gcc's behavior, trigraphs are only enabled if -trigraphs + // is specified, or -std is set to a conforming mode. + // Trigraphs are disabled by default in c++1z onwards. + Opts.Trigraphs = !Opts.GNUMode && !Opts.MSVCCompat && !Opts.CPlusPlus17; + Opts.Trigraphs = + Args.hasFlag(OPT_ftrigraphs, OPT_fno_trigraphs, Opts.Trigraphs); + + Opts.DollarIdents = Args.hasFlag(OPT_fdollars_in_identifiers, + OPT_fno_dollars_in_identifiers, + Opts.DollarIdents); + Opts.PascalStrings = Args.hasArg(OPT_fpascal_strings); + Opts.VtorDispMode = getLastArgIntValue(Args, OPT_vtordisp_mode_EQ, 1, 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); + + // -ffixed-point + Opts.FixedPoint = + Args.hasFlag(OPT_ffixed_point, OPT_fno_fixed_point, /*Default=*/false) && + !Opts.CPlusPlus; + Opts.PaddingOnUnsignedFixedPoint = + Args.hasFlag(OPT_fpadding_on_unsigned_fixed_point, + OPT_fno_padding_on_unsigned_fixed_point, + /*Default=*/false) && + Opts.FixedPoint; + + // Handle exception personalities + Arg *A = Args.getLastArg(options::OPT_fsjlj_exceptions, + options::OPT_fseh_exceptions, + options::OPT_fdwarf_exceptions); + if (A) { + const Option &Opt = A->getOption(); + llvm::Triple T(TargetOpts.Triple); + if (T.isWindowsMSVCEnvironment()) + Diags.Report(diag::err_fe_invalid_exception_model) + << Opt.getName() << T.str(); + + Opts.SjLjExceptions = Opt.matches(options::OPT_fsjlj_exceptions); + Opts.SEHExceptions = Opt.matches(options::OPT_fseh_exceptions); + Opts.DWARFExceptions = Opt.matches(options::OPT_fdwarf_exceptions); + } + + Opts.ExternCNoUnwind = Args.hasArg(OPT_fexternc_nounwind); + Opts.TraditionalCPP = Args.hasArg(OPT_traditional_cpp); + + Opts.RTTI = Opts.CPlusPlus && !Args.hasArg(OPT_fno_rtti); + Opts.RTTIData = Opts.RTTI && !Args.hasArg(OPT_fno_rtti_data); + Opts.Blocks = Args.hasArg(OPT_fblocks) || (Opts.OpenCL + && Opts.OpenCLVersion == 200); + Opts.BlocksRuntimeOptional = Args.hasArg(OPT_fblocks_runtime_optional); + Opts.CoroutinesTS = Args.hasArg(OPT_fcoroutines_ts); + + // Enable [[]] attributes in C++11 by default. + Opts.DoubleSquareBracketAttributes = + Args.hasFlag(OPT_fdouble_square_bracket_attributes, + OPT_fno_double_square_bracket_attributes, Opts.CPlusPlus11); + + Opts.ModulesTS = Args.hasArg(OPT_fmodules_ts); + Opts.Modules = Args.hasArg(OPT_fmodules) || Opts.ModulesTS; + Opts.ModulesStrictDeclUse = Args.hasArg(OPT_fmodules_strict_decluse); + Opts.ModulesDeclUse = + Args.hasArg(OPT_fmodules_decluse) || Opts.ModulesStrictDeclUse; + Opts.ModulesLocalVisibility = + Args.hasArg(OPT_fmodules_local_submodule_visibility) || Opts.ModulesTS; + Opts.ModulesCodegen = Args.hasArg(OPT_fmodules_codegen); + Opts.ModulesDebugInfo = Args.hasArg(OPT_fmodules_debuginfo); + Opts.ModulesSearchAll = Opts.Modules && + !Args.hasArg(OPT_fno_modules_search_all) && + Args.hasArg(OPT_fmodules_search_all); + Opts.ModulesErrorRecovery = !Args.hasArg(OPT_fno_modules_error_recovery); + Opts.ImplicitModules = !Args.hasArg(OPT_fno_implicit_modules); + Opts.CharIsSigned = Opts.OpenCL || !Args.hasArg(OPT_fno_signed_char); + Opts.WChar = Opts.CPlusPlus && !Args.hasArg(OPT_fno_wchar); + Opts.Char8 = Args.hasFlag(OPT_fchar8__t, OPT_fno_char8__t, Opts.CPlusPlus2a); + if (const Arg *A = Args.getLastArg(OPT_fwchar_type_EQ)) { + Opts.WCharSize = llvm::StringSwitch<unsigned>(A->getValue()) + .Case("char", 1) + .Case("short", 2) + .Case("int", 4) + .Default(0); + if (Opts.WCharSize == 0) + Diags.Report(diag::err_fe_invalid_wchar_type) << A->getValue(); + } + Opts.WCharIsSigned = Args.hasFlag(OPT_fsigned_wchar, OPT_fno_signed_wchar, true); + Opts.ShortEnums = Args.hasArg(OPT_fshort_enums); + Opts.Freestanding = Args.hasArg(OPT_ffreestanding); + Opts.NoBuiltin = Args.hasArg(OPT_fno_builtin) || Opts.Freestanding; + if (!Opts.NoBuiltin) + getAllNoBuiltinFuncValues(Args, Opts.NoBuiltinFuncs); + Opts.NoMathBuiltin = Args.hasArg(OPT_fno_math_builtin); + Opts.RelaxedTemplateTemplateArgs = + Args.hasArg(OPT_frelaxed_template_template_args); + Opts.SizedDeallocation = Args.hasArg(OPT_fsized_deallocation); + Opts.AlignedAllocation = + Args.hasFlag(OPT_faligned_allocation, OPT_fno_aligned_allocation, + Opts.AlignedAllocation); + Opts.AlignedAllocationUnavailable = + Opts.AlignedAllocation && Args.hasArg(OPT_aligned_alloc_unavailable); + Opts.NewAlignOverride = + getLastArgIntValue(Args, OPT_fnew_alignment_EQ, 0, Diags); + if (Opts.NewAlignOverride && !llvm::isPowerOf2_32(Opts.NewAlignOverride)) { + Arg *A = Args.getLastArg(OPT_fnew_alignment_EQ); + Diags.Report(diag::err_fe_invalid_alignment) << A->getAsString(Args) + << A->getValue(); + Opts.NewAlignOverride = 0; + } + Opts.ConceptsTS = Args.hasArg(OPT_fconcepts_ts); + 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, 1024, 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.MaxTypeAlign = getLastArgIntValue(Args, OPT_fmax_type_align_EQ, 0, Diags); + Opts.AlignDouble = Args.hasArg(OPT_malign_double); + Opts.PICLevel = getLastArgIntValue(Args, OPT_pic_level, 0, Diags); + Opts.PIE = Args.hasArg(OPT_pic_is_pie); + 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.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.ModuleName = Args.getLastArgValue(OPT_fmodule_name_EQ); + Opts.CurrentModule = Opts.ModuleName; + Opts.AppExt = Args.hasArg(OPT_fapplication_extension); + Opts.ModuleFeatures = Args.getAllArgValues(OPT_fmodule_feature); + llvm::sort(Opts.ModuleFeatures); + Opts.NativeHalfType |= Args.hasArg(OPT_fnative_half_type); + Opts.NativeHalfArgsAndReturns |= Args.hasArg(OPT_fnative_half_arguments_and_returns); + // Enable HalfArgsAndReturns if present in Args or if NativeHalfArgsAndReturns + // is enabled. + Opts.HalfArgsAndReturns = Args.hasArg(OPT_fallow_half_arguments_and_returns) + | Opts.NativeHalfArgsAndReturns; + Opts.GNUAsm = !Args.hasArg(OPT_fno_gnu_inline_asm); + + // __declspec is enabled by default for the PS4 by the driver, and also + // enabled for Microsoft Extensions or Borland Extensions, here. + // + // FIXME: __declspec is also currently enabled for CUDA, but isn't really a + // CUDA extension. However, it is required for supporting + // __clang_cuda_builtin_vars.h, which uses __declspec(property). Once that has + // been rewritten in terms of something more generic, remove the Opts.CUDA + // term here. + Opts.DeclSpecKeyword = + Args.hasFlag(OPT_fdeclspec, OPT_fno_declspec, + (Opts.MicrosoftExt || Opts.Borland || Opts.CUDA)); + + 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; + } + } + + if (Arg *A = Args.getLastArg(OPT_fms_memptr_rep_EQ)) { + LangOptions::PragmaMSPointersToMembersKind InheritanceModel = + llvm::StringSwitch<LangOptions::PragmaMSPointersToMembersKind>( + A->getValue()) + .Case("single", + LangOptions::PPTMK_FullGeneralitySingleInheritance) + .Case("multiple", + LangOptions::PPTMK_FullGeneralityMultipleInheritance) + .Case("virtual", + LangOptions::PPTMK_FullGeneralityVirtualInheritance) + .Default(LangOptions::PPTMK_BestCase); + if (InheritanceModel == LangOptions::PPTMK_BestCase) + Diags.Report(diag::err_drv_invalid_value) + << "-fms-memptr-rep=" << A->getValue(); + + Opts.setMSPointerToMemberRepresentationMethod(InheritanceModel); + } + + // Check for MS default calling conventions being specified. + if (Arg *A = Args.getLastArg(OPT_fdefault_calling_conv_EQ)) { + LangOptions::DefaultCallingConvention DefaultCC = + llvm::StringSwitch<LangOptions::DefaultCallingConvention>(A->getValue()) + .Case("cdecl", LangOptions::DCC_CDecl) + .Case("fastcall", LangOptions::DCC_FastCall) + .Case("stdcall", LangOptions::DCC_StdCall) + .Case("vectorcall", LangOptions::DCC_VectorCall) + .Case("regcall", LangOptions::DCC_RegCall) + .Default(LangOptions::DCC_None); + if (DefaultCC == LangOptions::DCC_None) + Diags.Report(diag::err_drv_invalid_value) + << "-fdefault-calling-conv=" << A->getValue(); + + llvm::Triple T(TargetOpts.Triple); + llvm::Triple::ArchType Arch = T.getArch(); + bool emitError = (DefaultCC == LangOptions::DCC_FastCall || + DefaultCC == LangOptions::DCC_StdCall) && + Arch != llvm::Triple::x86; + emitError |= (DefaultCC == LangOptions::DCC_VectorCall || + DefaultCC == LangOptions::DCC_RegCall) && + !(Arch == llvm::Triple::x86 || Arch == llvm::Triple::x86_64); + if (emitError) + Diags.Report(diag::err_drv_argument_not_allowed_with) + << A->getSpelling() << T.getTriple(); + else + Opts.setDefaultCallingConv(DefaultCC); + } + + // -mrtd option + if (Arg *A = Args.getLastArg(OPT_mrtd)) { + if (Opts.getDefaultCallingConv() != LangOptions::DCC_None) + Diags.Report(diag::err_drv_argument_not_allowed_with) + << A->getSpelling() << "-fdefault-calling-conv"; + else { + llvm::Triple T(TargetOpts.Triple); + if (T.getArch() != llvm::Triple::x86) + Diags.Report(diag::err_drv_argument_not_allowed_with) + << A->getSpelling() << T.getTriple(); + else + Opts.setDefaultCallingConv(LangOptions::DCC_StdCall); + } + } + + // Check if -fopenmp is specified. + Opts.OpenMP = Args.hasArg(options::OPT_fopenmp) ? 1 : 0; + // Check if -fopenmp-simd is specified. + bool IsSimdSpecified = + Args.hasFlag(options::OPT_fopenmp_simd, options::OPT_fno_openmp_simd, + /*Default=*/false); + Opts.OpenMPSimd = !Opts.OpenMP && IsSimdSpecified; + Opts.OpenMPUseTLS = + Opts.OpenMP && !Args.hasArg(options::OPT_fnoopenmp_use_tls); + Opts.OpenMPIsDevice = + Opts.OpenMP && Args.hasArg(options::OPT_fopenmp_is_device); + bool IsTargetSpecified = + Opts.OpenMPIsDevice || Args.hasArg(options::OPT_fopenmp_targets_EQ); + + if (Opts.OpenMP || Opts.OpenMPSimd) { + if (int Version = getLastArgIntValue( + Args, OPT_fopenmp_version_EQ, + (IsSimdSpecified || IsTargetSpecified) ? 45 : Opts.OpenMP, Diags)) + Opts.OpenMP = Version; + else if (IsSimdSpecified || IsTargetSpecified) + Opts.OpenMP = 45; + // Provide diagnostic when a given target is not expected to be an OpenMP + // device or host. + if (!Opts.OpenMPIsDevice) { + switch (T.getArch()) { + default: + break; + // Add unsupported host targets here: + case llvm::Triple::nvptx: + case llvm::Triple::nvptx64: + Diags.Report(diag::err_drv_omp_host_target_not_supported) + << TargetOpts.Triple; + break; + } + } + } + + // Set the flag to prevent the implementation from emitting device exception + // handling code for those requiring so. + Opts.OpenMPHostCXXExceptions = Opts.Exceptions && Opts.CXXExceptions; + if ((Opts.OpenMPIsDevice && T.isNVPTX()) || Opts.OpenCLCPlusPlus) { + Opts.Exceptions = 0; + Opts.CXXExceptions = 0; + } + if (Opts.OpenMPIsDevice && T.isNVPTX()) { + Opts.OpenMPCUDANumSMs = + getLastArgIntValue(Args, options::OPT_fopenmp_cuda_number_of_sm_EQ, + Opts.OpenMPCUDANumSMs, Diags); + Opts.OpenMPCUDABlocksPerSM = + getLastArgIntValue(Args, options::OPT_fopenmp_cuda_blocks_per_sm_EQ, + Opts.OpenMPCUDABlocksPerSM, Diags); + } + + // Prevent auto-widening the representation of loop counters during an + // OpenMP collapse clause. + Opts.OpenMPOptimisticCollapse = + Args.hasArg(options::OPT_fopenmp_optimistic_collapse) ? 1 : 0; + + // Get the OpenMP target triples if any. + if (Arg *A = Args.getLastArg(options::OPT_fopenmp_targets_EQ)) { + + for (unsigned i = 0; i < A->getNumValues(); ++i) { + llvm::Triple TT(A->getValue(i)); + + if (TT.getArch() == llvm::Triple::UnknownArch || + !(TT.getArch() == llvm::Triple::ppc || + TT.getArch() == llvm::Triple::ppc64 || + TT.getArch() == llvm::Triple::ppc64le || + TT.getArch() == llvm::Triple::nvptx || + TT.getArch() == llvm::Triple::nvptx64 || + TT.getArch() == llvm::Triple::x86 || + TT.getArch() == llvm::Triple::x86_64)) + Diags.Report(diag::err_drv_invalid_omp_target) << A->getValue(i); + else + Opts.OMPTargetTriples.push_back(TT); + } + } + + // Get OpenMP host file path if any and report if a non existent file is + // found + if (Arg *A = Args.getLastArg(options::OPT_fopenmp_host_ir_file_path)) { + Opts.OMPHostIRFile = A->getValue(); + if (!llvm::sys::fs::exists(Opts.OMPHostIRFile)) + Diags.Report(diag::err_drv_omp_host_ir_file_not_found) + << Opts.OMPHostIRFile; + } + + // Set CUDA mode for OpenMP target NVPTX if specified in options + Opts.OpenMPCUDAMode = Opts.OpenMPIsDevice && T.isNVPTX() && + Args.hasArg(options::OPT_fopenmp_cuda_mode); + + // Set CUDA mode for OpenMP target NVPTX if specified in options + Opts.OpenMPCUDAForceFullRuntime = + Opts.OpenMPIsDevice && T.isNVPTX() && + Args.hasArg(options::OPT_fopenmp_cuda_force_full_runtime); + + // 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 = !Opts.Optimize; + if (Arg *InlineArg = Args.getLastArg( + options::OPT_finline_functions, options::OPT_finline_hint_functions, + options::OPT_fno_inline_functions, options::OPT_fno_inline)) + if (InlineArg->getOption().matches(options::OPT_fno_inline)) + Opts.NoInlineDefine = true; + + Opts.FastMath = Args.hasArg(OPT_ffast_math) || + Args.hasArg(OPT_cl_fast_relaxed_math); + Opts.FiniteMathOnly = Args.hasArg(OPT_ffinite_math_only) || + Args.hasArg(OPT_cl_finite_math_only) || + Args.hasArg(OPT_cl_fast_relaxed_math); + Opts.UnsafeFPMath = Args.hasArg(OPT_menable_unsafe_fp_math) || + Args.hasArg(OPT_cl_unsafe_math_optimizations) || + Args.hasArg(OPT_cl_fast_relaxed_math); + + if (Arg *A = Args.getLastArg(OPT_ffp_contract)) { + StringRef Val = A->getValue(); + if (Val == "fast") + Opts.setDefaultFPContractMode(LangOptions::FPC_Fast); + else if (Val == "on") + Opts.setDefaultFPContractMode(LangOptions::FPC_On); + else if (Val == "off") + Opts.setDefaultFPContractMode(LangOptions::FPC_Off); + else + Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Val; + } + + 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::SSPStrong); break; + case 3: Opts.setStackProtector(LangOptions::SSPReq); break; + } + + if (Arg *A = Args.getLastArg(OPT_ftrivial_auto_var_init)) { + StringRef Val = A->getValue(); + if (Val == "uninitialized") + Opts.setTrivialAutoVarInit( + LangOptions::TrivialAutoVarInitKind::Uninitialized); + else if (Val == "zero") + Opts.setTrivialAutoVarInit(LangOptions::TrivialAutoVarInitKind::Zero); + else if (Val == "pattern") + Opts.setTrivialAutoVarInit(LangOptions::TrivialAutoVarInitKind::Pattern); + else + Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Val; + } + + // Parse -fsanitize= arguments. + parseSanitizerKinds("-fsanitize=", Args.getAllArgValues(OPT_fsanitize_EQ), + Diags, Opts.Sanitize); + // -fsanitize-address-field-padding=N has to be a LangOpt, parse it here. + Opts.SanitizeAddressFieldPadding = + getLastArgIntValue(Args, OPT_fsanitize_address_field_padding, 0, Diags); + Opts.SanitizerBlacklistFiles = Args.getAllArgValues(OPT_fsanitize_blacklist); + + // -fxray-instrument + Opts.XRayInstrument = + Args.hasFlag(OPT_fxray_instrument, OPT_fnoxray_instrument, false); + + // -fxray-always-emit-customevents + Opts.XRayAlwaysEmitCustomEvents = + Args.hasFlag(OPT_fxray_always_emit_customevents, + OPT_fnoxray_always_emit_customevents, false); + + // -fxray-always-emit-typedevents + Opts.XRayAlwaysEmitTypedEvents = + Args.hasFlag(OPT_fxray_always_emit_typedevents, + OPT_fnoxray_always_emit_customevents, false); + + // -fxray-{always,never}-instrument= filenames. + Opts.XRayAlwaysInstrumentFiles = + Args.getAllArgValues(OPT_fxray_always_instrument); + Opts.XRayNeverInstrumentFiles = + Args.getAllArgValues(OPT_fxray_never_instrument); + Opts.XRayAttrListFiles = Args.getAllArgValues(OPT_fxray_attr_list); + + // -fforce-emit-vtables + Opts.ForceEmitVTables = Args.hasArg(OPT_fforce_emit_vtables); + + // -fallow-editor-placeholders + Opts.AllowEditorPlaceholders = Args.hasArg(OPT_fallow_editor_placeholders); + + Opts.RegisterStaticDestructors = !Args.hasArg(OPT_fno_cxx_static_destructors); + + if (Arg *A = Args.getLastArg(OPT_fclang_abi_compat_EQ)) { + Opts.setClangABICompat(LangOptions::ClangABI::Latest); + + StringRef Ver = A->getValue(); + std::pair<StringRef, StringRef> VerParts = Ver.split('.'); + unsigned Major, Minor = 0; + + // Check the version number is valid: either 3.x (0 <= x <= 9) or + // y or y.0 (4 <= y <= current version). + if (!VerParts.first.startswith("0") && + !VerParts.first.getAsInteger(10, Major) && + 3 <= Major && Major <= CLANG_VERSION_MAJOR && + (Major == 3 ? VerParts.second.size() == 1 && + !VerParts.second.getAsInteger(10, Minor) + : VerParts.first.size() == Ver.size() || + VerParts.second == "0")) { + // Got a valid version number. + if (Major == 3 && Minor <= 8) + Opts.setClangABICompat(LangOptions::ClangABI::Ver3_8); + else if (Major <= 4) + Opts.setClangABICompat(LangOptions::ClangABI::Ver4); + else if (Major <= 6) + Opts.setClangABICompat(LangOptions::ClangABI::Ver6); + else if (Major <= 7) + Opts.setClangABICompat(LangOptions::ClangABI::Ver7); + } else if (Ver != "latest") { + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << A->getValue(); + } + } + + Opts.CompleteMemberPointers = Args.hasArg(OPT_fcomplete_member_pointers); + Opts.BuildingPCHWithObjectFile = Args.hasArg(OPT_building_pch_with_obj); +} + +static bool isStrictlyPreprocessorAction(frontend::ActionKind Action) { + 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::GenerateModuleInterface: + case frontend::GenerateHeaderModule: + case frontend::GeneratePCH: + case frontend::ParseSyntaxOnly: + case frontend::ModuleFileInfo: + case frontend::VerifyPCH: + case frontend::PluginAction: + case frontend::RewriteObjC: + case frontend::RewriteTest: + case frontend::RunAnalysis: + case frontend::TemplightDump: + case frontend::MigrateSource: + return false; + + case frontend::DumpCompilerOptions: + case frontend::DumpRawTokens: + case frontend::DumpTokens: + case frontend::InitOnly: + case frontend::PrintPreamble: + case frontend::PrintPreprocessedInput: + case frontend::RewriteMacros: + case frontend::RunPreprocessorOnly: + return true; + } + llvm_unreachable("invalid frontend action"); +} + +static void ParsePreprocessorArgs(PreprocessorOptions &Opts, ArgList &Args, + DiagnosticsEngine &Diags, + frontend::ActionKind Action) { + Opts.ImplicitPCHInclude = Args.getLastArgValue(OPT_include_pch); + Opts.PCHWithHdrStop = Args.hasArg(OPT_pch_through_hdrstop_create) || + Args.hasArg(OPT_pch_through_hdrstop_use); + Opts.PCHWithHdrStopCreate = Args.hasArg(OPT_pch_through_hdrstop_create); + Opts.PCHThroughHeader = Args.getLastArgValue(OPT_pch_through_header_EQ); + Opts.UsePredefines = !Args.hasArg(OPT_undef); + Opts.DetailedRecord = Args.hasArg(OPT_detailed_preprocessing_record); + Opts.DisablePCHValidation = Args.hasArg(OPT_fno_validate_pch); + Opts.AllowPCHWithCompilerErrors = Args.hasArg(OPT_fallow_pch_with_errors); + + Opts.DumpDeserializedPCHDecls = Args.hasArg(OPT_dump_deserialized_pch_decls); + for (const auto *A : Args.filtered(OPT_error_on_deserialized_pch_decl)) + 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 the __CET__ macro if a CFProtection option is set. + if (const Arg *A = Args.getLastArg(OPT_fcf_protection_EQ)) { + StringRef Name = A->getValue(); + if (Name == "branch") + Opts.addMacroDef("__CET__=1"); + else if (Name == "return") + Opts.addMacroDef("__CET__=2"); + else if (Name == "full") + Opts.addMacroDef("__CET__=3"); + } + + // Add macros from the command line. + for (const auto *A : Args.filtered(OPT_D, OPT_U)) { + if (A->getOption().matches(OPT_D)) + Opts.addMacroDef(A->getValue()); + else + Opts.addMacroUndef(A->getValue()); + } + + Opts.MacroIncludes = Args.getAllArgValues(OPT_imacros); + + // Add the ordered list of -includes. + for (const auto *A : Args.filtered(OPT_include)) + Opts.Includes.emplace_back(A->getValue()); + + for (const auto *A : Args.filtered(OPT_chain_include)) + Opts.ChainedIncludes.emplace_back(A->getValue()); + + for (const auto *A : Args.filtered(OPT_remap_file)) { + 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; + } + + // Always avoid lexing editor placeholders when we're just running the + // preprocessor as we never want to emit the + // "editor placeholder in source file" error in PP only mode. + if (isStrictlyPreprocessorAction(Action)) + Opts.LexEditorPlaceholders = false; +} + +static void ParsePreprocessorOutputArgs(PreprocessorOutputOptions &Opts, + ArgList &Args, + frontend::ActionKind Action) { + if (isStrictlyPreprocessorAction(Action)) + Opts.ShowCPP = !Args.hasArg(OPT_dM); + else + Opts.ShowCPP = 0; + + 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.ShowIncludeDirectives = Args.hasArg(OPT_dI); + Opts.RewriteIncludes = Args.hasArg(OPT_frewrite_includes); + Opts.RewriteImports = Args.hasArg(OPT_frewrite_imports); + Opts.UseLineDirectives = Args.hasArg(OPT_fuse_line_directives); +} + +static void ParseTargetArgs(TargetOptions &Opts, ArgList &Args, + DiagnosticsEngine &Diags) { + Opts.CodeModel = getCodeModel(Args, Diags); + Opts.ABI = Args.getLastArgValue(OPT_target_abi); + if (Arg *A = Args.getLastArg(OPT_meabi)) { + StringRef Value = A->getValue(); + llvm::EABI EABIVersion = llvm::StringSwitch<llvm::EABI>(Value) + .Case("default", llvm::EABI::Default) + .Case("4", llvm::EABI::EABI4) + .Case("5", llvm::EABI::EABI5) + .Case("gnu", llvm::EABI::GNU) + .Default(llvm::EABI::Unknown); + if (EABIVersion == llvm::EABI::Unknown) + Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) + << Value; + else + Opts.EABIVersion = EABIVersion; + } + 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 = Args.getLastArgValue(OPT_triple); + // Use the default target triple if unspecified. + if (Opts.Triple.empty()) + Opts.Triple = llvm::sys::getDefaultTargetTriple(); + Opts.Triple = llvm::Triple::normalize(Opts.Triple); + Opts.OpenCLExtensionsAsWritten = Args.getAllArgValues(OPT_cl_ext_EQ); + Opts.ForceEnableInt128 = Args.hasArg(OPT_fforce_enable_int128); + Opts.NVPTXUseShortPointers = Args.hasFlag( + options::OPT_fcuda_short_ptr, options::OPT_fno_cuda_short_ptr, false); + if (Arg *A = Args.getLastArg(options::OPT_target_sdk_version_EQ)) { + llvm::VersionTuple Version; + if (Version.tryParse(A->getValue())) + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << A->getValue(); + else + Opts.SDKVersion = Version; + } +} + +bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res, + const char *const *ArgBegin, + const char *const *ArgEnd, + DiagnosticsEngine &Diags) { + bool Success = true; + + // Parse the arguments. + std::unique_ptr<OptTable> Opts = createDriverOptTable(); + const unsigned IncludedFlagsBitmask = options::CC1Option; + unsigned MissingArgIndex, MissingArgCount; + InputArgList Args = + Opts->ParseArgs(llvm::makeArrayRef(ArgBegin, ArgEnd), MissingArgIndex, + MissingArgCount, IncludedFlagsBitmask); + LangOptions &LangOpts = *Res.getLangOpts(); + + // 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 (const auto *A : Args.filtered(OPT_UNKNOWN)) { + auto ArgString = A->getAsString(Args); + std::string Nearest; + if (Opts->findNearest(ArgString, Nearest, IncludedFlagsBitmask) > 1) + Diags.Report(diag::err_drv_unknown_argument) << ArgString; + else + Diags.Report(diag::err_drv_unknown_argument_with_suggestion) + << ArgString << Nearest; + Success = false; + } + + Success &= ParseAnalyzerArgs(*Res.getAnalyzerOpts(), Args, Diags); + Success &= ParseMigratorArgs(Res.getMigratorOpts(), Args); + ParseDependencyOutputArgs(Res.getDependencyOutputOpts(), Args); + Success &= + ParseDiagnosticArgs(Res.getDiagnosticOpts(), Args, &Diags, + false /*DefaultDiagColor*/, false /*DefaultShowOpt*/); + ParseCommentArgs(LangOpts.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, + LangOpts.IsHeaderFile); + ParseTargetArgs(Res.getTargetOpts(), Args, Diags); + Success &= ParseCodeGenArgs(Res.getCodeGenOpts(), Args, DashX, Diags, + Res.getTargetOpts(), Res.getFrontendOpts()); + ParseHeaderSearchArgs(Res.getHeaderSearchOpts(), Args, + Res.getFileSystemOpts().WorkingDir); + llvm::Triple T(Res.getTargetOpts().Triple); + if (DashX.getFormat() == InputKind::Precompiled || + DashX.getLanguage() == InputKind::LLVM_IR) { + // ObjCAAutoRefCount and Sanitize LangOpts are used to setup the + // PassManager in BackendUtil.cpp. They need to be initializd no matter + // what the input type is. + if (Args.hasArg(OPT_fobjc_arc)) + LangOpts.ObjCAutoRefCount = 1; + // PIClevel and PIELevel are needed during code generation and this should be + // set regardless of the input type. + LangOpts.PICLevel = getLastArgIntValue(Args, OPT_pic_level, 0, Diags); + LangOpts.PIE = Args.hasArg(OPT_pic_is_pie); + parseSanitizerKinds("-fsanitize=", Args.getAllArgValues(OPT_fsanitize_EQ), + Diags, LangOpts.Sanitize); + } else { + // Other LangOpts are only initialized when the input is not AST or LLVM IR. + // FIXME: Should we really be calling this for an InputKind::Asm input? + ParseLangArgs(LangOpts, Args, DashX, Res.getTargetOpts(), + Res.getPreprocessorOpts(), Diags); + if (Res.getFrontendOpts().ProgramAction == frontend::RewriteObjC) + LangOpts.ObjCExceptions = 1; + if (T.isOSDarwin() && DashX.isPreprocessed()) { + // Supress the darwin-specific 'stdlibcxx-not-found' diagnostic for + // preprocessed input as we don't expect it to be used with -std=libc++ + // anyway. + Res.getDiagnosticOpts().Warnings.push_back("no-stdlibcxx-not-found"); + } + } + + LangOpts.FunctionAlignment = + getLastArgIntValue(Args, OPT_function_alignment, 0, Diags); + + if (LangOpts.CUDA) { + // During CUDA device-side compilation, the aux triple is the + // triple used for host compilation. + if (LangOpts.CUDAIsDevice) + Res.getTargetOpts().HostTriple = Res.getFrontendOpts().AuxTriple; + } + + // Set the triple of the host for OpenMP device compile. + if (LangOpts.OpenMPIsDevice) + Res.getTargetOpts().HostTriple = Res.getFrontendOpts().AuxTriple; + + // FIXME: Override value name discarding when asan or msan is used because the + // backend passes depend on the name of the alloca in order to print out + // names. + Res.getCodeGenOpts().DiscardValueNames &= + !LangOpts.Sanitize.has(SanitizerKind::Address) && + !LangOpts.Sanitize.has(SanitizerKind::KernelAddress) && + !LangOpts.Sanitize.has(SanitizerKind::Memory) && + !LangOpts.Sanitize.has(SanitizerKind::KernelMemory); + + ParsePreprocessorArgs(Res.getPreprocessorOpts(), Args, Diags, + Res.getFrontendOpts().ProgramAction); + ParsePreprocessorOutputArgs(Res.getPreprocessorOutputOpts(), Args, + Res.getFrontendOpts().ProgramAction); + + // Turn on -Wspir-compat for SPIR target. + auto Arch = T.getArch(); + if (Arch == llvm::Triple::spir || Arch == llvm::Triple::spir64) { + Res.getDiagnosticOpts().Warnings.push_back("spir-compat"); + } + + // If sanitizer is enabled, disable OPT_ffine_grained_bitfield_accesses. + if (Res.getCodeGenOpts().FineGrainedBitfieldAccesses && + !Res.getLangOpts()->Sanitize.empty()) { + Res.getCodeGenOpts().FineGrainedBitfieldAccesses = false; + Diags.Report(diag::warn_drv_fine_grained_bitfield_accesses_ignored); + } + return Success; +} + +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" + + for (StringRef Feature : LangOpts->ModuleFeatures) + code = hash_combine(code, Feature); + + // Extend the signature with the target options. + code = hash_combine(code, TargetOpts->Triple, TargetOpts->CPU, + TargetOpts->ABI); + for (const auto &FeatureAsWritten : TargetOpts->FeaturesAsWritten) + code = hash_combine(code, FeatureAsWritten); + + // Extend the signature with preprocessor options. + const PreprocessorOptions &ppOpts = getPreprocessorOpts(); + const HeaderSearchOptions &hsOpts = getHeaderSearchOpts(); + code = hash_combine(code, ppOpts.UsePredefines, ppOpts.DetailedRecord); + + for (const auto &I : getPreprocessorOpts().Macros) { + // 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( + llvm::CachedHashString(MacroDef.split('=').first))) + continue; + } + + code = hash_combine(code, I.first, I.second); + } + + // Extend the signature with the sysroot and other header search options. + code = hash_combine(code, hsOpts.Sysroot, + hsOpts.ModuleFormat, + hsOpts.UseDebugInfo, + hsOpts.UseBuiltinIncludes, + hsOpts.UseStandardSystemIncludes, + hsOpts.UseStandardCXXIncludes, + hsOpts.UseLibcxx, + hsOpts.ModulesValidateDiagnosticOptions); + code = hash_combine(code, hsOpts.ResourceDir); + + // Extend the signature with the user build path. + code = hash_combine(code, hsOpts.ModuleUserBuildPath); + + // Extend the signature with the module file extensions. + const FrontendOptions &frontendOpts = getFrontendOpts(); + for (const auto &ext : frontendOpts.ModuleFileExtensions) { + code = ext->hashExtension(code); + } + + // When compiling with -gmodules, also hash -fdebug-prefix-map as it + // affects the debug info in the PCM. + if (getCodeGenOpts().DebugTypeExtRefs) + for (const auto &KeyValue : getCodeGenOpts().DebugPrefixMap) + code = hash_combine(code, KeyValue.first, KeyValue.second); + + // Extend the signature with the enabled sanitizers, if at least one is + // enabled. Sanitizers which cannot affect AST generation aren't hashed. + SanitizerSet SanHash = LangOpts->Sanitize; + SanHash.clear(getPPTransparentSanitizers()); + if (!SanHash.empty()) + code = hash_combine(code, SanHash.Mask); + + return llvm::APInt(64, code).toString(36, /*Signed=*/false); +} + +template<typename IntTy> +static IntTy getLastArgIntValueImpl(const ArgList &Args, OptSpecifier Id, + IntTy Default, + DiagnosticsEngine *Diags) { + IntTy 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; +} + +namespace clang { + +// Declared in clang/Frontend/Utils.h. +int getLastArgIntValue(const ArgList &Args, OptSpecifier Id, int Default, + DiagnosticsEngine *Diags) { + return getLastArgIntValueImpl<int>(Args, Id, Default, Diags); +} + +uint64_t getLastArgUInt64Value(const ArgList &Args, OptSpecifier Id, + uint64_t Default, + DiagnosticsEngine *Diags) { + return getLastArgIntValueImpl<uint64_t>(Args, Id, Default, Diags); +} + +IntrusiveRefCntPtr<llvm::vfs::FileSystem> +createVFSFromCompilerInvocation(const CompilerInvocation &CI, + DiagnosticsEngine &Diags) { + return createVFSFromCompilerInvocation(CI, Diags, + llvm::vfs::getRealFileSystem()); +} + +IntrusiveRefCntPtr<llvm::vfs::FileSystem> createVFSFromCompilerInvocation( + const CompilerInvocation &CI, DiagnosticsEngine &Diags, + IntrusiveRefCntPtr<llvm::vfs::FileSystem> BaseFS) { + if (CI.getHeaderSearchOpts().VFSOverlayFiles.empty()) + return BaseFS; + + IntrusiveRefCntPtr<llvm::vfs::FileSystem> Result = BaseFS; + // earlier vfs files are on the bottom + for (const auto &File : CI.getHeaderSearchOpts().VFSOverlayFiles) { + llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buffer = + Result->getBufferForFile(File); + if (!Buffer) { + Diags.Report(diag::err_missing_vfs_overlay_file) << File; + continue; + } + + IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS = llvm::vfs::getVFSFromYAML( + std::move(Buffer.get()), /*DiagHandler*/ nullptr, File, + /*DiagContext*/ nullptr, Result); + if (!FS) { + Diags.Report(diag::err_invalid_vfs_overlay) << File; + continue; + } + + Result = FS; + } + return Result; +} + +} // namespace clang 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 000000000000..2d4c40f8b9f1 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/CreateInvocationFromCommandLine.cpp @@ -0,0 +1,106 @@ +//===--- 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/Action.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. +std::unique_ptr<CompilerInvocation> clang::createInvocationFromCommandLine( + ArrayRef<const char *> ArgList, IntrusiveRefCntPtr<DiagnosticsEngine> Diags, + IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) { + if (!Diags.get()) { + // 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(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(Args[0], llvm::sys::getDefaultTargetTriple(), + *Diags, VFS); + + // Don't check that inputs exist, they may have been remapped. + TheDriver.setCheckInputsExist(false); + + std::unique_ptr<driver::Compilation> C(TheDriver.BuildCompilation(Args)); + if (!C) + return nullptr; + + // 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 nullptr; + } + + // We expect to get back exactly one command job, if we didn't something + // failed. Offload compilation is an exception as it creates multiple jobs. If + // that's the case, we proceed with the first job. If caller needs a + // particular job, it should be controlled via options (e.g. + // --cuda-{host|device}-only for CUDA) passed to the driver. + const driver::JobList &Jobs = C->getJobs(); + bool OffloadCompilation = false; + if (Jobs.size() > 1) { + for (auto &A : C->getActions()){ + // On MacOSX real actions may end up being wrapped in BindArchAction + if (isa<driver::BindArchAction>(A)) + A = *A->input_begin(); + if (isa<driver::OffloadAction>(A)) { + OffloadCompilation = true; + break; + } + } + } + if (Jobs.size() == 0 || !isa<driver::Command>(*Jobs.begin()) || + (Jobs.size() > 1 && !OffloadCompilation)) { + SmallString<256> Msg; + llvm::raw_svector_ostream OS(Msg); + Jobs.Print(OS, "; ", true); + Diags->Report(diag::err_fe_expected_compiler_job) << OS.str(); + return nullptr; + } + + const driver::Command &Cmd = cast<driver::Command>(*Jobs.begin()); + if (StringRef(Cmd.getCreator().getName()) != "clang") { + Diags->Report(diag::err_fe_expected_clang_command); + return nullptr; + } + + const ArgStringList &CCArgs = Cmd.getArguments(); + auto CI = llvm::make_unique<CompilerInvocation>(); + if (!CompilerInvocation::CreateFromArgs(*CI, + const_cast<const char **>(CCArgs.data()), + const_cast<const char **>(CCArgs.data()) + + CCArgs.size(), + *Diags)) + return nullptr; + return CI; +} 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 000000000000..a03d4b79c8b9 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/DependencyFile.cpp @@ -0,0 +1,516 @@ +//===--- 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/ModuleMap.h" +#include "clang/Lex/PPCallbacks.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Serialization/ASTReader.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; + +namespace { +struct DepCollectorPPCallbacks : public PPCallbacks { + DependencyCollector &DepCollector; + SourceManager &SM; + DepCollectorPPCallbacks(DependencyCollector &L, SourceManager &SM) + : DepCollector(L), SM(SM) { } + + void FileChanged(SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, + FileID PrevFID) override { + 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! + const FileEntry *FE = + SM.getFileEntryForID(SM.getFileID(SM.getExpansionLoc(Loc))); + if (!FE) + return; + + StringRef Filename = + llvm::sys::path::remove_leading_dotslash(FE->getName()); + + DepCollector.maybeAddDependency(Filename, /*FromModule*/false, + isSystem(FileType), + /*IsModuleFile*/false, /*IsMissing*/false); + } + + void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, + StringRef FileName, bool IsAngled, + CharSourceRange FilenameRange, const FileEntry *File, + StringRef SearchPath, StringRef RelativePath, + const Module *Imported, + SrcMgr::CharacteristicKind FileType) override { + if (!File) + DepCollector.maybeAddDependency(FileName, /*FromModule*/false, + /*IsSystem*/false, /*IsModuleFile*/false, + /*IsMissing*/true); + // Files that actually exist are handled by FileChanged. + } + + void EndOfMainFile() override { + DepCollector.finishedMainFile(); + } +}; + +struct DepCollectorMMCallbacks : public ModuleMapCallbacks { + DependencyCollector &DepCollector; + DepCollectorMMCallbacks(DependencyCollector &DC) : DepCollector(DC) {} + + void moduleMapFileRead(SourceLocation Loc, const FileEntry &Entry, + bool IsSystem) override { + StringRef Filename = Entry.getName(); + DepCollector.maybeAddDependency(Filename, /*FromModule*/false, + /*IsSystem*/IsSystem, + /*IsModuleFile*/false, + /*IsMissing*/false); + } +}; + +struct DepCollectorASTListener : public ASTReaderListener { + DependencyCollector &DepCollector; + DepCollectorASTListener(DependencyCollector &L) : DepCollector(L) { } + bool needsInputFileVisitation() override { return true; } + bool needsSystemInputFileVisitation() override { + return DepCollector.needSystemDependencies(); + } + void visitModuleFile(StringRef Filename, + serialization::ModuleKind Kind) override { + DepCollector.maybeAddDependency(Filename, /*FromModule*/true, + /*IsSystem*/false, /*IsModuleFile*/true, + /*IsMissing*/false); + } + bool visitInputFile(StringRef Filename, bool IsSystem, + bool IsOverridden, bool IsExplicitModule) override { + if (IsOverridden || IsExplicitModule) + return true; + + DepCollector.maybeAddDependency(Filename, /*FromModule*/true, IsSystem, + /*IsModuleFile*/false, /*IsMissing*/false); + return true; + } +}; +} // end anonymous namespace + +void DependencyCollector::maybeAddDependency(StringRef Filename, bool FromModule, + bool IsSystem, bool IsModuleFile, + bool IsMissing) { + if (Seen.insert(Filename).second && + sawDependency(Filename, FromModule, IsSystem, IsModuleFile, IsMissing)) + Dependencies.push_back(Filename); +} + +static bool isSpecialFilename(StringRef Filename) { + return llvm::StringSwitch<bool>(Filename) + .Case("<built-in>", true) + .Case("<stdin>", true) + .Default(false); +} + +bool DependencyCollector::sawDependency(StringRef Filename, bool FromModule, + bool IsSystem, bool IsModuleFile, + bool IsMissing) { + return !isSpecialFilename(Filename) && + (needSystemDependencies() || !IsSystem); +} + +DependencyCollector::~DependencyCollector() { } +void DependencyCollector::attachToPreprocessor(Preprocessor &PP) { + PP.addPPCallbacks( + llvm::make_unique<DepCollectorPPCallbacks>(*this, PP.getSourceManager())); + PP.getHeaderSearchInfo().getModuleMap().addModuleMapCallbacks( + llvm::make_unique<DepCollectorMMCallbacks>(*this)); +} +void DependencyCollector::attachToASTReader(ASTReader &R) { + R.addListener(llvm::make_unique<DepCollectorASTListener>(*this)); +} + +namespace { +/// Private implementation for DependencyFileGenerator +class DFGImpl : 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; + bool IncludeModuleFiles; + DependencyOutputFormat OutputFormat; + unsigned InputFileIndex; + +private: + bool FileMatchesDepCriteria(const char *Filename, + SrcMgr::CharacteristicKind FileType); + void OutputDependencyFile(); + +public: + DFGImpl(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), + IncludeModuleFiles(Opts.IncludeModuleFiles), + OutputFormat(Opts.OutputFormat), + InputFileIndex(0) { + for (const auto &ExtraDep : Opts.ExtraDeps) { + if (AddFilename(ExtraDep)) + ++InputFileIndex; + } + } + + void FileChanged(SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, + FileID PrevFID) override; + + void FileSkipped(const FileEntry &SkippedFile, const Token &FilenameTok, + SrcMgr::CharacteristicKind FileType) override; + + void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, + StringRef FileName, bool IsAngled, + CharSourceRange FilenameRange, const FileEntry *File, + StringRef SearchPath, StringRef RelativePath, + const Module *Imported, + SrcMgr::CharacteristicKind FileType) override; + + void HasInclude(SourceLocation Loc, StringRef SpelledFilename, bool IsAngled, + const FileEntry *File, + SrcMgr::CharacteristicKind FileType) override; + + void EndOfMainFile() override { + OutputDependencyFile(); + } + + bool AddFilename(StringRef Filename); + bool includeSystemHeaders() const { return IncludeSystemHeaders; } + bool includeModuleFiles() const { return IncludeModuleFiles; } +}; + +class DFGMMCallback : public ModuleMapCallbacks { + DFGImpl &Parent; +public: + DFGMMCallback(DFGImpl &Parent) : Parent(Parent) {} + void moduleMapFileRead(SourceLocation Loc, const FileEntry &Entry, + bool IsSystem) override { + if (!IsSystem || Parent.includeSystemHeaders()) + Parent.AddFilename(Entry.getName()); + } +}; + +class DFGASTReaderListener : public ASTReaderListener { + DFGImpl &Parent; +public: + DFGASTReaderListener(DFGImpl &Parent) + : Parent(Parent) { } + bool needsInputFileVisitation() override { return true; } + bool needsSystemInputFileVisitation() override { + return Parent.includeSystemHeaders(); + } + void visitModuleFile(StringRef Filename, + serialization::ModuleKind Kind) override; + bool visitInputFile(StringRef Filename, bool isSystem, + bool isOverridden, bool isExplicitModule) override; +}; +} + +DependencyFileGenerator::DependencyFileGenerator(void *Impl) +: Impl(Impl) { } + +DependencyFileGenerator *DependencyFileGenerator::CreateAndAttachToPreprocessor( + clang::Preprocessor &PP, const clang::DependencyOutputOptions &Opts) { + + if (Opts.Targets.empty()) { + PP.getDiagnostics().Report(diag::err_fe_dependency_file_requires_MT); + return nullptr; + } + + // Disable the "file not found" diagnostic if the -MG option was given. + if (Opts.AddMissingHeaderDeps) + PP.SetSuppressIncludeNotFoundError(true); + + DFGImpl *Callback = new DFGImpl(&PP, Opts); + PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Callback)); + PP.getHeaderSearchInfo().getModuleMap().addModuleMapCallbacks( + llvm::make_unique<DFGMMCallback>(*Callback)); + return new DependencyFileGenerator(Callback); +} + +void DependencyFileGenerator::AttachToASTReader(ASTReader &R) { + DFGImpl *I = reinterpret_cast<DFGImpl *>(Impl); + assert(I && "missing implementation"); + R.addListener(llvm::make_unique<DFGASTReaderListener>(*I)); +} + +/// FileMatchesDepCriteria - Determine whether the given Filename should be +/// considered as a dependency. +bool DFGImpl::FileMatchesDepCriteria(const char *Filename, + SrcMgr::CharacteristicKind FileType) { + if (isSpecialFilename(Filename)) + return false; + + if (IncludeSystemHeaders) + return true; + + return !isSystem(FileType); +} + +void DFGImpl::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) return; + + StringRef Filename = FE->getName(); + if (!FileMatchesDepCriteria(Filename.data(), FileType)) + return; + + AddFilename(llvm::sys::path::remove_leading_dotslash(Filename)); +} + +void DFGImpl::FileSkipped(const FileEntry &SkippedFile, + const Token &FilenameTok, + SrcMgr::CharacteristicKind FileType) { + StringRef Filename = SkippedFile.getName(); + if (!FileMatchesDepCriteria(Filename.data(), FileType)) + return; + + AddFilename(llvm::sys::path::remove_leading_dotslash(Filename)); +} + +void DFGImpl::InclusionDirective(SourceLocation HashLoc, + const Token &IncludeTok, + StringRef FileName, + bool IsAngled, + CharSourceRange FilenameRange, + const FileEntry *File, + StringRef SearchPath, + StringRef RelativePath, + const Module *Imported, + SrcMgr::CharacteristicKind FileType) { + if (!File) { + if (AddMissingHeaderDeps) + AddFilename(FileName); + else + SeenMissingHeader = true; + } +} + +void DFGImpl::HasInclude(SourceLocation Loc, StringRef SpelledFilename, + bool IsAngled, const FileEntry *File, + SrcMgr::CharacteristicKind FileType) { + if (!File) + return; + StringRef Filename = File->getName(); + if (!FileMatchesDepCriteria(Filename.data(), FileType)) + return; + AddFilename(llvm::sys::path::remove_leading_dotslash(Filename)); +} + +bool DFGImpl::AddFilename(StringRef Filename) { + if (FilesSet.insert(Filename).second) { + Files.push_back(Filename); + return true; + } + return false; +} + +/// Print the filename, with escaping or quoting that accommodates the three +/// most likely tools that use dependency files: GNU Make, BSD Make, and +/// NMake/Jom. +/// +/// BSD Make is the simplest case: It does no escaping at all. This means +/// characters that are normally delimiters, i.e. space and # (the comment +/// character) simply aren't supported in filenames. +/// +/// GNU Make does allow space and # in filenames, but to avoid being treated +/// as a delimiter or comment, these must be escaped with a backslash. Because +/// backslash is itself the escape character, if a backslash appears in a +/// filename, it should be escaped as well. (As a special case, $ is escaped +/// as $$, which is the normal Make way to handle the $ character.) +/// For compatibility with BSD Make and historical practice, if GNU Make +/// un-escapes characters in a filename but doesn't find a match, it will +/// retry with the unmodified original string. +/// +/// GCC tries to accommodate both Make formats by escaping any space or # +/// characters in the original filename, but not escaping backslashes. The +/// apparent intent is so that filenames with backslashes will be handled +/// correctly by BSD Make, and by GNU Make in its fallback mode of using the +/// unmodified original string; filenames with # or space characters aren't +/// supported by BSD Make at all, but will be handled correctly by GNU Make +/// due to the escaping. +/// +/// A corner case that GCC gets only partly right is when the original filename +/// has a backslash immediately followed by space or #. GNU Make would expect +/// this backslash to be escaped; however GCC escapes the original backslash +/// only when followed by space, not #. It will therefore take a dependency +/// from a directive such as +/// #include "a\ b\#c.h" +/// and emit it as +/// a\\\ b\\#c.h +/// which GNU Make will interpret as +/// a\ b\ +/// followed by a comment. Failing to find this file, it will fall back to the +/// original string, which probably doesn't exist either; in any case it won't +/// find +/// a\ b\#c.h +/// which is the actual filename specified by the include directive. +/// +/// Clang does what GCC does, rather than what GNU Make expects. +/// +/// NMake/Jom has a different set of scary characters, but wraps filespecs in +/// double-quotes to avoid misinterpreting them; see +/// https://msdn.microsoft.com/en-us/library/dd9y37ha.aspx for NMake info, +/// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx +/// for Windows file-naming info. +static void PrintFilename(raw_ostream &OS, StringRef Filename, + DependencyOutputFormat OutputFormat) { + // Convert filename to platform native path + llvm::SmallString<256> NativePath; + llvm::sys::path::native(Filename.str(), NativePath); + + if (OutputFormat == DependencyOutputFormat::NMake) { + // Add quotes if needed. These are the characters listed as "special" to + // NMake, that are legal in a Windows filespec, and that could cause + // misinterpretation of the dependency string. + if (NativePath.find_first_of(" #${}^!") != StringRef::npos) + OS << '\"' << NativePath << '\"'; + else + OS << NativePath; + return; + } + assert(OutputFormat == DependencyOutputFormat::Make); + for (unsigned i = 0, e = NativePath.size(); i != e; ++i) { + if (NativePath[i] == '#') // Handle '#' the broken gcc way. + OS << '\\'; + else if (NativePath[i] == ' ') { // Handle space correctly. + OS << '\\'; + unsigned j = i; + while (j > 0 && NativePath[--j] == '\\') + OS << '\\'; + } else if (NativePath[i] == '$') // $ is escaped by $$. + OS << '$'; + OS << NativePath[i]; + } +} + +void DFGImpl::OutputDependencyFile() { + if (SeenMissingHeader) { + llvm::sys::fs::remove(OutputFile); + return; + } + + std::error_code EC; + llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::F_Text); + if (EC) { + PP->getDiagnostics().Report(diag::err_fe_error_opening) << OutputFile + << EC.message(); + 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 (StringRef Target : Targets) { + unsigned N = Target.size(); + 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 << Target; + } + + OS << ':'; + Columns += 1; + + // Now add each dependency in the order it was seen, but avoiding + // duplicates. + for (StringRef File : Files) { + // 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 = File.size(); + if (Columns + (N + 1) + 2 > MaxColumns) { + OS << " \\\n "; + Columns = 2; + } + OS << ' '; + PrintFilename(OS, File, OutputFormat); + Columns += N + 1; + } + OS << '\n'; + + // Create phony targets if requested. + if (PhonyTarget && !Files.empty()) { + unsigned Index = 0; + for (auto I = Files.begin(), E = Files.end(); I != E; ++I) { + if (Index++ == InputFileIndex) + continue; + OS << '\n'; + PrintFilename(OS, *I, OutputFormat); + OS << ":\n"; + } + } +} + +bool DFGASTReaderListener::visitInputFile(llvm::StringRef Filename, + bool IsSystem, bool IsOverridden, + bool IsExplicitModule) { + assert(!IsSystem || needsSystemInputFileVisitation()); + if (IsOverridden || IsExplicitModule) + return true; + + Parent.AddFilename(Filename); + return true; +} + +void DFGASTReaderListener::visitModuleFile(llvm::StringRef Filename, + serialization::ModuleKind Kind) { + if (Parent.includeModuleFiles()) + Parent.AddFilename(Filename); +} 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 000000000000..c6c9ac2ea2fa --- /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()) { } + + void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, + StringRef FileName, bool IsAngled, + CharSourceRange FilenameRange, const FileEntry *File, + StringRef SearchPath, StringRef RelativePath, + const Module *Imported, + SrcMgr::CharacteristicKind FileType) override; + + void EndOfMainFile() override { + OutputGraphFile(); + } + +}; +} + +void clang::AttachDependencyGraphGen(Preprocessor &PP, StringRef OutputFile, + StringRef SysRoot) { + PP.addPPCallbacks(llvm::make_unique<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, + SrcMgr::CharacteristicKind FileType) { + if (!File) + return; + + SourceManager &SM = PP->getSourceManager(); + const FileEntry *FromFile + = SM.getFileEntryForID(SM.getFileID(SM.getExpansionLoc(HashLoc))); + if (!FromFile) + 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::error_code EC; + llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::F_Text); + if (EC) { + PP->getDiagnostics().Report(diag::err_fe_error_opening) << OutputFile + << EC.message(); + 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 000000000000..3bd86dc5beaa --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/DiagnosticRenderer.cpp @@ -0,0 +1,629 @@ +//===- 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/Diagnostic.h" +#include "clang/Basic/DiagnosticOptions.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.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/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <cassert> +#include <iterator> +#include <utility> + +using namespace clang; + +DiagnosticRenderer::DiagnosticRenderer(const LangOptions &LangOpts, + DiagnosticOptions *DiagOpts) + : LangOpts(LangOpts), DiagOpts(DiagOpts), LastLevel() {} + +DiagnosticRenderer::~DiagnosticRenderer() = default; + +namespace { + +class FixitReceiver : public edit::EditsReceiver { + SmallVectorImpl<FixItHint> &MergedFixits; + +public: + FixitReceiver(SmallVectorImpl<FixItHint> &MergedFixits) + : MergedFixits(MergedFixits) {} + + void insert(SourceLocation loc, StringRef text) override { + MergedFixits.push_back(FixItHint::CreateInsertion(loc, text)); + } + + void replace(CharSourceRange range, StringRef text) override { + MergedFixits.push_back(FixItHint::CreateReplacement(range, text)); + } +}; + +} // namespace + +static void mergeFixits(ArrayRef<FixItHint> FixItHints, + const SourceManager &SM, const LangOptions &LangOpts, + SmallVectorImpl<FixItHint> &MergedFixits) { + edit::Commit commit(SM, LangOpts); + for (const auto &Hint : FixItHints) + 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(FullSourceLoc Loc, + DiagnosticsEngine::Level Level, + StringRef Message, + ArrayRef<CharSourceRange> Ranges, + ArrayRef<FixItHint> FixItHints, + DiagOrStoredDiag D) { + assert(Loc.hasManager() || 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, 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, Loc.getManager(), LangOpts, MergedFixits); + FixItHints = MergedFixits; + } + + for (const auto &Hint : FixItHints) + if (Hint.RemoveRange.isValid()) + MutableRanges.push_back(Hint.RemoveRange); + + FullSourceLoc UnexpandedLoc = Loc; + + // Find the ultimate expansion location for the diagnostic. + Loc = Loc.getFileLoc(); + + PresumedLoc PLoc = Loc.getPresumedLoc(DiagOpts->ShowPresumedLoc); + + // First, if this diagnostic is not in the main file, print out the + // "included from" lines. + emitIncludeStack(Loc, PLoc, Level); + + // Next, emit the actual diagnostic message and caret. + emitDiagnosticMessage(Loc, PLoc, Level, Message, Ranges, D); + emitCaret(Loc, Level, MutableRanges, FixItHints); + + // If this location is within a macro, walk from UnexpandedLoc up to Loc + // and produce a macro backtrace. + if (UnexpandedLoc.isValid() && UnexpandedLoc.isMacroID()) { + emitMacroExpansions(UnexpandedLoc, Level, MutableRanges, FixItHints); + } + } + + LastLoc = Loc; + LastLevel = Level; + + endDiagnostic(D, Level); +} + +void DiagnosticRenderer::emitStoredDiagnostic(StoredDiagnostic &Diag) { + emitDiagnostic(Diag.getLocation(), Diag.getLevel(), Diag.getMessage(), + Diag.getRanges(), Diag.getFixIts(), + &Diag); +} + +void DiagnosticRenderer::emitBasicNote(StringRef Message) { + emitDiagnosticMessage(FullSourceLoc(), PresumedLoc(), DiagnosticsEngine::Note, + Message, None, DiagOrStoredDiag()); +} + +/// 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(FullSourceLoc Loc, PresumedLoc PLoc, + DiagnosticsEngine::Level Level) { + FullSourceLoc IncludeLoc = + PLoc.isInvalid() ? FullSourceLoc() + : FullSourceLoc(PLoc.getIncludeLoc(), Loc.getManager()); + + // Skip redundant include stacks altogether. + if (LastIncludeLoc == IncludeLoc) + return; + + LastIncludeLoc = IncludeLoc; + + if (!DiagOpts->ShowNoteIncludeStack && Level == DiagnosticsEngine::Note) + return; + + if (IncludeLoc.isValid()) + emitIncludeStackRecursively(IncludeLoc); + else { + emitModuleBuildStack(Loc.getManager()); + emitImportStack(Loc); + } +} + +/// Helper to recursively walk up the include stack and print each layer +/// on the way back down. +void DiagnosticRenderer::emitIncludeStackRecursively(FullSourceLoc Loc) { + if (Loc.isInvalid()) { + emitModuleBuildStack(Loc.getManager()); + return; + } + + PresumedLoc PLoc = Loc.getPresumedLoc(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<FullSourceLoc, StringRef> Imported = Loc.getModuleImportLoc(); + if (!Imported.second.empty()) { + // This location was imported by a module. Emit the module import stack. + emitImportStackRecursively(Imported.first, Imported.second); + return; + } + + // Emit the other include frames first. + emitIncludeStackRecursively( + FullSourceLoc(PLoc.getIncludeLoc(), Loc.getManager())); + + // Emit the inclusion text/note. + emitIncludeLocation(Loc, PLoc); +} + +/// Emit the module import stack associated with the current location. +void DiagnosticRenderer::emitImportStack(FullSourceLoc Loc) { + if (Loc.isInvalid()) { + emitModuleBuildStack(Loc.getManager()); + return; + } + + std::pair<FullSourceLoc, StringRef> NextImportLoc = Loc.getModuleImportLoc(); + emitImportStackRecursively(NextImportLoc.first, NextImportLoc.second); +} + +/// Helper to recursively walk up the import stack and print each layer +/// on the way back down. +void DiagnosticRenderer::emitImportStackRecursively(FullSourceLoc Loc, + StringRef ModuleName) { + if (ModuleName.empty()) { + return; + } + + PresumedLoc PLoc = Loc.getPresumedLoc(DiagOpts->ShowPresumedLoc); + + // Emit the other import frames first. + std::pair<FullSourceLoc, StringRef> NextImportLoc = Loc.getModuleImportLoc(); + emitImportStackRecursively(NextImportLoc.first, NextImportLoc.second); + + // Emit the inclusion text/note. + emitImportLocation(Loc, PLoc, ModuleName); +} + +/// 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 (const auto &I : Stack) { + emitBuildingModuleLocation(I.second, I.second.getPresumedLoc( + DiagOpts->ShowPresumedLoc), + I.first); + } +} + +/// A recursive function to trace all possible backtrace locations +/// to match the \p CaretLocFileID. +static SourceLocation +retrieveMacroLocation(SourceLocation Loc, FileID MacroFileID, + FileID CaretFileID, + const SmallVectorImpl<FileID> &CommonArgExpansions, + bool IsBegin, const SourceManager *SM, + bool &IsTokenRange) { + assert(SM->getFileID(Loc) == MacroFileID); + if (MacroFileID == CaretFileID) + return Loc; + if (!Loc.isMacroID()) + return {}; + + CharSourceRange MacroRange, MacroArgRange; + + if (SM->isMacroArgExpansion(Loc)) { + // Only look at the immediate spelling location of this macro argument if + // the other location in the source range is also present in that expansion. + if (std::binary_search(CommonArgExpansions.begin(), + CommonArgExpansions.end(), MacroFileID)) + MacroRange = + CharSourceRange(SM->getImmediateSpellingLoc(Loc), IsTokenRange); + MacroArgRange = SM->getImmediateExpansionRange(Loc); + } else { + MacroRange = SM->getImmediateExpansionRange(Loc); + MacroArgRange = + CharSourceRange(SM->getImmediateSpellingLoc(Loc), IsTokenRange); + } + + SourceLocation MacroLocation = + IsBegin ? MacroRange.getBegin() : MacroRange.getEnd(); + if (MacroLocation.isValid()) { + MacroFileID = SM->getFileID(MacroLocation); + bool TokenRange = IsBegin ? IsTokenRange : MacroRange.isTokenRange(); + MacroLocation = + retrieveMacroLocation(MacroLocation, MacroFileID, CaretFileID, + CommonArgExpansions, IsBegin, SM, TokenRange); + if (MacroLocation.isValid()) { + IsTokenRange = TokenRange; + return MacroLocation; + } + } + + // If we moved the end of the range to an expansion location, we now have + // a range of the same kind as the expansion range. + if (!IsBegin) + IsTokenRange = MacroArgRange.isTokenRange(); + + SourceLocation MacroArgLocation = + IsBegin ? MacroArgRange.getBegin() : MacroArgRange.getEnd(); + MacroFileID = SM->getFileID(MacroArgLocation); + return retrieveMacroLocation(MacroArgLocation, MacroFileID, CaretFileID, + CommonArgExpansions, IsBegin, SM, IsTokenRange); +} + +/// Walk up the chain of macro expansions and collect the FileIDs identifying the +/// expansions. +static void getMacroArgExpansionFileIDs(SourceLocation Loc, + SmallVectorImpl<FileID> &IDs, + bool IsBegin, const SourceManager *SM) { + while (Loc.isMacroID()) { + if (SM->isMacroArgExpansion(Loc)) { + IDs.push_back(SM->getFileID(Loc)); + Loc = SM->getImmediateSpellingLoc(Loc); + } else { + auto ExpRange = SM->getImmediateExpansionRange(Loc); + Loc = IsBegin ? ExpRange.getBegin() : ExpRange.getEnd(); + } + } +} + +/// Collect the expansions of the begin and end locations and compute the set +/// intersection. Produces a sorted vector of FileIDs in CommonArgExpansions. +static void computeCommonMacroArgExpansionFileIDs( + SourceLocation Begin, SourceLocation End, const SourceManager *SM, + SmallVectorImpl<FileID> &CommonArgExpansions) { + SmallVector<FileID, 4> BeginArgExpansions; + SmallVector<FileID, 4> EndArgExpansions; + getMacroArgExpansionFileIDs(Begin, BeginArgExpansions, /*IsBegin=*/true, SM); + getMacroArgExpansionFileIDs(End, EndArgExpansions, /*IsBegin=*/false, SM); + llvm::sort(BeginArgExpansions); + llvm::sort(EndArgExpansions); + std::set_intersection(BeginArgExpansions.begin(), BeginArgExpansions.end(), + EndArgExpansions.begin(), EndArgExpansions.end(), + std::back_inserter(CommonArgExpansions)); +} + +// 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(FullSourceLoc CaretLoc, ArrayRef<CharSourceRange> Ranges, + SmallVectorImpl<CharSourceRange> &SpellingRanges) { + FileID CaretLocFileID = CaretLoc.getFileID(); + + const SourceManager *SM = &CaretLoc.getManager(); + + for (const auto &Range : Ranges) { + if (Range.isInvalid()) + continue; + + SourceLocation Begin = Range.getBegin(), End = Range.getEnd(); + bool IsTokenRange = Range.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).getBegin(); + BeginFileID = SM->getFileID(Begin); + } + + // Then, crawl the expansion chain for the end of the range. + if (BeginFileID != EndFileID) { + while (End.isMacroID() && !BeginLocsMap.count(EndFileID)) { + auto Exp = SM->getImmediateExpansionRange(End); + IsTokenRange = Exp.isTokenRange(); + End = Exp.getEnd(); + EndFileID = SM->getFileID(End); + } + if (End.isMacroID()) { + Begin = BeginLocsMap[EndFileID]; + BeginFileID = EndFileID; + } + } + + // Do the backtracking. + SmallVector<FileID, 4> CommonArgExpansions; + computeCommonMacroArgExpansionFileIDs(Begin, End, SM, CommonArgExpansions); + Begin = retrieveMacroLocation(Begin, BeginFileID, CaretLocFileID, + CommonArgExpansions, /*IsBegin=*/true, SM, + IsTokenRange); + End = retrieveMacroLocation(End, BeginFileID, CaretLocFileID, + CommonArgExpansions, /*IsBegin=*/false, SM, + IsTokenRange); + if (Begin.isInvalid() || End.isInvalid()) continue; + + // 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(FullSourceLoc Loc, + DiagnosticsEngine::Level Level, + ArrayRef<CharSourceRange> Ranges, + ArrayRef<FixItHint> Hints) { + SmallVector<CharSourceRange, 4> SpellingRanges; + mapDiagnosticRanges(Loc, Ranges, SpellingRanges); + emitCodeContext(Loc, Level, SpellingRanges, Hints); +} + +/// A helper function for emitMacroExpansion to print the +/// macro expansion message +void DiagnosticRenderer::emitSingleMacroExpansion( + FullSourceLoc Loc, DiagnosticsEngine::Level Level, + ArrayRef<CharSourceRange> Ranges) { + // Find the spelling location for the macro definition. We must use the + // spelling location here to avoid emitting a macro backtrace for the note. + FullSourceLoc SpellingLoc = Loc.getSpellingLoc(); + + // Map the ranges into the FileID of the diagnostic location. + SmallVector<CharSourceRange, 4> SpellingRanges; + mapDiagnosticRanges(Loc, Ranges, SpellingRanges); + + SmallString<100> MessageStorage; + llvm::raw_svector_ostream Message(MessageStorage); + StringRef MacroName = Lexer::getImmediateMacroNameForDiagnostics( + Loc, Loc.getManager(), LangOpts); + if (MacroName.empty()) + Message << "expanded from here"; + else + Message << "expanded from macro '" << MacroName << "'"; + + emitDiagnostic(SpellingLoc, DiagnosticsEngine::Note, Message.str(), + SpellingRanges, None); +} + +/// Check that the macro argument location of Loc starts with ArgumentLoc. +/// The starting location of the macro expansions is used to differeniate +/// different macro expansions. +static bool checkLocForMacroArgExpansion(SourceLocation Loc, + const SourceManager &SM, + SourceLocation ArgumentLoc) { + SourceLocation MacroLoc; + if (SM.isMacroArgExpansion(Loc, &MacroLoc)) { + if (ArgumentLoc == MacroLoc) return true; + } + + return false; +} + +/// Check if all the locations in the range have the same macro argument +/// expansion, and that the expansion starts with ArgumentLoc. +static bool checkRangeForMacroArgExpansion(CharSourceRange Range, + const SourceManager &SM, + SourceLocation ArgumentLoc) { + SourceLocation BegLoc = Range.getBegin(), EndLoc = Range.getEnd(); + while (BegLoc != EndLoc) { + if (!checkLocForMacroArgExpansion(BegLoc, SM, ArgumentLoc)) + return false; + BegLoc.getLocWithOffset(1); + } + + return checkLocForMacroArgExpansion(BegLoc, SM, ArgumentLoc); +} + +/// A helper function to check if the current ranges are all inside the same +/// macro argument expansion as Loc. +static bool checkRangesForMacroArgExpansion(FullSourceLoc Loc, + ArrayRef<CharSourceRange> Ranges) { + assert(Loc.isMacroID() && "Must be a macro expansion!"); + + SmallVector<CharSourceRange, 4> SpellingRanges; + mapDiagnosticRanges(Loc, Ranges, SpellingRanges); + + /// Count all valid ranges. + unsigned ValidCount = 0; + for (const auto &Range : Ranges) + if (Range.isValid()) + ValidCount++; + + if (ValidCount > SpellingRanges.size()) + return false; + + /// To store the source location of the argument location. + FullSourceLoc ArgumentLoc; + + /// Set the ArgumentLoc to the beginning location of the expansion of Loc + /// so to check if the ranges expands to the same beginning location. + if (!Loc.isMacroArgExpansion(&ArgumentLoc)) + return false; + + for (const auto &Range : SpellingRanges) + if (!checkRangeForMacroArgExpansion(Range, Loc.getManager(), ArgumentLoc)) + return false; + + return true; +} + +/// 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. +void DiagnosticRenderer::emitMacroExpansions(FullSourceLoc Loc, + DiagnosticsEngine::Level Level, + ArrayRef<CharSourceRange> Ranges, + ArrayRef<FixItHint> Hints) { + assert(Loc.isValid() && "must have a valid source location here"); + const SourceManager &SM = Loc.getManager(); + SourceLocation L = Loc; + + // Produce a stack of macro backtraces. + SmallVector<SourceLocation, 8> LocationStack; + unsigned IgnoredEnd = 0; + while (L.isMacroID()) { + // 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(L)) + LocationStack.push_back(SM.getImmediateExpansionRange(L).getBegin()); + else + LocationStack.push_back(L); + + if (checkRangesForMacroArgExpansion(FullSourceLoc(L, SM), Ranges)) + IgnoredEnd = LocationStack.size(); + + L = SM.getImmediateMacroCallerLoc(L); + + // Once the location no longer points into a macro, try stepping through + // the last found location. This sometimes produces additional useful + // backtraces. + if (L.isFileID()) + L = SM.getImmediateMacroCallerLoc(LocationStack.back()); + assert(L.isValid() && "must have a valid source location here"); + } + + LocationStack.erase(LocationStack.begin(), + LocationStack.begin() + IgnoredEnd); + + unsigned MacroDepth = LocationStack.size(); + unsigned MacroLimit = DiagOpts->MacroBacktraceLimit; + if (MacroDepth <= MacroLimit || MacroLimit == 0) { + for (auto I = LocationStack.rbegin(), E = LocationStack.rend(); + I != E; ++I) + emitSingleMacroExpansion(FullSourceLoc(*I, SM), Level, Ranges); + return; + } + + unsigned MacroStartMessages = MacroLimit / 2; + unsigned MacroEndMessages = MacroLimit / 2 + MacroLimit % 2; + + for (auto I = LocationStack.rbegin(), + E = LocationStack.rbegin() + MacroStartMessages; + I != E; ++I) + emitSingleMacroExpansion(FullSourceLoc(*I, SM), Level, Ranges); + + SmallString<200> MessageStorage; + llvm::raw_svector_ostream Message(MessageStorage); + Message << "(skipping " << (MacroDepth - MacroLimit) + << " expansions in backtrace; use -fmacro-backtrace-limit=0 to " + "see all)"; + emitBasicNote(Message.str()); + + for (auto I = LocationStack.rend() - MacroEndMessages, + E = LocationStack.rend(); + I != E; ++I) + emitSingleMacroExpansion(FullSourceLoc(*I, SM), Level, Ranges); +} + +DiagnosticNoteRenderer::~DiagnosticNoteRenderer() = default; + +void DiagnosticNoteRenderer::emitIncludeLocation(FullSourceLoc Loc, + PresumedLoc PLoc) { + // 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()); +} + +void DiagnosticNoteRenderer::emitImportLocation(FullSourceLoc Loc, + PresumedLoc PLoc, + StringRef ModuleName) { + // Generate a note indicating the include location. + SmallString<200> MessageStorage; + llvm::raw_svector_ostream Message(MessageStorage); + Message << "in module '" << ModuleName; + if (PLoc.isValid()) + Message << "' imported from " << PLoc.getFilename() << ':' + << PLoc.getLine(); + Message << ":"; + emitNote(Loc, Message.str()); +} + +void DiagnosticNoteRenderer::emitBuildingModuleLocation(FullSourceLoc Loc, + PresumedLoc PLoc, + StringRef ModuleName) { + // Generate a note indicating the include location. + SmallString<200> MessageStorage; + llvm::raw_svector_ostream Message(MessageStorage); + if (PLoc.isValid()) + Message << "while building module '" << ModuleName << "' imported from " + << PLoc.getFilename() << ':' << PLoc.getLine() << ":"; + else + Message << "while building module '" << ModuleName << "':"; + emitNote(Loc, Message.str()); +} 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 000000000000..f5226380b4dd --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/FrontendAction.cpp @@ -0,0 +1,1093 @@ +//===--- 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/CompilerInstance.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "clang/Frontend/LayoutOverrideSource.h" +#include "clang/Frontend/MultiplexConsumer.h" +#include "clang/Frontend/Utils.h" +#include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/LiteralSupport.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Lex/PreprocessorOptions.h" +#include "clang/Parse/ParseAST.h" +#include "clang/Serialization/ASTDeserializationListener.h" +#include "clang/Serialization/ASTReader.h" +#include "clang/Serialization/GlobalModuleIndex.h" +#include "llvm/Support/BuryPointer.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/raw_ostream.h" +#include <system_error> +using namespace clang; + +LLVM_INSTANTIATE_REGISTRY(FrontendPluginRegistry) + +namespace { + +class DelegatingDeserializationListener : public ASTDeserializationListener { + ASTDeserializationListener *Previous; + bool DeletePrevious; + +public: + explicit DelegatingDeserializationListener( + ASTDeserializationListener *Previous, bool DeletePrevious) + : Previous(Previous), DeletePrevious(DeletePrevious) {} + ~DelegatingDeserializationListener() override { + if (DeletePrevious) + delete Previous; + } + + void ReaderInitialized(ASTReader *Reader) override { + if (Previous) + Previous->ReaderInitialized(Reader); + } + void IdentifierRead(serialization::IdentID ID, + IdentifierInfo *II) override { + if (Previous) + Previous->IdentifierRead(ID, II); + } + void TypeRead(serialization::TypeIdx Idx, QualType T) override { + if (Previous) + Previous->TypeRead(Idx, T); + } + void DeclRead(serialization::DeclID ID, const Decl *D) override { + if (Previous) + Previous->DeclRead(ID, D); + } + void SelectorRead(serialization::SelectorID ID, Selector Sel) override { + if (Previous) + Previous->SelectorRead(ID, Sel); + } + void MacroDefinitionRead(serialization::PreprocessedEntityID PPID, + MacroDefinitionRecord *MD) override { + if (Previous) + Previous->MacroDefinitionRead(PPID, MD); + } +}; + +/// Dumps deserialized declarations. +class DeserializedDeclsDumper : public DelegatingDeserializationListener { +public: + explicit DeserializedDeclsDumper(ASTDeserializationListener *Previous, + bool DeletePrevious) + : DelegatingDeserializationListener(Previous, DeletePrevious) {} + + void DeclRead(serialization::DeclID ID, const Decl *D) override { + llvm::outs() << "PCH DECL: " << D->getDeclKindName(); + if (const NamedDecl *ND = dyn_cast<NamedDecl>(D)) { + llvm::outs() << " - "; + ND->printQualifiedName(llvm::outs()); + } + llvm::outs() << "\n"; + + DelegatingDeserializationListener::DeclRead(ID, D); + } +}; + +/// 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, + bool DeletePrevious) + : DelegatingDeserializationListener(Previous, DeletePrevious), Ctx(Ctx), + NamesToCheck(NamesToCheck) {} + + void DeclRead(serialization::DeclID ID, const Decl *D) override { + 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(nullptr) {} + +FrontendAction::~FrontendAction() {} + +void FrontendAction::setCurrentInput(const FrontendInputFile &CurrentInput, + std::unique_ptr<ASTUnit> AST) { + this->CurrentInput = CurrentInput; + CurrentASTUnit = std::move(AST); +} + +Module *FrontendAction::getCurrentModule() const { + CompilerInstance &CI = getCompilerInstance(); + return CI.getPreprocessor().getHeaderSearchInfo().lookupModule( + CI.getLangOpts().CurrentModule, /*AllowSearch*/false); +} + +std::unique_ptr<ASTConsumer> +FrontendAction::CreateWrappedASTConsumer(CompilerInstance &CI, + StringRef InFile) { + std::unique_ptr<ASTConsumer> Consumer = CreateASTConsumer(CI, InFile); + if (!Consumer) + return nullptr; + + // Validate -add-plugin args. + bool FoundAllPlugins = true; + for (const std::string &Arg : CI.getFrontendOpts().AddPluginActions) { + bool Found = false; + for (FrontendPluginRegistry::iterator it = FrontendPluginRegistry::begin(), + ie = FrontendPluginRegistry::end(); + it != ie; ++it) { + if (it->getName() == Arg) + Found = true; + } + if (!Found) { + CI.getDiagnostics().Report(diag::err_fe_invalid_plugin_name) << Arg; + FoundAllPlugins = false; + } + } + if (!FoundAllPlugins) + return nullptr; + + // If there are no registered plugins we don't need to wrap the consumer + if (FrontendPluginRegistry::begin() == FrontendPluginRegistry::end()) + return Consumer; + + // If this is a code completion run, avoid invoking the plugin consumers + if (CI.hasCodeCompletionConsumer()) + return Consumer; + + // Collect the list of plugins that go before the main action (in Consumers) + // or after it (in AfterConsumers) + std::vector<std::unique_ptr<ASTConsumer>> Consumers; + std::vector<std::unique_ptr<ASTConsumer>> AfterConsumers; + for (FrontendPluginRegistry::iterator it = FrontendPluginRegistry::begin(), + ie = FrontendPluginRegistry::end(); + it != ie; ++it) { + std::unique_ptr<PluginASTAction> P = it->instantiate(); + PluginASTAction::ActionType ActionType = P->getActionType(); + if (ActionType == PluginASTAction::Cmdline) { + // This is O(|plugins| * |add_plugins|), but since both numbers are + // way below 50 in practice, that's ok. + for (size_t i = 0, e = CI.getFrontendOpts().AddPluginActions.size(); + i != e; ++i) { + if (it->getName() == CI.getFrontendOpts().AddPluginActions[i]) { + ActionType = PluginASTAction::AddAfterMainAction; + break; + } + } + } + if ((ActionType == PluginASTAction::AddBeforeMainAction || + ActionType == PluginASTAction::AddAfterMainAction) && + P->ParseArgs(CI, CI.getFrontendOpts().PluginArgs[it->getName()])) { + std::unique_ptr<ASTConsumer> PluginConsumer = P->CreateASTConsumer(CI, InFile); + if (ActionType == PluginASTAction::AddBeforeMainAction) { + Consumers.push_back(std::move(PluginConsumer)); + } else { + AfterConsumers.push_back(std::move(PluginConsumer)); + } + } + } + + // Add to Consumers the main consumer, then all the plugins that go after it + Consumers.push_back(std::move(Consumer)); + for (auto &C : AfterConsumers) { + Consumers.push_back(std::move(C)); + } + + return llvm::make_unique<MultiplexConsumer>(std::move(Consumers)); +} + +/// For preprocessed files, if the first line is the linemarker and specifies +/// the original source file name, use that name as the input file name. +/// Returns the location of the first token after the line marker directive. +/// +/// \param CI The compiler instance. +/// \param InputFile Populated with the filename from the line marker. +/// \param IsModuleMap If \c true, add a line note corresponding to this line +/// directive. (We need to do this because the directive will not be +/// visited by the preprocessor.) +static SourceLocation ReadOriginalFileName(CompilerInstance &CI, + std::string &InputFile, + bool IsModuleMap = false) { + auto &SourceMgr = CI.getSourceManager(); + auto MainFileID = SourceMgr.getMainFileID(); + + bool Invalid = false; + const auto *MainFileBuf = SourceMgr.getBuffer(MainFileID, &Invalid); + if (Invalid) + return SourceLocation(); + + std::unique_ptr<Lexer> RawLexer( + new Lexer(MainFileID, MainFileBuf, SourceMgr, CI.getLangOpts())); + + // If the first line has the syntax of + // + // # NUM "FILENAME" + // + // we use FILENAME as the input file name. + Token T; + if (RawLexer->LexFromRawLexer(T) || T.getKind() != tok::hash) + return SourceLocation(); + if (RawLexer->LexFromRawLexer(T) || T.isAtStartOfLine() || + T.getKind() != tok::numeric_constant) + return SourceLocation(); + + unsigned LineNo; + SourceLocation LineNoLoc = T.getLocation(); + if (IsModuleMap) { + llvm::SmallString<16> Buffer; + if (Lexer::getSpelling(LineNoLoc, Buffer, SourceMgr, CI.getLangOpts()) + .getAsInteger(10, LineNo)) + return SourceLocation(); + } + + RawLexer->LexFromRawLexer(T); + if (T.isAtStartOfLine() || T.getKind() != tok::string_literal) + return SourceLocation(); + + StringLiteralParser Literal(T, CI.getPreprocessor()); + if (Literal.hadError) + return SourceLocation(); + RawLexer->LexFromRawLexer(T); + if (T.isNot(tok::eof) && !T.isAtStartOfLine()) + return SourceLocation(); + InputFile = Literal.GetString().str(); + + if (IsModuleMap) + CI.getSourceManager().AddLineNote( + LineNoLoc, LineNo, SourceMgr.getLineTableFilenameID(InputFile), false, + false, SrcMgr::C_User_ModuleMap); + + return T.getLocation(); +} + +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, + bool IsExternC) { + if (IsExternC && LangOpts.CPlusPlus) + Includes += "extern \"C\" {\n"; + if (LangOpts.ObjC) + Includes += "#import \""; + else + Includes += "#include \""; + + Includes += HeaderName; + + Includes += "\"\n"; + if (IsExternC && LangOpts.CPlusPlus) + Includes += "}\n"; +} + +/// 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 std::error_code collectModuleHeaderIncludes( + const LangOptions &LangOpts, FileManager &FileMgr, DiagnosticsEngine &Diag, + ModuleMap &ModMap, clang::Module *Module, SmallVectorImpl<char> &Includes) { + // Don't collect any headers for unavailable modules. + if (!Module->isAvailable()) + return std::error_code(); + + // Resolve all lazy header directives to header files. + ModMap.resolveHeaderDirectives(Module); + + // If any headers are missing, we can't build this module. In most cases, + // diagnostics for this should have already been produced; we only get here + // if explicit stat information was provided. + // FIXME: If the name resolves to a file with different stat information, + // produce a better diagnostic. + if (!Module->MissingHeaders.empty()) { + auto &MissingHeader = Module->MissingHeaders.front(); + Diag.Report(MissingHeader.FileNameLoc, diag::err_module_header_missing) + << MissingHeader.IsUmbrella << MissingHeader.FileName; + return std::error_code(); + } + + // Add includes for each of these headers. + for (auto HK : {Module::HK_Normal, Module::HK_Private}) { + for (Module::Header &H : Module->Headers[HK]) { + Module->addTopHeader(H.Entry); + // Use the path as specified in the module map file. We'll look for this + // file relative to the module build directory (the directory containing + // the module map file) so this will find the same file that we found + // while parsing the module map. + addHeaderInclude(H.NameAsWritten, Includes, LangOpts, Module->IsExternC); + } + } + // Note that Module->PrivateHeaders will not be a TopHeader. + + if (Module::Header UmbrellaHeader = Module->getUmbrellaHeader()) { + Module->addTopHeader(UmbrellaHeader.Entry); + if (Module->Parent) + // Include the umbrella header for submodules. + addHeaderInclude(UmbrellaHeader.NameAsWritten, Includes, LangOpts, + Module->IsExternC); + } else if (Module::DirectoryName UmbrellaDir = Module->getUmbrellaDir()) { + // Add all of the headers we find in this subdirectory. + std::error_code EC; + SmallString<128> DirNative; + llvm::sys::path::native(UmbrellaDir.Entry->getName(), DirNative); + + llvm::vfs::FileSystem &FS = *FileMgr.getVirtualFileSystem(); + for (llvm::vfs::recursive_directory_iterator Dir(FS, DirNative, EC), End; + Dir != End && !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; + + const FileEntry *Header = FileMgr.getFile(Dir->path()); + // FIXME: This shouldn't happen unless there is a file system race. Is + // that worth diagnosing? + if (!Header) + continue; + + // If this header is marked 'unavailable' in this module, don't include + // it. + if (ModMap.isHeaderUnavailableInModule(Header, Module)) + continue; + + // Compute the relative path from the directory to this file. + SmallVector<StringRef, 16> Components; + auto PathIt = llvm::sys::path::rbegin(Dir->path()); + for (int I = 0; I != Dir.level() + 1; ++I, ++PathIt) + Components.push_back(*PathIt); + SmallString<128> RelativeHeader(UmbrellaDir.NameAsWritten); + for (auto It = Components.rbegin(), End = Components.rend(); It != End; + ++It) + llvm::sys::path::append(RelativeHeader, *It); + + // Include this header as part of the umbrella directory. + Module->addTopHeader(Header); + addHeaderInclude(RelativeHeader, Includes, LangOpts, Module->IsExternC); + } + + if (EC) + return EC; + } + + // Recurse into submodules. + for (clang::Module::submodule_iterator Sub = Module->submodule_begin(), + SubEnd = Module->submodule_end(); + Sub != SubEnd; ++Sub) + if (std::error_code Err = collectModuleHeaderIncludes( + LangOpts, FileMgr, Diag, ModMap, *Sub, Includes)) + return Err; + + return std::error_code(); +} + +static bool loadModuleMapForModuleBuild(CompilerInstance &CI, bool IsSystem, + bool IsPreprocessed, + std::string &PresumedModuleMapFile, + unsigned &Offset) { + auto &SrcMgr = CI.getSourceManager(); + HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo(); + + // Map the current input to a file. + FileID ModuleMapID = SrcMgr.getMainFileID(); + const FileEntry *ModuleMap = SrcMgr.getFileEntryForID(ModuleMapID); + + // If the module map is preprocessed, handle the initial line marker; + // line directives are not part of the module map syntax in general. + Offset = 0; + if (IsPreprocessed) { + SourceLocation EndOfLineMarker = + ReadOriginalFileName(CI, PresumedModuleMapFile, /*IsModuleMap*/ true); + if (EndOfLineMarker.isValid()) + Offset = CI.getSourceManager().getDecomposedLoc(EndOfLineMarker).second; + } + + // Load the module map file. + if (HS.loadModuleMapFile(ModuleMap, IsSystem, ModuleMapID, &Offset, + PresumedModuleMapFile)) + return true; + + if (SrcMgr.getBuffer(ModuleMapID)->getBufferSize() == Offset) + Offset = 0; + + return false; +} + +static Module *prepareToBuildModule(CompilerInstance &CI, + StringRef ModuleMapFilename) { + 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 nullptr; + } + + // Dig out the module definition. + HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo(); + Module *M = HS.lookupModule(CI.getLangOpts().CurrentModule, + /*AllowSearch=*/false); + if (!M) { + CI.getDiagnostics().Report(diag::err_missing_module) + << CI.getLangOpts().CurrentModule << ModuleMapFilename; + + return nullptr; + } + + // Check whether we can build this module at all. + if (Preprocessor::checkModuleIsAvailable(CI.getLangOpts(), CI.getTarget(), + CI.getDiagnostics(), M)) + return nullptr; + + // Inform the preprocessor that includes from within the input buffer should + // be resolved relative to the build directory of the module map file. + CI.getPreprocessor().setMainFileDir(M->Directory); + + // If the module was inferred from a different module map (via an expanded + // umbrella module definition), track that fact. + // FIXME: It would be preferable to fill this in as part of processing + // the module map, rather than adding it after the fact. + StringRef OriginalModuleMapName = CI.getFrontendOpts().OriginalModuleMap; + if (!OriginalModuleMapName.empty()) { + auto *OriginalModuleMap = + CI.getFileManager().getFile(OriginalModuleMapName, + /*openFile*/ true); + if (!OriginalModuleMap) { + CI.getDiagnostics().Report(diag::err_module_map_not_found) + << OriginalModuleMapName; + return nullptr; + } + if (OriginalModuleMap != CI.getSourceManager().getFileEntryForID( + CI.getSourceManager().getMainFileID())) { + M->IsInferred = true; + CI.getPreprocessor().getHeaderSearchInfo().getModuleMap() + .setInferredModuleAllowedBy(M, OriginalModuleMap); + } + } + + // If we're being run from the command-line, the module build stack will not + // have been filled in yet, so complete it now in order to allow us to detect + // module cycles. + SourceManager &SourceMgr = CI.getSourceManager(); + if (SourceMgr.getModuleBuildStack().empty()) + SourceMgr.pushModuleBuildStack(CI.getLangOpts().CurrentModule, + FullSourceLoc(SourceLocation(), SourceMgr)); + return M; +} + +/// Compute the input buffer that should be used to build the specified module. +static std::unique_ptr<llvm::MemoryBuffer> +getInputBufferForModule(CompilerInstance &CI, Module *M) { + FileManager &FileMgr = CI.getFileManager(); + + // Collect the set of #includes we need to build the module. + SmallString<256> HeaderContents; + std::error_code Err = std::error_code(); + if (Module::Header UmbrellaHeader = M->getUmbrellaHeader()) + addHeaderInclude(UmbrellaHeader.NameAsWritten, HeaderContents, + CI.getLangOpts(), M->IsExternC); + Err = collectModuleHeaderIncludes( + CI.getLangOpts(), FileMgr, CI.getDiagnostics(), + CI.getPreprocessor().getHeaderSearchInfo().getModuleMap(), M, + HeaderContents); + + if (Err) { + CI.getDiagnostics().Report(diag::err_module_cannot_create_includes) + << M->getFullModuleName() << Err.message(); + return nullptr; + } + + return llvm::MemoryBuffer::getMemBufferCopy( + HeaderContents, Module::getModuleInputBufferName()); +} + +bool FrontendAction::BeginSourceFile(CompilerInstance &CI, + const FrontendInputFile &RealInput) { + FrontendInputFile Input(RealInput); + assert(!Instance && "Already processing a source file!"); + assert(!Input.isEmpty() && "Unexpected empty filename!"); + setCurrentInput(Input); + setCompilerInstance(&CI); + + bool HasBegunSourceFile = false; + bool ReplayASTFile = Input.getKind().getFormat() == InputKind::Precompiled && + usesPreprocessorOnly(); + if (!BeginInvocation(CI)) + goto failure; + + // If we're replaying the build of an AST file, import it and set up + // the initial state from its build. + if (ReplayASTFile) { + IntrusiveRefCntPtr<DiagnosticsEngine> Diags(&CI.getDiagnostics()); + + // The AST unit populates its own diagnostics engine rather than ours. + IntrusiveRefCntPtr<DiagnosticsEngine> ASTDiags( + new DiagnosticsEngine(Diags->getDiagnosticIDs(), + &Diags->getDiagnosticOptions())); + ASTDiags->setClient(Diags->getClient(), /*OwnsClient*/false); + + // FIXME: What if the input is a memory buffer? + StringRef InputFile = Input.getFile(); + + std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromASTFile( + InputFile, CI.getPCHContainerReader(), ASTUnit::LoadPreprocessorOnly, + ASTDiags, CI.getFileSystemOpts(), CI.getCodeGenOpts().DebugTypeExtRefs); + if (!AST) + goto failure; + + // Options relating to how we treat the input (but not what we do with it) + // are inherited from the AST unit. + CI.getHeaderSearchOpts() = AST->getHeaderSearchOpts(); + CI.getPreprocessorOpts() = AST->getPreprocessorOpts(); + CI.getLangOpts() = AST->getLangOpts(); + + // 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.createSourceManager(CI.getFileManager()); + CI.getSourceManager().initializeForReplay(AST->getSourceManager()); + + // Preload all the module files loaded transitively by the AST unit. Also + // load all module map files that were parsed as part of building the AST + // unit. + if (auto ASTReader = AST->getASTReader()) { + auto &MM = ASTReader->getModuleManager(); + auto &PrimaryModule = MM.getPrimaryModule(); + + for (serialization::ModuleFile &MF : MM) + if (&MF != &PrimaryModule) + CI.getFrontendOpts().ModuleFiles.push_back(MF.FileName); + + ASTReader->visitTopLevelModuleMaps(PrimaryModule, + [&](const FileEntry *FE) { + CI.getFrontendOpts().ModuleMapFiles.push_back(FE->getName()); + }); + } + + // Set up the input file for replay purposes. + auto Kind = AST->getInputKind(); + if (Kind.getFormat() == InputKind::ModuleMap) { + Module *ASTModule = + AST->getPreprocessor().getHeaderSearchInfo().lookupModule( + AST->getLangOpts().CurrentModule, /*AllowSearch*/ false); + assert(ASTModule && "module file does not define its own module"); + Input = FrontendInputFile(ASTModule->PresumedModuleMapFile, Kind); + } else { + auto &OldSM = AST->getSourceManager(); + FileID ID = OldSM.getMainFileID(); + if (auto *File = OldSM.getFileEntryForID(ID)) + Input = FrontendInputFile(File->getName(), Kind); + else + Input = FrontendInputFile(OldSM.getBuffer(ID), Kind); + } + setCurrentInput(Input, std::move(AST)); + } + + // AST files follow a very different path, since they share objects via the + // AST unit. + if (Input.getKind().getFormat() == InputKind::Precompiled) { + assert(!usesPreprocessorOnly() && "this case was handled above"); + assert(hasASTFileSupport() && + "This action does not have AST file support!"); + + IntrusiveRefCntPtr<DiagnosticsEngine> Diags(&CI.getDiagnostics()); + + // FIXME: What if the input is a memory buffer? + StringRef InputFile = Input.getFile(); + + std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromASTFile( + InputFile, CI.getPCHContainerReader(), ASTUnit::LoadEverything, Diags, + CI.getFileSystemOpts(), CI.getCodeGenOpts().DebugTypeExtRefs); + + if (!AST) + goto failure; + + // Inform the diagnostic client we are processing a source file. + CI.getDiagnosticClient().BeginSourceFile(CI.getLangOpts(), nullptr); + 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->getPreprocessorPtr()); + Preprocessor &PP = CI.getPreprocessor(); + PP.getBuiltinInfo().initializeBuiltins(PP.getIdentifierTable(), + PP.getLangOpts()); + CI.setASTContext(&AST->getASTContext()); + + setCurrentInput(Input, std::move(AST)); + + // Initialize the action. + if (!BeginSourceFileAction(CI)) + 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()) { + if (!CI.createFileManager()) { + goto failure; + } + } + if (!CI.hasSourceManager()) + CI.createSourceManager(CI.getFileManager()); + + // Set up embedding for any specified files. Do this before we load any + // source files, including the primary module map for the compilation. + for (const auto &F : CI.getFrontendOpts().ModulesEmbedFiles) { + if (const auto *FE = CI.getFileManager().getFile(F, /*openFile*/true)) + CI.getSourceManager().setFileIsTransient(FE); + else + CI.getDiagnostics().Report(diag::err_modules_embed_file_not_found) << F; + } + if (CI.getFrontendOpts().ModulesEmbedAllFiles) + CI.getSourceManager().setAllFilesAreTransient(true); + + // IR files bypass the rest of initialization. + if (Input.getKind().getLanguage() == InputKind::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(), nullptr); + HasBegunSourceFile = true; + + // Initialize the action. + if (!BeginSourceFileAction(CI)) + goto failure; + + // Initialize the main file entry. + if (!CI.InitializeSourceManager(CurrentInput)) + 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; + std::string SpecificModuleCachePath = CI.getSpecificModuleCachePath(); + if (const DirectoryEntry *PCHDir = FileMgr.getDirectory(PCHInclude)) { + std::error_code EC; + SmallString<128> DirNative; + llvm::sys::path::native(PCHDir->getName(), DirNative); + bool Found = false; + llvm::vfs::FileSystem &FS = *FileMgr.getVirtualFileSystem(); + for (llvm::vfs::directory_iterator Dir = FS.dir_begin(DirNative, EC), + DirEnd; + Dir != DirEnd && !EC; Dir.increment(EC)) { + // Check whether this is an acceptable AST file. + if (ASTReader::isAcceptableASTFile( + Dir->path(), FileMgr, CI.getPCHContainerReader(), + CI.getLangOpts(), CI.getTargetOpts(), CI.getPreprocessorOpts(), + SpecificModuleCachePath)) { + PPOpts.ImplicitPCHInclude = Dir->path(); + Found = true; + break; + } + } + + if (!Found) { + CI.getDiagnostics().Report(diag::err_fe_no_pch_in_dir) << PCHInclude; + goto failure; + } + } + } + + // Set up the preprocessor if needed. When parsing model files the + // preprocessor of the original source is reused. + if (!isModelParsingAction()) + CI.createPreprocessor(getTranslationUnitKind()); + + // Inform the diagnostic client we are processing a source file. + CI.getDiagnosticClient().BeginSourceFile(CI.getLangOpts(), + &CI.getPreprocessor()); + HasBegunSourceFile = true; + + // Initialize the main file entry. + if (!CI.InitializeSourceManager(Input)) + goto failure; + + // For module map files, we first parse the module map and synthesize a + // "<module-includes>" buffer before more conventional processing. + if (Input.getKind().getFormat() == InputKind::ModuleMap) { + CI.getLangOpts().setCompilingModule(LangOptions::CMK_ModuleMap); + + std::string PresumedModuleMapFile; + unsigned OffsetToContents; + if (loadModuleMapForModuleBuild(CI, Input.isSystem(), + Input.isPreprocessed(), + PresumedModuleMapFile, OffsetToContents)) + goto failure; + + auto *CurrentModule = prepareToBuildModule(CI, Input.getFile()); + if (!CurrentModule) + goto failure; + + CurrentModule->PresumedModuleMapFile = PresumedModuleMapFile; + + if (OffsetToContents) + // If the module contents are in the same file, skip to them. + CI.getPreprocessor().setSkipMainFilePreamble(OffsetToContents, true); + else { + // Otherwise, convert the module description to a suitable input buffer. + auto Buffer = getInputBufferForModule(CI, CurrentModule); + if (!Buffer) + goto failure; + + // Reinitialize the main file entry to refer to the new input. + auto Kind = CurrentModule->IsSystem ? SrcMgr::C_System : SrcMgr::C_User; + auto &SourceMgr = CI.getSourceManager(); + auto BufferID = SourceMgr.createFileID(std::move(Buffer), Kind); + assert(BufferID.isValid() && "couldn't creaate module buffer ID"); + SourceMgr.setMainFileID(BufferID); + } + } + + // Initialize the action. + if (!BeginSourceFileAction(CI)) + goto failure; + + // If we were asked to load any module map files, do so now. + for (const auto &Filename : CI.getFrontendOpts().ModuleMapFiles) { + if (auto *File = CI.getFileManager().getFile(Filename)) + CI.getPreprocessor().getHeaderSearchInfo().loadModuleMapFile( + File, /*IsSystem*/false); + else + CI.getDiagnostics().Report(diag::err_module_map_not_found) << Filename; + } + + // Add a module declaration scope so that modules from -fmodule-map-file + // arguments may shadow modules found implicitly in search paths. + CI.getPreprocessor() + .getHeaderSearchInfo() + .getModuleMap() + .finishModuleDeclarationScope(); + + // Create the AST context and consumer unless this is a preprocessor only + // action. + if (!usesPreprocessorOnly()) { + // Parsing a model file should reuse the existing ASTContext. + if (!isModelParsingAction()) + CI.createASTContext(); + + // For preprocessed files, check if the first line specifies the original + // source file name with a linemarker. + std::string PresumedInputFile = getCurrentFileOrBufferName(); + if (Input.isPreprocessed()) + ReadOriginalFileName(CI, PresumedInputFile); + + std::unique_ptr<ASTConsumer> Consumer = + CreateWrappedASTConsumer(CI, PresumedInputFile); + if (!Consumer) + goto failure; + + // FIXME: should not overwrite ASTMutationListener when parsing model files? + if (!isModelParsingAction()) + CI.getASTContext().setASTMutationListener(Consumer->GetASTMutationListener()); + + if (!CI.getPreprocessorOpts().ChainedIncludes.empty()) { + // Convert headers to PCH and chain them. + IntrusiveRefCntPtr<ExternalSemaSource> source, FinalReader; + source = createChainedIncludesSource(CI, FinalReader); + if (!source) + goto failure; + CI.setModuleManager(static_cast<ASTReader *>(FinalReader.get())); + CI.getASTContext().setExternalSource(source); + } else if (CI.getLangOpts().Modules || + !CI.getPreprocessorOpts().ImplicitPCHInclude.empty()) { + // Use PCM or PCH. + assert(hasPCHSupport() && "This action does not have PCH support!"); + ASTDeserializationListener *DeserialListener = + Consumer->GetASTDeserializationListener(); + bool DeleteDeserialListener = false; + if (CI.getPreprocessorOpts().DumpDeserializedPCHDecls) { + DeserialListener = new DeserializedDeclsDumper(DeserialListener, + DeleteDeserialListener); + DeleteDeserialListener = true; + } + if (!CI.getPreprocessorOpts().DeserializedPCHDeclsToErrorOn.empty()) { + DeserialListener = new DeserializedDeclsChecker( + CI.getASTContext(), + CI.getPreprocessorOpts().DeserializedPCHDeclsToErrorOn, + DeserialListener, DeleteDeserialListener); + DeleteDeserialListener = true; + } + if (!CI.getPreprocessorOpts().ImplicitPCHInclude.empty()) { + CI.createPCHExternalASTSource( + CI.getPreprocessorOpts().ImplicitPCHInclude, + CI.getPreprocessorOpts().DisablePCHValidation, + CI.getPreprocessorOpts().AllowPCHWithCompilerErrors, DeserialListener, + DeleteDeserialListener); + if (!CI.getASTContext().getExternalSource()) + goto failure; + } + // If modules are enabled, create the module manager before creating + // any builtins, so that all declarations know that they might be + // extended by an external source. + if (CI.getLangOpts().Modules || !CI.hasASTContext() || + !CI.getASTContext().getExternalSource()) { + CI.createModuleManager(); + CI.getModuleManager()->setDeserializationListener(DeserialListener, + DeleteDeserialListener); + } + } + + CI.setASTConsumer(std::move(Consumer)); + if (!CI.hasASTConsumer()) + goto failure; + } + + // Initialize built-in info as long as we aren't using an external AST + // source. + if (CI.getLangOpts().Modules || !CI.hasASTContext() || + !CI.getASTContext().getExternalSource()) { + Preprocessor &PP = CI.getPreprocessor(); + PP.getBuiltinInfo().initializeBuiltins(PP.getIdentifierTable(), + PP.getLangOpts()); + } else { + // FIXME: If this is a problem, recover from it by creating a multiplex + // source. + assert((!CI.getLangOpts().Modules || CI.getModuleManager()) && + "modules enabled but created an external source that " + "doesn't support modules"); + } + + // If we were asked to load any module files, do so now. + for (const auto &ModuleFile : CI.getFrontendOpts().ModuleFiles) + if (!CI.loadModuleFile(ModuleFile)) + goto failure; + + // 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()) { + IntrusiveRefCntPtr<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 (HasBegunSourceFile) + CI.getDiagnosticClient().EndSourceFile(); + CI.clearOutputFiles(/*EraseFiles=*/true); + CI.getLangOpts().setCompilingModule(LangOptions::CMK_None); + setCurrentInput(FrontendInputFile()); + setCompilerInstance(nullptr); + return false; +} + +bool FrontendAction::Execute() { + CompilerInstance &CI = getCompilerInstance(); + + 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()) { + StringRef Cache = + CI.getPreprocessor().getHeaderSearchInfo().getModuleCachePath(); + if (!Cache.empty()) + GlobalModuleIndex::writeIndex(CI.getFileManager(), + CI.getPCHContainerReader(), Cache); + } + + return true; +} + +void FrontendAction::EndSourceFile() { + CompilerInstance &CI = getCompilerInstance(); + + // Inform the diagnostic client we are done with this source file. + CI.getDiagnosticClient().EndSourceFile(); + + // Inform the preprocessor we are done. + if (CI.hasPreprocessor()) + CI.getPreprocessor().EndSourceFile(); + + // Finalize the action. + EndSourceFileAction(); + + // Sema references the ast consumer, so reset sema first. + // + // FIXME: There is more per-file stuff we could just drop here? + bool DisableFree = CI.getFrontendOpts().DisableFree; + if (DisableFree) { + CI.resetAndLeakSema(); + CI.resetAndLeakASTContext(); + llvm::BuryPointer(CI.takeASTConsumer().get()); + } else { + CI.setSema(nullptr); + CI.setASTContext(nullptr); + CI.setASTConsumer(nullptr); + } + + 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()) { + if (DisableFree) { + CI.resetAndLeakPreprocessor(); + CI.resetAndLeakSourceManager(); + CI.resetAndLeakFileManager(); + llvm::BuryPointer(std::move(CurrentASTUnit)); + } else { + CI.setPreprocessor(nullptr); + CI.setSourceManager(nullptr); + CI.setFileManager(nullptr); + } + } + + setCompilerInstance(nullptr); + setCurrentInput(FrontendInputFile()); + CI.getLangOpts().setCompilingModule(LangOptions::CMK_None); +} + +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 = nullptr; + 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() { } + +std::unique_ptr<ASTConsumer> +PreprocessorFrontendAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + llvm_unreachable("Invalid CreateASTConsumer on preprocessor action!"); +} + +std::unique_ptr<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) { + WrappedAction->setCurrentInput(getCurrentInput()); + WrappedAction->setCompilerInstance(&CI); + auto Ret = WrappedAction->BeginSourceFileAction(CI); + // BeginSourceFileAction may change CurrentInput, e.g. during module builds. + setCurrentInput(WrappedAction->getCurrentInput()); + return Ret; +} +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( + std::unique_ptr<FrontendAction> WrappedAction) + : WrappedAction(std::move(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 000000000000..a407dfc162bb --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/FrontendActions.cpp @@ -0,0 +1,910 @@ +//===--- 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/CompilerInstance.h" +#include "clang/Frontend/FrontendDiagnostic.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/TemplateInstCallback.h" +#include "clang/Serialization/ASTReader.h" +#include "clang/Serialization/ASTWriter.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/YAMLTraits.h" +#include <memory> +#include <system_error> + +using namespace clang; + +namespace { +CodeCompleteConsumer *GetCodeCompletionConsumer(CompilerInstance &CI) { + return CI.hasCodeCompletionConsumer() ? &CI.getCodeCompletionConsumer() + : nullptr; +} + +void EnsureSemaIsCreated(CompilerInstance &CI, FrontendAction &Action) { + if (Action.hasCodeCompletionSupport() && + !CI.getFrontendOpts().CodeCompletionAt.FileName.empty()) + CI.createCodeCompletionConsumer(); + + if (!CI.hasSema()) + CI.createSema(Action.getTranslationUnitKind(), + GetCodeCompletionConsumer(CI)); +} +} // namespace + +//===----------------------------------------------------------------------===// +// Custom Actions +//===----------------------------------------------------------------------===// + +std::unique_ptr<ASTConsumer> +InitOnlyAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + return llvm::make_unique<ASTConsumer>(); +} + +void InitOnlyAction::ExecuteAction() { +} + +//===----------------------------------------------------------------------===// +// AST Consumer Actions +//===----------------------------------------------------------------------===// + +std::unique_ptr<ASTConsumer> +ASTPrintAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + if (std::unique_ptr<raw_ostream> OS = + CI.createDefaultOutputFile(false, InFile)) + return CreateASTPrinter(std::move(OS), CI.getFrontendOpts().ASTDumpFilter); + return nullptr; +} + +std::unique_ptr<ASTConsumer> +ASTDumpAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + return CreateASTDumper(nullptr /*Dump to stdout.*/, + CI.getFrontendOpts().ASTDumpFilter, + CI.getFrontendOpts().ASTDumpDecls, + CI.getFrontendOpts().ASTDumpAll, + CI.getFrontendOpts().ASTDumpLookups); +} + +std::unique_ptr<ASTConsumer> +ASTDeclListAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + return CreateASTDeclNodeLister(); +} + +std::unique_ptr<ASTConsumer> +ASTViewAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + return CreateASTViewer(); +} + +std::unique_ptr<ASTConsumer> +GeneratePCHAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + std::string Sysroot; + if (!ComputeASTConsumerArguments(CI, /*ref*/ Sysroot)) + return nullptr; + + std::string OutputFile; + std::unique_ptr<raw_pwrite_stream> OS = + CreateOutputFile(CI, InFile, /*ref*/ OutputFile); + if (!OS) + return nullptr; + + if (!CI.getFrontendOpts().RelocatablePCH) + Sysroot.clear(); + + const auto &FrontendOpts = CI.getFrontendOpts(); + auto Buffer = std::make_shared<PCHBuffer>(); + std::vector<std::unique_ptr<ASTConsumer>> Consumers; + Consumers.push_back(llvm::make_unique<PCHGenerator>( + CI.getPreprocessor(), OutputFile, Sysroot, + Buffer, FrontendOpts.ModuleFileExtensions, + CI.getPreprocessorOpts().AllowPCHWithCompilerErrors, + FrontendOpts.IncludeTimestamps)); + Consumers.push_back(CI.getPCHContainerWriter().CreatePCHContainerGenerator( + CI, InFile, OutputFile, std::move(OS), Buffer)); + + return llvm::make_unique<MultiplexConsumer>(std::move(Consumers)); +} + +bool GeneratePCHAction::ComputeASTConsumerArguments(CompilerInstance &CI, + std::string &Sysroot) { + Sysroot = CI.getHeaderSearchOpts().Sysroot; + if (CI.getFrontendOpts().RelocatablePCH && Sysroot.empty()) { + CI.getDiagnostics().Report(diag::err_relocatable_without_isysroot); + return false; + } + + return true; +} + +std::unique_ptr<llvm::raw_pwrite_stream> +GeneratePCHAction::CreateOutputFile(CompilerInstance &CI, StringRef InFile, + std::string &OutputFile) { + // 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. + std::unique_ptr<raw_pwrite_stream> OS = + CI.createOutputFile(CI.getFrontendOpts().OutputFile, /*Binary=*/true, + /*RemoveFileOnSignal=*/false, InFile, + /*Extension=*/"", /*useTemporary=*/true); + if (!OS) + return nullptr; + + OutputFile = CI.getFrontendOpts().OutputFile; + return OS; +} + +bool GeneratePCHAction::shouldEraseOutputFiles() { + if (getCompilerInstance().getPreprocessorOpts().AllowPCHWithCompilerErrors) + return false; + return ASTFrontendAction::shouldEraseOutputFiles(); +} + +bool GeneratePCHAction::BeginSourceFileAction(CompilerInstance &CI) { + CI.getLangOpts().CompilingPCH = true; + return true; +} + +std::unique_ptr<ASTConsumer> +GenerateModuleAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + std::unique_ptr<raw_pwrite_stream> OS = CreateOutputFile(CI, InFile); + if (!OS) + return nullptr; + + std::string OutputFile = CI.getFrontendOpts().OutputFile; + std::string Sysroot; + + auto Buffer = std::make_shared<PCHBuffer>(); + std::vector<std::unique_ptr<ASTConsumer>> Consumers; + + Consumers.push_back(llvm::make_unique<PCHGenerator>( + CI.getPreprocessor(), OutputFile, Sysroot, + Buffer, CI.getFrontendOpts().ModuleFileExtensions, + /*AllowASTWithErrors=*/false, + /*IncludeTimestamps=*/ + +CI.getFrontendOpts().BuildingImplicitModule)); + Consumers.push_back(CI.getPCHContainerWriter().CreatePCHContainerGenerator( + CI, InFile, OutputFile, std::move(OS), Buffer)); + return llvm::make_unique<MultiplexConsumer>(std::move(Consumers)); +} + +bool GenerateModuleFromModuleMapAction::BeginSourceFileAction( + CompilerInstance &CI) { + if (!CI.getLangOpts().Modules) { + CI.getDiagnostics().Report(diag::err_module_build_requires_fmodules); + return false; + } + + return GenerateModuleAction::BeginSourceFileAction(CI); +} + +std::unique_ptr<raw_pwrite_stream> +GenerateModuleFromModuleMapAction::CreateOutputFile(CompilerInstance &CI, + StringRef InFile) { + // If no output file was provided, figure out where this module would go + // in the module cache. + if (CI.getFrontendOpts().OutputFile.empty()) { + StringRef ModuleMapFile = CI.getFrontendOpts().OriginalModuleMap; + if (ModuleMapFile.empty()) + ModuleMapFile = InFile; + + HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo(); + CI.getFrontendOpts().OutputFile = + HS.getCachedModuleFileName(CI.getLangOpts().CurrentModule, + ModuleMapFile); + } + + // 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. + return CI.createOutputFile(CI.getFrontendOpts().OutputFile, /*Binary=*/true, + /*RemoveFileOnSignal=*/false, InFile, + /*Extension=*/"", /*useTemporary=*/true, + /*CreateMissingDirectories=*/true); +} + +bool GenerateModuleInterfaceAction::BeginSourceFileAction( + CompilerInstance &CI) { + if (!CI.getLangOpts().ModulesTS) { + CI.getDiagnostics().Report(diag::err_module_interface_requires_modules_ts); + return false; + } + + CI.getLangOpts().setCompilingModule(LangOptions::CMK_ModuleInterface); + + return GenerateModuleAction::BeginSourceFileAction(CI); +} + +std::unique_ptr<raw_pwrite_stream> +GenerateModuleInterfaceAction::CreateOutputFile(CompilerInstance &CI, + StringRef InFile) { + return CI.createDefaultOutputFile(/*Binary=*/true, InFile, "pcm"); +} + +bool GenerateHeaderModuleAction::PrepareToExecuteAction( + CompilerInstance &CI) { + if (!CI.getLangOpts().Modules && !CI.getLangOpts().ModulesTS) { + CI.getDiagnostics().Report(diag::err_header_module_requires_modules); + return false; + } + + auto &Inputs = CI.getFrontendOpts().Inputs; + if (Inputs.empty()) + return GenerateModuleAction::BeginInvocation(CI); + + auto Kind = Inputs[0].getKind(); + + // Convert the header file inputs into a single module input buffer. + SmallString<256> HeaderContents; + ModuleHeaders.reserve(Inputs.size()); + for (const FrontendInputFile &FIF : Inputs) { + // FIXME: We should support re-compiling from an AST file. + if (FIF.getKind().getFormat() != InputKind::Source || !FIF.isFile()) { + CI.getDiagnostics().Report(diag::err_module_header_file_not_found) + << (FIF.isFile() ? FIF.getFile() + : FIF.getBuffer()->getBufferIdentifier()); + return true; + } + + HeaderContents += "#include \""; + HeaderContents += FIF.getFile(); + HeaderContents += "\"\n"; + ModuleHeaders.push_back(FIF.getFile()); + } + Buffer = llvm::MemoryBuffer::getMemBufferCopy( + HeaderContents, Module::getModuleInputBufferName()); + + // Set that buffer up as our "real" input. + Inputs.clear(); + Inputs.push_back(FrontendInputFile(Buffer.get(), Kind, /*IsSystem*/false)); + + return GenerateModuleAction::PrepareToExecuteAction(CI); +} + +bool GenerateHeaderModuleAction::BeginSourceFileAction( + CompilerInstance &CI) { + CI.getLangOpts().setCompilingModule(LangOptions::CMK_HeaderModule); + + // Synthesize a Module object for the given headers. + auto &HS = CI.getPreprocessor().getHeaderSearchInfo(); + SmallVector<Module::Header, 16> Headers; + for (StringRef Name : ModuleHeaders) { + const DirectoryLookup *CurDir = nullptr; + const FileEntry *FE = HS.LookupFile( + Name, SourceLocation(), /*Angled*/ false, nullptr, CurDir, + None, nullptr, nullptr, nullptr, nullptr, nullptr); + if (!FE) { + CI.getDiagnostics().Report(diag::err_module_header_file_not_found) + << Name; + continue; + } + Headers.push_back({Name, FE}); + } + HS.getModuleMap().createHeaderModule(CI.getLangOpts().CurrentModule, Headers); + + return GenerateModuleAction::BeginSourceFileAction(CI); +} + +std::unique_ptr<raw_pwrite_stream> +GenerateHeaderModuleAction::CreateOutputFile(CompilerInstance &CI, + StringRef InFile) { + return CI.createDefaultOutputFile(/*Binary=*/true, InFile, "pcm"); +} + +SyntaxOnlyAction::~SyntaxOnlyAction() { +} + +std::unique_ptr<ASTConsumer> +SyntaxOnlyAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + return llvm::make_unique<ASTConsumer>(); +} + +std::unique_ptr<ASTConsumer> +DumpModuleInfoAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + return llvm::make_unique<ASTConsumer>(); +} + +std::unique_ptr<ASTConsumer> +VerifyPCHAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + return llvm::make_unique<ASTConsumer>(); +} + +void VerifyPCHAction::ExecuteAction() { + CompilerInstance &CI = getCompilerInstance(); + bool Preamble = CI.getPreprocessorOpts().PrecompiledPreambleBytes.first != 0; + const std::string &Sysroot = CI.getHeaderSearchOpts().Sysroot; + std::unique_ptr<ASTReader> Reader(new ASTReader( + CI.getPreprocessor(), &CI.getASTContext(), CI.getPCHContainerReader(), + CI.getFrontendOpts().ModuleFileExtensions, + Sysroot.empty() ? "" : Sysroot.c_str(), + /*DisableValidation*/ false, + /*AllowPCHWithCompilerErrors*/ false, + /*AllowConfigurationMismatch*/ true, + /*ValidateSystemInputs*/ true)); + + Reader->ReadAST(getCurrentFile(), + Preamble ? serialization::MK_Preamble + : serialization::MK_PCH, + SourceLocation(), + ASTReader::ARR_ConfigurationMismatch); +} + +namespace { +struct TemplightEntry { + std::string Name; + std::string Kind; + std::string Event; + std::string DefinitionLocation; + std::string PointOfInstantiation; +}; +} // namespace + +namespace llvm { +namespace yaml { +template <> struct MappingTraits<TemplightEntry> { + static void mapping(IO &io, TemplightEntry &fields) { + io.mapRequired("name", fields.Name); + io.mapRequired("kind", fields.Kind); + io.mapRequired("event", fields.Event); + io.mapRequired("orig", fields.DefinitionLocation); + io.mapRequired("poi", fields.PointOfInstantiation); + } +}; +} // namespace yaml +} // namespace llvm + +namespace { +class DefaultTemplateInstCallback : public TemplateInstantiationCallback { + using CodeSynthesisContext = Sema::CodeSynthesisContext; + +public: + void initialize(const Sema &) override {} + + void finalize(const Sema &) override {} + + void atTemplateBegin(const Sema &TheSema, + const CodeSynthesisContext &Inst) override { + displayTemplightEntry<true>(llvm::outs(), TheSema, Inst); + } + + void atTemplateEnd(const Sema &TheSema, + const CodeSynthesisContext &Inst) override { + displayTemplightEntry<false>(llvm::outs(), TheSema, Inst); + } + +private: + static std::string toString(CodeSynthesisContext::SynthesisKind Kind) { + switch (Kind) { + case CodeSynthesisContext::TemplateInstantiation: + return "TemplateInstantiation"; + case CodeSynthesisContext::DefaultTemplateArgumentInstantiation: + return "DefaultTemplateArgumentInstantiation"; + case CodeSynthesisContext::DefaultFunctionArgumentInstantiation: + return "DefaultFunctionArgumentInstantiation"; + case CodeSynthesisContext::ExplicitTemplateArgumentSubstitution: + return "ExplicitTemplateArgumentSubstitution"; + case CodeSynthesisContext::DeducedTemplateArgumentSubstitution: + return "DeducedTemplateArgumentSubstitution"; + case CodeSynthesisContext::PriorTemplateArgumentSubstitution: + return "PriorTemplateArgumentSubstitution"; + case CodeSynthesisContext::DefaultTemplateArgumentChecking: + return "DefaultTemplateArgumentChecking"; + case CodeSynthesisContext::ExceptionSpecEvaluation: + return "ExceptionSpecEvaluation"; + case CodeSynthesisContext::ExceptionSpecInstantiation: + return "ExceptionSpecInstantiation"; + case CodeSynthesisContext::DeclaringSpecialMember: + return "DeclaringSpecialMember"; + case CodeSynthesisContext::DefiningSynthesizedFunction: + return "DefiningSynthesizedFunction"; + case CodeSynthesisContext::Memoization: + return "Memoization"; + } + return ""; + } + + template <bool BeginInstantiation> + static void displayTemplightEntry(llvm::raw_ostream &Out, const Sema &TheSema, + const CodeSynthesisContext &Inst) { + std::string YAML; + { + llvm::raw_string_ostream OS(YAML); + llvm::yaml::Output YO(OS); + TemplightEntry Entry = + getTemplightEntry<BeginInstantiation>(TheSema, Inst); + llvm::yaml::EmptyContext Context; + llvm::yaml::yamlize(YO, Entry, true, Context); + } + Out << "---" << YAML << "\n"; + } + + template <bool BeginInstantiation> + static TemplightEntry getTemplightEntry(const Sema &TheSema, + const CodeSynthesisContext &Inst) { + TemplightEntry Entry; + Entry.Kind = toString(Inst.Kind); + Entry.Event = BeginInstantiation ? "Begin" : "End"; + if (auto *NamedTemplate = dyn_cast_or_null<NamedDecl>(Inst.Entity)) { + llvm::raw_string_ostream OS(Entry.Name); + NamedTemplate->getNameForDiagnostic(OS, TheSema.getLangOpts(), true); + const PresumedLoc DefLoc = + TheSema.getSourceManager().getPresumedLoc(Inst.Entity->getLocation()); + if(!DefLoc.isInvalid()) + Entry.DefinitionLocation = std::string(DefLoc.getFilename()) + ":" + + std::to_string(DefLoc.getLine()) + ":" + + std::to_string(DefLoc.getColumn()); + } + const PresumedLoc PoiLoc = + TheSema.getSourceManager().getPresumedLoc(Inst.PointOfInstantiation); + if (!PoiLoc.isInvalid()) { + Entry.PointOfInstantiation = std::string(PoiLoc.getFilename()) + ":" + + std::to_string(PoiLoc.getLine()) + ":" + + std::to_string(PoiLoc.getColumn()); + } + return Entry; + } +}; +} // namespace + +std::unique_ptr<ASTConsumer> +TemplightDumpAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + return llvm::make_unique<ASTConsumer>(); +} + +void TemplightDumpAction::ExecuteAction() { + CompilerInstance &CI = getCompilerInstance(); + + // This part is normally done by ASTFrontEndAction, but needs to happen + // before Templight observers can be created + // FIXME: Move the truncation aspect of this into Sema, we delayed this till + // here so the source manager would be initialized. + EnsureSemaIsCreated(CI, *this); + + CI.getSema().TemplateInstCallbacks.push_back( + llvm::make_unique<DefaultTemplateInstCallback>()); + ASTFrontendAction::ExecuteAction(); +} + +namespace { + /// 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" + + bool ReadFullVersionInformation(StringRef FullVersion) override { + Out.indent(2) + << "Generated by " + << (FullVersion == getClangFullRepositoryVersion()? "this" + : "a different") + << " Clang: " << FullVersion << "\n"; + return ASTReaderListener::ReadFullVersionInformation(FullVersion); + } + + void ReadModuleName(StringRef ModuleName) override { + Out.indent(2) << "Module name: " << ModuleName << "\n"; + } + void ReadModuleMapFile(StringRef ModuleMapPath) override { + Out.indent(2) << "Module map file: " << ModuleMapPath << "\n"; + } + + bool ReadLanguageOptions(const LangOptions &LangOpts, bool Complain, + bool AllowCompatibleDifferences) override { + 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" + + if (!LangOpts.ModuleFeatures.empty()) { + Out.indent(4) << "Module features:\n"; + for (StringRef Feature : LangOpts.ModuleFeatures) + Out.indent(6) << Feature << "\n"; + } + + return false; + } + + bool ReadTargetOptions(const TargetOptions &TargetOpts, bool Complain, + bool AllowCompatibleDifferences) override { + 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"; + + 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; + } + + bool ReadDiagnosticOptions(IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts, + bool Complain) override { + Out.indent(2) << "Diagnostic options:\n"; +#define DIAGOPT(Name, Bits, Default) DUMP_BOOLEAN(DiagOpts->Name, #Name); +#define ENUM_DIAGOPT(Name, Type, Bits, Default) \ + Out.indent(4) << #Name << ": " << DiagOpts->get##Name() << "\n"; +#define VALUE_DIAGOPT(Name, Bits, Default) \ + Out.indent(4) << #Name << ": " << DiagOpts->Name << "\n"; +#include "clang/Basic/DiagnosticOptions.def" + + Out.indent(4) << "Diagnostic flags:\n"; + for (const std::string &Warning : DiagOpts->Warnings) + Out.indent(6) << "-W" << Warning << "\n"; + for (const std::string &Remark : DiagOpts->Remarks) + Out.indent(6) << "-R" << Remark << "\n"; + + return false; + } + + bool ReadHeaderSearchOptions(const HeaderSearchOptions &HSOpts, + StringRef SpecificModuleCachePath, + bool Complain) override { + Out.indent(2) << "Header search options:\n"; + Out.indent(4) << "System root [-isysroot=]: '" << HSOpts.Sysroot << "'\n"; + Out.indent(4) << "Resource dir [ -resource-dir=]: '" << HSOpts.ResourceDir << "'\n"; + Out.indent(4) << "Module Cache: '" << SpecificModuleCachePath << "'\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; + } + + bool ReadPreprocessorOptions(const PreprocessorOptions &PPOpts, + bool Complain, + std::string &SuggestedPredefines) override { + 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; + } + + /// Indicates that a particular module file extension has been read. + void readModuleFileExtension( + const ModuleFileExtensionMetadata &Metadata) override { + Out.indent(2) << "Module file extension '" + << Metadata.BlockName << "' " << Metadata.MajorVersion + << "." << Metadata.MinorVersion; + if (!Metadata.UserInfo.empty()) { + Out << ": "; + Out.write_escaped(Metadata.UserInfo); + } + + Out << "\n"; + } + + /// Tells the \c ASTReaderListener that we want to receive the + /// input files of the AST file via \c visitInputFile. + bool needsInputFileVisitation() override { return true; } + + /// Tells the \c ASTReaderListener that we want to receive the + /// input files of the AST file via \c visitInputFile. + bool needsSystemInputFileVisitation() override { return true; } + + /// Indicates that the AST file contains particular input file. + /// + /// \returns true to continue receiving the next input file, false to stop. + bool visitInputFile(StringRef Filename, bool isSystem, + bool isOverridden, bool isExplicitModule) override { + + Out.indent(2) << "Input file: " << Filename; + + if (isSystem || isOverridden || isExplicitModule) { + Out << " ["; + if (isSystem) { + Out << "System"; + if (isOverridden || isExplicitModule) + Out << ", "; + } + if (isOverridden) { + Out << "Overridden"; + if (isExplicitModule) + Out << ", "; + } + if (isExplicitModule) + Out << "ExplicitModule"; + + Out << "]"; + } + + Out << "\n"; + + return true; + } + + /// Returns true if this \c ASTReaderListener wants to receive the + /// imports of the AST file via \c visitImport, false otherwise. + bool needsImportVisitation() const override { return true; } + + /// If needsImportVisitation returns \c true, this is called for each + /// AST file imported by this AST file. + void visitImport(StringRef ModuleName, StringRef Filename) override { + Out.indent(2) << "Imports module '" << ModuleName + << "': " << Filename.str() << "\n"; + } +#undef DUMP_BOOLEAN + }; +} + +bool DumpModuleInfoAction::BeginInvocation(CompilerInstance &CI) { + // The Object file reader also supports raw ast files and there is no point in + // being strict about the module file format in -module-file-info mode. + CI.getHeaderSearchOpts().ModuleFormat = "obj"; + return true; +} + +void DumpModuleInfoAction::ExecuteAction() { + // Set up the output file. + std::unique_ptr<llvm::raw_fd_ostream> OutFile; + StringRef OutputFileName = getCompilerInstance().getFrontendOpts().OutputFile; + if (!OutputFileName.empty() && OutputFileName != "-") { + std::error_code EC; + OutFile.reset(new llvm::raw_fd_ostream(OutputFileName.str(), EC, + llvm::sys::fs::F_Text)); + } + llvm::raw_ostream &Out = OutFile.get()? *OutFile.get() : llvm::outs(); + + Out << "Information for module file '" << getCurrentFile() << "':\n"; + auto &FileMgr = getCompilerInstance().getFileManager(); + auto Buffer = FileMgr.getBufferForFile(getCurrentFile()); + StringRef Magic = (*Buffer)->getMemBufferRef().getBuffer(); + bool IsRaw = (Magic.size() >= 4 && Magic[0] == 'C' && Magic[1] == 'P' && + Magic[2] == 'C' && Magic[3] == 'H'); + Out << " Module format: " << (IsRaw ? "raw" : "obj") << "\n"; + + Preprocessor &PP = getCompilerInstance().getPreprocessor(); + DumpModuleInfoListener Listener(Out); + HeaderSearchOptions &HSOpts = + PP.getHeaderSearchInfo().getHeaderSearchOpts(); + ASTReader::readASTFileControlBlock( + getCurrentFile(), FileMgr, getCompilerInstance().getPCHContainerReader(), + /*FindModuleFileExtensions=*/true, Listener, + HSOpts.ModulesValidateDiagnosticOptions); +} + +//===----------------------------------------------------------------------===// +// 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 PreprocessOnlyAction::ExecuteAction() { + Preprocessor &PP = getCompilerInstance().getPreprocessor(); + + // Ignore unknown pragmas. + PP.IgnorePragmas(); + + 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; + } + } + + std::unique_ptr<raw_ostream> OS = + CI.createDefaultOutputFile(BinaryMode, getCurrentFileOrBufferName()); + if (!OS) return; + + // If we're preprocessing a module map, start by dumping the contents of the + // module itself before switching to the input buffer. + auto &Input = getCurrentInput(); + if (Input.getKind().getFormat() == InputKind::ModuleMap) { + if (Input.isFile()) { + (*OS) << "# 1 \""; + OS->write_escaped(Input.getFile()); + (*OS) << "\"\n"; + } + getCurrentModule()->print(*OS); + (*OS) << "#pragma clang module contents\n"; + } + + DoPrintPreprocessedInput(CI.getPreprocessor(), OS.get(), + CI.getPreprocessorOutputOpts()); +} + +void PrintPreambleAction::ExecuteAction() { + switch (getCurrentFileKind().getLanguage()) { + case InputKind::C: + case InputKind::CXX: + case InputKind::ObjC: + case InputKind::ObjCXX: + case InputKind::OpenCL: + case InputKind::CUDA: + case InputKind::HIP: + break; + + case InputKind::Unknown: + case InputKind::Asm: + case InputKind::LLVM_IR: + case InputKind::RenderScript: + // We can't do anything with these. + return; + } + + // We don't expect to find any #include directives in a preprocessed input. + if (getCurrentFileKind().isPreprocessed()) + return; + + CompilerInstance &CI = getCompilerInstance(); + auto Buffer = CI.getFileManager().getBufferForFile(getCurrentFile()); + if (Buffer) { + unsigned Preamble = + Lexer::ComputePreamble((*Buffer)->getBuffer(), CI.getLangOpts()).Size; + llvm::outs().write((*Buffer)->getBufferStart(), Preamble); + } +} + +void DumpCompilerOptionsAction::ExecuteAction() { + CompilerInstance &CI = getCompilerInstance(); + std::unique_ptr<raw_ostream> OSP = + CI.createDefaultOutputFile(false, getCurrentFile()); + if (!OSP) + return; + + raw_ostream &OS = *OSP; + const Preprocessor &PP = CI.getPreprocessor(); + const LangOptions &LangOpts = PP.getLangOpts(); + + // FIXME: Rather than manually format the JSON (which is awkward due to + // needing to remove trailing commas), this should make use of a JSON library. + // FIXME: Instead of printing enums as an integral value and specifying the + // type as a separate field, use introspection to print the enumerator. + + OS << "{\n"; + OS << "\n\"features\" : [\n"; + { + llvm::SmallString<128> Str; +#define FEATURE(Name, Predicate) \ + ("\t{\"" #Name "\" : " + llvm::Twine(Predicate ? "true" : "false") + "},\n") \ + .toVector(Str); +#include "clang/Basic/Features.def" +#undef FEATURE + // Remove the newline and comma from the last entry to ensure this remains + // valid JSON. + OS << Str.substr(0, Str.size() - 2); + } + OS << "\n],\n"; + + OS << "\n\"extensions\" : [\n"; + { + llvm::SmallString<128> Str; +#define EXTENSION(Name, Predicate) \ + ("\t{\"" #Name "\" : " + llvm::Twine(Predicate ? "true" : "false") + "},\n") \ + .toVector(Str); +#include "clang/Basic/Features.def" +#undef EXTENSION + // Remove the newline and comma from the last entry to ensure this remains + // valid JSON. + OS << Str.substr(0, Str.size() - 2); + } + OS << "\n]\n"; + + OS << "}"; +} 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 000000000000..0744d447e816 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/FrontendOptions.cpp @@ -0,0 +1,35 @@ +//===- 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", InputKind(InputKind::Unknown, InputKind::Precompiled)) + .Case("c", InputKind::C) + .Cases("S", "s", InputKind::Asm) + .Case("i", InputKind(InputKind::C).getPreprocessed()) + .Case("ii", InputKind(InputKind::CXX).getPreprocessed()) + .Case("cui", InputKind(InputKind::CUDA).getPreprocessed()) + .Case("m", InputKind::ObjC) + .Case("mi", InputKind(InputKind::ObjC).getPreprocessed()) + .Cases("mm", "M", InputKind::ObjCXX) + .Case("mii", InputKind(InputKind::ObjCXX).getPreprocessed()) + .Cases("C", "cc", "cp", InputKind::CXX) + .Cases("cpp", "CPP", "c++", "cxx", "hpp", InputKind::CXX) + .Case("cppm", InputKind::CXX) + .Case("iim", InputKind(InputKind::CXX).getPreprocessed()) + .Case("cl", InputKind::OpenCL) + .Case("cu", InputKind::CUDA) + .Cases("ll", "bc", InputKind::LLVM_IR) + .Default(InputKind::Unknown); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/FrontendTiming.cpp b/contrib/llvm/tools/clang/lib/Frontend/FrontendTiming.cpp new file mode 100644 index 000000000000..9ea7347e7797 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/FrontendTiming.cpp @@ -0,0 +1,20 @@ +//===- FronendTiming.cpp - Implements Frontend timing utils --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file keps implementation of frontend timing utils. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/Utils.h" + +namespace clang { + +bool FrontendTimesIsEnabled = false; + +} 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 000000000000..9dc107c9d546 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/HeaderIncludeGen.cpp @@ -0,0 +1,180 @@ +//===--- 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/DependencyOutputOptions.h" +#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; + const DependencyOutputOptions &DepOpts; + unsigned CurrentIncludeDepth; + bool HasProcessedPredefines; + bool OwnsOutputFile; + bool ShowAllHeaders; + bool ShowDepth; + bool MSStyle; + +public: + HeaderIncludesCallback(const Preprocessor *PP, bool ShowAllHeaders_, + raw_ostream *OutputFile_, + const DependencyOutputOptions &DepOpts, + bool OwnsOutputFile_, bool ShowDepth_, bool MSStyle_) + : SM(PP->getSourceManager()), OutputFile(OutputFile_), DepOpts(DepOpts), + CurrentIncludeDepth(0), HasProcessedPredefines(false), + OwnsOutputFile(OwnsOutputFile_), ShowAllHeaders(ShowAllHeaders_), + ShowDepth(ShowDepth_), MSStyle(MSStyle_) {} + + ~HeaderIncludesCallback() override { + if (OwnsOutputFile) + delete OutputFile; + } + + void FileChanged(SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, + FileID PrevFID) override; +}; +} + +static void PrintHeaderInfo(raw_ostream *OutputFile, StringRef Filename, + bool ShowDepth, unsigned CurrentIncludeDepth, + bool MSStyle) { + // Write to a temporary string to avoid unnecessary flushing on errs(). + SmallString<512> Pathname(Filename); + if (!MSStyle) + Lexer::Stringify(Pathname); + + 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 += Pathname; + Msg += '\n'; + + *OutputFile << Msg; + OutputFile->flush(); +} + +void clang::AttachHeaderIncludeGen(Preprocessor &PP, + const DependencyOutputOptions &DepOpts, + bool ShowAllHeaders, StringRef OutputPath, + bool ShowDepth, bool MSStyle) { + raw_ostream *OutputFile = &llvm::errs(); + bool OwnsOutputFile = false; + + // Choose output stream, when printing in cl.exe /showIncludes style. + if (MSStyle) { + switch (DepOpts.ShowIncludesDest) { + default: + llvm_unreachable("Invalid destination for /showIncludes output!"); + case ShowIncludesDestination::Stderr: + OutputFile = &llvm::errs(); + break; + case ShowIncludesDestination::Stdout: + OutputFile = &llvm::outs(); + break; + } + } + + // Open the output file, if used. + if (!OutputPath.empty()) { + std::error_code EC; + llvm::raw_fd_ostream *OS = new llvm::raw_fd_ostream( + OutputPath.str(), EC, llvm::sys::fs::F_Append | llvm::sys::fs::F_Text); + if (EC) { + PP.getDiagnostics().Report(clang::diag::warn_fe_cc_print_header_failure) + << EC.message(); + delete OS; + } else { + OS->SetUnbuffered(); + OutputFile = OS; + OwnsOutputFile = true; + } + } + + // Print header info for extra headers, pretending they were discovered by + // the regular preprocessor. The primary use case is to support proper + // generation of Make / Ninja file dependencies for implicit includes, such + // as sanitizer blacklists. It's only important for cl.exe compatibility, + // the GNU way to generate rules is -M / -MM / -MD / -MMD. + for (const auto &Header : DepOpts.ExtraDeps) + PrintHeaderInfo(OutputFile, Header, ShowDepth, 2, MSStyle); + PP.addPPCallbacks(llvm::make_unique<HeaderIncludesCallback>( + &PP, ShowAllHeaders, OutputFile, DepOpts, 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) { + if (!DepOpts.ShowIncludesPretendHeader.empty()) { + PrintHeaderInfo(OutputFile, DepOpts.ShowIncludesPretendHeader, + ShowDepth, 2, MSStyle); + } + 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)); + unsigned IncludeDepth = CurrentIncludeDepth; + if (!HasProcessedPredefines) + --IncludeDepth; // Ignore indent from <built-in>. + else if (!DepOpts.ShowIncludesPretendHeader.empty()) + ++IncludeDepth; // Pretend inclusion by ShowIncludesPretendHeader. + + // Dump the header include information we are past the predefines buffer or + // are showing all headers and this isn't the magic implicit <command line> + // header. + // FIXME: Identify headers in a more robust way than comparing their name to + // "<command line>" and "<built-in>" in a bunch of places. + if (ShowHeader && Reason == PPCallbacks::EnterFile && + UserLoc.getFilename() != StringRef("<command line>")) { + PrintHeaderInfo(OutputFile, UserLoc.getFilename(), ShowDepth, IncludeDepth, + MSStyle); + } +} 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 000000000000..ac3bb713ddcc --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/InitHeaderSearch.cpp @@ -0,0 +1,681 @@ +//===--- 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/Basic/FileManager.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Config/config.h" // C_INCLUDE_DIRS +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/Utils.h" +#include "clang/Lex/HeaderMap.h" +#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/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. + /// Returns true if the path exists, false if it was ignored. + bool AddPath(const Twine &Path, IncludeDirGroup Group, bool isFramework); + + /// AddUnmappedPath - Add the specified path to the specified group list, + /// without performing any sysroot remapping. + /// Returns true if the path exists, false if it was ignored. + bool 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.emplace_back(Prefix, IsSystemHeader); + } + + /// AddGnuCPlusPlusIncludePaths - Add the necessary paths to support a gnu + /// libstdc++. + /// Returns true if the \p Base path was found, false if it does not exist. + bool 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); + + // 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 LangOptions &LangOpts, + 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 +} + +bool 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)) { + return AddUnmappedPath(IncludeSysroot + Path, Group, isFramework); + } + } + + return AddUnmappedPath(Path, Group, isFramework); +} + +bool 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 true; + } + + // 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 true; + } + } + } + + if (Verbose) + llvm::errs() << "ignoring nonexistent directory \"" + << MappedPathStr << "\"\n"; + return false; +} + +bool InitHeaderSearch::AddGnuCPlusPlusIncludePaths(StringRef Base, + StringRef ArchDir, + StringRef Dir32, + StringRef Dir64, + const llvm::Triple &triple) { + // Add the base dir + bool IsBaseFound = 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); + return IsBaseFound; +} + +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::AddDefaultCIncludePaths(const llvm::Triple &triple, + const HeaderSearchOptions &HSOpts) { + llvm::Triple::OSType os = triple.getOS(); + + if (HSOpts.UseStandardSystemIncludes) { + switch (os) { + case llvm::Triple::CloudABI: + case llvm::Triple::FreeBSD: + case llvm::Triple::NetBSD: + case llvm::Triple::OpenBSD: + case llvm::Triple::NaCl: + case llvm::Triple::PS4: + case llvm::Triple::ELFIAMCU: + case llvm::Triple::Fuchsia: + break; + case llvm::Triple::Win32: + if (triple.getEnvironment() != llvm::Triple::Cygnus) + break; + LLVM_FALLTHROUGH; + 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, 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 (StringRef dir : dirs) + AddPath(dir, ExternCSystem, false); + return; + } + + switch (os) { + case llvm::Triple::Linux: + case llvm::Triple::Hurd: + case llvm::Triple::Solaris: + llvm_unreachable("Include management is handled in the driver."); + + case llvm::Triple::CloudABI: { + // <sysroot>/<triple>/include + SmallString<128> P = StringRef(HSOpts.ResourceDir); + llvm::sys::path::append(P, "../../..", triple.str(), "include"); + AddPath(P, System, false); + break; + } + + case llvm::Triple::Haiku: + AddPath("/boot/system/non-packaged/develop/headers", System, false); + AddPath("/boot/system/develop/headers/os", System, false); + AddPath("/boot/system/develop/headers/os/app", System, false); + AddPath("/boot/system/develop/headers/os/arch", System, false); + AddPath("/boot/system/develop/headers/os/device", System, false); + AddPath("/boot/system/develop/headers/os/drivers", System, false); + AddPath("/boot/system/develop/headers/os/game", System, false); + AddPath("/boot/system/develop/headers/os/interface", System, false); + AddPath("/boot/system/develop/headers/os/kernel", System, false); + AddPath("/boot/system/develop/headers/os/locale", System, false); + AddPath("/boot/system/develop/headers/os/mail", System, false); + AddPath("/boot/system/develop/headers/os/media", System, false); + AddPath("/boot/system/develop/headers/os/midi", System, false); + AddPath("/boot/system/develop/headers/os/midi2", System, false); + AddPath("/boot/system/develop/headers/os/net", System, false); + AddPath("/boot/system/develop/headers/os/opengl", System, false); + AddPath("/boot/system/develop/headers/os/storage", System, false); + AddPath("/boot/system/develop/headers/os/support", System, false); + AddPath("/boot/system/develop/headers/os/translation", System, false); + AddPath("/boot/system/develop/headers/os/add-ons/graphics", System, false); + AddPath("/boot/system/develop/headers/os/add-ons/input_server", System, false); + AddPath("/boot/system/develop/headers/os/add-ons/mail_daemon", System, false); + AddPath("/boot/system/develop/headers/os/add-ons/registrar", System, false); + AddPath("/boot/system/develop/headers/os/add-ons/screen_saver", System, false); + AddPath("/boot/system/develop/headers/os/add-ons/tracker", System, false); + AddPath("/boot/system/develop/headers/os/be_apps/Deskbar", System, false); + AddPath("/boot/system/develop/headers/os/be_apps/NetPositive", System, false); + AddPath("/boot/system/develop/headers/os/be_apps/Tracker", System, false); + AddPath("/boot/system/develop/headers/3rdparty", System, false); + AddPath("/boot/system/develop/headers/bsd", System, false); + AddPath("/boot/system/develop/headers/glibc", System, false); + AddPath("/boot/system/develop/headers/posix", System, false); + AddPath("/boot/system/develop/headers", System, false); + break; + case llvm::Triple::RTEMS: + break; + case llvm::Triple::Win32: + switch (triple.getEnvironment()) { + default: llvm_unreachable("Include management is handled in the driver."); + case llvm::Triple::Cygnus: + AddPath("/usr/include/w32api", System, false); + break; + case llvm::Triple::GNU: + break; + } + break; + default: + break; + } + + switch (os) { + case llvm::Triple::CloudABI: + case llvm::Triple::RTEMS: + case llvm::Triple::NaCl: + case llvm::Triple::ELFIAMCU: + case llvm::Triple::Fuchsia: + break; + case llvm::Triple::PS4: { + // <isysroot> gets prepended later in AddPath(). + std::string BaseSDKPath = ""; + if (!HasSysroot) { + const char *envValue = getenv("SCE_ORBIS_SDK_DIR"); + if (envValue) + BaseSDKPath = envValue; + else { + // HSOpts.ResourceDir variable contains the location of Clang's + // resource files. + // Assuming that Clang is configured for PS4 without + // --with-clang-resource-dir option, the location of Clang's resource + // files is <SDK_DIR>/host_tools/lib/clang + SmallString<128> P = StringRef(HSOpts.ResourceDir); + llvm::sys::path::append(P, "../../.."); + BaseSDKPath = P.str(); + } + } + AddPath(BaseSDKPath + "/target/include", System, false); + if (triple.isPS4CPU()) + AddPath(BaseSDKPath + "/target/include_common", System, false); + LLVM_FALLTHROUGH; + } + default: + AddPath("/usr/include", ExternCSystem, false); + break; + } +} + +void InitHeaderSearch::AddDefaultCPlusPlusIncludePaths( + const LangOptions &LangOpts, const llvm::Triple &triple, + const HeaderSearchOptions &HSOpts) { + llvm::Triple::OSType os = triple.getOS(); + // FIXME: temporary hack: hard-coded paths. + + if (triple.isOSDarwin()) { + bool IsBaseFound = true; + switch (triple.getArch()) { + default: break; + + case llvm::Triple::ppc: + case llvm::Triple::ppc64: + IsBaseFound = AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.2.1", + "powerpc-apple-darwin10", "", + "ppc64", triple); + IsBaseFound |= AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.0.0", + "powerpc-apple-darwin10", "", + "ppc64", triple); + break; + + case llvm::Triple::x86: + case llvm::Triple::x86_64: + IsBaseFound = AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.2.1", + "i686-apple-darwin10", "", + "x86_64", triple); + IsBaseFound |= AddGnuCPlusPlusIncludePaths( + "/usr/include/c++/4.0.0", "i686-apple-darwin8", "", "", triple); + break; + + case llvm::Triple::arm: + case llvm::Triple::thumb: + IsBaseFound = AddGnuCPlusPlusIncludePaths( + "/usr/include/c++/4.2.1", "arm-apple-darwin10", "v7", "", triple); + IsBaseFound |= AddGnuCPlusPlusIncludePaths( + "/usr/include/c++/4.2.1", "arm-apple-darwin10", "v6", "", triple); + break; + + case llvm::Triple::aarch64: + IsBaseFound = AddGnuCPlusPlusIncludePaths( + "/usr/include/c++/4.2.1", "arm64-apple-darwin10", "", "", triple); + break; + } + // Warn when compiling pure C++ / Objective-C++ only. + if (!IsBaseFound && + !(LangOpts.CUDA || LangOpts.OpenCL || LangOpts.RenderScript)) { + Headers.getDiags().Report(SourceLocation(), + diag::warn_stdlibcxx_not_found); + } + return; + } + + switch (os) { + case llvm::Triple::Linux: + case llvm::Triple::Hurd: + case llvm::Triple::Solaris: + llvm_unreachable("Include management is handled in the driver."); + break; + case llvm::Triple::Win32: + switch (triple.getEnvironment()) { + default: llvm_unreachable("Include management is handled in the driver."); + case llvm::Triple::Cygnus: + // 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; + } + break; + case llvm::Triple::DragonFly: + AddPath("/usr/include/c++/5.0", CXXSystem, false); + 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; + 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::Hurd: + case llvm::Triple::Solaris: + return; + + case llvm::Triple::Win32: + if (triple.getEnvironment() != llvm::Triple::Cygnus || + triple.isOSBinFormatMachO()) + return; + break; + } + + if (Lang.CPlusPlus && !Lang.AsmPreprocessor && + HSOpts.UseStandardCXXIncludes && HSOpts.UseStandardSystemIncludes) { + if (HSOpts.UseLibcxx) { + AddPath("/usr/include/c++/v1", CXXSystem, false); + } else { + AddDefaultCPlusPlusIncludePaths(Lang, 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()).second) + 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()).second) + 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()).second) + 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 = First;; ++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 (auto &Include : IncludePath) + if (Include.first == Quoted) + SearchList.push_back(Include.second); + + // Deduplicate and remember index. + RemoveDuplicates(SearchList, 0, Verbose); + unsigned NumQuoted = SearchList.size(); + + for (auto &Include : IncludePath) + if (Include.first == Angled || Include.first == IndexHeaderMap) + SearchList.push_back(Include.second); + + RemoveDuplicates(SearchList, NumQuoted, Verbose); + unsigned NumAngled = SearchList.size(); + + for (auto &Include : IncludePath) + if (Include.first == System || Include.first == ExternCSystem || + (!Lang.ObjC && !Lang.CPlusPlus && Include.first == CSystem) || + (/*FIXME !Lang.ObjC && */ Lang.CPlusPlus && + Include.first == CXXSystem) || + (Lang.ObjC && !Lang.CPlusPlus && Include.first == ObjCSystem) || + (Lang.ObjC && Lang.CPlusPlus && Include.first == ObjCXXSystem)) + SearchList.push_back(Include.second); + + for (auto &Include : IncludePath) + if (Include.first == After) + SearchList.push_back(Include.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"; + StringRef 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)) + 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 000000000000..66807b097d40 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/InitPreprocessor.cpp @@ -0,0 +1,1176 @@ +//===--- InitPreprocessor.cpp - PP initialization code. ---------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the clang::InitializePreprocessor function. +// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/FileManager.h" +#include "clang/Basic/MacroBuilder.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/SyncScope.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Basic/Version.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/FrontendOptions.h" +#include "clang/Frontend/Utils.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" +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. +/// As these includes are generated by -include arguments the header search +/// logic is going to search relatively to the current working directory. +static void AddImplicitInclude(MacroBuilder &Builder, StringRef File) { + Builder.append(Twine("#include \"") + File + "\""); +} + +static void AddImplicitIncludeMacros(MacroBuilder &Builder, StringRef File) { + Builder.append(Twine("#__include_macros \"") + File + "\""); + // Marker token to stop the __include_macros fetch loop. + Builder.append("##"); // ##? +} + +/// Add an implicit \#include using the original file used to generate +/// a PCH file. +static void AddImplicitIncludePCH(MacroBuilder &Builder, Preprocessor &PP, + const PCHContainerReader &PCHContainerRdr, + StringRef ImplicitIncludePCH) { + std::string OriginalFile = + ASTReader::getOriginalSourceFile(ImplicitIncludePCH, PP.getFileManager(), + PCHContainerRdr, PP.getDiagnostics()); + if (OriginalFile.empty()) + return; + + AddImplicitInclude(Builder, OriginalFile); +} + +/// PickFP - This is used to pick a value based on the FP semantics of the +/// specified FP model. +template <typename T> +static T PickFP(const llvm::fltSemantics *Sem, T IEEEHalfVal, T IEEESingleVal, + T IEEEDoubleVal, T X87DoubleExtendedVal, T PPCDoubleDoubleVal, + T IEEEQuadVal) { + if (Sem == (const llvm::fltSemantics*)&llvm::APFloat::IEEEhalf()) + return IEEEHalfVal; + 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, "5.9604644775390625e-8", "1.40129846e-45", + "4.9406564584124654e-324", "3.64519953188247460253e-4951", + "4.94065645841246544176568792868221e-324", + "6.47517511943802511092443895822764655e-4966"); + int Digits = PickFP(Sem, 3, 6, 15, 18, 31, 33); + int DecimalDigits = PickFP(Sem, 5, 9, 17, 21, 33, 36); + Epsilon = PickFP(Sem, "9.765625e-4", "1.19209290e-7", + "2.2204460492503131e-16", "1.08420217248550443401e-19", + "4.94065645841246544176568792868221e-324", + "1.92592994438723585305597794258492732e-34"); + int MantissaDigits = PickFP(Sem, 11, 24, 53, 64, 106, 113); + int Min10Exp = PickFP(Sem, -13, -37, -307, -4931, -291, -4931); + int Max10Exp = PickFP(Sem, 4, 38, 308, 4932, 308, 4932); + int MinExp = PickFP(Sem, -14, -125, -1021, -16381, -968, -16381); + int MaxExp = PickFP(Sem, 15, 128, 1024, 16384, 1024, 16384); + Min = PickFP(Sem, "6.103515625e-5", "1.17549435e-38", "2.2250738585072014e-308", + "3.36210314311209350626e-4932", + "2.00416836000897277799610805135016e-292", + "3.36210314311209350626267781732175260e-4932"); + Max = PickFP(Sem, "6.5504e+4", "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 + "DECIMAL_DIG__", Twine(DecimalDigits)); + 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(const Twine &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(const Twine &MacroName, TargetInfo::IntType Ty, + const TargetInfo &TI, MacroBuilder &Builder) { + DefineTypeSize(MacroName, TI.getTypeWidth(Ty), TI.getTypeConstantSuffix(Ty), + TI.isTypeSigned(Ty), Builder); +} + +static void DefineFmt(const Twine &Prefix, TargetInfo::IntType Ty, + const TargetInfo &TI, MacroBuilder &Builder) { + bool IsSigned = TI.isTypeSigned(Ty); + StringRef FmtModifier = TI.getTypeFormatModifier(Ty); + for (const char *Fmt = IsSigned ? "di" : "ouxX"; *Fmt; ++Fmt) { + Builder.defineMacro(Prefix + "_FMT" + Twine(*Fmt) + "__", + Twine("\"") + FmtModifier + Twine(*Fmt) + "\""); + } +} + +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); + bool IsSigned = TI.isTypeSigned(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 = IsSigned ? TI.getInt64Type() : TI.getUInt64Type(); + + const char *Prefix = IsSigned ? "__INT" : "__UINT"; + + DefineType(Prefix + Twine(TypeWidth) + "_TYPE__", Ty, Builder); + DefineFmt(Prefix + Twine(TypeWidth), Ty, TI, Builder); + + StringRef ConstSuffix(TI.getTypeConstantSuffix(Ty)); + Builder.defineMacro(Prefix + Twine(TypeWidth) + "_C_SUFFIX__", ConstSuffix); +} + +static void DefineExactWidthIntTypeSize(TargetInfo::IntType Ty, + const TargetInfo &TI, + MacroBuilder &Builder) { + int TypeWidth = TI.getTypeWidth(Ty); + bool IsSigned = TI.isTypeSigned(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 = IsSigned ? TI.getInt64Type() : TI.getUInt64Type(); + + const char *Prefix = IsSigned ? "__INT" : "__UINT"; + DefineTypeSize(Prefix + Twine(TypeWidth) + "_MAX__", Ty, TI, Builder); +} + +static void DefineLeastWidthIntType(unsigned TypeWidth, bool IsSigned, + const TargetInfo &TI, + MacroBuilder &Builder) { + TargetInfo::IntType Ty = TI.getLeastIntTypeByWidth(TypeWidth, IsSigned); + if (Ty == TargetInfo::NoInt) + return; + + const char *Prefix = IsSigned ? "__INT_LEAST" : "__UINT_LEAST"; + DefineType(Prefix + Twine(TypeWidth) + "_TYPE__", Ty, Builder); + DefineTypeSize(Prefix + Twine(TypeWidth) + "_MAX__", Ty, TI, Builder); + DefineFmt(Prefix + Twine(TypeWidth), Ty, TI, Builder); +} + +static void DefineFastIntType(unsigned TypeWidth, bool IsSigned, + const TargetInfo &TI, MacroBuilder &Builder) { + // stdint.h currently defines the fast int types as equivalent to the least + // types. + TargetInfo::IntType Ty = TI.getLeastIntTypeByWidth(TypeWidth, IsSigned); + if (Ty == TargetInfo::NoInt) + return; + + const char *Prefix = IsSigned ? "__INT_FAST" : "__UINT_FAST"; + DefineType(Prefix + Twine(TypeWidth) + "_TYPE__", Ty, Builder); + DefineTypeSize(Prefix + Twine(TypeWidth) + "_MAX__", Ty, TI, Builder); + + DefineFmt(Prefix + Twine(TypeWidth), Ty, TI, Builder); +} + + +/// 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" +} + +/// 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"; + + if (LangOpts.ObjCAutoRefCount) { + 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.ObjCWeak) { + 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"; + } + + if (LangOpts.ObjCAutoRefCount) { + 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.MSVCCompat && !LangOpts.TraditionalCPP) + Builder.defineMacro("__STDC__"); + if (LangOpts.Freestanding) + Builder.defineMacro("__STDC_HOSTED__", "0"); + else + Builder.defineMacro("__STDC_HOSTED__"); + + if (!LangOpts.CPlusPlus) { + if (LangOpts.C17) + Builder.defineMacro("__STDC_VERSION__", "201710L"); + else 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 correct value for C++20. + if (LangOpts.CPlusPlus2a) + Builder.defineMacro("__cplusplus", "201707L"); + // C++17 [cpp.predefined]p1: + // The name __cplusplus is defined to the value 201703L when compiling a + // C++ translation unit. + else if (LangOpts.CPlusPlus17) + Builder.defineMacro("__cplusplus", "201703L"); + // C++1y [cpp.predefined]p1: + // The name __cplusplus is defined to the value 201402L when compiling a + // C++ translation unit. + else if (LangOpts.CPlusPlus14) + Builder.defineMacro("__cplusplus", "201402L"); + // 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"); + + // C++1z [cpp.predefined]p1: + // An integer literal of type std::size_t whose value is the alignment + // guaranteed by a call to operator new(std::size_t) + // + // We provide this in all language modes, since it seems generally useful. + Builder.defineMacro("__STDCPP_DEFAULT_NEW_ALIGNMENT__", + Twine(TI.getNewAlign() / TI.getCharWidth()) + + TI.getTypeConstantSuffix(TI.getSizeType())); + } + + // 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.ObjC) + Builder.defineMacro("__OBJC__"); + + // OpenCL v1.0/1.1 s6.9, v1.2/2.0 s6.10: Preprocessor Directives and Macros. + if (LangOpts.OpenCL) { + if (LangOpts.CPlusPlus) { + if (LangOpts.OpenCLCPlusPlusVersion == 100) + Builder.defineMacro("__OPENCL_CPP_VERSION__", "100"); + else + llvm_unreachable("Unsupported OpenCL C++ version"); + Builder.defineMacro("__CL_CPP_VERSION_1_0__", "100"); + } else { + // OpenCL v1.0 and v1.1 do not have a predefined macro to indicate the + // language standard with which the program is compiled. __OPENCL_VERSION__ + // is for the OpenCL version supported by the OpenCL device, which is not + // necessarily the language standard with which the program is compiled. + // A shared OpenCL header file requires a macro to indicate the language + // standard. As a workaround, __OPENCL_C_VERSION__ is defined for + // OpenCL v1.0 and v1.1. + switch (LangOpts.OpenCLVersion) { + case 100: + Builder.defineMacro("__OPENCL_C_VERSION__", "100"); + break; + case 110: + Builder.defineMacro("__OPENCL_C_VERSION__", "110"); + break; + case 120: + Builder.defineMacro("__OPENCL_C_VERSION__", "120"); + break; + case 200: + Builder.defineMacro("__OPENCL_C_VERSION__", "200"); + break; + default: + llvm_unreachable("Unsupported OpenCL version"); + } + Builder.defineMacro("CL_VERSION_1_0", "100"); + Builder.defineMacro("CL_VERSION_1_1", "110"); + Builder.defineMacro("CL_VERSION_1_2", "120"); + Builder.defineMacro("CL_VERSION_2_0", "200"); + + if (TI.isLittleEndian()) + Builder.defineMacro("__ENDIAN_LITTLE__"); + + if (LangOpts.FastRelaxedMath) + Builder.defineMacro("__FAST_RELAXED_MATH__"); + } + } + // Not "standard" per se, but available even with the -undef flag. + if (LangOpts.AsmPreprocessor) + Builder.defineMacro("__ASSEMBLER__"); + if (LangOpts.CUDA && !LangOpts.HIP) + Builder.defineMacro("__CUDA__"); + if (LangOpts.HIP) { + Builder.defineMacro("__HIP__"); + Builder.defineMacro("__HIPCC__"); + if (LangOpts.CUDAIsDevice) + Builder.defineMacro("__HIP_DEVICE_COMPILE__"); + } +} + +/// 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++98 features. + if (LangOpts.RTTI) + Builder.defineMacro("__cpp_rtti", "199711L"); + if (LangOpts.CXXExceptions) + Builder.defineMacro("__cpp_exceptions", "199711L"); + + // C++11 features. + if (LangOpts.CPlusPlus11) { + Builder.defineMacro("__cpp_unicode_characters", "200704L"); + Builder.defineMacro("__cpp_raw_strings", "200710L"); + Builder.defineMacro("__cpp_unicode_literals", "200710L"); + Builder.defineMacro("__cpp_user_defined_literals", "200809L"); + Builder.defineMacro("__cpp_lambdas", "200907L"); + Builder.defineMacro("__cpp_constexpr", + LangOpts.CPlusPlus17 ? "201603L" : + LangOpts.CPlusPlus14 ? "201304L" : "200704"); + Builder.defineMacro("__cpp_range_based_for", + LangOpts.CPlusPlus17 ? "201603L" : "200907"); + Builder.defineMacro("__cpp_static_assert", + LangOpts.CPlusPlus17 ? "201411L" : "200410"); + Builder.defineMacro("__cpp_decltype", "200707L"); + Builder.defineMacro("__cpp_attributes", "200809L"); + Builder.defineMacro("__cpp_rvalue_references", "200610L"); + Builder.defineMacro("__cpp_variadic_templates", "200704L"); + Builder.defineMacro("__cpp_initializer_lists", "200806L"); + Builder.defineMacro("__cpp_delegating_constructors", "200604L"); + Builder.defineMacro("__cpp_nsdmi", "200809L"); + Builder.defineMacro("__cpp_inheriting_constructors", "201511L"); + Builder.defineMacro("__cpp_ref_qualifiers", "200710L"); + Builder.defineMacro("__cpp_alias_templates", "200704L"); + } + if (LangOpts.ThreadsafeStatics) + Builder.defineMacro("__cpp_threadsafe_static_init", "200806L"); + + // C++14 features. + if (LangOpts.CPlusPlus14) { + Builder.defineMacro("__cpp_binary_literals", "201304L"); + Builder.defineMacro("__cpp_digit_separators", "201309L"); + Builder.defineMacro("__cpp_init_captures", "201304L"); + Builder.defineMacro("__cpp_generic_lambdas", "201304L"); + Builder.defineMacro("__cpp_decltype_auto", "201304L"); + Builder.defineMacro("__cpp_return_type_deduction", "201304L"); + Builder.defineMacro("__cpp_aggregate_nsdmi", "201304L"); + Builder.defineMacro("__cpp_variable_templates", "201304L"); + } + if (LangOpts.SizedDeallocation) + Builder.defineMacro("__cpp_sized_deallocation", "201309L"); + + // C++17 features. + if (LangOpts.CPlusPlus17) { + Builder.defineMacro("__cpp_hex_float", "201603L"); + Builder.defineMacro("__cpp_inline_variables", "201606L"); + Builder.defineMacro("__cpp_noexcept_function_type", "201510L"); + Builder.defineMacro("__cpp_capture_star_this", "201603L"); + Builder.defineMacro("__cpp_if_constexpr", "201606L"); + Builder.defineMacro("__cpp_deduction_guides", "201703L"); + Builder.defineMacro("__cpp_template_auto", "201606L"); // (old name) + Builder.defineMacro("__cpp_namespace_attributes", "201411L"); + Builder.defineMacro("__cpp_enumerator_attributes", "201411L"); + Builder.defineMacro("__cpp_nested_namespace_definitions", "201411L"); + Builder.defineMacro("__cpp_variadic_using", "201611L"); + Builder.defineMacro("__cpp_aggregate_bases", "201603L"); + Builder.defineMacro("__cpp_structured_bindings", "201606L"); + Builder.defineMacro("__cpp_nontype_template_args", "201411L"); + Builder.defineMacro("__cpp_fold_expressions", "201603L"); + Builder.defineMacro("__cpp_guaranteed_copy_elision", "201606L"); + Builder.defineMacro("__cpp_nontype_template_parameter_auto", "201606L"); + } + if (LangOpts.AlignedAllocation && !LangOpts.AlignedAllocationUnavailable) + Builder.defineMacro("__cpp_aligned_new", "201606L"); + if (LangOpts.RelaxedTemplateTemplateArgs) + Builder.defineMacro("__cpp_template_template_args", "201611L"); + + // C++20 features. + if (LangOpts.Char8) + Builder.defineMacro("__cpp_char8_t", "201811L"); + Builder.defineMacro("__cpp_impl_destroying_delete", "201806L"); + + // TS features. + if (LangOpts.ConceptsTS) + Builder.defineMacro("__cpp_experimental_concepts", "1L"); + if (LangOpts.CoroutinesTS) + Builder.defineMacro("__cpp_coroutines", "201703L"); +} + +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)); + Builder.defineMacro("__clang_patchlevel__", TOSTR(CLANG_VERSION_PATCHLEVEL)); +#undef TOSTR +#undef TOSTR2 + Builder.defineMacro("__clang_version__", + "\"" CLANG_VERSION_STRING " " + + getClangFullRepositoryVersion() + "\""); + if (!LangOpts.MSVCCompat) { + // 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"); + + // Define macros for the OpenCL memory scope. + // The values should match AtomicScopeOpenCLModel::ID enum. + static_assert( + static_cast<unsigned>(AtomicScopeOpenCLModel::WorkGroup) == 1 && + static_cast<unsigned>(AtomicScopeOpenCLModel::Device) == 2 && + static_cast<unsigned>(AtomicScopeOpenCLModel::AllSVMDevices) == 3 && + static_cast<unsigned>(AtomicScopeOpenCLModel::SubGroup) == 4, + "Invalid OpenCL memory scope enum definition"); + Builder.defineMacro("__OPENCL_MEMORY_SCOPE_WORK_ITEM", "0"); + Builder.defineMacro("__OPENCL_MEMORY_SCOPE_WORK_GROUP", "1"); + Builder.defineMacro("__OPENCL_MEMORY_SCOPE_DEVICE", "2"); + Builder.defineMacro("__OPENCL_MEMORY_SCOPE_ALL_SVM_DEVICES", "3"); + Builder.defineMacro("__OPENCL_MEMORY_SCOPE_SUB_GROUP", "4"); + + // 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 && !LangOpts.MSVCCompat) + Builder.defineMacro("__STRICT_ANSI__"); + + if (!LangOpts.MSVCCompat && LangOpts.CPlusPlus11) + Builder.defineMacro("__GXX_EXPERIMENTAL_CXX0X__"); + + if (LangOpts.ObjC) { + 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::GNUstep) { + auto version = LangOpts.ObjCRuntime.getVersion(); + std::string versionString = "1"; + // Don't rely on the tuple argument, because we can be asked to target + // later ABIs than we actually support, so clamp these values to those + // currently supported + if (version >= VersionTuple(2, 0)) + Builder.defineMacro("__OBJC_GNUSTEP_RUNTIME_ABI__", "20"); + else + Builder.defineMacro("__OBJC_GNUSTEP_RUNTIME_ABI__", + "1" + Twine(std::min(8U, version.getMinor().getValueOr(0)))); + } + + 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)"); + Builder.defineMacro("IBInspectable", ""); + Builder.defineMacro("IB_DESIGNABLE", ""); + } + + // Define a macro that describes the Objective-C boolean type even for C + // and C++ since BOOL can be used from non Objective-C code. + Builder.defineMacro("__OBJC_BOOL_IS_BOOL", + Twine(TI.useSignedCharForObjCBool() ? "0" : "1")); + + 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.ObjC) + 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.MSVCCompat && LangOpts.Exceptions) + Builder.defineMacro("__EXCEPTIONS"); + if (!LangOpts.MSVCCompat && LangOpts.RTTI) + Builder.defineMacro("__GXX_RTTI"); + + if (LangOpts.SjLjExceptions) + Builder.defineMacro("__USING_SJLJ_EXCEPTIONS__"); + else if (LangOpts.SEHExceptions) + Builder.defineMacro("__SEH__"); + else if (LangOpts.DWARFExceptions && + (TI.getTriple().isThumb() || TI.getTriple().isARM())) + Builder.defineMacro("__ARM_DWARF_EH__"); + + if (LangOpts.Deprecated) + Builder.defineMacro("__DEPRECATED"); + + if (!LangOpts.MSVCCompat && LangOpts.CPlusPlus) { + Builder.defineMacro("__GNUG__", "4"); + Builder.defineMacro("__GXX_WEAK__"); + Builder.defineMacro("__private_extern__", "extern"); + } + + if (LangOpts.MicrosoftExt) { + if (LangOpts.WChar) { + // wchar_t supported as a keyword. + Builder.defineMacro("_WCHAR_T_DEFINED"); + Builder.defineMacro("_NATIVE_WCHAR_T_DEFINED"); + } + } + + 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__"); + Builder.defineMacro("__BIG_ENDIAN__"); + } else { + Builder.defineMacro("__BYTE_ORDER__", "__ORDER_LITTLE_ENDIAN__"); + Builder.defineMacro("__LITTLE_ENDIAN__"); + } + + if (TI.getPointerWidth(0) == 64 && TI.getLongWidth() == 64 + && TI.getIntWidth() == 32) { + Builder.defineMacro("_LP64"); + Builder.defineMacro("__LP64__"); + } + + if (TI.getPointerWidth(0) == 32 && TI.getLongWidth() == 32 + && TI.getIntWidth() == 32) { + Builder.defineMacro("_ILP32"); + Builder.defineMacro("__ILP32__"); + } + + // Define type sizing macros based on the target properties. + assert(TI.getCharWidth() == 8 && "Only support 8-bit char so far"); + Builder.defineMacro("__CHAR_BIT__", Twine(TI.getCharWidth())); + + 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("__WINT_MAX__", TI.getWIntType(), TI, Builder); + DefineTypeSize("__INTMAX_MAX__", TI.getIntMaxType(), TI, Builder); + DefineTypeSize("__SIZE_MAX__", TI.getSizeType(), TI, Builder); + + DefineTypeSize("__UINTMAX_MAX__", TI.getUIntMaxType(), TI, Builder); + DefineTypeSize("__PTRDIFF_MAX__", TI.getPtrDiffType(0), TI, Builder); + DefineTypeSize("__INTPTR_MAX__", TI.getIntPtrType(), TI, Builder); + DefineTypeSize("__UINTPTR_MAX__", TI.getUIntPtrType(), 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); + DefineFmt("__INTMAX", TI.getIntMaxType(), TI, Builder); + Builder.defineMacro("__INTMAX_C_SUFFIX__", + TI.getTypeConstantSuffix(TI.getIntMaxType())); + DefineType("__UINTMAX_TYPE__", TI.getUIntMaxType(), Builder); + DefineFmt("__UINTMAX", TI.getUIntMaxType(), TI, Builder); + Builder.defineMacro("__UINTMAX_C_SUFFIX__", + TI.getTypeConstantSuffix(TI.getUIntMaxType())); + DefineTypeWidth("__INTMAX_WIDTH__", TI.getIntMaxType(), TI, Builder); + DefineType("__PTRDIFF_TYPE__", TI.getPtrDiffType(0), Builder); + DefineFmt("__PTRDIFF", TI.getPtrDiffType(0), TI, Builder); + DefineTypeWidth("__PTRDIFF_WIDTH__", TI.getPtrDiffType(0), TI, Builder); + DefineType("__INTPTR_TYPE__", TI.getIntPtrType(), Builder); + DefineFmt("__INTPTR", TI.getIntPtrType(), TI, Builder); + DefineTypeWidth("__INTPTR_WIDTH__", TI.getIntPtrType(), TI, Builder); + DefineType("__SIZE_TYPE__", TI.getSizeType(), Builder); + DefineFmt("__SIZE", TI.getSizeType(), TI, 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); + DefineTypeSize("__SIG_ATOMIC_MAX__", TI.getSigAtomicType(), TI, Builder); + DefineType("__CHAR16_TYPE__", TI.getChar16Type(), Builder); + DefineType("__CHAR32_TYPE__", TI.getChar32Type(), Builder); + + DefineTypeWidth("__UINTMAX_WIDTH__", TI.getUIntMaxType(), TI, Builder); + DefineType("__UINTPTR_TYPE__", TI.getUIntPtrType(), Builder); + DefineFmt("__UINTPTR", TI.getUIntPtrType(), TI, Builder); + DefineTypeWidth("__UINTPTR_WIDTH__", TI.getUIntPtrType(), TI, Builder); + + DefineFloatMacros(Builder, "FLT16", &TI.getHalfFormat(), "F16"); + 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))); + + // Define __BIGGEST_ALIGNMENT__ to be compatible with gcc. + Builder.defineMacro("__BIGGEST_ALIGNMENT__", + Twine(TI.getSuitableAlign() / TI.getCharWidth()) ); + + 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 + DefineExactWidthIntType(TargetInfo::SignedChar, TI, Builder); + + 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); + + DefineExactWidthIntType(TargetInfo::UnsignedChar, TI, Builder); + DefineExactWidthIntTypeSize(TargetInfo::UnsignedChar, TI, Builder); + DefineExactWidthIntTypeSize(TargetInfo::SignedChar, TI, Builder); + + if (TI.getShortWidth() > TI.getCharWidth()) { + DefineExactWidthIntType(TargetInfo::UnsignedShort, TI, Builder); + DefineExactWidthIntTypeSize(TargetInfo::UnsignedShort, TI, Builder); + DefineExactWidthIntTypeSize(TargetInfo::SignedShort, TI, Builder); + } + + if (TI.getIntWidth() > TI.getShortWidth()) { + DefineExactWidthIntType(TargetInfo::UnsignedInt, TI, Builder); + DefineExactWidthIntTypeSize(TargetInfo::UnsignedInt, TI, Builder); + DefineExactWidthIntTypeSize(TargetInfo::SignedInt, TI, Builder); + } + + if (TI.getLongWidth() > TI.getIntWidth()) { + DefineExactWidthIntType(TargetInfo::UnsignedLong, TI, Builder); + DefineExactWidthIntTypeSize(TargetInfo::UnsignedLong, TI, Builder); + DefineExactWidthIntTypeSize(TargetInfo::SignedLong, TI, Builder); + } + + if (TI.getLongLongWidth() > TI.getLongWidth()) { + DefineExactWidthIntType(TargetInfo::UnsignedLongLong, TI, Builder); + DefineExactWidthIntTypeSize(TargetInfo::UnsignedLongLong, TI, Builder); + DefineExactWidthIntTypeSize(TargetInfo::SignedLongLong, TI, Builder); + } + + DefineLeastWidthIntType(8, true, TI, Builder); + DefineLeastWidthIntType(8, false, TI, Builder); + DefineLeastWidthIntType(16, true, TI, Builder); + DefineLeastWidthIntType(16, false, TI, Builder); + DefineLeastWidthIntType(32, true, TI, Builder); + DefineLeastWidthIntType(32, false, TI, Builder); + DefineLeastWidthIntType(64, true, TI, Builder); + DefineLeastWidthIntType(64, false, TI, Builder); + + DefineFastIntType(8, true, TI, Builder); + DefineFastIntType(8, false, TI, Builder); + DefineFastIntType(16, true, TI, Builder); + DefineFastIntType(16, false, TI, Builder); + DefineFastIntType(32, true, TI, Builder); + DefineFastIntType(32, false, TI, Builder); + DefineFastIntType(64, true, TI, Builder); + DefineFastIntType(64, false, TI, Builder); + + char UserLabelPrefix[2] = {TI.getDataLayout().getGlobalPrefix(), 0}; + Builder.defineMacro("__USER_LABEL_PREFIX__", UserLabelPrefix); + + if (LangOpts.FastMath || LangOpts.FiniteMathOnly) + Builder.defineMacro("__FINITE_MATH_ONLY__", "1"); + else + Builder.defineMacro("__FINITE_MATH_ONLY__", "0"); + + if (!LangOpts.MSVCCompat) { + if (LangOpts.GNUInline || LangOpts.CPlusPlus) + 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"); + } + + auto addLockFreeMacros = [&](const llvm::Twine &Prefix) { + // Used by libc++ and libstdc++ to implement ATOMIC_<foo>_LOCK_FREE. + unsigned InlineWidthBits = TI.getMaxAtomicInlineWidth(); +#define DEFINE_LOCK_FREE_MACRO(TYPE, Type) \ + Builder.defineMacro(Prefix + #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); + if (LangOpts.Char8) + DEFINE_LOCK_FREE_MACRO(CHAR8_T, Char); // Treat char8_t like 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(Prefix + "POINTER_LOCK_FREE", + getLockFreeValue(TI.getPointerWidth(0), + TI.getPointerAlign(0), + InlineWidthBits)); +#undef DEFINE_LOCK_FREE_MACRO + }; + addLockFreeMacros("__CLANG_ATOMIC_"); + if (!LangOpts.MSVCCompat) + addLockFreeMacros("__GCC_ATOMIC_"); + + if (LangOpts.NoInlineDefine) + Builder.defineMacro("__NO_INLINE__"); + + if (unsigned PICLevel = LangOpts.PICLevel) { + Builder.defineMacro("__PIC__", Twine(PICLevel)); + Builder.defineMacro("__pic__", Twine(PICLevel)); + if (LangOpts.PIE) { + Builder.defineMacro("__PIE__", Twine(PICLevel)); + Builder.defineMacro("__pie__", Twine(PICLevel)); + } + } + + // Macros to control C99 numerics and <float.h> + Builder.defineMacro("__FLT_EVAL_METHOD__", Twine(TI.getFloatEvalMethod())); + Builder.defineMacro("__FLT_RADIX__", "2"); + Builder.defineMacro("__DECIMAL_DIG__", "__LDBL_DECIMAL_DIG__"); + + if (LangOpts.getStackProtector() == LangOptions::SSPOn) + Builder.defineMacro("__SSP__"); + else if (LangOpts.getStackProtector() == LangOptions::SSPStrong) + Builder.defineMacro("__SSP_STRONG__", "2"); + else if (LangOpts.getStackProtector() == LangOptions::SSPReq) + Builder.defineMacro("__SSP_ALL__", "3"); + + // 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 (FEOpts.ProgramAction == frontend::RewriteObjC || + LangOpts.getGC() != LangOptions::NonGC) { + Builder.defineMacro("__weak", "__attribute__((objc_gc(weak)))"); + Builder.defineMacro("__strong", "__attribute__((objc_gc(strong)))"); + Builder.defineMacro("__autoreleasing", ""); + Builder.defineMacro("__unsafe_unretained", ""); + } else if (LangOpts.ObjC) { + 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)))"); + } + + // On Darwin, there are __double_underscored variants of the type + // nullability qualifiers. + if (TI.getTriple().isOSDarwin()) { + Builder.defineMacro("__nonnull", "_Nonnull"); + Builder.defineMacro("__null_unspecified", "_Null_unspecified"); + Builder.defineMacro("__nullable", "_Nullable"); + } + + // Add a macro to differentiate between regular iOS/tvOS/watchOS targets and + // the corresponding simulator targets. + if (TI.getTriple().isOSDarwin() && TI.getTriple().isSimulatorEnvironment()) + Builder.defineMacro("__APPLE_EMBEDDED_SIMULATOR__", "1"); + + // OpenMP definition + // 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. + if (!LangOpts.OpenMPSimd) { + switch (LangOpts.OpenMP) { + case 0: + break; + case 40: + Builder.defineMacro("_OPENMP", "201307"); + break; + case 45: + Builder.defineMacro("_OPENMP", "201511"); + break; + default: + // Default version is OpenMP 3.1 + Builder.defineMacro("_OPENMP", "201107"); + break; + } + } + + // CUDA device path compilaton + if (LangOpts.CUDAIsDevice && !LangOpts.HIP) { + // The CUDA_ARCH value is set for the GPU target specified in the NVPTX + // backend's target defines. + Builder.defineMacro("__CUDA_ARCH__"); + } + + // We need to communicate this to our CUDA header wrapper, which in turn + // informs the proper CUDA headers of this choice. + if (LangOpts.CUDADeviceApproxTranscendentals || LangOpts.FastMath) { + Builder.defineMacro("__CLANG_CUDA_APPROX_TRANSCENDENTALS__"); + } + + // OpenCL definitions. + if (LangOpts.OpenCL) { +#define OPENCLEXT(Ext) \ + if (TI.getSupportedOpenCLOpts().isSupported(#Ext, \ + LangOpts.OpenCLVersion)) \ + Builder.defineMacro(#Ext); +#include "clang/Basic/OpenCLExtensions.def" + + auto Arch = TI.getTriple().getArch(); + if (Arch == llvm::Triple::spir || Arch == llvm::Triple::spir64) + Builder.defineMacro("__IMAGE_SUPPORT__"); + } + + if (TI.hasInt128Type() && LangOpts.CPlusPlus && LangOpts.GNUMode) { + // For each extended integer type, g++ defines a macro mapping the + // index of the type (0 in this case) in some list of extended types + // to the type. + Builder.defineMacro("__GLIBCXX_TYPE_INT_N_0", "__int128"); + Builder.defineMacro("__GLIBCXX_BITSIZE_INT_N_0", "128"); + } + + // Get other target #defines. + TI.getTargetDefines(LangOpts, Builder); +} + +/// 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 PCHContainerReader &PCHContainerRdr, + const FrontendOptions &FEOpts) { + const LangOptions &LangOpts = PP.getLangOpts(); + std::string PredefineBuffer; + PredefineBuffer.reserve(4080); + llvm::raw_string_ostream Predefines(PredefineBuffer); + MacroBuilder Builder(Predefines); + + // 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) { + // FIXME: This will create multiple definitions for most of the predefined + // macros. This is not the right way to handle this. + if ((LangOpts.CUDA || LangOpts.OpenMPIsDevice) && PP.getAuxTargetInfo()) + InitializePredefinedMacros(*PP.getAuxTargetInfo(), LangOpts, FEOpts, + Builder); + + InitializePredefinedMacros(PP.getTargetInfo(), LangOpts, FEOpts, Builder); + + // Install definitions to make Objective-C++ ARC work well with various + // C++ Standard Library implementations. + if (LangOpts.ObjC && LangOpts.CPlusPlus && + (LangOpts.ObjCAutoRefCount || LangOpts.ObjCWeak)) { + 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()); + } + + // Exit the command line and go back to <built-in> (2 is LC_LEAVE). + if (!PP.getLangOpts().AsmPreprocessor) + Builder.append("# 1 \"<built-in>\" 2"); + + // If -imacros are specified, include them now. These are processed before + // any -include directives. + for (unsigned i = 0, e = InitOpts.MacroIncludes.size(); i != e; ++i) + AddImplicitIncludeMacros(Builder, InitOpts.MacroIncludes[i]); + + // Process -include-pch/-include-pth directives. + if (!InitOpts.ImplicitPCHInclude.empty()) + AddImplicitIncludePCH(Builder, PP, PCHContainerRdr, + InitOpts.ImplicitPCHInclude); + + // Process -include directives. + for (unsigned i = 0, e = InitOpts.Includes.size(); i != e; ++i) { + const std::string &Path = InitOpts.Includes[i]; + AddImplicitInclude(Builder, Path); + } + + // Instruct the preprocessor to skip the preamble. + PP.setSkipMainFilePreamble(InitOpts.PrecompiledPreambleBytes.first, + InitOpts.PrecompiledPreambleBytes.second); + + // Copy PredefinedBuffer into the Preprocessor. + PP.setPredefines(Predefines.str()); +} 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 000000000000..47023e58fa0b --- /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, lang, desc, features) \ +static const LangStandard Lang_##id = { name, desc, features, InputKind::lang }; +#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, lang, 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, lang, desc, features) \ + .Case(name, lang_##id) +#include "clang/Frontend/LangStandards.def" + .Default(lang_unspecified); + if (K == lang_unspecified) + return nullptr; + + 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 000000000000..b31fbd087ba7 --- /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; + +/// 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; +} + +LLVM_DUMP_METHOD 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 000000000000..9998f65457cf --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/LogDiagnosticPrinter.cpp @@ -0,0 +1,165 @@ +//===--- 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/PlistSupport.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; +using namespace markup; + +LogDiagnosticPrinter::LogDiagnosticPrinter( + raw_ostream &os, DiagnosticOptions *diags, + std::unique_ptr<raw_ostream> StreamOwner) + : OS(os), StreamOwner(std::move(StreamOwner)), LangOpts(nullptr), + DiagOpts(diags) {} + +static StringRef getLevelName(DiagnosticsEngine::Level Level) { + switch (Level) { + case DiagnosticsEngine::Ignored: return "ignored"; + case DiagnosticsEngine::Remark: return "remark"; + 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!"); +} + +void +LogDiagnosticPrinter::EmitDiagEntry(llvm::raw_ostream &OS, + const LogDiagnosticPrinter::DiagEntry &DE) { + OS << " <dict>\n"; + OS << " <key>level</key>\n" + << " "; + EmitString(OS, getLevelName(DE.DiagnosticLevel)) << '\n'; + if (!DE.Filename.empty()) { + OS << " <key>filename</key>\n" + << " "; + EmitString(OS, DE.Filename) << '\n'; + } + if (DE.Line != 0) { + OS << " <key>line</key>\n" + << " "; + EmitInteger(OS, DE.Line) << '\n'; + } + if (DE.Column != 0) { + OS << " <key>column</key>\n" + << " "; + EmitInteger(OS, DE.Column) << '\n'; + } + if (!DE.Message.empty()) { + OS << " <key>message</key>\n" + << " "; + EmitString(OS, DE.Message) << '\n'; + } + OS << " <key>ID</key>\n" + << " "; + EmitInteger(OS, DE.DiagnosticID) << '\n'; + if (!DE.WarningOption.empty()) { + OS << " <key>WarningOption</key>\n" + << " "; + EmitString(OS, DE.WarningOption) << '\n'; + } + OS << " </dict>\n"; +} + +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" + << " "; + EmitString(OS, MainFilename) << '\n'; + } + if (!DwarfDebugFlags.empty()) { + OS << " <key>dwarf-debug-flags</key>\n" + << " "; + EmitString(OS, DwarfDebugFlags) << '\n'; + } + OS << " <key>diagnostics</key>\n"; + OS << " <array>\n"; + for (auto &DE : Entries) + EmitDiagEntry(OS, DE); + 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.isValid()) { + const FileEntry *FE = SM.getFileEntryForID(FID); + if (FE && FE->isValid()) + MainFilename = FE->getName(); + } + } + + // Create the diag entry. + DiagEntry DE; + DE.DiagnosticID = Info.getID(); + DE.DiagnosticLevel = Level; + + DE.WarningOption = DiagnosticIDs::getWarningOptionForDiag(DE.DiagnosticID); + + // 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.isValid()) { + const FileEntry *FE = SM.getFileEntryForID(FID); + if (FE && FE->isValid()) + 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/ModuleDependencyCollector.cpp b/contrib/llvm/tools/clang/lib/Frontend/ModuleDependencyCollector.cpp new file mode 100644 index 000000000000..fa8efcc3b53c --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/ModuleDependencyCollector.cpp @@ -0,0 +1,259 @@ +//===--- ModuleDependencyCollector.cpp - Collect module dependencies ------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Collect the dependencies of a set of modules. +// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/CharInfo.h" +#include "clang/Frontend/Utils.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Serialization/ASTReader.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/Config/llvm-config.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; + +namespace { +/// Private implementations for ModuleDependencyCollector +class ModuleDependencyListener : public ASTReaderListener { + ModuleDependencyCollector &Collector; +public: + ModuleDependencyListener(ModuleDependencyCollector &Collector) + : Collector(Collector) {} + bool needsInputFileVisitation() override { return true; } + bool needsSystemInputFileVisitation() override { return true; } + bool visitInputFile(StringRef Filename, bool IsSystem, bool IsOverridden, + bool IsExplicitModule) override { + Collector.addFile(Filename); + return true; + } +}; + +struct ModuleDependencyPPCallbacks : public PPCallbacks { + ModuleDependencyCollector &Collector; + SourceManager &SM; + ModuleDependencyPPCallbacks(ModuleDependencyCollector &Collector, + SourceManager &SM) + : Collector(Collector), SM(SM) {} + + void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, + StringRef FileName, bool IsAngled, + CharSourceRange FilenameRange, const FileEntry *File, + StringRef SearchPath, StringRef RelativePath, + const Module *Imported, + SrcMgr::CharacteristicKind FileType) override { + if (!File) + return; + Collector.addFile(File->getName()); + } +}; + +struct ModuleDependencyMMCallbacks : public ModuleMapCallbacks { + ModuleDependencyCollector &Collector; + ModuleDependencyMMCallbacks(ModuleDependencyCollector &Collector) + : Collector(Collector) {} + + void moduleMapAddHeader(StringRef HeaderPath) override { + if (llvm::sys::path::is_absolute(HeaderPath)) + Collector.addFile(HeaderPath); + } + void moduleMapAddUmbrellaHeader(FileManager *FileMgr, + const FileEntry *Header) override { + StringRef HeaderFilename = Header->getName(); + moduleMapAddHeader(HeaderFilename); + // The FileManager can find and cache the symbolic link for a framework + // header before its real path, this means a module can have some of its + // headers to use other paths. Although this is usually not a problem, it's + // inconsistent, and not collecting the original path header leads to + // umbrella clashes while rebuilding modules in the crash reproducer. For + // example: + // ApplicationServices.framework/Frameworks/ImageIO.framework/ImageIO.h + // instead of: + // ImageIO.framework/ImageIO.h + // + // FIXME: this shouldn't be necessary once we have FileName instances + // around instead of FileEntry ones. For now, make sure we collect all + // that we need for the reproducer to work correctly. + StringRef UmbreallDirFromHeader = + llvm::sys::path::parent_path(HeaderFilename); + StringRef UmbrellaDir = Header->getDir()->getName(); + if (!UmbrellaDir.equals(UmbreallDirFromHeader)) { + SmallString<128> AltHeaderFilename; + llvm::sys::path::append(AltHeaderFilename, UmbrellaDir, + llvm::sys::path::filename(HeaderFilename)); + if (FileMgr->getFile(AltHeaderFilename)) + moduleMapAddHeader(AltHeaderFilename); + } + } +}; + +} + +// TODO: move this to Support/Path.h and check for HAVE_REALPATH? +static bool real_path(StringRef SrcPath, SmallVectorImpl<char> &RealPath) { +#ifdef LLVM_ON_UNIX + char CanonicalPath[PATH_MAX]; + + // TODO: emit a warning in case this fails...? + if (!realpath(SrcPath.str().c_str(), CanonicalPath)) + return false; + + SmallString<256> RPath(CanonicalPath); + RealPath.swap(RPath); + return true; +#else + // FIXME: Add support for systems without realpath. + return false; +#endif +} + +void ModuleDependencyCollector::attachToASTReader(ASTReader &R) { + R.addListener(llvm::make_unique<ModuleDependencyListener>(*this)); +} + +void ModuleDependencyCollector::attachToPreprocessor(Preprocessor &PP) { + PP.addPPCallbacks(llvm::make_unique<ModuleDependencyPPCallbacks>( + *this, PP.getSourceManager())); + PP.getHeaderSearchInfo().getModuleMap().addModuleMapCallbacks( + llvm::make_unique<ModuleDependencyMMCallbacks>(*this)); +} + +static bool isCaseSensitivePath(StringRef Path) { + SmallString<256> TmpDest = Path, UpperDest, RealDest; + // Remove component traversals, links, etc. + if (!real_path(Path, TmpDest)) + return true; // Current default value in vfs.yaml + Path = TmpDest; + + // Change path to all upper case and ask for its real path, if the latter + // exists and is equal to Path, it's not case sensitive. Default to case + // sensitive in the absence of realpath, since this is what the VFSWriter + // already expects when sensitivity isn't setup. + for (auto &C : Path) + UpperDest.push_back(toUppercase(C)); + if (real_path(UpperDest, RealDest) && Path.equals(RealDest)) + return false; + return true; +} + +void ModuleDependencyCollector::writeFileMap() { + if (Seen.empty()) + return; + + StringRef VFSDir = getDest(); + + // Default to use relative overlay directories in the VFS yaml file. This + // allows crash reproducer scripts to work across machines. + VFSWriter.setOverlayDir(VFSDir); + + // Explicitly set case sensitivity for the YAML writer. For that, find out + // the sensitivity at the path where the headers all collected to. + VFSWriter.setCaseSensitivity(isCaseSensitivePath(VFSDir)); + + // Do not rely on real path names when executing the crash reproducer scripts + // since we only want to actually use the files we have on the VFS cache. + VFSWriter.setUseExternalNames(false); + + std::error_code EC; + SmallString<256> YAMLPath = VFSDir; + llvm::sys::path::append(YAMLPath, "vfs.yaml"); + llvm::raw_fd_ostream OS(YAMLPath, EC, llvm::sys::fs::F_Text); + if (EC) { + HasErrors = true; + return; + } + VFSWriter.write(OS); +} + +bool ModuleDependencyCollector::getRealPath(StringRef SrcPath, + SmallVectorImpl<char> &Result) { + using namespace llvm::sys; + SmallString<256> RealPath; + StringRef FileName = path::filename(SrcPath); + std::string Dir = path::parent_path(SrcPath).str(); + auto DirWithSymLink = SymLinkMap.find(Dir); + + // Use real_path to fix any symbolic link component present in a path. + // Computing the real path is expensive, cache the search through the + // parent path directory. + if (DirWithSymLink == SymLinkMap.end()) { + if (!real_path(Dir, RealPath)) + return false; + SymLinkMap[Dir] = RealPath.str(); + } else { + RealPath = DirWithSymLink->second; + } + + path::append(RealPath, FileName); + Result.swap(RealPath); + return true; +} + +std::error_code ModuleDependencyCollector::copyToRoot(StringRef Src, + StringRef Dst) { + using namespace llvm::sys; + + // We need an absolute src path to append to the root. + SmallString<256> AbsoluteSrc = Src; + fs::make_absolute(AbsoluteSrc); + // Canonicalize src to a native path to avoid mixed separator styles. + path::native(AbsoluteSrc); + // Remove redundant leading "./" pieces and consecutive separators. + AbsoluteSrc = path::remove_leading_dotslash(AbsoluteSrc); + + // Canonicalize the source path by removing "..", "." components. + SmallString<256> VirtualPath = AbsoluteSrc; + path::remove_dots(VirtualPath, /*remove_dot_dot=*/true); + + // If a ".." component is present after a symlink component, remove_dots may + // lead to the wrong real destination path. Let the source be canonicalized + // like that but make sure we always use the real path for the destination. + SmallString<256> CopyFrom; + if (!getRealPath(AbsoluteSrc, CopyFrom)) + CopyFrom = VirtualPath; + SmallString<256> CacheDst = getDest(); + + if (Dst.empty()) { + // The common case is to map the virtual path to the same path inside the + // cache. + path::append(CacheDst, path::relative_path(CopyFrom)); + } else { + // When collecting entries from input vfsoverlays, copy the external + // contents into the cache but still map from the source. + if (!fs::exists(Dst)) + return std::error_code(); + path::append(CacheDst, Dst); + CopyFrom = Dst; + } + + // Copy the file into place. + if (std::error_code EC = fs::create_directories(path::parent_path(CacheDst), + /*IgnoreExisting=*/true)) + return EC; + if (std::error_code EC = fs::copy_file(CopyFrom, CacheDst)) + return EC; + + // Always map a canonical src path to its real path into the YAML, by doing + // this we map different virtual src paths to the same entry in the VFS + // overlay, which is a way to emulate symlink inside the VFS; this is also + // needed for correctness, not doing that can lead to module redefinition + // errors. + addFileMapping(VirtualPath, CacheDst); + return std::error_code(); +} + +void ModuleDependencyCollector::addFile(StringRef Filename, StringRef FileDst) { + if (insertSeen(Filename)) + if (copyToRoot(Filename, FileDst)) + HasErrors = true; +} 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 000000000000..c6e18d9cae21 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/MultiplexConsumer.cpp @@ -0,0 +1,360 @@ +//===- 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" + +using namespace clang; + +namespace clang { + +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::MacroRead( + serialization::MacroID ID, MacroInfo *MI) { + for (auto &Listener : Listeners) + Listener->MacroRead(ID, MI); +} + +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, MacroDefinitionRecord *MD) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->MacroDefinitionRead(ID, MD); +} + +void MultiplexASTDeserializationListener::ModuleRead( + serialization::SubmoduleID ID, Module *Mod) { + for (auto &Listener : Listeners) + Listener->ModuleRead(ID, Mod); +} + +// 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); + void CompletedTagDefinition(const TagDecl *D) override; + void AddedVisibleDecl(const DeclContext *DC, const Decl *D) override; + void AddedCXXImplicitMember(const CXXRecordDecl *RD, const Decl *D) override; + void AddedCXXTemplateSpecialization(const ClassTemplateDecl *TD, + const ClassTemplateSpecializationDecl *D) override; + void AddedCXXTemplateSpecialization(const VarTemplateDecl *TD, + const VarTemplateSpecializationDecl *D) override; + void AddedCXXTemplateSpecialization(const FunctionTemplateDecl *TD, + const FunctionDecl *D) override; + void ResolvedExceptionSpec(const FunctionDecl *FD) override; + void DeducedReturnType(const FunctionDecl *FD, QualType ReturnType) override; + void ResolvedOperatorDelete(const CXXDestructorDecl *DD, + const FunctionDecl *Delete, + Expr *ThisArg) override; + void CompletedImplicitDefinition(const FunctionDecl *D) override; + void InstantiationRequested(const ValueDecl *D) override; + void VariableDefinitionInstantiated(const VarDecl *D) override; + void FunctionDefinitionInstantiated(const FunctionDecl *D) override; + void DefaultArgumentInstantiated(const ParmVarDecl *D) override; + void DefaultMemberInitializerInstantiated(const FieldDecl *D) override; + void AddedObjCCategoryToInterface(const ObjCCategoryDecl *CatD, + const ObjCInterfaceDecl *IFD) override; + void DeclarationMarkedUsed(const Decl *D) override; + void DeclarationMarkedOpenMPThreadPrivate(const Decl *D) override; + void DeclarationMarkedOpenMPDeclareTarget(const Decl *D, + const Attr *Attr) override; + void RedefinedHiddenDefinition(const NamedDecl *D, Module *M) override; + void AddedAttributeToRecord(const Attr *Attr, + const RecordDecl *Record) 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::ResolvedExceptionSpec( + const FunctionDecl *FD) { + for (auto &Listener : Listeners) + Listener->ResolvedExceptionSpec(FD); +} +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::ResolvedOperatorDelete( + const CXXDestructorDecl *DD, const FunctionDecl *Delete, Expr *ThisArg) { + for (auto *L : Listeners) + L->ResolvedOperatorDelete(DD, Delete, ThisArg); +} +void MultiplexASTMutationListener::CompletedImplicitDefinition( + const FunctionDecl *D) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->CompletedImplicitDefinition(D); +} +void MultiplexASTMutationListener::InstantiationRequested(const ValueDecl *D) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->InstantiationRequested(D); +} +void MultiplexASTMutationListener::VariableDefinitionInstantiated( + const VarDecl *D) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->VariableDefinitionInstantiated(D); +} +void MultiplexASTMutationListener::FunctionDefinitionInstantiated( + const FunctionDecl *D) { + for (auto &Listener : Listeners) + Listener->FunctionDefinitionInstantiated(D); +} +void MultiplexASTMutationListener::DefaultArgumentInstantiated( + const ParmVarDecl *D) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->DefaultArgumentInstantiated(D); +} +void MultiplexASTMutationListener::DefaultMemberInitializerInstantiated( + const FieldDecl *D) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->DefaultMemberInitializerInstantiated(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::DeclarationMarkedUsed(const Decl *D) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->DeclarationMarkedUsed(D); +} +void MultiplexASTMutationListener::DeclarationMarkedOpenMPThreadPrivate( + const Decl *D) { + for (size_t i = 0, e = Listeners.size(); i != e; ++i) + Listeners[i]->DeclarationMarkedOpenMPThreadPrivate(D); +} +void MultiplexASTMutationListener::DeclarationMarkedOpenMPDeclareTarget( + const Decl *D, const Attr *Attr) { + for (auto *L : Listeners) + L->DeclarationMarkedOpenMPDeclareTarget(D, Attr); +} +void MultiplexASTMutationListener::RedefinedHiddenDefinition(const NamedDecl *D, + Module *M) { + for (auto *L : Listeners) + L->RedefinedHiddenDefinition(D, M); +} + +void MultiplexASTMutationListener::AddedAttributeToRecord( + const Attr *Attr, + const RecordDecl *Record) { + for (auto *L : Listeners) + L->AddedAttributeToRecord(Attr, Record); +} + +} // end namespace clang + +MultiplexConsumer::MultiplexConsumer( + std::vector<std::unique_ptr<ASTConsumer>> C) + : Consumers(std::move(C)), MutationListener(), DeserializationListener() { + // 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 (auto &Consumer : Consumers) { + if (auto *mutationListener = Consumer->GetASTMutationListener()) + mutationListeners.push_back(mutationListener); + if (auto *serializationListener = Consumer->GetASTDeserializationListener()) + serializationListeners.push_back(serializationListener); + } + if (!mutationListeners.empty()) { + MutationListener = + llvm::make_unique<MultiplexASTMutationListener>(mutationListeners); + } + if (!serializationListeners.empty()) { + DeserializationListener = + llvm::make_unique<MultiplexASTDeserializationListener>( + serializationListeners); + } +} + +MultiplexConsumer::~MultiplexConsumer() {} + +void MultiplexConsumer::Initialize(ASTContext &Context) { + for (auto &Consumer : Consumers) + Consumer->Initialize(Context); +} + +bool MultiplexConsumer::HandleTopLevelDecl(DeclGroupRef D) { + bool Continue = true; + for (auto &Consumer : Consumers) + Continue = Continue && Consumer->HandleTopLevelDecl(D); + return Continue; +} + +void MultiplexConsumer::HandleInlineFunctionDefinition(FunctionDecl *D) { + for (auto &Consumer : Consumers) + Consumer->HandleInlineFunctionDefinition(D); +} + +void MultiplexConsumer::HandleCXXStaticMemberVarInstantiation(VarDecl *VD) { + for (auto &Consumer : Consumers) + Consumer->HandleCXXStaticMemberVarInstantiation(VD); +} + +void MultiplexConsumer::HandleInterestingDecl(DeclGroupRef D) { + for (auto &Consumer : Consumers) + Consumer->HandleInterestingDecl(D); +} + +void MultiplexConsumer::HandleTranslationUnit(ASTContext &Ctx) { + for (auto &Consumer : Consumers) + Consumer->HandleTranslationUnit(Ctx); +} + +void MultiplexConsumer::HandleTagDeclDefinition(TagDecl *D) { + for (auto &Consumer : Consumers) + Consumer->HandleTagDeclDefinition(D); +} + +void MultiplexConsumer::HandleTagDeclRequiredDefinition(const TagDecl *D) { + for (auto &Consumer : Consumers) + Consumer->HandleTagDeclRequiredDefinition(D); +} + +void MultiplexConsumer::HandleCXXImplicitFunctionInstantiation(FunctionDecl *D){ + for (auto &Consumer : Consumers) + Consumer->HandleCXXImplicitFunctionInstantiation(D); +} + +void MultiplexConsumer::HandleTopLevelDeclInObjCContainer(DeclGroupRef D) { + for (auto &Consumer : Consumers) + Consumer->HandleTopLevelDeclInObjCContainer(D); +} + +void MultiplexConsumer::HandleImplicitImportDecl(ImportDecl *D) { + for (auto &Consumer : Consumers) + Consumer->HandleImplicitImportDecl(D); +} + +void MultiplexConsumer::CompleteTentativeDefinition(VarDecl *D) { + for (auto &Consumer : Consumers) + Consumer->CompleteTentativeDefinition(D); +} + +void MultiplexConsumer::AssignInheritanceModel(CXXRecordDecl *RD) { + for (auto &Consumer : Consumers) + Consumer->AssignInheritanceModel(RD); +} + +void MultiplexConsumer::HandleVTable(CXXRecordDecl *RD) { + for (auto &Consumer : Consumers) + Consumer->HandleVTable(RD); +} + +ASTMutationListener *MultiplexConsumer::GetASTMutationListener() { + return MutationListener.get(); +} + +ASTDeserializationListener *MultiplexConsumer::GetASTDeserializationListener() { + return DeserializationListener.get(); +} + +void MultiplexConsumer::PrintStats() { + for (auto &Consumer : Consumers) + Consumer->PrintStats(); +} + +bool MultiplexConsumer::shouldSkipFunctionBody(Decl *D) { + bool Skip = true; + for (auto &Consumer : Consumers) + Skip = Skip && Consumer->shouldSkipFunctionBody(D); + return Skip; +} + +void MultiplexConsumer::InitializeSema(Sema &S) { + for (auto &Consumer : Consumers) + if (SemaConsumer *SC = dyn_cast<SemaConsumer>(Consumer.get())) + SC->InitializeSema(S); +} + +void MultiplexConsumer::ForgetSema() { + for (auto &Consumer : Consumers) + if (SemaConsumer *SC = dyn_cast<SemaConsumer>(Consumer.get())) + SC->ForgetSema(); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/PrecompiledPreamble.cpp b/contrib/llvm/tools/clang/lib/Frontend/PrecompiledPreamble.cpp new file mode 100644 index 000000000000..1930af187e7a --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/PrecompiledPreamble.cpp @@ -0,0 +1,769 @@ +//===--- PrecompiledPreamble.cpp - Build precompiled preambles --*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Helper class to build precompiled preamble. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/PrecompiledPreamble.h" +#include "clang/AST/DeclObjC.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/CompilerInvocation.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Frontend/FrontendOptions.h" +#include "clang/Lex/Lexer.h" +#include "clang/Lex/PreprocessorOptions.h" +#include "clang/Serialization/ASTWriter.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/Config/llvm-config.h" +#include "llvm/Support/CrashRecoveryContext.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Mutex.h" +#include "llvm/Support/MutexGuard.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/VirtualFileSystem.h" +#include <limits> +#include <utility> + +using namespace clang; + +namespace { + +StringRef getInMemoryPreamblePath() { +#if defined(LLVM_ON_UNIX) + return "/__clang_tmp/___clang_inmemory_preamble___"; +#elif defined(_WIN32) + return "C:\\__clang_tmp\\___clang_inmemory_preamble___"; +#else +#warning "Unknown platform. Defaulting to UNIX-style paths for in-memory PCHs" + return "/__clang_tmp/___clang_inmemory_preamble___"; +#endif +} + +IntrusiveRefCntPtr<llvm::vfs::FileSystem> +createVFSOverlayForPreamblePCH(StringRef PCHFilename, + std::unique_ptr<llvm::MemoryBuffer> PCHBuffer, + IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) { + // We want only the PCH file from the real filesystem to be available, + // so we create an in-memory VFS with just that and overlay it on top. + IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> PCHFS( + new llvm::vfs::InMemoryFileSystem()); + PCHFS->addFile(PCHFilename, 0, std::move(PCHBuffer)); + IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> Overlay( + new llvm::vfs::OverlayFileSystem(VFS)); + Overlay->pushOverlay(PCHFS); + return Overlay; +} + +class PreambleDependencyCollector : public DependencyCollector { +public: + // We want to collect all dependencies for correctness. Avoiding the real + // system dependencies (e.g. stl from /usr/lib) would probably be a good idea, + // but there is no way to distinguish between those and the ones that can be + // spuriously added by '-isystem' (e.g. to suppress warnings from those + // headers). + bool needSystemDependencies() override { return true; } +}; + +/// Keeps a track of files to be deleted in destructor. +class TemporaryFiles { +public: + // A static instance to be used by all clients. + static TemporaryFiles &getInstance(); + +private: + // Disallow constructing the class directly. + TemporaryFiles() = default; + // Disallow copy. + TemporaryFiles(const TemporaryFiles &) = delete; + +public: + ~TemporaryFiles(); + + /// Adds \p File to a set of tracked files. + void addFile(StringRef File); + + /// Remove \p File from disk and from the set of tracked files. + void removeFile(StringRef File); + +private: + llvm::sys::SmartMutex<false> Mutex; + llvm::StringSet<> Files; +}; + +TemporaryFiles &TemporaryFiles::getInstance() { + static TemporaryFiles Instance; + return Instance; +} + +TemporaryFiles::~TemporaryFiles() { + llvm::MutexGuard Guard(Mutex); + for (const auto &File : Files) + llvm::sys::fs::remove(File.getKey()); +} + +void TemporaryFiles::addFile(StringRef File) { + llvm::MutexGuard Guard(Mutex); + auto IsInserted = Files.insert(File).second; + (void)IsInserted; + assert(IsInserted && "File has already been added"); +} + +void TemporaryFiles::removeFile(StringRef File) { + llvm::MutexGuard Guard(Mutex); + auto WasPresent = Files.erase(File); + (void)WasPresent; + assert(WasPresent && "File was not tracked"); + llvm::sys::fs::remove(File); +} + +class PrecompilePreambleAction : public ASTFrontendAction { +public: + PrecompilePreambleAction(std::string *InMemStorage, + PreambleCallbacks &Callbacks) + : InMemStorage(InMemStorage), Callbacks(Callbacks) {} + + std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) override; + + bool hasEmittedPreamblePCH() const { return HasEmittedPreamblePCH; } + + void setEmittedPreamblePCH(ASTWriter &Writer) { + this->HasEmittedPreamblePCH = true; + Callbacks.AfterPCHEmitted(Writer); + } + + bool shouldEraseOutputFiles() override { return !hasEmittedPreamblePCH(); } + bool hasCodeCompletionSupport() const override { return false; } + bool hasASTFileSupport() const override { return false; } + TranslationUnitKind getTranslationUnitKind() override { return TU_Prefix; } + +private: + friend class PrecompilePreambleConsumer; + + bool HasEmittedPreamblePCH = false; + std::string *InMemStorage; + PreambleCallbacks &Callbacks; +}; + +class PrecompilePreambleConsumer : public PCHGenerator { +public: + PrecompilePreambleConsumer(PrecompilePreambleAction &Action, + const Preprocessor &PP, StringRef isysroot, + std::unique_ptr<raw_ostream> Out) + : PCHGenerator(PP, "", isysroot, std::make_shared<PCHBuffer>(), + ArrayRef<std::shared_ptr<ModuleFileExtension>>(), + /*AllowASTWithErrors=*/true), + Action(Action), Out(std::move(Out)) {} + + bool HandleTopLevelDecl(DeclGroupRef DG) override { + Action.Callbacks.HandleTopLevelDecl(DG); + return true; + } + + void HandleTranslationUnit(ASTContext &Ctx) override { + PCHGenerator::HandleTranslationUnit(Ctx); + if (!hasEmittedPCH()) + return; + + // Write the generated bitstream to "Out". + *Out << getPCH(); + // Make sure it hits disk now. + Out->flush(); + // Free the buffer. + llvm::SmallVector<char, 0> Empty; + getPCH() = std::move(Empty); + + Action.setEmittedPreamblePCH(getWriter()); + } + +private: + PrecompilePreambleAction &Action; + std::unique_ptr<raw_ostream> Out; +}; + +std::unique_ptr<ASTConsumer> +PrecompilePreambleAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + std::string Sysroot; + if (!GeneratePCHAction::ComputeASTConsumerArguments(CI, Sysroot)) + return nullptr; + + std::unique_ptr<llvm::raw_ostream> OS; + if (InMemStorage) { + OS = llvm::make_unique<llvm::raw_string_ostream>(*InMemStorage); + } else { + std::string OutputFile; + OS = GeneratePCHAction::CreateOutputFile(CI, InFile, OutputFile); + } + if (!OS) + return nullptr; + + if (!CI.getFrontendOpts().RelocatablePCH) + Sysroot.clear(); + + return llvm::make_unique<PrecompilePreambleConsumer>( + *this, CI.getPreprocessor(), Sysroot, std::move(OS)); +} + +template <class T> bool moveOnNoError(llvm::ErrorOr<T> Val, T &Output) { + if (!Val) + return false; + Output = std::move(*Val); + return true; +} + +} // namespace + +PreambleBounds clang::ComputePreambleBounds(const LangOptions &LangOpts, + llvm::MemoryBuffer *Buffer, + unsigned MaxLines) { + return Lexer::ComputePreamble(Buffer->getBuffer(), LangOpts, MaxLines); +} + +llvm::ErrorOr<PrecompiledPreamble> PrecompiledPreamble::Build( + const CompilerInvocation &Invocation, + const llvm::MemoryBuffer *MainFileBuffer, PreambleBounds Bounds, + DiagnosticsEngine &Diagnostics, + IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS, + std::shared_ptr<PCHContainerOperations> PCHContainerOps, bool StoreInMemory, + PreambleCallbacks &Callbacks) { + assert(VFS && "VFS is null"); + + auto PreambleInvocation = std::make_shared<CompilerInvocation>(Invocation); + FrontendOptions &FrontendOpts = PreambleInvocation->getFrontendOpts(); + PreprocessorOptions &PreprocessorOpts = + PreambleInvocation->getPreprocessorOpts(); + + llvm::Optional<TempPCHFile> TempFile; + if (!StoreInMemory) { + // Create a temporary file for the precompiled preamble. In rare + // circumstances, this can fail. + llvm::ErrorOr<PrecompiledPreamble::TempPCHFile> PreamblePCHFile = + PrecompiledPreamble::TempPCHFile::CreateNewPreamblePCHFile(); + if (!PreamblePCHFile) + return BuildPreambleError::CouldntCreateTempFile; + TempFile = std::move(*PreamblePCHFile); + } + + PCHStorage Storage = StoreInMemory ? PCHStorage(InMemoryPreamble()) + : PCHStorage(std::move(*TempFile)); + + // Save the preamble text for later; we'll need to compare against it for + // subsequent reparses. + std::vector<char> PreambleBytes(MainFileBuffer->getBufferStart(), + MainFileBuffer->getBufferStart() + + Bounds.Size); + bool PreambleEndsAtStartOfLine = Bounds.PreambleEndsAtStartOfLine; + + // Tell the compiler invocation to generate a temporary precompiled header. + FrontendOpts.ProgramAction = frontend::GeneratePCH; + FrontendOpts.OutputFile = StoreInMemory ? getInMemoryPreamblePath() + : Storage.asFile().getFilePath(); + PreprocessorOpts.PrecompiledPreambleBytes.first = 0; + PreprocessorOpts.PrecompiledPreambleBytes.second = false; + // Inform preprocessor to record conditional stack when building the preamble. + PreprocessorOpts.GeneratePreamble = true; + + // Create the compiler instance to use for building the precompiled preamble. + std::unique_ptr<CompilerInstance> Clang( + new CompilerInstance(std::move(PCHContainerOps))); + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<CompilerInstance> CICleanup( + Clang.get()); + + Clang->setInvocation(std::move(PreambleInvocation)); + Clang->setDiagnostics(&Diagnostics); + + // Create the target instance. + Clang->setTarget(TargetInfo::CreateTargetInfo( + Clang->getDiagnostics(), Clang->getInvocation().TargetOpts)); + if (!Clang->hasTarget()) + return BuildPreambleError::CouldntCreateTargetInfo; + + // 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().adjust(Clang->getLangOpts()); + + assert(Clang->getFrontendOpts().Inputs.size() == 1 && + "Invocation must have exactly one source file!"); + assert(Clang->getFrontendOpts().Inputs[0].getKind().getFormat() == + InputKind::Source && + "FIXME: AST inputs not yet supported here!"); + assert(Clang->getFrontendOpts().Inputs[0].getKind().getLanguage() != + InputKind::LLVM_IR && + "IR inputs not support here!"); + + // Clear out old caches and data. + Diagnostics.Reset(); + ProcessWarningOptions(Diagnostics, Clang->getDiagnosticOpts()); + + VFS = + createVFSFromCompilerInvocation(Clang->getInvocation(), Diagnostics, VFS); + + // Create a file manager object to provide access to and cache the filesystem. + Clang->setFileManager(new FileManager(Clang->getFileSystemOpts(), VFS)); + + // Create the source manager. + Clang->setSourceManager( + new SourceManager(Diagnostics, Clang->getFileManager())); + + auto PreambleDepCollector = std::make_shared<PreambleDependencyCollector>(); + Clang->addDependencyCollector(PreambleDepCollector); + + // Remap the main source file to the preamble buffer. + StringRef MainFilePath = FrontendOpts.Inputs[0].getFile(); + auto PreambleInputBuffer = llvm::MemoryBuffer::getMemBufferCopy( + MainFileBuffer->getBuffer().slice(0, Bounds.Size), MainFilePath); + if (PreprocessorOpts.RetainRemappedFileBuffers) { + // MainFileBuffer will be deleted by unique_ptr after leaving the method. + PreprocessorOpts.addRemappedFile(MainFilePath, PreambleInputBuffer.get()); + } else { + // In that case, remapped buffer will be deleted by CompilerInstance on + // BeginSourceFile, so we call release() to avoid double deletion. + PreprocessorOpts.addRemappedFile(MainFilePath, + PreambleInputBuffer.release()); + } + + std::unique_ptr<PrecompilePreambleAction> Act; + Act.reset(new PrecompilePreambleAction( + StoreInMemory ? &Storage.asMemory().Data : nullptr, Callbacks)); + Callbacks.BeforeExecute(*Clang); + if (!Act->BeginSourceFile(*Clang.get(), Clang->getFrontendOpts().Inputs[0])) + return BuildPreambleError::BeginSourceFileFailed; + + std::unique_ptr<PPCallbacks> DelegatedPPCallbacks = + Callbacks.createPPCallbacks(); + if (DelegatedPPCallbacks) + Clang->getPreprocessor().addPPCallbacks(std::move(DelegatedPPCallbacks)); + + Act->Execute(); + + // Run the callbacks. + Callbacks.AfterExecute(*Clang); + + Act->EndSourceFile(); + + if (!Act->hasEmittedPreamblePCH()) + return BuildPreambleError::CouldntEmitPCH; + + // Keep track of all of the files that the source manager knows about, + // so we can verify whether they have changed or not. + llvm::StringMap<PrecompiledPreamble::PreambleFileHash> FilesInPreamble; + + SourceManager &SourceMgr = Clang->getSourceManager(); + for (auto &Filename : PreambleDepCollector->getDependencies()) { + const FileEntry *File = Clang->getFileManager().getFile(Filename); + if (!File || File == SourceMgr.getFileEntryForID(SourceMgr.getMainFileID())) + continue; + if (time_t ModTime = File->getModificationTime()) { + FilesInPreamble[File->getName()] = + PrecompiledPreamble::PreambleFileHash::createForFile(File->getSize(), + ModTime); + } else { + llvm::MemoryBuffer *Buffer = SourceMgr.getMemoryBufferForFile(File); + FilesInPreamble[File->getName()] = + PrecompiledPreamble::PreambleFileHash::createForMemoryBuffer(Buffer); + } + } + + return PrecompiledPreamble(std::move(Storage), std::move(PreambleBytes), + PreambleEndsAtStartOfLine, + std::move(FilesInPreamble)); +} + +PreambleBounds PrecompiledPreamble::getBounds() const { + return PreambleBounds(PreambleBytes.size(), PreambleEndsAtStartOfLine); +} + +std::size_t PrecompiledPreamble::getSize() const { + switch (Storage.getKind()) { + case PCHStorage::Kind::Empty: + assert(false && "Calling getSize() on invalid PrecompiledPreamble. " + "Was it std::moved?"); + return 0; + case PCHStorage::Kind::InMemory: + return Storage.asMemory().Data.size(); + case PCHStorage::Kind::TempFile: { + uint64_t Result; + if (llvm::sys::fs::file_size(Storage.asFile().getFilePath(), Result)) + return 0; + + assert(Result <= std::numeric_limits<std::size_t>::max() && + "file size did not fit into size_t"); + return Result; + } + } + llvm_unreachable("Unhandled storage kind"); +} + +bool PrecompiledPreamble::CanReuse(const CompilerInvocation &Invocation, + const llvm::MemoryBuffer *MainFileBuffer, + PreambleBounds Bounds, + llvm::vfs::FileSystem *VFS) const { + + assert( + Bounds.Size <= MainFileBuffer->getBufferSize() && + "Buffer is too large. Bounds were calculated from a different buffer?"); + + auto PreambleInvocation = std::make_shared<CompilerInvocation>(Invocation); + PreprocessorOptions &PreprocessorOpts = + PreambleInvocation->getPreprocessorOpts(); + + // 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 (PreambleBytes.size() != Bounds.Size || + PreambleEndsAtStartOfLine != Bounds.PreambleEndsAtStartOfLine || + !std::equal(PreambleBytes.begin(), PreambleBytes.end(), + MainFileBuffer->getBuffer().begin())) + return false; + // 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. + // First, make a record of those files that have been overridden via + // remapping or unsaved_files. + std::map<llvm::sys::fs::UniqueID, PreambleFileHash> OverriddenFiles; + for (const auto &R : PreprocessorOpts.RemappedFiles) { + llvm::vfs::Status Status; + if (!moveOnNoError(VFS->status(R.second), Status)) { + // If we can't stat the file we're remapping to, assume that something + // horrible happened. + return false; + } + + OverriddenFiles[Status.getUniqueID()] = PreambleFileHash::createForFile( + Status.getSize(), llvm::sys::toTimeT(Status.getLastModificationTime())); + } + + for (const auto &RB : PreprocessorOpts.RemappedFileBuffers) { + llvm::vfs::Status Status; + if (!moveOnNoError(VFS->status(RB.first), Status)) + return false; + + OverriddenFiles[Status.getUniqueID()] = + PreambleFileHash::createForMemoryBuffer(RB.second); + } + + // Check whether anything has changed. + for (const auto &F : FilesInPreamble) { + llvm::vfs::Status Status; + if (!moveOnNoError(VFS->status(F.first()), Status)) { + // If we can't stat the file, assume that something horrible happened. + return false; + } + + std::map<llvm::sys::fs::UniqueID, PreambleFileHash>::iterator Overridden = + OverriddenFiles.find(Status.getUniqueID()); + 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) + return false; + continue; + } + + // The file was not remapped; check whether it has changed on disk. + if (Status.getSize() != uint64_t(F.second.Size) || + llvm::sys::toTimeT(Status.getLastModificationTime()) != + F.second.ModTime) + return false; + } + return true; +} + +void PrecompiledPreamble::AddImplicitPreamble( + CompilerInvocation &CI, IntrusiveRefCntPtr<llvm::vfs::FileSystem> &VFS, + llvm::MemoryBuffer *MainFileBuffer) const { + PreambleBounds Bounds(PreambleBytes.size(), PreambleEndsAtStartOfLine); + configurePreamble(Bounds, CI, VFS, MainFileBuffer); +} + +void PrecompiledPreamble::OverridePreamble( + CompilerInvocation &CI, IntrusiveRefCntPtr<llvm::vfs::FileSystem> &VFS, + llvm::MemoryBuffer *MainFileBuffer) const { + auto Bounds = ComputePreambleBounds(*CI.getLangOpts(), MainFileBuffer, 0); + configurePreamble(Bounds, CI, VFS, MainFileBuffer); +} + +PrecompiledPreamble::PrecompiledPreamble( + PCHStorage Storage, std::vector<char> PreambleBytes, + bool PreambleEndsAtStartOfLine, + llvm::StringMap<PreambleFileHash> FilesInPreamble) + : Storage(std::move(Storage)), FilesInPreamble(std::move(FilesInPreamble)), + PreambleBytes(std::move(PreambleBytes)), + PreambleEndsAtStartOfLine(PreambleEndsAtStartOfLine) { + assert(this->Storage.getKind() != PCHStorage::Kind::Empty); +} + +llvm::ErrorOr<PrecompiledPreamble::TempPCHFile> +PrecompiledPreamble::TempPCHFile::CreateNewPreamblePCHFile() { + // 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 TempPCHFile::createFromCustomPath(TmpFile); + return TempPCHFile::createInSystemTempDir("preamble", "pch"); +} + +llvm::ErrorOr<PrecompiledPreamble::TempPCHFile> +PrecompiledPreamble::TempPCHFile::createInSystemTempDir(const Twine &Prefix, + StringRef Suffix) { + llvm::SmallString<64> File; + // Using a version of createTemporaryFile with a file descriptor guarantees + // that we would never get a race condition in a multi-threaded setting + // (i.e., multiple threads getting the same temporary path). + int FD; + auto EC = llvm::sys::fs::createTemporaryFile(Prefix, Suffix, FD, File); + if (EC) + return EC; + // We only needed to make sure the file exists, close the file right away. + llvm::sys::Process::SafelyCloseFileDescriptor(FD); + return TempPCHFile(std::move(File).str()); +} + +llvm::ErrorOr<PrecompiledPreamble::TempPCHFile> +PrecompiledPreamble::TempPCHFile::createFromCustomPath(const Twine &Path) { + return TempPCHFile(Path.str()); +} + +PrecompiledPreamble::TempPCHFile::TempPCHFile(std::string FilePath) + : FilePath(std::move(FilePath)) { + TemporaryFiles::getInstance().addFile(*this->FilePath); +} + +PrecompiledPreamble::TempPCHFile::TempPCHFile(TempPCHFile &&Other) { + FilePath = std::move(Other.FilePath); + Other.FilePath = None; +} + +PrecompiledPreamble::TempPCHFile &PrecompiledPreamble::TempPCHFile:: +operator=(TempPCHFile &&Other) { + RemoveFileIfPresent(); + + FilePath = std::move(Other.FilePath); + Other.FilePath = None; + return *this; +} + +PrecompiledPreamble::TempPCHFile::~TempPCHFile() { RemoveFileIfPresent(); } + +void PrecompiledPreamble::TempPCHFile::RemoveFileIfPresent() { + if (FilePath) { + TemporaryFiles::getInstance().removeFile(*FilePath); + FilePath = None; + } +} + +llvm::StringRef PrecompiledPreamble::TempPCHFile::getFilePath() const { + assert(FilePath && "TempPCHFile doesn't have a FilePath. Had it been moved?"); + return *FilePath; +} + +PrecompiledPreamble::PCHStorage::PCHStorage(TempPCHFile File) + : StorageKind(Kind::TempFile) { + new (&asFile()) TempPCHFile(std::move(File)); +} + +PrecompiledPreamble::PCHStorage::PCHStorage(InMemoryPreamble Memory) + : StorageKind(Kind::InMemory) { + new (&asMemory()) InMemoryPreamble(std::move(Memory)); +} + +PrecompiledPreamble::PCHStorage::PCHStorage(PCHStorage &&Other) : PCHStorage() { + *this = std::move(Other); +} + +PrecompiledPreamble::PCHStorage &PrecompiledPreamble::PCHStorage:: +operator=(PCHStorage &&Other) { + destroy(); + + StorageKind = Other.StorageKind; + switch (StorageKind) { + case Kind::Empty: + // do nothing; + break; + case Kind::TempFile: + new (&asFile()) TempPCHFile(std::move(Other.asFile())); + break; + case Kind::InMemory: + new (&asMemory()) InMemoryPreamble(std::move(Other.asMemory())); + break; + } + + Other.setEmpty(); + return *this; +} + +PrecompiledPreamble::PCHStorage::~PCHStorage() { destroy(); } + +PrecompiledPreamble::PCHStorage::Kind +PrecompiledPreamble::PCHStorage::getKind() const { + return StorageKind; +} + +PrecompiledPreamble::TempPCHFile &PrecompiledPreamble::PCHStorage::asFile() { + assert(getKind() == Kind::TempFile); + return *reinterpret_cast<TempPCHFile *>(Storage.buffer); +} + +const PrecompiledPreamble::TempPCHFile & +PrecompiledPreamble::PCHStorage::asFile() const { + return const_cast<PCHStorage *>(this)->asFile(); +} + +PrecompiledPreamble::InMemoryPreamble & +PrecompiledPreamble::PCHStorage::asMemory() { + assert(getKind() == Kind::InMemory); + return *reinterpret_cast<InMemoryPreamble *>(Storage.buffer); +} + +const PrecompiledPreamble::InMemoryPreamble & +PrecompiledPreamble::PCHStorage::asMemory() const { + return const_cast<PCHStorage *>(this)->asMemory(); +} + +void PrecompiledPreamble::PCHStorage::destroy() { + switch (StorageKind) { + case Kind::Empty: + return; + case Kind::TempFile: + asFile().~TempPCHFile(); + return; + case Kind::InMemory: + asMemory().~InMemoryPreamble(); + return; + } +} + +void PrecompiledPreamble::PCHStorage::setEmpty() { + destroy(); + StorageKind = Kind::Empty; +} + +PrecompiledPreamble::PreambleFileHash +PrecompiledPreamble::PreambleFileHash::createForFile(off_t Size, + time_t ModTime) { + PreambleFileHash Result; + Result.Size = Size; + Result.ModTime = ModTime; + Result.MD5 = {}; + return Result; +} + +PrecompiledPreamble::PreambleFileHash +PrecompiledPreamble::PreambleFileHash::createForMemoryBuffer( + const llvm::MemoryBuffer *Buffer) { + PreambleFileHash Result; + Result.Size = Buffer->getBufferSize(); + Result.ModTime = 0; + + llvm::MD5 MD5Ctx; + MD5Ctx.update(Buffer->getBuffer().data()); + MD5Ctx.final(Result.MD5); + + return Result; +} + +void PrecompiledPreamble::configurePreamble( + PreambleBounds Bounds, CompilerInvocation &CI, + IntrusiveRefCntPtr<llvm::vfs::FileSystem> &VFS, + llvm::MemoryBuffer *MainFileBuffer) const { + assert(VFS); + + auto &PreprocessorOpts = CI.getPreprocessorOpts(); + + // Remap main file to point to MainFileBuffer. + auto MainFilePath = CI.getFrontendOpts().Inputs[0].getFile(); + PreprocessorOpts.addRemappedFile(MainFilePath, MainFileBuffer); + + // Configure ImpicitPCHInclude. + PreprocessorOpts.PrecompiledPreambleBytes.first = Bounds.Size; + PreprocessorOpts.PrecompiledPreambleBytes.second = + Bounds.PreambleEndsAtStartOfLine; + PreprocessorOpts.DisablePCHValidation = true; + + setupPreambleStorage(Storage, PreprocessorOpts, VFS); +} + +void PrecompiledPreamble::setupPreambleStorage( + const PCHStorage &Storage, PreprocessorOptions &PreprocessorOpts, + IntrusiveRefCntPtr<llvm::vfs::FileSystem> &VFS) { + if (Storage.getKind() == PCHStorage::Kind::TempFile) { + const TempPCHFile &PCHFile = Storage.asFile(); + PreprocessorOpts.ImplicitPCHInclude = PCHFile.getFilePath(); + + // Make sure we can access the PCH file even if we're using a VFS + IntrusiveRefCntPtr<llvm::vfs::FileSystem> RealFS = + llvm::vfs::getRealFileSystem(); + auto PCHPath = PCHFile.getFilePath(); + if (VFS == RealFS || VFS->exists(PCHPath)) + return; + auto Buf = RealFS->getBufferForFile(PCHPath); + if (!Buf) { + // We can't read the file even from RealFS, this is clearly an error, + // but we'll just leave the current VFS as is and let clang's code + // figure out what to do with missing PCH. + return; + } + + // We have a slight inconsistency here -- we're using the VFS to + // read files, but the PCH was generated in the real file system. + VFS = createVFSOverlayForPreamblePCH(PCHPath, std::move(*Buf), VFS); + } else { + assert(Storage.getKind() == PCHStorage::Kind::InMemory); + // For in-memory preamble, we have to provide a VFS overlay that makes it + // accessible. + StringRef PCHPath = getInMemoryPreamblePath(); + PreprocessorOpts.ImplicitPCHInclude = PCHPath; + + auto Buf = llvm::MemoryBuffer::getMemBuffer(Storage.asMemory().Data); + VFS = createVFSOverlayForPreamblePCH(PCHPath, std::move(Buf), VFS); + } +} + +void PreambleCallbacks::BeforeExecute(CompilerInstance &CI) {} +void PreambleCallbacks::AfterExecute(CompilerInstance &CI) {} +void PreambleCallbacks::AfterPCHEmitted(ASTWriter &Writer) {} +void PreambleCallbacks::HandleTopLevelDecl(DeclGroupRef DG) {} +std::unique_ptr<PPCallbacks> PreambleCallbacks::createPPCallbacks() { + return nullptr; +} + +static llvm::ManagedStatic<BuildPreambleErrorCategory> BuildPreambleErrCategory; + +std::error_code clang::make_error_code(BuildPreambleError Error) { + return std::error_code(static_cast<int>(Error), *BuildPreambleErrCategory); +} + +const char *BuildPreambleErrorCategory::name() const noexcept { + return "build-preamble.error"; +} + +std::string BuildPreambleErrorCategory::message(int condition) const { + switch (static_cast<BuildPreambleError>(condition)) { + case BuildPreambleError::CouldntCreateTempFile: + return "Could not create temporary file for PCH"; + case BuildPreambleError::CouldntCreateTargetInfo: + return "CreateTargetInfo() return null"; + case BuildPreambleError::BeginSourceFileFailed: + return "BeginSourceFile() return an error"; + case BuildPreambleError::CouldntEmitPCH: + return "Could not emit PCH"; + } + llvm_unreachable("unexpected BuildPreambleError"); +} 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 000000000000..3b835985a54c --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/PrintPreprocessedOutput.cpp @@ -0,0 +1,909 @@ +//===--- 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.param_empty()) { + MacroInfo::param_iterator AI = MI.param_begin(), E = MI.param_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 (const auto &T : MI.tokens()) { + if (T.hasLeadingSpace()) + OS << ' '; + + OS << PP.getSpelling(T, 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 DumpIncludeDirectives; + bool UseLineDirectives; + bool IsFirstFileEntered; +public: + PrintPPOutputPPCallbacks(Preprocessor &pp, raw_ostream &os, bool lineMarkers, + bool defines, bool DumpIncludeDirectives, + bool UseLineDirectives) + : PP(pp), SM(PP.getSourceManager()), ConcatInfo(PP), OS(os), + DisableLineMarkers(lineMarkers), DumpDefines(defines), + DumpIncludeDirectives(DumpIncludeDirectives), + UseLineDirectives(UseLineDirectives) { + CurLine = 0; + CurFilename += "<uninit>"; + EmittedTokensOnThisLine = false; + EmittedDirectiveOnThisLine = false; + FileType = SrcMgr::C_User; + Initialized = false; + IsFirstFileEntered = false; + } + + void setEmittedTokensOnThisLine() { EmittedTokensOnThisLine = true; } + bool hasEmittedTokensOnThisLine() const { return EmittedTokensOnThisLine; } + + void setEmittedDirectiveOnThisLine() { EmittedDirectiveOnThisLine = true; } + bool hasEmittedDirectiveOnThisLine() const { + return EmittedDirectiveOnThisLine; + } + + bool startNewLineIfNeeded(bool ShouldUpdateCurrentLine = true); + + void FileChanged(SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, + FileID PrevFID) override; + void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, + StringRef FileName, bool IsAngled, + CharSourceRange FilenameRange, const FileEntry *File, + StringRef SearchPath, StringRef RelativePath, + const Module *Imported, + SrcMgr::CharacteristicKind FileType) override; + void Ident(SourceLocation Loc, StringRef str) override; + void PragmaMessage(SourceLocation Loc, StringRef Namespace, + PragmaMessageKind Kind, StringRef Str) override; + void PragmaDebug(SourceLocation Loc, StringRef DebugType) override; + void PragmaDiagnosticPush(SourceLocation Loc, StringRef Namespace) override; + void PragmaDiagnosticPop(SourceLocation Loc, StringRef Namespace) override; + void PragmaDiagnostic(SourceLocation Loc, StringRef Namespace, + diag::Severity Map, StringRef Str) override; + void PragmaWarning(SourceLocation Loc, StringRef WarningSpec, + ArrayRef<int> Ids) override; + void PragmaWarningPush(SourceLocation Loc, int Level) override; + void PragmaWarningPop(SourceLocation Loc) override; + void PragmaAssumeNonNullBegin(SourceLocation Loc) override; + void PragmaAssumeNonNullEnd(SourceLocation Loc) override; + + 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=nullptr, + 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) override; + + /// MacroUndefined - This hook is called whenever a macro #undef is seen. + void MacroUndefined(const Token &MacroNameTok, + const MacroDefinition &MD, + const MacroDirective *Undef) override; + + void BeginModule(const Module *M); + void EndModule(const Module *M); +}; +} // 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 (UseLineDirectives) { + 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, nullptr, 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, + SrcMgr::CharacteristicKind FileType) { + // In -dI mode, dump #include directives prior to dumping their content or + // interpretation. + if (DumpIncludeDirectives) { + startNewLineIfNeeded(); + MoveToLine(HashLoc); + const std::string TokenText = PP.getSpelling(IncludeTok); + assert(!TokenText.empty()); + OS << "#" << TokenText << " " + << (IsAngled ? '<' : '"') << FileName << (IsAngled ? '>' : '"') + << " /* clang -E -dI */"; + setEmittedDirectiveOnThisLine(); + startNewLineIfNeeded(); + } + + // When preprocessing, turn implicit imports into module import pragmas. + if (Imported) { + switch (IncludeTok.getIdentifierInfo()->getPPKeywordID()) { + case tok::pp_include: + case tok::pp_import: + case tok::pp_include_next: + startNewLineIfNeeded(); + MoveToLine(HashLoc); + OS << "#pragma clang module import " << Imported->getFullModuleName(true) + << " /* clang -E: implicit import for " + << "#" << PP.getSpelling(IncludeTok) << " " + << (IsAngled ? '<' : '"') << FileName << (IsAngled ? '>' : '"') + << " */"; + // Since we want a newline after the pragma, but not a #<line>, start a + // new line immediately. + EmittedTokensOnThisLine = true; + startNewLineIfNeeded(); + break; + + case tok::pp___include_macros: + // #__include_macros has no effect on a user of a preprocessed source + // file; the only effect is on preprocessing. + // + // FIXME: That's not *quite* true: it causes the module in question to + // be loaded, which can affect downstream diagnostics. + break; + + default: + llvm_unreachable("unknown include directive kind"); + break; + } + } +} + +/// Handle entering the scope of a module during a module compilation. +void PrintPPOutputPPCallbacks::BeginModule(const Module *M) { + startNewLineIfNeeded(); + OS << "#pragma clang module begin " << M->getFullModuleName(true); + setEmittedDirectiveOnThisLine(); +} + +/// Handle leaving the scope of a module during a module compilation. +void PrintPPOutputPPCallbacks::EndModule(const Module *M) { + startNewLineIfNeeded(); + OS << "#pragma clang module end /*" << M->getFullModuleName(true) << "*/"; + setEmittedDirectiveOnThisLine(); +} + +/// Ident - Handle #ident directives when read by the preprocessor. +/// +void PrintPPOutputPPCallbacks::Ident(SourceLocation Loc, StringRef S) { + MoveToLine(Loc); + + OS.write("#ident ", strlen("#ident ")); + OS.write(S.begin(), S.size()); + EmittedTokensOnThisLine = true; +} + +/// 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 MacroDefinition &MD, + const MacroDirective *Undef) { + // Only print out macro definitions in -dD mode. + if (!DumpDefines) return; + + MoveToLine(MacroNameTok.getLocation()); + OS << "#undef " << MacroNameTok.getIdentifierInfo()->getName(); + setEmittedDirectiveOnThisLine(); +} + +static void outputPrintable(raw_ostream &OS, StringRef Str) { + for (unsigned char Char : Str) { + 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::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::Severity Map, + StringRef Str) { + startNewLineIfNeeded(); + MoveToLine(Loc); + OS << "#pragma " << Namespace << " diagnostic "; + switch (Map) { + case diag::Severity::Remark: + OS << "remark"; + break; + case diag::Severity::Warning: + OS << "warning"; + break; + case diag::Severity::Error: + OS << "error"; + break; + case diag::Severity::Ignored: + OS << "ignored"; + break; + case diag::Severity::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(); +} + +void PrintPPOutputPPCallbacks:: +PragmaAssumeNonNullBegin(SourceLocation Loc) { + startNewLineIfNeeded(); + MoveToLine(Loc); + OS << "#pragma clang assume_nonnull begin"; + setEmittedDirectiveOnThisLine(); +} + +void PrintPPOutputPPCallbacks:: +PragmaAssumeNonNullEnd(SourceLocation Loc) { + startNewLineIfNeeded(); + MoveToLine(Loc); + OS << "#pragma clang assume_nonnull end"; + 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()); + + // The first token on a line can have a column number of 1, yet still expect + // leading white space, if a macro expansion in column 1 starts with an empty + // macro argument, or an empty nested macro expansion. In this case, move the + // token to column 2. + if (ColNo == 1 && Tok.hasLeadingSpace()) + ColNo = 2; + + // 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; + + // Set to true if tokens should be expanded + bool ShouldExpandTokens; + + UnknownPragmaHandler(const char *prefix, PrintPPOutputPPCallbacks *callbacks, + bool RequireTokenExpansion) + : Prefix(prefix), Callbacks(callbacks), + ShouldExpandTokens(RequireTokenExpansion) {} + void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer, + Token &PragmaTok) override { + // 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)); + + if (ShouldExpandTokens) { + // The first token does not have expanded macros. Expand them, if + // required. + auto Toks = llvm::make_unique<Token[]>(1); + Toks[0] = PragmaTok; + PP.EnterTokenStream(std::move(Toks), /*NumToks=*/1, + /*DisableMacroExpansion=*/false); + PP.Lex(PragmaTok); + } + Token PrevToken; + Token PrevPrevToken; + PrevToken.startToken(); + PrevPrevToken.startToken(); + + // Read and print all of the pragma tokens. + while (PragmaTok.isNot(tok::eod)) { + if (PragmaTok.hasLeadingSpace() || + Callbacks->AvoidConcat(PrevPrevToken, PrevToken, PragmaTok)) + Callbacks->OS << ' '; + std::string TokSpell = PP.getSpelling(PragmaTok); + Callbacks->OS.write(&TokSpell[0], TokSpell.size()); + + PrevPrevToken = PrevToken; + PrevToken = PragmaTok; + + if (ShouldExpandTokens) + PP.Lex(PragmaTok); + else + 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::eod)) { + // Don't print end of directive tokens, since they are typically newlines + // that mess up our line tracking. These come from unknown pre-processor + // directives or hash-prefixed comments in standalone assembly files. + PP.Lex(Tok); + continue; + } 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 (Tok.is(tok::annot_module_begin)) { + // FIXME: We retrieve this token after the FileChanged callback, and + // retrieve the module_end token before the FileChanged callback, so + // we render this within the file and render the module end outside the + // file, but this is backwards from the token locations: the module_begin + // token is at the include location (outside the file) and the module_end + // token is at the EOF location (within the file). + Callbacks->BeginModule( + reinterpret_cast<Module *>(Tok.getAnnotationValue())); + PP.Lex(Tok); + continue; + } else if (Tok.is(tok::annot_module_end)) { + Callbacks->EndModule( + reinterpret_cast<Module *>(Tok.getAnnotationValue())); + PP.Lex(Tok); + continue; + } else if (Tok.isAnnotation()) { + // Ignore annotation tokens created by pragmas - the pragmas themselves + // will be reproduced in the preprocessed output. + 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() < llvm::array_lengthof(Buffer)) { + 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.IgnorePragmas(); + + // -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) { + auto *MD = I->second.getLatest(); + if (MD && MD->isDefined()) + MacrosByID.push_back(id_macro_pair(I->first, MD->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, + Opts.ShowIncludeDirectives, Opts.UseLineDirectives); + + // Expand macros in pragmas with -fms-extensions. The assumption is that + // the majority of pragmas in such a file will be Microsoft pragmas. + // Remember the handlers we will add so that we can remove them later. + std::unique_ptr<UnknownPragmaHandler> MicrosoftExtHandler( + new UnknownPragmaHandler( + "#pragma", Callbacks, + /*RequireTokenExpansion=*/PP.getLangOpts().MicrosoftExt)); + + std::unique_ptr<UnknownPragmaHandler> GCCHandler(new UnknownPragmaHandler( + "#pragma GCC", Callbacks, + /*RequireTokenExpansion=*/PP.getLangOpts().MicrosoftExt)); + + std::unique_ptr<UnknownPragmaHandler> ClangHandler(new UnknownPragmaHandler( + "#pragma clang", Callbacks, + /*RequireTokenExpansion=*/PP.getLangOpts().MicrosoftExt)); + + PP.AddPragmaHandler(MicrosoftExtHandler.get()); + PP.AddPragmaHandler("GCC", GCCHandler.get()); + PP.AddPragmaHandler("clang", ClangHandler.get()); + + // The tokens after pragma omp need to be expanded. + // + // OpenMP [2.1, Directive format] + // Preprocessing tokens following the #pragma omp are subject to macro + // replacement. + std::unique_ptr<UnknownPragmaHandler> OpenMPHandler( + new UnknownPragmaHandler("#pragma omp", Callbacks, + /*RequireTokenExpansion=*/true)); + PP.AddPragmaHandler("omp", OpenMPHandler.get()); + + PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(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'; + + // Remove the handlers we just added to leave the preprocessor in a sane state + // so that it can be reused (for example by a clang::Parser instance). + PP.RemovePragmaHandler(MicrosoftExtHandler.get()); + PP.RemovePragmaHandler("GCC", GCCHandler.get()); + PP.RemovePragmaHandler("clang", ClangHandler.get()); + PP.RemovePragmaHandler("omp", OpenMPHandler.get()); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/Rewrite/FixItRewriter.cpp b/contrib/llvm/tools/clang/lib/Frontend/Rewrite/FixItRewriter.cpp new file mode 100644 index 000000000000..1c2efe63aa19 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/Rewrite/FixItRewriter.cpp @@ -0,0 +1,208 @@ +//===- FixItRewriter.cpp - Fix-It Rewriter 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 diagnostic client adaptor that performs rewrites as +// suggested by code modification hints attached to diagnostics. It +// then forwards any diagnostics to the adapted diagnostic client. +// +//===----------------------------------------------------------------------===// + +#include "clang/Rewrite/Frontend/FixItRewriter.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Edit/Commit.h" +#include "clang/Edit/EditsReceiver.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Rewrite/Core/RewriteBuffer.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/raw_ostream.h" +#include <cstdio> +#include <memory> +#include <string> +#include <system_error> +#include <utility> + +using namespace clang; + +FixItRewriter::FixItRewriter(DiagnosticsEngine &Diags, SourceManager &SourceMgr, + const LangOptions &LangOpts, + FixItOptions *FixItOpts) + : Diags(Diags), Editor(SourceMgr, LangOpts), Rewrite(SourceMgr, LangOpts), + FixItOpts(FixItOpts) { + Owner = Diags.takeClient(); + Client = Diags.getClient(); + Diags.setClient(this, false); +} + +FixItRewriter::~FixItRewriter() { + Diags.setClient(Client, Owner.release() != nullptr); +} + +bool FixItRewriter::WriteFixedFile(FileID ID, raw_ostream &OS) { + const RewriteBuffer *RewriteBuf = Rewrite.getRewriteBufferFor(ID); + if (!RewriteBuf) return true; + RewriteBuf->write(OS); + OS.flush(); + return false; +} + +namespace { + +class RewritesReceiver : public edit::EditsReceiver { + Rewriter &Rewrite; + +public: + RewritesReceiver(Rewriter &Rewrite) : Rewrite(Rewrite) {} + + void insert(SourceLocation loc, StringRef text) override { + Rewrite.InsertText(loc, text); + } + + void replace(CharSourceRange range, StringRef text) override { + Rewrite.ReplaceText(range.getBegin(), Rewrite.getRangeSize(range), text); + } +}; + +} // namespace + +bool FixItRewriter::WriteFixedFiles( + std::vector<std::pair<std::string, std::string>> *RewrittenFiles) { + if (NumFailures > 0 && !FixItOpts->FixWhatYouCan) { + Diag(FullSourceLoc(), diag::warn_fixit_no_changes); + return true; + } + + RewritesReceiver Rec(Rewrite); + Editor.applyRewrites(Rec); + + if (FixItOpts->InPlace) { + // Overwriting open files on Windows is tricky, but the rewriter can do it + // for us. + Rewrite.overwriteChangedFiles(); + return false; + } + + for (iterator I = buffer_begin(), E = buffer_end(); I != E; ++I) { + const FileEntry *Entry = Rewrite.getSourceMgr().getFileEntryForID(I->first); + int fd; + std::string Filename = FixItOpts->RewriteFilename(Entry->getName(), fd); + std::error_code EC; + std::unique_ptr<llvm::raw_fd_ostream> OS; + if (fd != -1) { + OS.reset(new llvm::raw_fd_ostream(fd, /*shouldClose=*/true)); + } else { + OS.reset(new llvm::raw_fd_ostream(Filename, EC, llvm::sys::fs::F_None)); + } + if (EC) { + Diags.Report(clang::diag::err_fe_unable_to_open_output) << Filename + << EC.message(); + continue; + } + RewriteBuffer &RewriteBuf = I->second; + RewriteBuf.write(*OS); + OS->flush(); + + if (RewrittenFiles) + RewrittenFiles->push_back(std::make_pair(Entry->getName(), Filename)); + } + + return false; +} + +bool FixItRewriter::IncludeInDiagnosticCounts() const { + return Client ? Client->IncludeInDiagnosticCounts() : true; +} + +void FixItRewriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, + const Diagnostic &Info) { + // Default implementation (Warnings/errors count). + DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info); + + if (!FixItOpts->Silent || + DiagLevel >= DiagnosticsEngine::Error || + (DiagLevel == DiagnosticsEngine::Note && !PrevDiagSilenced) || + (DiagLevel > DiagnosticsEngine::Note && Info.getNumFixItHints())) { + Client->HandleDiagnostic(DiagLevel, Info); + PrevDiagSilenced = false; + } else { + PrevDiagSilenced = true; + } + + // Skip over any diagnostics that are ignored or notes. + if (DiagLevel <= DiagnosticsEngine::Note) + return; + // Skip over errors if we are only fixing warnings. + if (DiagLevel >= DiagnosticsEngine::Error && FixItOpts->FixOnlyWarnings) { + ++NumFailures; + return; + } + + // Make sure that we can perform all of the modifications we + // in this diagnostic. + edit::Commit commit(Editor); + for (unsigned Idx = 0, Last = Info.getNumFixItHints(); + Idx < Last; ++Idx) { + const FixItHint &Hint = Info.getFixItHint(Idx); + + 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); + } + } + bool CanRewrite = Info.getNumFixItHints() > 0 && commit.isCommitable(); + + if (!CanRewrite) { + if (Info.getNumFixItHints() > 0) + Diag(Info.getLocation(), diag::note_fixit_in_macro); + + // If this was an error, refuse to perform any rewriting. + if (DiagLevel >= DiagnosticsEngine::Error) { + if (++NumFailures == 1) + Diag(Info.getLocation(), diag::note_fixit_unfixed_error); + } + return; + } + + if (!Editor.commit(commit)) { + ++NumFailures; + Diag(Info.getLocation(), diag::note_fixit_failed); + return; + } + + Diag(Info.getLocation(), diag::note_fixit_applied); +} + +/// Emit a diagnostic via the adapted diagnostic client. +void FixItRewriter::Diag(SourceLocation Loc, unsigned DiagID) { + // When producing this diagnostic, we temporarily bypass ourselves, + // clear out any current diagnostic, and let the downstream client + // format the diagnostic. + Diags.setClient(Client, false); + Diags.Clear(); + Diags.Report(Loc, DiagID); + Diags.setClient(this, false); +} + +FixItOptions::~FixItOptions() = default; diff --git a/contrib/llvm/tools/clang/lib/Frontend/Rewrite/FrontendActions.cpp b/contrib/llvm/tools/clang/lib/Frontend/Rewrite/FrontendActions.cpp new file mode 100644 index 000000000000..bcf6d215c998 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/Rewrite/FrontendActions.cpp @@ -0,0 +1,319 @@ +//===--- 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/Rewrite/Frontend/FrontendActions.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/Basic/CharInfo.h" +#include "clang/Config/config.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/Utils.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Lex/PreprocessorOptions.h" +#include "clang/Rewrite/Frontend/ASTConsumers.h" +#include "clang/Rewrite/Frontend/FixItRewriter.h" +#include "clang/Rewrite/Frontend/Rewriters.h" +#include "clang/Serialization/ASTReader.h" +#include "clang/Serialization/Module.h" +#include "clang/Serialization/ModuleManager.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/Support/CrashRecoveryContext.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include <memory> +#include <utility> + +using namespace clang; + +//===----------------------------------------------------------------------===// +// AST Consumer Actions +//===----------------------------------------------------------------------===// + +std::unique_ptr<ASTConsumer> +HTMLPrintAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + if (std::unique_ptr<raw_ostream> OS = + CI.createDefaultOutputFile(false, InFile)) + return CreateHTMLPrinter(std::move(OS), CI.getPreprocessor()); + return nullptr; +} + +FixItAction::FixItAction() {} +FixItAction::~FixItAction() {} + +std::unique_ptr<ASTConsumer> +FixItAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + return llvm::make_unique<ASTConsumer>(); +} + +namespace { +class FixItRewriteInPlace : public FixItOptions { +public: + FixItRewriteInPlace() { InPlace = true; } + + std::string RewriteFilename(const std::string &Filename, int &fd) override { + llvm_unreachable("don't call RewriteFilename for inplace rewrites"); + } +}; + +class FixItActionSuffixInserter : public FixItOptions { + std::string NewSuffix; + +public: + FixItActionSuffixInserter(std::string NewSuffix, bool FixWhatYouCan) + : NewSuffix(std::move(NewSuffix)) { + this->FixWhatYouCan = FixWhatYouCan; + } + + std::string RewriteFilename(const std::string &Filename, int &fd) override { + fd = -1; + SmallString<128> Path(Filename); + llvm::sys::path::replace_extension(Path, + NewSuffix + llvm::sys::path::extension(Path)); + return Path.str(); + } +}; + +class FixItRewriteToTemp : public FixItOptions { +public: + std::string RewriteFilename(const std::string &Filename, int &fd) override { + SmallString<128> Path; + llvm::sys::fs::createTemporaryFile(llvm::sys::path::filename(Filename), + llvm::sys::path::extension(Filename).drop_front(), fd, + Path); + return Path.str(); + } +}; +} // end anonymous namespace + +bool FixItAction::BeginSourceFileAction(CompilerInstance &CI) { + const FrontendOptions &FEOpts = getCompilerInstance().getFrontendOpts(); + if (!FEOpts.FixItSuffix.empty()) { + FixItOpts.reset(new FixItActionSuffixInserter(FEOpts.FixItSuffix, + FEOpts.FixWhatYouCan)); + } else { + FixItOpts.reset(new FixItRewriteInPlace); + FixItOpts->FixWhatYouCan = FEOpts.FixWhatYouCan; + } + Rewriter.reset(new FixItRewriter(CI.getDiagnostics(), CI.getSourceManager(), + CI.getLangOpts(), FixItOpts.get())); + return true; +} + +void FixItAction::EndSourceFileAction() { + // Otherwise rewrite all files. + Rewriter->WriteFixedFiles(); +} + +bool FixItRecompile::BeginInvocation(CompilerInstance &CI) { + + std::vector<std::pair<std::string, std::string> > RewrittenFiles; + bool err = false; + { + const FrontendOptions &FEOpts = CI.getFrontendOpts(); + std::unique_ptr<FrontendAction> FixAction(new SyntaxOnlyAction()); + if (FixAction->BeginSourceFile(CI, FEOpts.Inputs[0])) { + std::unique_ptr<FixItOptions> FixItOpts; + if (FEOpts.FixToTemporaries) + FixItOpts.reset(new FixItRewriteToTemp()); + else + FixItOpts.reset(new FixItRewriteInPlace()); + FixItOpts->Silent = true; + FixItOpts->FixWhatYouCan = FEOpts.FixWhatYouCan; + FixItOpts->FixOnlyWarnings = FEOpts.FixOnlyWarnings; + FixItRewriter Rewriter(CI.getDiagnostics(), CI.getSourceManager(), + CI.getLangOpts(), FixItOpts.get()); + FixAction->Execute(); + + err = Rewriter.WriteFixedFiles(&RewrittenFiles); + + FixAction->EndSourceFile(); + CI.setSourceManager(nullptr); + CI.setFileManager(nullptr); + } else { + err = true; + } + } + if (err) + return false; + CI.getDiagnosticClient().clear(); + CI.getDiagnostics().Reset(); + + PreprocessorOptions &PPOpts = CI.getPreprocessorOpts(); + PPOpts.RemappedFiles.insert(PPOpts.RemappedFiles.end(), + RewrittenFiles.begin(), RewrittenFiles.end()); + PPOpts.RemappedFilesKeepOriginalName = false; + + return true; +} + +#if CLANG_ENABLE_OBJC_REWRITER + +std::unique_ptr<ASTConsumer> +RewriteObjCAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + if (std::unique_ptr<raw_ostream> OS = + CI.createDefaultOutputFile(false, InFile, "cpp")) { + if (CI.getLangOpts().ObjCRuntime.isNonFragile()) + return CreateModernObjCRewriter( + InFile, std::move(OS), CI.getDiagnostics(), CI.getLangOpts(), + CI.getDiagnosticOpts().NoRewriteMacros, + (CI.getCodeGenOpts().getDebugInfo() != codegenoptions::NoDebugInfo)); + return CreateObjCRewriter(InFile, std::move(OS), CI.getDiagnostics(), + CI.getLangOpts(), + CI.getDiagnosticOpts().NoRewriteMacros); + } + return nullptr; +} + +#endif + +//===----------------------------------------------------------------------===// +// Preprocessor Actions +//===----------------------------------------------------------------------===// + +void RewriteMacrosAction::ExecuteAction() { + CompilerInstance &CI = getCompilerInstance(); + std::unique_ptr<raw_ostream> OS = + CI.createDefaultOutputFile(true, getCurrentFileOrBufferName()); + if (!OS) return; + + RewriteMacrosInInput(CI.getPreprocessor(), OS.get()); +} + +void RewriteTestAction::ExecuteAction() { + CompilerInstance &CI = getCompilerInstance(); + std::unique_ptr<raw_ostream> OS = + CI.createDefaultOutputFile(false, getCurrentFileOrBufferName()); + if (!OS) return; + + DoRewriteTest(CI.getPreprocessor(), OS.get()); +} + +class RewriteIncludesAction::RewriteImportsListener : public ASTReaderListener { + CompilerInstance &CI; + std::weak_ptr<raw_ostream> Out; + + llvm::DenseSet<const FileEntry*> Rewritten; + +public: + RewriteImportsListener(CompilerInstance &CI, std::shared_ptr<raw_ostream> Out) + : CI(CI), Out(Out) {} + + void visitModuleFile(StringRef Filename, + serialization::ModuleKind Kind) override { + auto *File = CI.getFileManager().getFile(Filename); + assert(File && "missing file for loaded module?"); + + // Only rewrite each module file once. + if (!Rewritten.insert(File).second) + return; + + serialization::ModuleFile *MF = + CI.getModuleManager()->getModuleManager().lookup(File); + assert(File && "missing module file for loaded module?"); + + // Not interested in PCH / preambles. + if (!MF->isModule()) + return; + + auto OS = Out.lock(); + assert(OS && "loaded module file after finishing rewrite action?"); + + (*OS) << "#pragma clang module build "; + if (isValidIdentifier(MF->ModuleName)) + (*OS) << MF->ModuleName; + else { + (*OS) << '"'; + OS->write_escaped(MF->ModuleName); + (*OS) << '"'; + } + (*OS) << '\n'; + + // Rewrite the contents of the module in a separate compiler instance. + CompilerInstance Instance(CI.getPCHContainerOperations(), + &CI.getPreprocessor().getPCMCache()); + Instance.setInvocation( + std::make_shared<CompilerInvocation>(CI.getInvocation())); + Instance.createDiagnostics( + new ForwardingDiagnosticConsumer(CI.getDiagnosticClient()), + /*ShouldOwnClient=*/true); + Instance.getFrontendOpts().DisableFree = false; + Instance.getFrontendOpts().Inputs.clear(); + Instance.getFrontendOpts().Inputs.emplace_back( + Filename, InputKind(InputKind::Unknown, InputKind::Precompiled)); + Instance.getFrontendOpts().ModuleFiles.clear(); + Instance.getFrontendOpts().ModuleMapFiles.clear(); + // Don't recursively rewrite imports. We handle them all at the top level. + Instance.getPreprocessorOutputOpts().RewriteImports = false; + + llvm::CrashRecoveryContext().RunSafelyOnThread([&]() { + RewriteIncludesAction Action; + Action.OutputStream = OS; + Instance.ExecuteAction(Action); + }); + + (*OS) << "#pragma clang module endbuild /*" << MF->ModuleName << "*/\n"; + } +}; + +bool RewriteIncludesAction::BeginSourceFileAction(CompilerInstance &CI) { + if (!OutputStream) { + OutputStream = + CI.createDefaultOutputFile(true, getCurrentFileOrBufferName()); + if (!OutputStream) + return false; + } + + auto &OS = *OutputStream; + + // If we're preprocessing a module map, start by dumping the contents of the + // module itself before switching to the input buffer. + auto &Input = getCurrentInput(); + if (Input.getKind().getFormat() == InputKind::ModuleMap) { + if (Input.isFile()) { + OS << "# 1 \""; + OS.write_escaped(Input.getFile()); + OS << "\"\n"; + } + getCurrentModule()->print(OS); + OS << "#pragma clang module contents\n"; + } + + // If we're rewriting imports, set up a listener to track when we import + // module files. + if (CI.getPreprocessorOutputOpts().RewriteImports) { + CI.createModuleManager(); + CI.getModuleManager()->addListener( + llvm::make_unique<RewriteImportsListener>(CI, OutputStream)); + } + + return true; +} + +void RewriteIncludesAction::ExecuteAction() { + CompilerInstance &CI = getCompilerInstance(); + + // If we're rewriting imports, emit the module build output first rather + // than switching back and forth (potentially in the middle of a line). + if (CI.getPreprocessorOutputOpts().RewriteImports) { + std::string Buffer; + llvm::raw_string_ostream OS(Buffer); + + RewriteIncludesInInput(CI.getPreprocessor(), &OS, + CI.getPreprocessorOutputOpts()); + + (*OutputStream) << OS.str(); + } else { + RewriteIncludesInInput(CI.getPreprocessor(), OutputStream.get(), + CI.getPreprocessorOutputOpts()); + } + + OutputStream.reset(); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/Rewrite/HTMLPrint.cpp b/contrib/llvm/tools/clang/lib/Frontend/Rewrite/HTMLPrint.cpp new file mode 100644 index 000000000000..34ee9673cc54 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/Rewrite/HTMLPrint.cpp @@ -0,0 +1,92 @@ +//===--- HTMLPrint.cpp - Source code -> HTML pretty-printing --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Pretty-printing of source code to HTML. +// +//===----------------------------------------------------------------------===// + +#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/Lex/Preprocessor.h" +#include "clang/Rewrite/Core/HTMLRewrite.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "clang/Rewrite/Frontend/ASTConsumers.h" +#include "llvm/Support/raw_ostream.h" +using namespace clang; + +//===----------------------------------------------------------------------===// +// Functional HTML pretty-printing. +//===----------------------------------------------------------------------===// + +namespace { + class HTMLPrinter : public ASTConsumer { + Rewriter R; + std::unique_ptr<raw_ostream> Out; + Preprocessor &PP; + bool SyntaxHighlight, HighlightMacros; + + public: + HTMLPrinter(std::unique_ptr<raw_ostream> OS, Preprocessor &pp, + bool _SyntaxHighlight, bool _HighlightMacros) + : Out(std::move(OS)), PP(pp), SyntaxHighlight(_SyntaxHighlight), + HighlightMacros(_HighlightMacros) {} + + void Initialize(ASTContext &context) override; + void HandleTranslationUnit(ASTContext &Ctx) override; + }; +} + +std::unique_ptr<ASTConsumer> +clang::CreateHTMLPrinter(std::unique_ptr<raw_ostream> OS, Preprocessor &PP, + bool SyntaxHighlight, bool HighlightMacros) { + return llvm::make_unique<HTMLPrinter>(std::move(OS), PP, SyntaxHighlight, + HighlightMacros); +} + +void HTMLPrinter::Initialize(ASTContext &context) { + R.setSourceMgr(context.getSourceManager(), context.getLangOpts()); +} + +void HTMLPrinter::HandleTranslationUnit(ASTContext &Ctx) { + if (PP.getDiagnostics().hasErrorOccurred()) + return; + + // Format the file. + FileID FID = R.getSourceMgr().getMainFileID(); + const FileEntry* Entry = R.getSourceMgr().getFileEntryForID(FID); + StringRef Name; + // In some cases, in particular the case where the input is from stdin, + // there is no entry. Fall back to the memory buffer for a name in those + // cases. + if (Entry) + Name = Entry->getName(); + else + Name = R.getSourceMgr().getBuffer(FID)->getBufferIdentifier(); + + html::AddLineNumbers(R, FID); + html::AddHeaderFooterInternalBuiltinCSS(R, FID, Name); + + // If we have a preprocessor, relex the file and syntax highlight. + // We might not have a preprocessor if we come from a deserialized AST file, + // for example. + + if (SyntaxHighlight) html::SyntaxHighlight(R, FID, PP); + if (HighlightMacros) html::HighlightMacros(R, FID, PP); + html::EscapeText(R, FID, false, true); + + // Emit the HTML. + const RewriteBuffer &RewriteBuf = R.getEditBuffer(FID); + std::unique_ptr<char[]> Buffer(new char[RewriteBuf.size()]); + std::copy(RewriteBuf.begin(), RewriteBuf.end(), Buffer.get()); + Out->write(Buffer.get(), RewriteBuf.size()); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/Rewrite/InclusionRewriter.cpp b/contrib/llvm/tools/clang/lib/Frontend/Rewrite/InclusionRewriter.cpp new file mode 100644 index 000000000000..2e7baa3d9581 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/Rewrite/InclusionRewriter.cpp @@ -0,0 +1,634 @@ +//===--- InclusionRewriter.cpp - Rewrite includes into their expansions ---===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This code rewrites include invocations into their expansions. This gives you +// a file with all included files merged into it. +// +//===----------------------------------------------------------------------===// + +#include "clang/Rewrite/Frontend/Rewriters.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Frontend/PreprocessorOutputOptions.h" +#include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/Pragma.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace llvm; + +namespace { + +class InclusionRewriter : public PPCallbacks { + /// Information about which #includes were actually performed, + /// created by preprocessor callbacks. + struct IncludedFile { + FileID Id; + SrcMgr::CharacteristicKind FileType; + const DirectoryLookup *DirLookup; + IncludedFile(FileID Id, SrcMgr::CharacteristicKind FileType, + const DirectoryLookup *DirLookup) + : Id(Id), FileType(FileType), DirLookup(DirLookup) {} + }; + Preprocessor &PP; ///< Used to find inclusion directives. + SourceManager &SM; ///< Used to read and manage source files. + raw_ostream &OS; ///< The destination stream for rewritten contents. + StringRef MainEOL; ///< The line ending marker to use. + const llvm::MemoryBuffer *PredefinesBuffer; ///< The preprocessor predefines. + bool ShowLineMarkers; ///< Show #line markers. + bool UseLineDirectives; ///< Use of line directives or line markers. + /// Tracks where inclusions that change the file are found. + std::map<unsigned, IncludedFile> FileIncludes; + /// Tracks where inclusions that import modules are found. + std::map<unsigned, const Module *> ModuleIncludes; + /// Tracks where inclusions that enter modules (in a module build) are found. + std::map<unsigned, const Module *> ModuleEntryIncludes; + /// Used transitively for building up the FileIncludes mapping over the + /// various \c PPCallbacks callbacks. + SourceLocation LastInclusionLocation; +public: + InclusionRewriter(Preprocessor &PP, raw_ostream &OS, bool ShowLineMarkers, + bool UseLineDirectives); + void Process(FileID FileId, SrcMgr::CharacteristicKind FileType, + const DirectoryLookup *DirLookup); + void setPredefinesBuffer(const llvm::MemoryBuffer *Buf) { + PredefinesBuffer = Buf; + } + void detectMainFileEOL(); + void handleModuleBegin(Token &Tok) { + assert(Tok.getKind() == tok::annot_module_begin); + ModuleEntryIncludes.insert({Tok.getLocation().getRawEncoding(), + (Module *)Tok.getAnnotationValue()}); + } +private: + void FileChanged(SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, + FileID PrevFID) override; + void FileSkipped(const FileEntry &SkippedFile, const Token &FilenameTok, + SrcMgr::CharacteristicKind FileType) override; + void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, + StringRef FileName, bool IsAngled, + CharSourceRange FilenameRange, const FileEntry *File, + StringRef SearchPath, StringRef RelativePath, + const Module *Imported, + SrcMgr::CharacteristicKind FileType) override; + void WriteLineInfo(StringRef Filename, int Line, + SrcMgr::CharacteristicKind FileType, + StringRef Extra = StringRef()); + void WriteImplicitModuleImport(const Module *Mod); + void OutputContentUpTo(const MemoryBuffer &FromFile, + unsigned &WriteFrom, unsigned WriteTo, + StringRef EOL, int &lines, + bool EnsureNewline); + void CommentOutDirective(Lexer &DirectivesLex, const Token &StartToken, + const MemoryBuffer &FromFile, StringRef EOL, + unsigned &NextToWrite, int &Lines); + bool HandleHasInclude(FileID FileId, Lexer &RawLex, + const DirectoryLookup *Lookup, Token &Tok, + bool &FileExists); + const IncludedFile *FindIncludeAtLocation(SourceLocation Loc) const; + const Module *FindModuleAtLocation(SourceLocation Loc) const; + const Module *FindEnteredModule(SourceLocation Loc) const; + StringRef NextIdentifierName(Lexer &RawLex, Token &RawToken); +}; + +} // end anonymous namespace + +/// Initializes an InclusionRewriter with a \p PP source and \p OS destination. +InclusionRewriter::InclusionRewriter(Preprocessor &PP, raw_ostream &OS, + bool ShowLineMarkers, + bool UseLineDirectives) + : PP(PP), SM(PP.getSourceManager()), OS(OS), MainEOL("\n"), + PredefinesBuffer(nullptr), ShowLineMarkers(ShowLineMarkers), + UseLineDirectives(UseLineDirectives), + LastInclusionLocation(SourceLocation()) {} + +/// Write appropriate line information as either #line directives or GNU line +/// markers depending on what mode we're in, including the \p Filename and +/// \p Line we are located at, using the specified \p EOL line separator, and +/// any \p Extra context specifiers in GNU line directives. +void InclusionRewriter::WriteLineInfo(StringRef Filename, int Line, + SrcMgr::CharacteristicKind FileType, + StringRef Extra) { + if (!ShowLineMarkers) + return; + if (UseLineDirectives) { + OS << "#line" << ' ' << Line << ' ' << '"'; + OS.write_escaped(Filename); + OS << '"'; + } else { + // Use GNU linemarkers as described here: + // http://gcc.gnu.org/onlinedocs/cpp/Preprocessor-Output.html + OS << '#' << ' ' << Line << ' ' << '"'; + OS.write_escaped(Filename); + OS << '"'; + if (!Extra.empty()) + OS << Extra; + if (FileType == SrcMgr::C_System) + // "`3' This indicates that the following text comes from a system header + // file, so certain warnings should be suppressed." + OS << " 3"; + else if (FileType == SrcMgr::C_ExternCSystem) + // as above for `3', plus "`4' This indicates that the following text + // should be treated as being wrapped in an implicit extern "C" block." + OS << " 3 4"; + } + OS << MainEOL; +} + +void InclusionRewriter::WriteImplicitModuleImport(const Module *Mod) { + OS << "#pragma clang module import " << Mod->getFullModuleName(true) + << " /* clang -frewrite-includes: implicit import */" << MainEOL; +} + +/// FileChanged - Whenever the preprocessor enters or exits a #include file +/// it invokes this handler. +void InclusionRewriter::FileChanged(SourceLocation Loc, + FileChangeReason Reason, + SrcMgr::CharacteristicKind NewFileType, + FileID) { + if (Reason != EnterFile) + return; + if (LastInclusionLocation.isInvalid()) + // we didn't reach this file (eg: the main file) via an inclusion directive + return; + FileID Id = FullSourceLoc(Loc, SM).getFileID(); + auto P = FileIncludes.insert( + std::make_pair(LastInclusionLocation.getRawEncoding(), + IncludedFile(Id, NewFileType, PP.GetCurDirLookup()))); + (void)P; + assert(P.second && "Unexpected revisitation of the same include directive"); + LastInclusionLocation = SourceLocation(); +} + +/// Called whenever an inclusion is skipped due to canonical header protection +/// macros. +void InclusionRewriter::FileSkipped(const FileEntry &/*SkippedFile*/, + const Token &/*FilenameTok*/, + SrcMgr::CharacteristicKind /*FileType*/) { + assert(LastInclusionLocation.isValid() && + "A file, that wasn't found via an inclusion directive, was skipped"); + LastInclusionLocation = SourceLocation(); +} + +/// This should be called whenever the preprocessor encounters include +/// directives. It does not say whether the file has been included, but it +/// provides more information about the directive (hash location instead +/// of location inside the included file). It is assumed that the matching +/// FileChanged() or FileSkipped() is called after this (or neither is +/// called if this #include results in an error or does not textually include +/// anything). +void InclusionRewriter::InclusionDirective(SourceLocation HashLoc, + const Token &/*IncludeTok*/, + StringRef /*FileName*/, + bool /*IsAngled*/, + CharSourceRange /*FilenameRange*/, + const FileEntry * /*File*/, + StringRef /*SearchPath*/, + StringRef /*RelativePath*/, + const Module *Imported, + SrcMgr::CharacteristicKind FileType){ + if (Imported) { + auto P = ModuleIncludes.insert( + std::make_pair(HashLoc.getRawEncoding(), Imported)); + (void)P; + assert(P.second && "Unexpected revisitation of the same include directive"); + } else + LastInclusionLocation = HashLoc; +} + +/// Simple lookup for a SourceLocation (specifically one denoting the hash in +/// an inclusion directive) in the map of inclusion information, FileChanges. +const InclusionRewriter::IncludedFile * +InclusionRewriter::FindIncludeAtLocation(SourceLocation Loc) const { + const auto I = FileIncludes.find(Loc.getRawEncoding()); + if (I != FileIncludes.end()) + return &I->second; + return nullptr; +} + +/// Simple lookup for a SourceLocation (specifically one denoting the hash in +/// an inclusion directive) in the map of module inclusion information. +const Module * +InclusionRewriter::FindModuleAtLocation(SourceLocation Loc) const { + const auto I = ModuleIncludes.find(Loc.getRawEncoding()); + if (I != ModuleIncludes.end()) + return I->second; + return nullptr; +} + +/// Simple lookup for a SourceLocation (specifically one denoting the hash in +/// an inclusion directive) in the map of module entry information. +const Module * +InclusionRewriter::FindEnteredModule(SourceLocation Loc) const { + const auto I = ModuleEntryIncludes.find(Loc.getRawEncoding()); + if (I != ModuleEntryIncludes.end()) + return I->second; + return nullptr; +} + +/// Detect the likely line ending style of \p FromFile by examining the first +/// newline found within it. +static StringRef DetectEOL(const MemoryBuffer &FromFile) { + // Detect what line endings the file uses, so that added content does not mix + // the style. We need to check for "\r\n" first because "\n\r" will match + // "\r\n\r\n". + const char *Pos = strchr(FromFile.getBufferStart(), '\n'); + if (!Pos) + return "\n"; + if (Pos - 1 >= FromFile.getBufferStart() && Pos[-1] == '\r') + return "\r\n"; + if (Pos + 1 < FromFile.getBufferEnd() && Pos[1] == '\r') + return "\n\r"; + return "\n"; +} + +void InclusionRewriter::detectMainFileEOL() { + bool Invalid; + const MemoryBuffer &FromFile = *SM.getBuffer(SM.getMainFileID(), &Invalid); + assert(!Invalid); + if (Invalid) + return; // Should never happen, but whatever. + MainEOL = DetectEOL(FromFile); +} + +/// Writes out bytes from \p FromFile, starting at \p NextToWrite and ending at +/// \p WriteTo - 1. +void InclusionRewriter::OutputContentUpTo(const MemoryBuffer &FromFile, + unsigned &WriteFrom, unsigned WriteTo, + StringRef LocalEOL, int &Line, + bool EnsureNewline) { + if (WriteTo <= WriteFrom) + return; + if (&FromFile == PredefinesBuffer) { + // Ignore the #defines of the predefines buffer. + WriteFrom = WriteTo; + return; + } + + // If we would output half of a line ending, advance one character to output + // the whole line ending. All buffers are null terminated, so looking ahead + // one byte is safe. + if (LocalEOL.size() == 2 && + LocalEOL[0] == (FromFile.getBufferStart() + WriteTo)[-1] && + LocalEOL[1] == (FromFile.getBufferStart() + WriteTo)[0]) + WriteTo++; + + StringRef TextToWrite(FromFile.getBufferStart() + WriteFrom, + WriteTo - WriteFrom); + + if (MainEOL == LocalEOL) { + OS << TextToWrite; + // count lines manually, it's faster than getPresumedLoc() + Line += TextToWrite.count(LocalEOL); + if (EnsureNewline && !TextToWrite.endswith(LocalEOL)) + OS << MainEOL; + } else { + // Output the file one line at a time, rewriting the line endings as we go. + StringRef Rest = TextToWrite; + while (!Rest.empty()) { + StringRef LineText; + std::tie(LineText, Rest) = Rest.split(LocalEOL); + OS << LineText; + Line++; + if (!Rest.empty()) + OS << MainEOL; + } + if (TextToWrite.endswith(LocalEOL) || EnsureNewline) + OS << MainEOL; + } + WriteFrom = WriteTo; +} + +/// Print characters from \p FromFile starting at \p NextToWrite up until the +/// inclusion directive at \p StartToken, then print out the inclusion +/// inclusion directive disabled by a #if directive, updating \p NextToWrite +/// and \p Line to track the number of source lines visited and the progress +/// through the \p FromFile buffer. +void InclusionRewriter::CommentOutDirective(Lexer &DirectiveLex, + const Token &StartToken, + const MemoryBuffer &FromFile, + StringRef LocalEOL, + unsigned &NextToWrite, int &Line) { + OutputContentUpTo(FromFile, NextToWrite, + SM.getFileOffset(StartToken.getLocation()), LocalEOL, Line, + false); + Token DirectiveToken; + do { + DirectiveLex.LexFromRawLexer(DirectiveToken); + } while (!DirectiveToken.is(tok::eod) && DirectiveToken.isNot(tok::eof)); + if (&FromFile == PredefinesBuffer) { + // OutputContentUpTo() would not output anything anyway. + return; + } + OS << "#if 0 /* expanded by -frewrite-includes */" << MainEOL; + OutputContentUpTo(FromFile, NextToWrite, + SM.getFileOffset(DirectiveToken.getLocation()) + + DirectiveToken.getLength(), + LocalEOL, Line, true); + OS << "#endif /* expanded by -frewrite-includes */" << MainEOL; +} + +/// Find the next identifier in the pragma directive specified by \p RawToken. +StringRef InclusionRewriter::NextIdentifierName(Lexer &RawLex, + Token &RawToken) { + RawLex.LexFromRawLexer(RawToken); + if (RawToken.is(tok::raw_identifier)) + PP.LookUpIdentifierInfo(RawToken); + if (RawToken.is(tok::identifier)) + return RawToken.getIdentifierInfo()->getName(); + return StringRef(); +} + +// Expand __has_include and __has_include_next if possible. If there's no +// definitive answer return false. +bool InclusionRewriter::HandleHasInclude( + FileID FileId, Lexer &RawLex, const DirectoryLookup *Lookup, Token &Tok, + bool &FileExists) { + // Lex the opening paren. + RawLex.LexFromRawLexer(Tok); + if (Tok.isNot(tok::l_paren)) + return false; + + RawLex.LexFromRawLexer(Tok); + + SmallString<128> FilenameBuffer; + StringRef Filename; + // Since the raw lexer doesn't give us angle_literals we have to parse them + // ourselves. + // FIXME: What to do if the file name is a macro? + if (Tok.is(tok::less)) { + RawLex.LexFromRawLexer(Tok); + + FilenameBuffer += '<'; + do { + if (Tok.is(tok::eod)) // Sanity check. + return false; + + if (Tok.is(tok::raw_identifier)) + PP.LookUpIdentifierInfo(Tok); + + // Get the string piece. + SmallVector<char, 128> TmpBuffer; + bool Invalid = false; + StringRef TmpName = PP.getSpelling(Tok, TmpBuffer, &Invalid); + if (Invalid) + return false; + + FilenameBuffer += TmpName; + + RawLex.LexFromRawLexer(Tok); + } while (Tok.isNot(tok::greater)); + + FilenameBuffer += '>'; + Filename = FilenameBuffer; + } else { + if (Tok.isNot(tok::string_literal)) + return false; + + bool Invalid = false; + Filename = PP.getSpelling(Tok, FilenameBuffer, &Invalid); + if (Invalid) + return false; + } + + // Lex the closing paren. + RawLex.LexFromRawLexer(Tok); + if (Tok.isNot(tok::r_paren)) + return false; + + // Now ask HeaderInfo if it knows about the header. + // FIXME: Subframeworks aren't handled here. Do we care? + bool isAngled = PP.GetIncludeFilenameSpelling(Tok.getLocation(), Filename); + const DirectoryLookup *CurDir; + const FileEntry *FileEnt = PP.getSourceManager().getFileEntryForID(FileId); + SmallVector<std::pair<const FileEntry *, const DirectoryEntry *>, 1> + Includers; + Includers.push_back(std::make_pair(FileEnt, FileEnt->getDir())); + // FIXME: Why don't we call PP.LookupFile here? + const FileEntry *File = PP.getHeaderSearchInfo().LookupFile( + Filename, SourceLocation(), isAngled, Lookup, CurDir, Includers, nullptr, + nullptr, nullptr, nullptr, nullptr); + + FileExists = File != nullptr; + return true; +} + +/// Use a raw lexer to analyze \p FileId, incrementally copying parts of it +/// and including content of included files recursively. +void InclusionRewriter::Process(FileID FileId, + SrcMgr::CharacteristicKind FileType, + const DirectoryLookup *DirLookup) { + bool Invalid; + const MemoryBuffer &FromFile = *SM.getBuffer(FileId, &Invalid); + assert(!Invalid && "Attempting to process invalid inclusion"); + StringRef FileName = FromFile.getBufferIdentifier(); + Lexer RawLex(FileId, &FromFile, PP.getSourceManager(), PP.getLangOpts()); + RawLex.SetCommentRetentionState(false); + + StringRef LocalEOL = DetectEOL(FromFile); + + // Per the GNU docs: "1" indicates entering a new file. + if (FileId == SM.getMainFileID() || FileId == PP.getPredefinesFileID()) + WriteLineInfo(FileName, 1, FileType, ""); + else + WriteLineInfo(FileName, 1, FileType, " 1"); + + if (SM.getFileIDSize(FileId) == 0) + return; + + // The next byte to be copied from the source file, which may be non-zero if + // the lexer handled a BOM. + unsigned NextToWrite = SM.getFileOffset(RawLex.getSourceLocation()); + assert(SM.getLineNumber(FileId, NextToWrite) == 1); + int Line = 1; // The current input file line number. + + Token RawToken; + RawLex.LexFromRawLexer(RawToken); + + // TODO: Consider adding a switch that strips possibly unimportant content, + // such as comments, to reduce the size of repro files. + while (RawToken.isNot(tok::eof)) { + if (RawToken.is(tok::hash) && RawToken.isAtStartOfLine()) { + RawLex.setParsingPreprocessorDirective(true); + Token HashToken = RawToken; + RawLex.LexFromRawLexer(RawToken); + if (RawToken.is(tok::raw_identifier)) + PP.LookUpIdentifierInfo(RawToken); + if (RawToken.getIdentifierInfo() != nullptr) { + switch (RawToken.getIdentifierInfo()->getPPKeywordID()) { + case tok::pp_include: + case tok::pp_include_next: + case tok::pp_import: { + CommentOutDirective(RawLex, HashToken, FromFile, LocalEOL, NextToWrite, + Line); + if (FileId != PP.getPredefinesFileID()) + WriteLineInfo(FileName, Line - 1, FileType, ""); + StringRef LineInfoExtra; + SourceLocation Loc = HashToken.getLocation(); + if (const Module *Mod = FindModuleAtLocation(Loc)) + WriteImplicitModuleImport(Mod); + else if (const IncludedFile *Inc = FindIncludeAtLocation(Loc)) { + const Module *Mod = FindEnteredModule(Loc); + if (Mod) + OS << "#pragma clang module begin " + << Mod->getFullModuleName(true) << "\n"; + + // Include and recursively process the file. + Process(Inc->Id, Inc->FileType, Inc->DirLookup); + + if (Mod) + OS << "#pragma clang module end /*" + << Mod->getFullModuleName(true) << "*/\n"; + + // Add line marker to indicate we're returning from an included + // file. + LineInfoExtra = " 2"; + } + // fix up lineinfo (since commented out directive changed line + // numbers) for inclusions that were skipped due to header guards + WriteLineInfo(FileName, Line, FileType, LineInfoExtra); + break; + } + case tok::pp_pragma: { + StringRef Identifier = NextIdentifierName(RawLex, RawToken); + if (Identifier == "clang" || Identifier == "GCC") { + if (NextIdentifierName(RawLex, RawToken) == "system_header") { + // keep the directive in, commented out + CommentOutDirective(RawLex, HashToken, FromFile, LocalEOL, + NextToWrite, Line); + // update our own type + FileType = SM.getFileCharacteristic(RawToken.getLocation()); + WriteLineInfo(FileName, Line, FileType); + } + } else if (Identifier == "once") { + // keep the directive in, commented out + CommentOutDirective(RawLex, HashToken, FromFile, LocalEOL, + NextToWrite, Line); + WriteLineInfo(FileName, Line, FileType); + } + break; + } + case tok::pp_if: + case tok::pp_elif: { + bool elif = (RawToken.getIdentifierInfo()->getPPKeywordID() == + tok::pp_elif); + // Rewrite special builtin macros to avoid pulling in host details. + do { + // Walk over the directive. + RawLex.LexFromRawLexer(RawToken); + if (RawToken.is(tok::raw_identifier)) + PP.LookUpIdentifierInfo(RawToken); + + if (RawToken.is(tok::identifier)) { + bool HasFile; + SourceLocation Loc = RawToken.getLocation(); + + // Rewrite __has_include(x) + if (RawToken.getIdentifierInfo()->isStr("__has_include")) { + if (!HandleHasInclude(FileId, RawLex, nullptr, RawToken, + HasFile)) + continue; + // Rewrite __has_include_next(x) + } else if (RawToken.getIdentifierInfo()->isStr( + "__has_include_next")) { + if (DirLookup) + ++DirLookup; + + if (!HandleHasInclude(FileId, RawLex, DirLookup, RawToken, + HasFile)) + continue; + } else { + continue; + } + // Replace the macro with (0) or (1), followed by the commented + // out macro for reference. + OutputContentUpTo(FromFile, NextToWrite, SM.getFileOffset(Loc), + LocalEOL, Line, false); + OS << '(' << (int) HasFile << ")/*"; + OutputContentUpTo(FromFile, NextToWrite, + SM.getFileOffset(RawToken.getLocation()) + + RawToken.getLength(), + LocalEOL, Line, false); + OS << "*/"; + } + } while (RawToken.isNot(tok::eod)); + if (elif) { + OutputContentUpTo(FromFile, NextToWrite, + SM.getFileOffset(RawToken.getLocation()) + + RawToken.getLength(), + LocalEOL, Line, /*EnsureNewline=*/ true); + WriteLineInfo(FileName, Line, FileType); + } + break; + } + case tok::pp_endif: + case tok::pp_else: { + // We surround every #include by #if 0 to comment it out, but that + // changes line numbers. These are fixed up right after that, but + // the whole #include could be inside a preprocessor conditional + // that is not processed. So it is necessary to fix the line + // numbers one the next line after each #else/#endif as well. + RawLex.SetKeepWhitespaceMode(true); + do { + RawLex.LexFromRawLexer(RawToken); + } while (RawToken.isNot(tok::eod) && RawToken.isNot(tok::eof)); + OutputContentUpTo(FromFile, NextToWrite, + SM.getFileOffset(RawToken.getLocation()) + + RawToken.getLength(), + LocalEOL, Line, /*EnsureNewline=*/ true); + WriteLineInfo(FileName, Line, FileType); + RawLex.SetKeepWhitespaceMode(false); + break; + } + default: + break; + } + } + RawLex.setParsingPreprocessorDirective(false); + } + RawLex.LexFromRawLexer(RawToken); + } + OutputContentUpTo(FromFile, NextToWrite, + SM.getFileOffset(SM.getLocForEndOfFile(FileId)), LocalEOL, + Line, /*EnsureNewline=*/true); +} + +/// InclusionRewriterInInput - Implement -frewrite-includes mode. +void clang::RewriteIncludesInInput(Preprocessor &PP, raw_ostream *OS, + const PreprocessorOutputOptions &Opts) { + SourceManager &SM = PP.getSourceManager(); + InclusionRewriter *Rewrite = new InclusionRewriter( + PP, *OS, Opts.ShowLineMarkers, Opts.UseLineDirectives); + Rewrite->detectMainFileEOL(); + + PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Rewrite)); + PP.IgnorePragmas(); + + // First let the preprocessor process the entire file and call callbacks. + // Callbacks will record which #include's were actually performed. + PP.EnterMainSourceFile(); + Token Tok; + // Only preprocessor directives matter here, so disable macro expansion + // everywhere else as an optimization. + // TODO: It would be even faster if the preprocessor could be switched + // to a mode where it would parse only preprocessor directives and comments, + // nothing else matters for parsing or processing. + PP.SetMacroExpansionOnlyInDirectives(); + do { + PP.Lex(Tok); + if (Tok.is(tok::annot_module_begin)) + Rewrite->handleModuleBegin(Tok); + } while (Tok.isNot(tok::eof)); + Rewrite->setPredefinesBuffer(SM.getBuffer(PP.getPredefinesFileID())); + Rewrite->Process(PP.getPredefinesFileID(), SrcMgr::C_User, nullptr); + Rewrite->Process(SM.getMainFileID(), SrcMgr::C_User, nullptr); + OS->flush(); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/Rewrite/RewriteMacros.cpp b/contrib/llvm/tools/clang/lib/Frontend/Rewrite/RewriteMacros.cpp new file mode 100644 index 000000000000..ae6b51bc814f --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/Rewrite/RewriteMacros.cpp @@ -0,0 +1,217 @@ +//===--- RewriteMacros.cpp - Rewrite macros into their expansions ---------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This code rewrites macro invocations into their expansions. This gives you +// a macro expanded file that retains comments and #includes. +// +//===----------------------------------------------------------------------===// + +#include "clang/Rewrite/Frontend/Rewriters.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include <cstdio> +#include <memory> + +using namespace clang; + +/// isSameToken - Return true if the two specified tokens start have the same +/// content. +static bool isSameToken(Token &RawTok, Token &PPTok) { + // If two tokens have the same kind and the same identifier info, they are + // obviously the same. + if (PPTok.getKind() == RawTok.getKind() && + PPTok.getIdentifierInfo() == RawTok.getIdentifierInfo()) + return true; + + // Otherwise, if they are different but have the same identifier info, they + // are also considered to be the same. This allows keywords and raw lexed + // identifiers with the same name to be treated the same. + if (PPTok.getIdentifierInfo() && + PPTok.getIdentifierInfo() == RawTok.getIdentifierInfo()) + return true; + + return false; +} + + +/// GetNextRawTok - Return the next raw token in the stream, skipping over +/// comments if ReturnComment is false. +static const Token &GetNextRawTok(const std::vector<Token> &RawTokens, + unsigned &CurTok, bool ReturnComment) { + assert(CurTok < RawTokens.size() && "Overran eof!"); + + // If the client doesn't want comments and we have one, skip it. + if (!ReturnComment && RawTokens[CurTok].is(tok::comment)) + ++CurTok; + + return RawTokens[CurTok++]; +} + + +/// LexRawTokensFromMainFile - Lets all the raw tokens from the main file into +/// the specified vector. +static void LexRawTokensFromMainFile(Preprocessor &PP, + std::vector<Token> &RawTokens) { + SourceManager &SM = PP.getSourceManager(); + + // Create a lexer to lex all the tokens of the main file in raw mode. Even + // though it is in raw mode, it will not return comments. + const llvm::MemoryBuffer *FromFile = SM.getBuffer(SM.getMainFileID()); + Lexer RawLex(SM.getMainFileID(), FromFile, SM, PP.getLangOpts()); + + // Switch on comment lexing because we really do want them. + RawLex.SetCommentRetentionState(true); + + Token RawTok; + do { + RawLex.LexFromRawLexer(RawTok); + + // If we have an identifier with no identifier info for our raw token, look + // up the identifier info. This is important for equality comparison of + // identifier tokens. + if (RawTok.is(tok::raw_identifier)) + PP.LookUpIdentifierInfo(RawTok); + + RawTokens.push_back(RawTok); + } while (RawTok.isNot(tok::eof)); +} + + +/// RewriteMacrosInInput - Implement -rewrite-macros mode. +void clang::RewriteMacrosInInput(Preprocessor &PP, raw_ostream *OS) { + SourceManager &SM = PP.getSourceManager(); + + Rewriter Rewrite; + Rewrite.setSourceMgr(SM, PP.getLangOpts()); + RewriteBuffer &RB = Rewrite.getEditBuffer(SM.getMainFileID()); + + std::vector<Token> RawTokens; + LexRawTokensFromMainFile(PP, RawTokens); + unsigned CurRawTok = 0; + Token RawTok = GetNextRawTok(RawTokens, CurRawTok, false); + + + // Get the first preprocessing token. + PP.EnterMainSourceFile(); + Token PPTok; + PP.Lex(PPTok); + + // Preprocess the input file in parallel with raw lexing the main file. Ignore + // all tokens that are preprocessed from a file other than the main file (e.g. + // a header). If we see tokens that are in the preprocessed file but not the + // lexed file, we have a macro expansion. If we see tokens in the lexed file + // that aren't in the preprocessed view, we have macros that expand to no + // tokens, or macro arguments etc. + while (RawTok.isNot(tok::eof) || PPTok.isNot(tok::eof)) { + SourceLocation PPLoc = SM.getExpansionLoc(PPTok.getLocation()); + + // If PPTok is from a different source file, ignore it. + if (!SM.isWrittenInMainFile(PPLoc)) { + PP.Lex(PPTok); + continue; + } + + // If the raw file hits a preprocessor directive, they will be extra tokens + // in the raw file that don't exist in the preprocsesed file. However, we + // choose to preserve them in the output file and otherwise handle them + // specially. + if (RawTok.is(tok::hash) && RawTok.isAtStartOfLine()) { + // If this is a #warning directive or #pragma mark (GNU extensions), + // comment the line out. + if (RawTokens[CurRawTok].is(tok::identifier)) { + const IdentifierInfo *II = RawTokens[CurRawTok].getIdentifierInfo(); + if (II->getName() == "warning") { + // Comment out #warning. + RB.InsertTextAfter(SM.getFileOffset(RawTok.getLocation()), "//"); + } else if (II->getName() == "pragma" && + RawTokens[CurRawTok+1].is(tok::identifier) && + (RawTokens[CurRawTok+1].getIdentifierInfo()->getName() == + "mark")) { + // Comment out #pragma mark. + RB.InsertTextAfter(SM.getFileOffset(RawTok.getLocation()), "//"); + } + } + + // Otherwise, if this is a #include or some other directive, just leave it + // in the file by skipping over the line. + RawTok = GetNextRawTok(RawTokens, CurRawTok, false); + while (!RawTok.isAtStartOfLine() && RawTok.isNot(tok::eof)) + RawTok = GetNextRawTok(RawTokens, CurRawTok, false); + continue; + } + + // Okay, both tokens are from the same file. Get their offsets from the + // start of the file. + unsigned PPOffs = SM.getFileOffset(PPLoc); + unsigned RawOffs = SM.getFileOffset(RawTok.getLocation()); + + // If the offsets are the same and the token kind is the same, ignore them. + if (PPOffs == RawOffs && isSameToken(RawTok, PPTok)) { + RawTok = GetNextRawTok(RawTokens, CurRawTok, false); + PP.Lex(PPTok); + continue; + } + + // If the PP token is farther along than the raw token, something was + // deleted. Comment out the raw token. + if (RawOffs <= PPOffs) { + // Comment out a whole run of tokens instead of bracketing each one with + // comments. Add a leading space if RawTok didn't have one. + bool HasSpace = RawTok.hasLeadingSpace(); + RB.InsertTextAfter(RawOffs, &" /*"[HasSpace]); + unsigned EndPos; + + do { + EndPos = RawOffs+RawTok.getLength(); + + RawTok = GetNextRawTok(RawTokens, CurRawTok, true); + RawOffs = SM.getFileOffset(RawTok.getLocation()); + + if (RawTok.is(tok::comment)) { + // Skip past the comment. + RawTok = GetNextRawTok(RawTokens, CurRawTok, false); + break; + } + + } while (RawOffs <= PPOffs && !RawTok.isAtStartOfLine() && + (PPOffs != RawOffs || !isSameToken(RawTok, PPTok))); + + RB.InsertTextBefore(EndPos, "*/"); + continue; + } + + // Otherwise, there was a replacement an expansion. Insert the new token + // in the output buffer. Insert the whole run of new tokens at once to get + // them in the right order. + unsigned InsertPos = PPOffs; + std::string Expansion; + while (PPOffs < RawOffs) { + Expansion += ' ' + PP.getSpelling(PPTok); + PP.Lex(PPTok); + PPLoc = SM.getExpansionLoc(PPTok.getLocation()); + PPOffs = SM.getFileOffset(PPLoc); + } + Expansion += ' '; + RB.InsertTextBefore(InsertPos, Expansion); + } + + // Get the buffer corresponding to MainFileID. If we haven't changed it, then + // we are done. + if (const RewriteBuffer *RewriteBuf = + Rewrite.getRewriteBufferFor(SM.getMainFileID())) { + //printf("Changed:\n"); + *OS << std::string(RewriteBuf->begin(), RewriteBuf->end()); + } else { + fprintf(stderr, "No changes\n"); + } + OS->flush(); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/Rewrite/RewriteModernObjC.cpp b/contrib/llvm/tools/clang/lib/Frontend/Rewrite/RewriteModernObjC.cpp new file mode 100644 index 000000000000..10ca9a785699 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/Rewrite/RewriteModernObjC.cpp @@ -0,0 +1,7575 @@ +//===--- RewriteObjC.cpp - Playground for the code rewriter ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Hacks and fun related to the code rewriter. +// +//===----------------------------------------------------------------------===// + +#include "clang/Rewrite/Frontend/ASTConsumers.h" +#include "clang/AST/AST.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/Attr.h" +#include "clang/AST/ParentMap.h" +#include "clang/Basic/CharInfo.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Config/config.h" +#include "clang/Lex/Lexer.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include <memory> + +#if CLANG_ENABLE_OBJC_REWRITER + +using namespace clang; +using llvm::utostr; + +namespace { + class RewriteModernObjC : public ASTConsumer { + protected: + + enum { + BLOCK_FIELD_IS_OBJECT = 3, /* id, NSObject, __attribute__((NSObject)), + block, ... */ + BLOCK_FIELD_IS_BLOCK = 7, /* a block variable */ + BLOCK_FIELD_IS_BYREF = 8, /* the on stack structure holding the + __block variable */ + BLOCK_FIELD_IS_WEAK = 16, /* declared __weak, only used in byref copy + helpers */ + BLOCK_BYREF_CALLER = 128, /* called from __block (byref) copy/dispose + support routines */ + BLOCK_BYREF_CURRENT_MAX = 256 + }; + + enum { + BLOCK_NEEDS_FREE = (1 << 24), + BLOCK_HAS_COPY_DISPOSE = (1 << 25), + BLOCK_HAS_CXX_OBJ = (1 << 26), + BLOCK_IS_GC = (1 << 27), + BLOCK_IS_GLOBAL = (1 << 28), + BLOCK_HAS_DESCRIPTOR = (1 << 29) + }; + + Rewriter Rewrite; + DiagnosticsEngine &Diags; + const LangOptions &LangOpts; + ASTContext *Context; + SourceManager *SM; + TranslationUnitDecl *TUDecl; + FileID MainFileID; + const char *MainFileStart, *MainFileEnd; + Stmt *CurrentBody; + ParentMap *PropParentMap; // created lazily. + std::string InFileName; + std::unique_ptr<raw_ostream> OutFile; + std::string Preamble; + + TypeDecl *ProtocolTypeDecl; + VarDecl *GlobalVarDecl; + Expr *GlobalConstructionExp; + unsigned RewriteFailedDiag; + unsigned GlobalBlockRewriteFailedDiag; + // ObjC string constant support. + unsigned NumObjCStringLiterals; + VarDecl *ConstantStringClassReference; + RecordDecl *NSStringRecord; + + // ObjC foreach break/continue generation support. + int BcLabelCount; + + unsigned TryFinallyContainsReturnDiag; + // Needed for super. + ObjCMethodDecl *CurMethodDef; + RecordDecl *SuperStructDecl; + RecordDecl *ConstantStringDecl; + + FunctionDecl *MsgSendFunctionDecl; + FunctionDecl *MsgSendSuperFunctionDecl; + FunctionDecl *MsgSendStretFunctionDecl; + FunctionDecl *MsgSendSuperStretFunctionDecl; + FunctionDecl *MsgSendFpretFunctionDecl; + FunctionDecl *GetClassFunctionDecl; + FunctionDecl *GetMetaClassFunctionDecl; + FunctionDecl *GetSuperClassFunctionDecl; + FunctionDecl *SelGetUidFunctionDecl; + FunctionDecl *CFStringFunctionDecl; + FunctionDecl *SuperConstructorFunctionDecl; + FunctionDecl *CurFunctionDef; + + /* Misc. containers needed for meta-data rewrite. */ + SmallVector<ObjCImplementationDecl *, 8> ClassImplementation; + SmallVector<ObjCCategoryImplDecl *, 8> CategoryImplementation; + llvm::SmallPtrSet<ObjCInterfaceDecl*, 8> ObjCSynthesizedStructs; + llvm::SmallPtrSet<ObjCProtocolDecl*, 8> ObjCSynthesizedProtocols; + llvm::SmallPtrSet<ObjCInterfaceDecl*, 8> ObjCWrittenInterfaces; + llvm::SmallPtrSet<TagDecl*, 32> GlobalDefinedTags; + SmallVector<ObjCInterfaceDecl*, 32> ObjCInterfacesSeen; + /// DefinedNonLazyClasses - List of defined "non-lazy" classes. + SmallVector<ObjCInterfaceDecl*, 8> DefinedNonLazyClasses; + + /// DefinedNonLazyCategories - List of defined "non-lazy" categories. + SmallVector<ObjCCategoryDecl *, 8> DefinedNonLazyCategories; + + SmallVector<Stmt *, 32> Stmts; + SmallVector<int, 8> ObjCBcLabelNo; + // Remember all the @protocol(<expr>) expressions. + llvm::SmallPtrSet<ObjCProtocolDecl *, 32> ProtocolExprDecls; + + llvm::DenseSet<uint64_t> CopyDestroyCache; + + // Block expressions. + SmallVector<BlockExpr *, 32> Blocks; + SmallVector<int, 32> InnerDeclRefsCount; + SmallVector<DeclRefExpr *, 32> InnerDeclRefs; + + SmallVector<DeclRefExpr *, 32> BlockDeclRefs; + + // Block related declarations. + SmallVector<ValueDecl *, 8> BlockByCopyDecls; + llvm::SmallPtrSet<ValueDecl *, 8> BlockByCopyDeclsPtrSet; + SmallVector<ValueDecl *, 8> BlockByRefDecls; + llvm::SmallPtrSet<ValueDecl *, 8> BlockByRefDeclsPtrSet; + llvm::DenseMap<ValueDecl *, unsigned> BlockByRefDeclNo; + llvm::SmallPtrSet<ValueDecl *, 8> ImportedBlockDecls; + llvm::SmallPtrSet<VarDecl *, 8> ImportedLocalExternalDecls; + + llvm::DenseMap<BlockExpr *, std::string> RewrittenBlockExprs; + llvm::DenseMap<ObjCInterfaceDecl *, + llvm::SmallSetVector<ObjCIvarDecl *, 8> > ReferencedIvars; + + // ivar bitfield grouping containers + llvm::DenseSet<const ObjCInterfaceDecl *> ObjCInterefaceHasBitfieldGroups; + llvm::DenseMap<const ObjCIvarDecl* , unsigned> IvarGroupNumber; + // This container maps an <class, group number for ivar> tuple to the type + // of the struct where the bitfield belongs. + llvm::DenseMap<std::pair<const ObjCInterfaceDecl*, unsigned>, QualType> GroupRecordType; + SmallVector<FunctionDecl*, 32> FunctionDefinitionsSeen; + + // This maps an original source AST to it's rewritten form. This allows + // us to avoid rewriting the same node twice (which is very uncommon). + // This is needed to support some of the exotic property rewriting. + llvm::DenseMap<Stmt *, Stmt *> ReplacedNodes; + + // Needed for header files being rewritten + bool IsHeader; + bool SilenceRewriteMacroWarning; + bool GenerateLineInfo; + bool objc_impl_method; + + bool DisableReplaceStmt; + class DisableReplaceStmtScope { + RewriteModernObjC &R; + bool SavedValue; + + public: + DisableReplaceStmtScope(RewriteModernObjC &R) + : R(R), SavedValue(R.DisableReplaceStmt) { + R.DisableReplaceStmt = true; + } + ~DisableReplaceStmtScope() { + R.DisableReplaceStmt = SavedValue; + } + }; + void InitializeCommon(ASTContext &context); + + public: + llvm::DenseMap<ObjCMethodDecl*, std::string> MethodInternalNames; + + // Top Level Driver code. + bool HandleTopLevelDecl(DeclGroupRef D) override { + for (DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; ++I) { + if (ObjCInterfaceDecl *Class = dyn_cast<ObjCInterfaceDecl>(*I)) { + if (!Class->isThisDeclarationADefinition()) { + RewriteForwardClassDecl(D); + break; + } else { + // Keep track of all interface declarations seen. + ObjCInterfacesSeen.push_back(Class); + break; + } + } + + if (ObjCProtocolDecl *Proto = dyn_cast<ObjCProtocolDecl>(*I)) { + if (!Proto->isThisDeclarationADefinition()) { + RewriteForwardProtocolDecl(D); + break; + } + } + + if (FunctionDecl *FDecl = dyn_cast<FunctionDecl>(*I)) { + // Under modern abi, we cannot translate body of the function + // yet until all class extensions and its implementation is seen. + // This is because they may introduce new bitfields which must go + // into their grouping struct. + if (FDecl->isThisDeclarationADefinition() && + // Not c functions defined inside an objc container. + !FDecl->isTopLevelDeclInObjCContainer()) { + FunctionDefinitionsSeen.push_back(FDecl); + break; + } + } + HandleTopLevelSingleDecl(*I); + } + return true; + } + + void HandleTopLevelDeclInObjCContainer(DeclGroupRef D) override { + for (DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; ++I) { + if (TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(*I)) { + if (isTopLevelBlockPointerType(TD->getUnderlyingType())) + RewriteBlockPointerDecl(TD); + else if (TD->getUnderlyingType()->isFunctionPointerType()) + CheckFunctionPointerDecl(TD->getUnderlyingType(), TD); + else + RewriteObjCQualifiedInterfaceTypes(TD); + } + } + } + + void HandleTopLevelSingleDecl(Decl *D); + void HandleDeclInMainFile(Decl *D); + RewriteModernObjC(std::string inFile, std::unique_ptr<raw_ostream> OS, + DiagnosticsEngine &D, const LangOptions &LOpts, + bool silenceMacroWarn, bool LineInfo); + + ~RewriteModernObjC() override {} + + void HandleTranslationUnit(ASTContext &C) override; + + void ReplaceStmt(Stmt *Old, Stmt *New) { + ReplaceStmtWithRange(Old, New, Old->getSourceRange()); + } + + void ReplaceStmtWithRange(Stmt *Old, Stmt *New, SourceRange SrcRange) { + assert(Old != nullptr && New != nullptr && "Expected non-null Stmt's"); + + Stmt *ReplacingStmt = ReplacedNodes[Old]; + if (ReplacingStmt) + return; // We can't rewrite the same node twice. + + if (DisableReplaceStmt) + return; + + // Measure the old text. + int Size = Rewrite.getRangeSize(SrcRange); + if (Size == -1) { + Diags.Report(Context->getFullLoc(Old->getBeginLoc()), RewriteFailedDiag) + << Old->getSourceRange(); + return; + } + // Get the new text. + std::string SStr; + llvm::raw_string_ostream S(SStr); + New->printPretty(S, nullptr, PrintingPolicy(LangOpts)); + const std::string &Str = S.str(); + + // If replacement succeeded or warning disabled return with no warning. + if (!Rewrite.ReplaceText(SrcRange.getBegin(), Size, Str)) { + ReplacedNodes[Old] = New; + return; + } + if (SilenceRewriteMacroWarning) + return; + Diags.Report(Context->getFullLoc(Old->getBeginLoc()), RewriteFailedDiag) + << Old->getSourceRange(); + } + + void InsertText(SourceLocation Loc, StringRef Str, + bool InsertAfter = true) { + // If insertion succeeded or warning disabled return with no warning. + if (!Rewrite.InsertText(Loc, Str, InsertAfter) || + SilenceRewriteMacroWarning) + return; + + Diags.Report(Context->getFullLoc(Loc), RewriteFailedDiag); + } + + void ReplaceText(SourceLocation Start, unsigned OrigLength, + StringRef Str) { + // If removal succeeded or warning disabled return with no warning. + if (!Rewrite.ReplaceText(Start, OrigLength, Str) || + SilenceRewriteMacroWarning) + return; + + Diags.Report(Context->getFullLoc(Start), RewriteFailedDiag); + } + + // Syntactic Rewriting. + void RewriteRecordBody(RecordDecl *RD); + void RewriteInclude(); + void RewriteLineDirective(const Decl *D); + void ConvertSourceLocationToLineDirective(SourceLocation Loc, + std::string &LineString); + void RewriteForwardClassDecl(DeclGroupRef D); + void RewriteForwardClassDecl(const SmallVectorImpl<Decl *> &DG); + void RewriteForwardClassEpilogue(ObjCInterfaceDecl *ClassDecl, + const std::string &typedefString); + void RewriteImplementations(); + void RewritePropertyImplDecl(ObjCPropertyImplDecl *PID, + ObjCImplementationDecl *IMD, + ObjCCategoryImplDecl *CID); + void RewriteInterfaceDecl(ObjCInterfaceDecl *Dcl); + void RewriteImplementationDecl(Decl *Dcl); + void RewriteObjCMethodDecl(const ObjCInterfaceDecl *IDecl, + ObjCMethodDecl *MDecl, std::string &ResultStr); + void RewriteTypeIntoString(QualType T, std::string &ResultStr, + const FunctionType *&FPRetType); + void RewriteByRefString(std::string &ResultStr, const std::string &Name, + ValueDecl *VD, bool def=false); + void RewriteCategoryDecl(ObjCCategoryDecl *Dcl); + void RewriteProtocolDecl(ObjCProtocolDecl *Dcl); + void RewriteForwardProtocolDecl(DeclGroupRef D); + void RewriteForwardProtocolDecl(const SmallVectorImpl<Decl *> &DG); + void RewriteMethodDeclaration(ObjCMethodDecl *Method); + void RewriteProperty(ObjCPropertyDecl *prop); + void RewriteFunctionDecl(FunctionDecl *FD); + void RewriteBlockPointerType(std::string& Str, QualType Type); + void RewriteBlockPointerTypeVariable(std::string& Str, ValueDecl *VD); + void RewriteBlockLiteralFunctionDecl(FunctionDecl *FD); + void RewriteObjCQualifiedInterfaceTypes(Decl *Dcl); + void RewriteTypeOfDecl(VarDecl *VD); + void RewriteObjCQualifiedInterfaceTypes(Expr *E); + + std::string getIvarAccessString(ObjCIvarDecl *D); + + // Expression Rewriting. + Stmt *RewriteFunctionBodyOrGlobalInitializer(Stmt *S); + Stmt *RewriteAtEncode(ObjCEncodeExpr *Exp); + Stmt *RewritePropertyOrImplicitGetter(PseudoObjectExpr *Pseudo); + Stmt *RewritePropertyOrImplicitSetter(PseudoObjectExpr *Pseudo); + Stmt *RewriteAtSelector(ObjCSelectorExpr *Exp); + Stmt *RewriteMessageExpr(ObjCMessageExpr *Exp); + Stmt *RewriteObjCStringLiteral(ObjCStringLiteral *Exp); + Stmt *RewriteObjCBoolLiteralExpr(ObjCBoolLiteralExpr *Exp); + Stmt *RewriteObjCBoxedExpr(ObjCBoxedExpr *Exp); + Stmt *RewriteObjCArrayLiteralExpr(ObjCArrayLiteral *Exp); + Stmt *RewriteObjCDictionaryLiteralExpr(ObjCDictionaryLiteral *Exp); + Stmt *RewriteObjCProtocolExpr(ObjCProtocolExpr *Exp); + Stmt *RewriteObjCTryStmt(ObjCAtTryStmt *S); + Stmt *RewriteObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S); + Stmt *RewriteObjCSynchronizedStmt(ObjCAtSynchronizedStmt *S); + Stmt *RewriteObjCThrowStmt(ObjCAtThrowStmt *S); + Stmt *RewriteObjCForCollectionStmt(ObjCForCollectionStmt *S, + SourceLocation OrigEnd); + Stmt *RewriteBreakStmt(BreakStmt *S); + Stmt *RewriteContinueStmt(ContinueStmt *S); + void RewriteCastExpr(CStyleCastExpr *CE); + void RewriteImplicitCastObjCExpr(CastExpr *IE); + + // Computes ivar bitfield group no. + unsigned ObjCIvarBitfieldGroupNo(ObjCIvarDecl *IV); + // Names field decl. for ivar bitfield group. + void ObjCIvarBitfieldGroupDecl(ObjCIvarDecl *IV, std::string &Result); + // Names struct type for ivar bitfield group. + void ObjCIvarBitfieldGroupType(ObjCIvarDecl *IV, std::string &Result); + // Names symbol for ivar bitfield group field offset. + void ObjCIvarBitfieldGroupOffset(ObjCIvarDecl *IV, std::string &Result); + // Given an ivar bitfield, it builds (or finds) its group record type. + QualType GetGroupRecordTypeForObjCIvarBitfield(ObjCIvarDecl *IV); + QualType SynthesizeBitfieldGroupStructType( + ObjCIvarDecl *IV, + SmallVectorImpl<ObjCIvarDecl *> &IVars); + + // Block rewriting. + void RewriteBlocksInFunctionProtoType(QualType funcType, NamedDecl *D); + + // Block specific rewrite rules. + void RewriteBlockPointerDecl(NamedDecl *VD); + void RewriteByRefVar(VarDecl *VD, bool firstDecl, bool lastDecl); + Stmt *RewriteBlockDeclRefExpr(DeclRefExpr *VD); + Stmt *RewriteLocalVariableExternalStorage(DeclRefExpr *DRE); + void RewriteBlockPointerFunctionArgs(FunctionDecl *FD); + + void RewriteObjCInternalStruct(ObjCInterfaceDecl *CDecl, + std::string &Result); + + void RewriteObjCFieldDecl(FieldDecl *fieldDecl, std::string &Result); + bool IsTagDefinedInsideClass(ObjCContainerDecl *IDecl, TagDecl *Tag, + bool &IsNamedDefinition); + void RewriteLocallyDefinedNamedAggregates(FieldDecl *fieldDecl, + std::string &Result); + + bool RewriteObjCFieldDeclType(QualType &Type, std::string &Result); + + void RewriteIvarOffsetSymbols(ObjCInterfaceDecl *CDecl, + std::string &Result); + + void Initialize(ASTContext &context) override; + + // Misc. AST transformation routines. Sometimes they end up calling + // rewriting routines on the new ASTs. + CallExpr *SynthesizeCallToFunctionDecl(FunctionDecl *FD, + ArrayRef<Expr *> Args, + SourceLocation StartLoc=SourceLocation(), + SourceLocation EndLoc=SourceLocation()); + + Expr *SynthMsgSendStretCallExpr(FunctionDecl *MsgSendStretFlavor, + QualType returnType, + SmallVectorImpl<QualType> &ArgTypes, + SmallVectorImpl<Expr*> &MsgExprs, + ObjCMethodDecl *Method); + + Stmt *SynthMessageExpr(ObjCMessageExpr *Exp, + SourceLocation StartLoc=SourceLocation(), + SourceLocation EndLoc=SourceLocation()); + + void SynthCountByEnumWithState(std::string &buf); + void SynthMsgSendFunctionDecl(); + void SynthMsgSendSuperFunctionDecl(); + void SynthMsgSendStretFunctionDecl(); + void SynthMsgSendFpretFunctionDecl(); + void SynthMsgSendSuperStretFunctionDecl(); + void SynthGetClassFunctionDecl(); + void SynthGetMetaClassFunctionDecl(); + void SynthGetSuperClassFunctionDecl(); + void SynthSelGetUidFunctionDecl(); + void SynthSuperConstructorFunctionDecl(); + + // Rewriting metadata + template<typename MethodIterator> + void RewriteObjCMethodsMetaData(MethodIterator MethodBegin, + MethodIterator MethodEnd, + bool IsInstanceMethod, + StringRef prefix, + StringRef ClassName, + std::string &Result); + void RewriteObjCProtocolMetaData(ObjCProtocolDecl *Protocol, + std::string &Result); + void RewriteObjCClassMetaData(ObjCImplementationDecl *IDecl, + std::string &Result); + void RewriteClassSetupInitHook(std::string &Result); + + void RewriteMetaDataIntoBuffer(std::string &Result); + void WriteImageInfo(std::string &Result); + void RewriteObjCCategoryImplDecl(ObjCCategoryImplDecl *CDecl, + std::string &Result); + void RewriteCategorySetupInitHook(std::string &Result); + + // Rewriting ivar + void RewriteIvarOffsetComputation(ObjCIvarDecl *ivar, + std::string &Result); + Stmt *RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV); + + + std::string SynthesizeByrefCopyDestroyHelper(VarDecl *VD, int flag); + std::string SynthesizeBlockHelperFuncs(BlockExpr *CE, int i, + StringRef funcName, std::string Tag); + std::string SynthesizeBlockFunc(BlockExpr *CE, int i, + StringRef funcName, std::string Tag); + std::string SynthesizeBlockImpl(BlockExpr *CE, + std::string Tag, std::string Desc); + std::string SynthesizeBlockDescriptor(std::string DescTag, + std::string ImplTag, + int i, StringRef funcName, + unsigned hasCopy); + Stmt *SynthesizeBlockCall(CallExpr *Exp, const Expr* BlockExp); + void SynthesizeBlockLiterals(SourceLocation FunLocStart, + StringRef FunName); + FunctionDecl *SynthBlockInitFunctionDecl(StringRef name); + Stmt *SynthBlockInitExpr(BlockExpr *Exp, + const SmallVectorImpl<DeclRefExpr *> &InnerBlockDeclRefs); + + // Misc. helper routines. + QualType getProtocolType(); + void WarnAboutReturnGotoStmts(Stmt *S); + void CheckFunctionPointerDecl(QualType dType, NamedDecl *ND); + void InsertBlockLiteralsWithinFunction(FunctionDecl *FD); + void InsertBlockLiteralsWithinMethod(ObjCMethodDecl *MD); + + bool IsDeclStmtInForeachHeader(DeclStmt *DS); + void CollectBlockDeclRefInfo(BlockExpr *Exp); + void GetBlockDeclRefExprs(Stmt *S); + void GetInnerBlockDeclRefExprs(Stmt *S, + SmallVectorImpl<DeclRefExpr *> &InnerBlockDeclRefs, + llvm::SmallPtrSetImpl<const DeclContext *> &InnerContexts); + + // We avoid calling Type::isBlockPointerType(), since it operates on the + // canonical type. We only care if the top-level type is a closure pointer. + bool isTopLevelBlockPointerType(QualType T) { + return isa<BlockPointerType>(T); + } + + /// convertBlockPointerToFunctionPointer - Converts a block-pointer type + /// to a function pointer type and upon success, returns true; false + /// otherwise. + bool convertBlockPointerToFunctionPointer(QualType &T) { + if (isTopLevelBlockPointerType(T)) { + const BlockPointerType *BPT = T->getAs<BlockPointerType>(); + T = Context->getPointerType(BPT->getPointeeType()); + return true; + } + return false; + } + + bool convertObjCTypeToCStyleType(QualType &T); + + bool needToScanForQualifiers(QualType T); + QualType getSuperStructType(); + QualType getConstantStringStructType(); + QualType convertFunctionTypeOfBlocks(const FunctionType *FT); + + void convertToUnqualifiedObjCType(QualType &T) { + if (T->isObjCQualifiedIdType()) { + bool isConst = T.isConstQualified(); + T = isConst ? Context->getObjCIdType().withConst() + : Context->getObjCIdType(); + } + else if (T->isObjCQualifiedClassType()) + T = Context->getObjCClassType(); + else if (T->isObjCObjectPointerType() && + T->getPointeeType()->isObjCQualifiedInterfaceType()) { + if (const ObjCObjectPointerType * OBJPT = + T->getAsObjCInterfacePointerType()) { + const ObjCInterfaceType *IFaceT = OBJPT->getInterfaceType(); + T = QualType(IFaceT, 0); + T = Context->getPointerType(T); + } + } + } + + // FIXME: This predicate seems like it would be useful to add to ASTContext. + bool isObjCType(QualType T) { + if (!LangOpts.ObjC) + return false; + + QualType OCT = Context->getCanonicalType(T).getUnqualifiedType(); + + if (OCT == Context->getCanonicalType(Context->getObjCIdType()) || + OCT == Context->getCanonicalType(Context->getObjCClassType())) + return true; + + if (const PointerType *PT = OCT->getAs<PointerType>()) { + if (isa<ObjCInterfaceType>(PT->getPointeeType()) || + PT->getPointeeType()->isObjCQualifiedIdType()) + return true; + } + return false; + } + + bool PointerTypeTakesAnyBlockArguments(QualType QT); + bool PointerTypeTakesAnyObjCQualifiedType(QualType QT); + void GetExtentOfArgList(const char *Name, const char *&LParen, + const char *&RParen); + + void QuoteDoublequotes(std::string &From, std::string &To) { + for (unsigned i = 0; i < From.length(); i++) { + if (From[i] == '"') + To += "\\\""; + else + To += From[i]; + } + } + + QualType getSimpleFunctionType(QualType result, + ArrayRef<QualType> args, + bool variadic = false) { + if (result == Context->getObjCInstanceType()) + result = Context->getObjCIdType(); + FunctionProtoType::ExtProtoInfo fpi; + fpi.Variadic = variadic; + return Context->getFunctionType(result, args, fpi); + } + + // Helper function: create a CStyleCastExpr with trivial type source info. + CStyleCastExpr* NoTypeInfoCStyleCastExpr(ASTContext *Ctx, QualType Ty, + CastKind Kind, Expr *E) { + TypeSourceInfo *TInfo = Ctx->getTrivialTypeSourceInfo(Ty, SourceLocation()); + return CStyleCastExpr::Create(*Ctx, Ty, VK_RValue, Kind, E, nullptr, + TInfo, SourceLocation(), SourceLocation()); + } + + bool ImplementationIsNonLazy(const ObjCImplDecl *OD) const { + IdentifierInfo* II = &Context->Idents.get("load"); + Selector LoadSel = Context->Selectors.getSelector(0, &II); + return OD->getClassMethod(LoadSel) != nullptr; + } + + StringLiteral *getStringLiteral(StringRef Str) { + QualType StrType = Context->getConstantArrayType( + Context->CharTy, llvm::APInt(32, Str.size() + 1), ArrayType::Normal, + 0); + return StringLiteral::Create(*Context, Str, StringLiteral::Ascii, + /*Pascal=*/false, StrType, SourceLocation()); + } + }; +} // end anonymous namespace + +void RewriteModernObjC::RewriteBlocksInFunctionProtoType(QualType funcType, + NamedDecl *D) { + if (const FunctionProtoType *fproto + = dyn_cast<FunctionProtoType>(funcType.IgnoreParens())) { + for (const auto &I : fproto->param_types()) + if (isTopLevelBlockPointerType(I)) { + // All the args are checked/rewritten. Don't call twice! + RewriteBlockPointerDecl(D); + break; + } + } +} + +void RewriteModernObjC::CheckFunctionPointerDecl(QualType funcType, NamedDecl *ND) { + const PointerType *PT = funcType->getAs<PointerType>(); + if (PT && PointerTypeTakesAnyBlockArguments(funcType)) + RewriteBlocksInFunctionProtoType(PT->getPointeeType(), ND); +} + +static bool IsHeaderFile(const std::string &Filename) { + std::string::size_type DotPos = Filename.rfind('.'); + + if (DotPos == std::string::npos) { + // no file extension + return false; + } + + std::string Ext = std::string(Filename.begin()+DotPos+1, Filename.end()); + // C header: .h + // C++ header: .hh or .H; + return Ext == "h" || Ext == "hh" || Ext == "H"; +} + +RewriteModernObjC::RewriteModernObjC(std::string inFile, + std::unique_ptr<raw_ostream> OS, + DiagnosticsEngine &D, + const LangOptions &LOpts, + bool silenceMacroWarn, bool LineInfo) + : Diags(D), LangOpts(LOpts), InFileName(inFile), OutFile(std::move(OS)), + SilenceRewriteMacroWarning(silenceMacroWarn), GenerateLineInfo(LineInfo) { + IsHeader = IsHeaderFile(inFile); + RewriteFailedDiag = Diags.getCustomDiagID(DiagnosticsEngine::Warning, + "rewriting sub-expression within a macro (may not be correct)"); + // FIXME. This should be an error. But if block is not called, it is OK. And it + // may break including some headers. + GlobalBlockRewriteFailedDiag = Diags.getCustomDiagID(DiagnosticsEngine::Warning, + "rewriting block literal declared in global scope is not implemented"); + + TryFinallyContainsReturnDiag = Diags.getCustomDiagID( + DiagnosticsEngine::Warning, + "rewriter doesn't support user-specified control flow semantics " + "for @try/@finally (code may not execute properly)"); +} + +std::unique_ptr<ASTConsumer> clang::CreateModernObjCRewriter( + const std::string &InFile, std::unique_ptr<raw_ostream> OS, + DiagnosticsEngine &Diags, const LangOptions &LOpts, + bool SilenceRewriteMacroWarning, bool LineInfo) { + return llvm::make_unique<RewriteModernObjC>(InFile, std::move(OS), Diags, + LOpts, SilenceRewriteMacroWarning, + LineInfo); +} + +void RewriteModernObjC::InitializeCommon(ASTContext &context) { + Context = &context; + SM = &Context->getSourceManager(); + TUDecl = Context->getTranslationUnitDecl(); + MsgSendFunctionDecl = nullptr; + MsgSendSuperFunctionDecl = nullptr; + MsgSendStretFunctionDecl = nullptr; + MsgSendSuperStretFunctionDecl = nullptr; + MsgSendFpretFunctionDecl = nullptr; + GetClassFunctionDecl = nullptr; + GetMetaClassFunctionDecl = nullptr; + GetSuperClassFunctionDecl = nullptr; + SelGetUidFunctionDecl = nullptr; + CFStringFunctionDecl = nullptr; + ConstantStringClassReference = nullptr; + NSStringRecord = nullptr; + CurMethodDef = nullptr; + CurFunctionDef = nullptr; + GlobalVarDecl = nullptr; + GlobalConstructionExp = nullptr; + SuperStructDecl = nullptr; + ProtocolTypeDecl = nullptr; + ConstantStringDecl = nullptr; + BcLabelCount = 0; + SuperConstructorFunctionDecl = nullptr; + NumObjCStringLiterals = 0; + PropParentMap = nullptr; + CurrentBody = nullptr; + DisableReplaceStmt = false; + objc_impl_method = false; + + // Get the ID and start/end of the main file. + MainFileID = SM->getMainFileID(); + const llvm::MemoryBuffer *MainBuf = SM->getBuffer(MainFileID); + MainFileStart = MainBuf->getBufferStart(); + MainFileEnd = MainBuf->getBufferEnd(); + + Rewrite.setSourceMgr(Context->getSourceManager(), Context->getLangOpts()); +} + +//===----------------------------------------------------------------------===// +// Top Level Driver Code +//===----------------------------------------------------------------------===// + +void RewriteModernObjC::HandleTopLevelSingleDecl(Decl *D) { + if (Diags.hasErrorOccurred()) + return; + + // Two cases: either the decl could be in the main file, or it could be in a + // #included file. If the former, rewrite it now. If the later, check to see + // if we rewrote the #include/#import. + SourceLocation Loc = D->getLocation(); + Loc = SM->getExpansionLoc(Loc); + + // If this is for a builtin, ignore it. + if (Loc.isInvalid()) return; + + // Look for built-in declarations that we need to refer during the rewrite. + if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + RewriteFunctionDecl(FD); + } else if (VarDecl *FVD = dyn_cast<VarDecl>(D)) { + // declared in <Foundation/NSString.h> + if (FVD->getName() == "_NSConstantStringClassReference") { + ConstantStringClassReference = FVD; + return; + } + } else if (ObjCCategoryDecl *CD = dyn_cast<ObjCCategoryDecl>(D)) { + RewriteCategoryDecl(CD); + } else if (ObjCProtocolDecl *PD = dyn_cast<ObjCProtocolDecl>(D)) { + if (PD->isThisDeclarationADefinition()) + RewriteProtocolDecl(PD); + } else if (LinkageSpecDecl *LSD = dyn_cast<LinkageSpecDecl>(D)) { + // Recurse into linkage specifications + for (DeclContext::decl_iterator DI = LSD->decls_begin(), + DIEnd = LSD->decls_end(); + DI != DIEnd; ) { + if (ObjCInterfaceDecl *IFace = dyn_cast<ObjCInterfaceDecl>((*DI))) { + if (!IFace->isThisDeclarationADefinition()) { + SmallVector<Decl *, 8> DG; + SourceLocation StartLoc = IFace->getBeginLoc(); + do { + if (isa<ObjCInterfaceDecl>(*DI) && + !cast<ObjCInterfaceDecl>(*DI)->isThisDeclarationADefinition() && + StartLoc == (*DI)->getBeginLoc()) + DG.push_back(*DI); + else + break; + + ++DI; + } while (DI != DIEnd); + RewriteForwardClassDecl(DG); + continue; + } + else { + // Keep track of all interface declarations seen. + ObjCInterfacesSeen.push_back(IFace); + ++DI; + continue; + } + } + + if (ObjCProtocolDecl *Proto = dyn_cast<ObjCProtocolDecl>((*DI))) { + if (!Proto->isThisDeclarationADefinition()) { + SmallVector<Decl *, 8> DG; + SourceLocation StartLoc = Proto->getBeginLoc(); + do { + if (isa<ObjCProtocolDecl>(*DI) && + !cast<ObjCProtocolDecl>(*DI)->isThisDeclarationADefinition() && + StartLoc == (*DI)->getBeginLoc()) + DG.push_back(*DI); + else + break; + + ++DI; + } while (DI != DIEnd); + RewriteForwardProtocolDecl(DG); + continue; + } + } + + HandleTopLevelSingleDecl(*DI); + ++DI; + } + } + // If we have a decl in the main file, see if we should rewrite it. + if (SM->isWrittenInMainFile(Loc)) + return HandleDeclInMainFile(D); +} + +//===----------------------------------------------------------------------===// +// Syntactic (non-AST) Rewriting Code +//===----------------------------------------------------------------------===// + +void RewriteModernObjC::RewriteInclude() { + SourceLocation LocStart = SM->getLocForStartOfFile(MainFileID); + StringRef MainBuf = SM->getBufferData(MainFileID); + const char *MainBufStart = MainBuf.begin(); + const char *MainBufEnd = MainBuf.end(); + size_t ImportLen = strlen("import"); + + // Loop over the whole file, looking for includes. + for (const char *BufPtr = MainBufStart; BufPtr < MainBufEnd; ++BufPtr) { + if (*BufPtr == '#') { + if (++BufPtr == MainBufEnd) + return; + while (*BufPtr == ' ' || *BufPtr == '\t') + if (++BufPtr == MainBufEnd) + return; + if (!strncmp(BufPtr, "import", ImportLen)) { + // replace import with include + SourceLocation ImportLoc = + LocStart.getLocWithOffset(BufPtr-MainBufStart); + ReplaceText(ImportLoc, ImportLen, "include"); + BufPtr += ImportLen; + } + } + } +} + +static void WriteInternalIvarName(const ObjCInterfaceDecl *IDecl, + ObjCIvarDecl *IvarDecl, std::string &Result) { + Result += "OBJC_IVAR_$_"; + Result += IDecl->getName(); + Result += "$"; + Result += IvarDecl->getName(); +} + +std::string +RewriteModernObjC::getIvarAccessString(ObjCIvarDecl *D) { + const ObjCInterfaceDecl *ClassDecl = D->getContainingInterface(); + + // Build name of symbol holding ivar offset. + std::string IvarOffsetName; + if (D->isBitField()) + ObjCIvarBitfieldGroupOffset(D, IvarOffsetName); + else + WriteInternalIvarName(ClassDecl, D, IvarOffsetName); + + std::string S = "(*("; + QualType IvarT = D->getType(); + if (D->isBitField()) + IvarT = GetGroupRecordTypeForObjCIvarBitfield(D); + + if (!isa<TypedefType>(IvarT) && IvarT->isRecordType()) { + RecordDecl *RD = IvarT->getAs<RecordType>()->getDecl(); + RD = RD->getDefinition(); + if (RD && !RD->getDeclName().getAsIdentifierInfo()) { + // decltype(((Foo_IMPL*)0)->bar) * + ObjCContainerDecl *CDecl = + dyn_cast<ObjCContainerDecl>(D->getDeclContext()); + // ivar in class extensions requires special treatment. + if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl)) + CDecl = CatDecl->getClassInterface(); + std::string RecName = CDecl->getName(); + RecName += "_IMPL"; + RecordDecl *RD = + RecordDecl::Create(*Context, TTK_Struct, TUDecl, SourceLocation(), + SourceLocation(), &Context->Idents.get(RecName)); + QualType PtrStructIMPL = Context->getPointerType(Context->getTagDeclType(RD)); + unsigned UnsignedIntSize = + static_cast<unsigned>(Context->getTypeSize(Context->UnsignedIntTy)); + Expr *Zero = IntegerLiteral::Create(*Context, + llvm::APInt(UnsignedIntSize, 0), + Context->UnsignedIntTy, SourceLocation()); + Zero = NoTypeInfoCStyleCastExpr(Context, PtrStructIMPL, CK_BitCast, Zero); + ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), + Zero); + FieldDecl *FD = FieldDecl::Create(*Context, nullptr, SourceLocation(), + SourceLocation(), + &Context->Idents.get(D->getNameAsString()), + IvarT, nullptr, + /*BitWidth=*/nullptr, /*Mutable=*/true, + ICIS_NoInit); + MemberExpr *ME = new (Context) + MemberExpr(PE, true, SourceLocation(), FD, SourceLocation(), + FD->getType(), VK_LValue, OK_Ordinary); + IvarT = Context->getDecltypeType(ME, ME->getType()); + } + } + convertObjCTypeToCStyleType(IvarT); + QualType castT = Context->getPointerType(IvarT); + std::string TypeString(castT.getAsString(Context->getPrintingPolicy())); + S += TypeString; + S += ")"; + + // ((char *)self + IVAR_OFFSET_SYMBOL_NAME) + S += "((char *)self + "; + S += IvarOffsetName; + S += "))"; + if (D->isBitField()) { + S += "."; + S += D->getNameAsString(); + } + ReferencedIvars[const_cast<ObjCInterfaceDecl *>(ClassDecl)].insert(D); + return S; +} + +/// mustSynthesizeSetterGetterMethod - returns true if setter or getter has not +/// been found in the class implementation. In this case, it must be synthesized. +static bool mustSynthesizeSetterGetterMethod(ObjCImplementationDecl *IMP, + ObjCPropertyDecl *PD, + bool getter) { + return getter ? !IMP->getInstanceMethod(PD->getGetterName()) + : !IMP->getInstanceMethod(PD->getSetterName()); + +} + +void RewriteModernObjC::RewritePropertyImplDecl(ObjCPropertyImplDecl *PID, + ObjCImplementationDecl *IMD, + ObjCCategoryImplDecl *CID) { + static bool objcGetPropertyDefined = false; + static bool objcSetPropertyDefined = false; + SourceLocation startGetterSetterLoc; + + if (PID->getBeginLoc().isValid()) { + SourceLocation startLoc = PID->getBeginLoc(); + InsertText(startLoc, "// "); + const char *startBuf = SM->getCharacterData(startLoc); + assert((*startBuf == '@') && "bogus @synthesize location"); + const char *semiBuf = strchr(startBuf, ';'); + assert((*semiBuf == ';') && "@synthesize: can't find ';'"); + startGetterSetterLoc = startLoc.getLocWithOffset(semiBuf-startBuf+1); + } else + startGetterSetterLoc = IMD ? IMD->getEndLoc() : CID->getEndLoc(); + + if (PID->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) + return; // FIXME: is this correct? + + // Generate the 'getter' function. + ObjCPropertyDecl *PD = PID->getPropertyDecl(); + ObjCIvarDecl *OID = PID->getPropertyIvarDecl(); + assert(IMD && OID && "Synthesized ivars must be attached to @implementation"); + + unsigned Attributes = PD->getPropertyAttributes(); + if (mustSynthesizeSetterGetterMethod(IMD, PD, true /*getter*/)) { + bool GenGetProperty = !(Attributes & ObjCPropertyDecl::OBJC_PR_nonatomic) && + (Attributes & (ObjCPropertyDecl::OBJC_PR_retain | + ObjCPropertyDecl::OBJC_PR_copy)); + std::string Getr; + if (GenGetProperty && !objcGetPropertyDefined) { + objcGetPropertyDefined = true; + // FIXME. Is this attribute correct in all cases? + Getr = "\nextern \"C\" __declspec(dllimport) " + "id objc_getProperty(id, SEL, long, bool);\n"; + } + RewriteObjCMethodDecl(OID->getContainingInterface(), + PD->getGetterMethodDecl(), Getr); + Getr += "{ "; + // Synthesize an explicit cast to gain access to the ivar. + // See objc-act.c:objc_synthesize_new_getter() for details. + if (GenGetProperty) { + // return objc_getProperty(self, _cmd, offsetof(ClassDecl, OID), 1) + Getr += "typedef "; + const FunctionType *FPRetType = nullptr; + RewriteTypeIntoString(PD->getGetterMethodDecl()->getReturnType(), Getr, + FPRetType); + Getr += " _TYPE"; + if (FPRetType) { + Getr += ")"; // close the precedence "scope" for "*". + + // Now, emit the argument types (if any). + if (const FunctionProtoType *FT = dyn_cast<FunctionProtoType>(FPRetType)){ + Getr += "("; + for (unsigned i = 0, e = FT->getNumParams(); i != e; ++i) { + if (i) Getr += ", "; + std::string ParamStr = + FT->getParamType(i).getAsString(Context->getPrintingPolicy()); + Getr += ParamStr; + } + if (FT->isVariadic()) { + if (FT->getNumParams()) + Getr += ", "; + Getr += "..."; + } + Getr += ")"; + } else + Getr += "()"; + } + Getr += ";\n"; + Getr += "return (_TYPE)"; + Getr += "objc_getProperty(self, _cmd, "; + RewriteIvarOffsetComputation(OID, Getr); + Getr += ", 1)"; + } + else + Getr += "return " + getIvarAccessString(OID); + Getr += "; }"; + InsertText(startGetterSetterLoc, Getr); + } + + if (PD->isReadOnly() || + !mustSynthesizeSetterGetterMethod(IMD, PD, false /*setter*/)) + return; + + // Generate the 'setter' function. + std::string Setr; + bool GenSetProperty = Attributes & (ObjCPropertyDecl::OBJC_PR_retain | + ObjCPropertyDecl::OBJC_PR_copy); + if (GenSetProperty && !objcSetPropertyDefined) { + objcSetPropertyDefined = true; + // FIXME. Is this attribute correct in all cases? + Setr = "\nextern \"C\" __declspec(dllimport) " + "void objc_setProperty (id, SEL, long, id, bool, bool);\n"; + } + + RewriteObjCMethodDecl(OID->getContainingInterface(), + PD->getSetterMethodDecl(), Setr); + Setr += "{ "; + // Synthesize an explicit cast to initialize the ivar. + // See objc-act.c:objc_synthesize_new_setter() for details. + if (GenSetProperty) { + Setr += "objc_setProperty (self, _cmd, "; + RewriteIvarOffsetComputation(OID, Setr); + Setr += ", (id)"; + Setr += PD->getName(); + Setr += ", "; + if (Attributes & ObjCPropertyDecl::OBJC_PR_nonatomic) + Setr += "0, "; + else + Setr += "1, "; + if (Attributes & ObjCPropertyDecl::OBJC_PR_copy) + Setr += "1)"; + else + Setr += "0)"; + } + else { + Setr += getIvarAccessString(OID) + " = "; + Setr += PD->getName(); + } + Setr += "; }\n"; + InsertText(startGetterSetterLoc, Setr); +} + +static void RewriteOneForwardClassDecl(ObjCInterfaceDecl *ForwardDecl, + std::string &typedefString) { + typedefString += "\n#ifndef _REWRITER_typedef_"; + typedefString += ForwardDecl->getNameAsString(); + typedefString += "\n"; + typedefString += "#define _REWRITER_typedef_"; + typedefString += ForwardDecl->getNameAsString(); + typedefString += "\n"; + typedefString += "typedef struct objc_object "; + typedefString += ForwardDecl->getNameAsString(); + // typedef struct { } _objc_exc_Classname; + typedefString += ";\ntypedef struct {} _objc_exc_"; + typedefString += ForwardDecl->getNameAsString(); + typedefString += ";\n#endif\n"; +} + +void RewriteModernObjC::RewriteForwardClassEpilogue(ObjCInterfaceDecl *ClassDecl, + const std::string &typedefString) { + SourceLocation startLoc = ClassDecl->getBeginLoc(); + const char *startBuf = SM->getCharacterData(startLoc); + const char *semiPtr = strchr(startBuf, ';'); + // Replace the @class with typedefs corresponding to the classes. + ReplaceText(startLoc, semiPtr-startBuf+1, typedefString); +} + +void RewriteModernObjC::RewriteForwardClassDecl(DeclGroupRef D) { + std::string typedefString; + for (DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; ++I) { + if (ObjCInterfaceDecl *ForwardDecl = dyn_cast<ObjCInterfaceDecl>(*I)) { + if (I == D.begin()) { + // Translate to typedef's that forward reference structs with the same name + // as the class. As a convenience, we include the original declaration + // as a comment. + typedefString += "// @class "; + typedefString += ForwardDecl->getNameAsString(); + typedefString += ";"; + } + RewriteOneForwardClassDecl(ForwardDecl, typedefString); + } + else + HandleTopLevelSingleDecl(*I); + } + DeclGroupRef::iterator I = D.begin(); + RewriteForwardClassEpilogue(cast<ObjCInterfaceDecl>(*I), typedefString); +} + +void RewriteModernObjC::RewriteForwardClassDecl( + const SmallVectorImpl<Decl *> &D) { + std::string typedefString; + for (unsigned i = 0; i < D.size(); i++) { + ObjCInterfaceDecl *ForwardDecl = cast<ObjCInterfaceDecl>(D[i]); + if (i == 0) { + typedefString += "// @class "; + typedefString += ForwardDecl->getNameAsString(); + typedefString += ";"; + } + RewriteOneForwardClassDecl(ForwardDecl, typedefString); + } + RewriteForwardClassEpilogue(cast<ObjCInterfaceDecl>(D[0]), typedefString); +} + +void RewriteModernObjC::RewriteMethodDeclaration(ObjCMethodDecl *Method) { + // When method is a synthesized one, such as a getter/setter there is + // nothing to rewrite. + if (Method->isImplicit()) + return; + SourceLocation LocStart = Method->getBeginLoc(); + SourceLocation LocEnd = Method->getEndLoc(); + + if (SM->getExpansionLineNumber(LocEnd) > + SM->getExpansionLineNumber(LocStart)) { + InsertText(LocStart, "#if 0\n"); + ReplaceText(LocEnd, 1, ";\n#endif\n"); + } else { + InsertText(LocStart, "// "); + } +} + +void RewriteModernObjC::RewriteProperty(ObjCPropertyDecl *prop) { + SourceLocation Loc = prop->getAtLoc(); + + ReplaceText(Loc, 0, "// "); + // FIXME: handle properties that are declared across multiple lines. +} + +void RewriteModernObjC::RewriteCategoryDecl(ObjCCategoryDecl *CatDecl) { + SourceLocation LocStart = CatDecl->getBeginLoc(); + + // FIXME: handle category headers that are declared across multiple lines. + if (CatDecl->getIvarRBraceLoc().isValid()) { + ReplaceText(LocStart, 1, "/** "); + ReplaceText(CatDecl->getIvarRBraceLoc(), 1, "**/ "); + } + else { + ReplaceText(LocStart, 0, "// "); + } + + for (auto *I : CatDecl->instance_properties()) + RewriteProperty(I); + + for (auto *I : CatDecl->instance_methods()) + RewriteMethodDeclaration(I); + for (auto *I : CatDecl->class_methods()) + RewriteMethodDeclaration(I); + + // Lastly, comment out the @end. + ReplaceText(CatDecl->getAtEndRange().getBegin(), + strlen("@end"), "/* @end */\n"); +} + +void RewriteModernObjC::RewriteProtocolDecl(ObjCProtocolDecl *PDecl) { + SourceLocation LocStart = PDecl->getBeginLoc(); + assert(PDecl->isThisDeclarationADefinition()); + + // FIXME: handle protocol headers that are declared across multiple lines. + ReplaceText(LocStart, 0, "// "); + + for (auto *I : PDecl->instance_methods()) + RewriteMethodDeclaration(I); + for (auto *I : PDecl->class_methods()) + RewriteMethodDeclaration(I); + for (auto *I : PDecl->instance_properties()) + RewriteProperty(I); + + // Lastly, comment out the @end. + SourceLocation LocEnd = PDecl->getAtEndRange().getBegin(); + ReplaceText(LocEnd, strlen("@end"), "/* @end */\n"); + + // Must comment out @optional/@required + const char *startBuf = SM->getCharacterData(LocStart); + const char *endBuf = SM->getCharacterData(LocEnd); + for (const char *p = startBuf; p < endBuf; p++) { + if (*p == '@' && !strncmp(p+1, "optional", strlen("optional"))) { + SourceLocation OptionalLoc = LocStart.getLocWithOffset(p-startBuf); + ReplaceText(OptionalLoc, strlen("@optional"), "/* @optional */"); + + } + else if (*p == '@' && !strncmp(p+1, "required", strlen("required"))) { + SourceLocation OptionalLoc = LocStart.getLocWithOffset(p-startBuf); + ReplaceText(OptionalLoc, strlen("@required"), "/* @required */"); + + } + } +} + +void RewriteModernObjC::RewriteForwardProtocolDecl(DeclGroupRef D) { + SourceLocation LocStart = (*D.begin())->getBeginLoc(); + if (LocStart.isInvalid()) + llvm_unreachable("Invalid SourceLocation"); + // FIXME: handle forward protocol that are declared across multiple lines. + ReplaceText(LocStart, 0, "// "); +} + +void +RewriteModernObjC::RewriteForwardProtocolDecl(const SmallVectorImpl<Decl *> &DG) { + SourceLocation LocStart = DG[0]->getBeginLoc(); + if (LocStart.isInvalid()) + llvm_unreachable("Invalid SourceLocation"); + // FIXME: handle forward protocol that are declared across multiple lines. + ReplaceText(LocStart, 0, "// "); +} + +void RewriteModernObjC::RewriteTypeIntoString(QualType T, std::string &ResultStr, + const FunctionType *&FPRetType) { + if (T->isObjCQualifiedIdType()) + ResultStr += "id"; + else if (T->isFunctionPointerType() || + T->isBlockPointerType()) { + // needs special handling, since pointer-to-functions have special + // syntax (where a decaration models use). + QualType retType = T; + QualType PointeeTy; + if (const PointerType* PT = retType->getAs<PointerType>()) + PointeeTy = PT->getPointeeType(); + else if (const BlockPointerType *BPT = retType->getAs<BlockPointerType>()) + PointeeTy = BPT->getPointeeType(); + if ((FPRetType = PointeeTy->getAs<FunctionType>())) { + ResultStr += + FPRetType->getReturnType().getAsString(Context->getPrintingPolicy()); + ResultStr += "(*"; + } + } else + ResultStr += T.getAsString(Context->getPrintingPolicy()); +} + +void RewriteModernObjC::RewriteObjCMethodDecl(const ObjCInterfaceDecl *IDecl, + ObjCMethodDecl *OMD, + std::string &ResultStr) { + //fprintf(stderr,"In RewriteObjCMethodDecl\n"); + const FunctionType *FPRetType = nullptr; + ResultStr += "\nstatic "; + RewriteTypeIntoString(OMD->getReturnType(), ResultStr, FPRetType); + ResultStr += " "; + + // Unique method name + std::string NameStr; + + if (OMD->isInstanceMethod()) + NameStr += "_I_"; + else + NameStr += "_C_"; + + NameStr += IDecl->getNameAsString(); + NameStr += "_"; + + if (ObjCCategoryImplDecl *CID = + dyn_cast<ObjCCategoryImplDecl>(OMD->getDeclContext())) { + NameStr += CID->getNameAsString(); + NameStr += "_"; + } + // Append selector names, replacing ':' with '_' + { + std::string selString = OMD->getSelector().getAsString(); + int len = selString.size(); + for (int i = 0; i < len; i++) + if (selString[i] == ':') + selString[i] = '_'; + NameStr += selString; + } + // Remember this name for metadata emission + MethodInternalNames[OMD] = NameStr; + ResultStr += NameStr; + + // Rewrite arguments + ResultStr += "("; + + // invisible arguments + if (OMD->isInstanceMethod()) { + QualType selfTy = Context->getObjCInterfaceType(IDecl); + selfTy = Context->getPointerType(selfTy); + if (!LangOpts.MicrosoftExt) { + if (ObjCSynthesizedStructs.count(const_cast<ObjCInterfaceDecl*>(IDecl))) + ResultStr += "struct "; + } + // When rewriting for Microsoft, explicitly omit the structure name. + ResultStr += IDecl->getNameAsString(); + ResultStr += " *"; + } + else + ResultStr += Context->getObjCClassType().getAsString( + Context->getPrintingPolicy()); + + ResultStr += " self, "; + ResultStr += Context->getObjCSelType().getAsString(Context->getPrintingPolicy()); + ResultStr += " _cmd"; + + // Method arguments. + for (const auto *PDecl : OMD->parameters()) { + ResultStr += ", "; + if (PDecl->getType()->isObjCQualifiedIdType()) { + ResultStr += "id "; + ResultStr += PDecl->getNameAsString(); + } else { + std::string Name = PDecl->getNameAsString(); + QualType QT = PDecl->getType(); + // Make sure we convert "t (^)(...)" to "t (*)(...)". + (void)convertBlockPointerToFunctionPointer(QT); + QT.getAsStringInternal(Name, Context->getPrintingPolicy()); + ResultStr += Name; + } + } + if (OMD->isVariadic()) + ResultStr += ", ..."; + ResultStr += ") "; + + if (FPRetType) { + ResultStr += ")"; // close the precedence "scope" for "*". + + // Now, emit the argument types (if any). + if (const FunctionProtoType *FT = dyn_cast<FunctionProtoType>(FPRetType)) { + ResultStr += "("; + for (unsigned i = 0, e = FT->getNumParams(); i != e; ++i) { + if (i) ResultStr += ", "; + std::string ParamStr = + FT->getParamType(i).getAsString(Context->getPrintingPolicy()); + ResultStr += ParamStr; + } + if (FT->isVariadic()) { + if (FT->getNumParams()) + ResultStr += ", "; + ResultStr += "..."; + } + ResultStr += ")"; + } else { + ResultStr += "()"; + } + } +} + +void RewriteModernObjC::RewriteImplementationDecl(Decl *OID) { + ObjCImplementationDecl *IMD = dyn_cast<ObjCImplementationDecl>(OID); + ObjCCategoryImplDecl *CID = dyn_cast<ObjCCategoryImplDecl>(OID); + + if (IMD) { + if (IMD->getIvarRBraceLoc().isValid()) { + ReplaceText(IMD->getBeginLoc(), 1, "/** "); + ReplaceText(IMD->getIvarRBraceLoc(), 1, "**/ "); + } + else { + InsertText(IMD->getBeginLoc(), "// "); + } + } + else + InsertText(CID->getBeginLoc(), "// "); + + for (auto *OMD : IMD ? IMD->instance_methods() : CID->instance_methods()) { + std::string ResultStr; + RewriteObjCMethodDecl(OMD->getClassInterface(), OMD, ResultStr); + SourceLocation LocStart = OMD->getBeginLoc(); + SourceLocation LocEnd = OMD->getCompoundBody()->getBeginLoc(); + + const char *startBuf = SM->getCharacterData(LocStart); + const char *endBuf = SM->getCharacterData(LocEnd); + ReplaceText(LocStart, endBuf-startBuf, ResultStr); + } + + for (auto *OMD : IMD ? IMD->class_methods() : CID->class_methods()) { + std::string ResultStr; + RewriteObjCMethodDecl(OMD->getClassInterface(), OMD, ResultStr); + SourceLocation LocStart = OMD->getBeginLoc(); + SourceLocation LocEnd = OMD->getCompoundBody()->getBeginLoc(); + + const char *startBuf = SM->getCharacterData(LocStart); + const char *endBuf = SM->getCharacterData(LocEnd); + ReplaceText(LocStart, endBuf-startBuf, ResultStr); + } + for (auto *I : IMD ? IMD->property_impls() : CID->property_impls()) + RewritePropertyImplDecl(I, IMD, CID); + + InsertText(IMD ? IMD->getEndLoc() : CID->getEndLoc(), "// "); +} + +void RewriteModernObjC::RewriteInterfaceDecl(ObjCInterfaceDecl *ClassDecl) { + // Do not synthesize more than once. + if (ObjCSynthesizedStructs.count(ClassDecl)) + return; + // Make sure super class's are written before current class is written. + ObjCInterfaceDecl *SuperClass = ClassDecl->getSuperClass(); + while (SuperClass) { + RewriteInterfaceDecl(SuperClass); + SuperClass = SuperClass->getSuperClass(); + } + std::string ResultStr; + if (!ObjCWrittenInterfaces.count(ClassDecl->getCanonicalDecl())) { + // we haven't seen a forward decl - generate a typedef. + RewriteOneForwardClassDecl(ClassDecl, ResultStr); + RewriteIvarOffsetSymbols(ClassDecl, ResultStr); + + RewriteObjCInternalStruct(ClassDecl, ResultStr); + // Mark this typedef as having been written into its c++ equivalent. + ObjCWrittenInterfaces.insert(ClassDecl->getCanonicalDecl()); + + for (auto *I : ClassDecl->instance_properties()) + RewriteProperty(I); + for (auto *I : ClassDecl->instance_methods()) + RewriteMethodDeclaration(I); + for (auto *I : ClassDecl->class_methods()) + RewriteMethodDeclaration(I); + + // Lastly, comment out the @end. + ReplaceText(ClassDecl->getAtEndRange().getBegin(), strlen("@end"), + "/* @end */\n"); + } +} + +Stmt *RewriteModernObjC::RewritePropertyOrImplicitSetter(PseudoObjectExpr *PseudoOp) { + SourceRange OldRange = PseudoOp->getSourceRange(); + + // We just magically know some things about the structure of this + // expression. + ObjCMessageExpr *OldMsg = + cast<ObjCMessageExpr>(PseudoOp->getSemanticExpr( + PseudoOp->getNumSemanticExprs() - 1)); + + // Because the rewriter doesn't allow us to rewrite rewritten code, + // we need to suppress rewriting the sub-statements. + Expr *Base; + SmallVector<Expr*, 2> Args; + { + DisableReplaceStmtScope S(*this); + + // Rebuild the base expression if we have one. + Base = nullptr; + if (OldMsg->getReceiverKind() == ObjCMessageExpr::Instance) { + Base = OldMsg->getInstanceReceiver(); + Base = cast<OpaqueValueExpr>(Base)->getSourceExpr(); + Base = cast<Expr>(RewriteFunctionBodyOrGlobalInitializer(Base)); + } + + unsigned numArgs = OldMsg->getNumArgs(); + for (unsigned i = 0; i < numArgs; i++) { + Expr *Arg = OldMsg->getArg(i); + if (isa<OpaqueValueExpr>(Arg)) + Arg = cast<OpaqueValueExpr>(Arg)->getSourceExpr(); + Arg = cast<Expr>(RewriteFunctionBodyOrGlobalInitializer(Arg)); + Args.push_back(Arg); + } + } + + // TODO: avoid this copy. + SmallVector<SourceLocation, 1> SelLocs; + OldMsg->getSelectorLocs(SelLocs); + + ObjCMessageExpr *NewMsg = nullptr; + switch (OldMsg->getReceiverKind()) { + case ObjCMessageExpr::Class: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + OldMsg->getClassReceiverTypeInfo(), + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + Args, + OldMsg->getRightLoc(), + OldMsg->isImplicit()); + break; + + case ObjCMessageExpr::Instance: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + Base, + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + Args, + OldMsg->getRightLoc(), + OldMsg->isImplicit()); + break; + + case ObjCMessageExpr::SuperClass: + case ObjCMessageExpr::SuperInstance: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + OldMsg->getSuperLoc(), + OldMsg->getReceiverKind() == ObjCMessageExpr::SuperInstance, + OldMsg->getSuperType(), + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + Args, + OldMsg->getRightLoc(), + OldMsg->isImplicit()); + break; + } + + Stmt *Replacement = SynthMessageExpr(NewMsg); + ReplaceStmtWithRange(PseudoOp, Replacement, OldRange); + return Replacement; +} + +Stmt *RewriteModernObjC::RewritePropertyOrImplicitGetter(PseudoObjectExpr *PseudoOp) { + SourceRange OldRange = PseudoOp->getSourceRange(); + + // We just magically know some things about the structure of this + // expression. + ObjCMessageExpr *OldMsg = + cast<ObjCMessageExpr>(PseudoOp->getResultExpr()->IgnoreImplicit()); + + // Because the rewriter doesn't allow us to rewrite rewritten code, + // we need to suppress rewriting the sub-statements. + Expr *Base = nullptr; + SmallVector<Expr*, 1> Args; + { + DisableReplaceStmtScope S(*this); + // Rebuild the base expression if we have one. + if (OldMsg->getReceiverKind() == ObjCMessageExpr::Instance) { + Base = OldMsg->getInstanceReceiver(); + Base = cast<OpaqueValueExpr>(Base)->getSourceExpr(); + Base = cast<Expr>(RewriteFunctionBodyOrGlobalInitializer(Base)); + } + unsigned numArgs = OldMsg->getNumArgs(); + for (unsigned i = 0; i < numArgs; i++) { + Expr *Arg = OldMsg->getArg(i); + if (isa<OpaqueValueExpr>(Arg)) + Arg = cast<OpaqueValueExpr>(Arg)->getSourceExpr(); + Arg = cast<Expr>(RewriteFunctionBodyOrGlobalInitializer(Arg)); + Args.push_back(Arg); + } + } + + // Intentionally empty. + SmallVector<SourceLocation, 1> SelLocs; + + ObjCMessageExpr *NewMsg = nullptr; + switch (OldMsg->getReceiverKind()) { + case ObjCMessageExpr::Class: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + OldMsg->getClassReceiverTypeInfo(), + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + Args, + OldMsg->getRightLoc(), + OldMsg->isImplicit()); + break; + + case ObjCMessageExpr::Instance: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + Base, + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + Args, + OldMsg->getRightLoc(), + OldMsg->isImplicit()); + break; + + case ObjCMessageExpr::SuperClass: + case ObjCMessageExpr::SuperInstance: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + OldMsg->getSuperLoc(), + OldMsg->getReceiverKind() == ObjCMessageExpr::SuperInstance, + OldMsg->getSuperType(), + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + Args, + OldMsg->getRightLoc(), + OldMsg->isImplicit()); + break; + } + + Stmt *Replacement = SynthMessageExpr(NewMsg); + ReplaceStmtWithRange(PseudoOp, Replacement, OldRange); + return Replacement; +} + +/// SynthCountByEnumWithState - To print: +/// ((NSUInteger (*) +/// (id, SEL, struct __objcFastEnumerationState *, id *, NSUInteger)) +/// (void *)objc_msgSend)((id)l_collection, +/// sel_registerName( +/// "countByEnumeratingWithState:objects:count:"), +/// &enumState, +/// (id *)__rw_items, (NSUInteger)16) +/// +void RewriteModernObjC::SynthCountByEnumWithState(std::string &buf) { + buf += "((_WIN_NSUInteger (*) (id, SEL, struct __objcFastEnumerationState *, " + "id *, _WIN_NSUInteger))(void *)objc_msgSend)"; + buf += "\n\t\t"; + buf += "((id)l_collection,\n\t\t"; + buf += "sel_registerName(\"countByEnumeratingWithState:objects:count:\"),"; + buf += "\n\t\t"; + buf += "&enumState, " + "(id *)__rw_items, (_WIN_NSUInteger)16)"; +} + +/// RewriteBreakStmt - Rewrite for a break-stmt inside an ObjC2's foreach +/// statement to exit to its outer synthesized loop. +/// +Stmt *RewriteModernObjC::RewriteBreakStmt(BreakStmt *S) { + if (Stmts.empty() || !isa<ObjCForCollectionStmt>(Stmts.back())) + return S; + // replace break with goto __break_label + std::string buf; + + SourceLocation startLoc = S->getBeginLoc(); + buf = "goto __break_label_"; + buf += utostr(ObjCBcLabelNo.back()); + ReplaceText(startLoc, strlen("break"), buf); + + return nullptr; +} + +void RewriteModernObjC::ConvertSourceLocationToLineDirective( + SourceLocation Loc, + std::string &LineString) { + if (Loc.isFileID() && GenerateLineInfo) { + LineString += "\n#line "; + PresumedLoc PLoc = SM->getPresumedLoc(Loc); + LineString += utostr(PLoc.getLine()); + LineString += " \""; + LineString += Lexer::Stringify(PLoc.getFilename()); + LineString += "\"\n"; + } +} + +/// RewriteContinueStmt - Rewrite for a continue-stmt inside an ObjC2's foreach +/// statement to continue with its inner synthesized loop. +/// +Stmt *RewriteModernObjC::RewriteContinueStmt(ContinueStmt *S) { + if (Stmts.empty() || !isa<ObjCForCollectionStmt>(Stmts.back())) + return S; + // replace continue with goto __continue_label + std::string buf; + + SourceLocation startLoc = S->getBeginLoc(); + buf = "goto __continue_label_"; + buf += utostr(ObjCBcLabelNo.back()); + ReplaceText(startLoc, strlen("continue"), buf); + + return nullptr; +} + +/// RewriteObjCForCollectionStmt - Rewriter for ObjC2's foreach statement. +/// It rewrites: +/// for ( type elem in collection) { stmts; } + +/// Into: +/// { +/// type elem; +/// struct __objcFastEnumerationState enumState = { 0 }; +/// id __rw_items[16]; +/// id l_collection = (id)collection; +/// NSUInteger limit = [l_collection countByEnumeratingWithState:&enumState +/// objects:__rw_items count:16]; +/// if (limit) { +/// unsigned long startMutations = *enumState.mutationsPtr; +/// do { +/// unsigned long counter = 0; +/// do { +/// if (startMutations != *enumState.mutationsPtr) +/// objc_enumerationMutation(l_collection); +/// elem = (type)enumState.itemsPtr[counter++]; +/// stmts; +/// __continue_label: ; +/// } while (counter < limit); +/// } while ((limit = [l_collection countByEnumeratingWithState:&enumState +/// objects:__rw_items count:16])); +/// elem = nil; +/// __break_label: ; +/// } +/// else +/// elem = nil; +/// } +/// +Stmt *RewriteModernObjC::RewriteObjCForCollectionStmt(ObjCForCollectionStmt *S, + SourceLocation OrigEnd) { + assert(!Stmts.empty() && "ObjCForCollectionStmt - Statement stack empty"); + assert(isa<ObjCForCollectionStmt>(Stmts.back()) && + "ObjCForCollectionStmt Statement stack mismatch"); + assert(!ObjCBcLabelNo.empty() && + "ObjCForCollectionStmt - Label No stack empty"); + + SourceLocation startLoc = S->getBeginLoc(); + const char *startBuf = SM->getCharacterData(startLoc); + StringRef elementName; + std::string elementTypeAsString; + std::string buf; + // line directive first. + SourceLocation ForEachLoc = S->getForLoc(); + ConvertSourceLocationToLineDirective(ForEachLoc, buf); + buf += "{\n\t"; + if (DeclStmt *DS = dyn_cast<DeclStmt>(S->getElement())) { + // type elem; + NamedDecl* D = cast<NamedDecl>(DS->getSingleDecl()); + QualType ElementType = cast<ValueDecl>(D)->getType(); + if (ElementType->isObjCQualifiedIdType() || + ElementType->isObjCQualifiedInterfaceType()) + // Simply use 'id' for all qualified types. + elementTypeAsString = "id"; + else + elementTypeAsString = ElementType.getAsString(Context->getPrintingPolicy()); + buf += elementTypeAsString; + buf += " "; + elementName = D->getName(); + buf += elementName; + buf += ";\n\t"; + } + else { + DeclRefExpr *DR = cast<DeclRefExpr>(S->getElement()); + elementName = DR->getDecl()->getName(); + ValueDecl *VD = DR->getDecl(); + if (VD->getType()->isObjCQualifiedIdType() || + VD->getType()->isObjCQualifiedInterfaceType()) + // Simply use 'id' for all qualified types. + elementTypeAsString = "id"; + else + elementTypeAsString = VD->getType().getAsString(Context->getPrintingPolicy()); + } + + // struct __objcFastEnumerationState enumState = { 0 }; + buf += "struct __objcFastEnumerationState enumState = { 0 };\n\t"; + // id __rw_items[16]; + buf += "id __rw_items[16];\n\t"; + // id l_collection = (id) + buf += "id l_collection = (id)"; + // Find start location of 'collection' the hard way! + const char *startCollectionBuf = startBuf; + startCollectionBuf += 3; // skip 'for' + startCollectionBuf = strchr(startCollectionBuf, '('); + startCollectionBuf++; // skip '(' + // find 'in' and skip it. + while (*startCollectionBuf != ' ' || + *(startCollectionBuf+1) != 'i' || *(startCollectionBuf+2) != 'n' || + (*(startCollectionBuf+3) != ' ' && + *(startCollectionBuf+3) != '[' && *(startCollectionBuf+3) != '(')) + startCollectionBuf++; + startCollectionBuf += 3; + + // Replace: "for (type element in" with string constructed thus far. + ReplaceText(startLoc, startCollectionBuf - startBuf, buf); + // Replace ')' in for '(' type elem in collection ')' with ';' + SourceLocation rightParenLoc = S->getRParenLoc(); + const char *rparenBuf = SM->getCharacterData(rightParenLoc); + SourceLocation lparenLoc = startLoc.getLocWithOffset(rparenBuf-startBuf); + buf = ";\n\t"; + + // unsigned long limit = [l_collection countByEnumeratingWithState:&enumState + // objects:__rw_items count:16]; + // which is synthesized into: + // NSUInteger limit = + // ((NSUInteger (*) + // (id, SEL, struct __objcFastEnumerationState *, id *, NSUInteger)) + // (void *)objc_msgSend)((id)l_collection, + // sel_registerName( + // "countByEnumeratingWithState:objects:count:"), + // (struct __objcFastEnumerationState *)&state, + // (id *)__rw_items, (NSUInteger)16); + buf += "_WIN_NSUInteger limit =\n\t\t"; + SynthCountByEnumWithState(buf); + buf += ";\n\t"; + /// if (limit) { + /// unsigned long startMutations = *enumState.mutationsPtr; + /// do { + /// unsigned long counter = 0; + /// do { + /// if (startMutations != *enumState.mutationsPtr) + /// objc_enumerationMutation(l_collection); + /// elem = (type)enumState.itemsPtr[counter++]; + buf += "if (limit) {\n\t"; + buf += "unsigned long startMutations = *enumState.mutationsPtr;\n\t"; + buf += "do {\n\t\t"; + buf += "unsigned long counter = 0;\n\t\t"; + buf += "do {\n\t\t\t"; + buf += "if (startMutations != *enumState.mutationsPtr)\n\t\t\t\t"; + buf += "objc_enumerationMutation(l_collection);\n\t\t\t"; + buf += elementName; + buf += " = ("; + buf += elementTypeAsString; + buf += ")enumState.itemsPtr[counter++];"; + // Replace ')' in for '(' type elem in collection ')' with all of these. + ReplaceText(lparenLoc, 1, buf); + + /// __continue_label: ; + /// } while (counter < limit); + /// } while ((limit = [l_collection countByEnumeratingWithState:&enumState + /// objects:__rw_items count:16])); + /// elem = nil; + /// __break_label: ; + /// } + /// else + /// elem = nil; + /// } + /// + buf = ";\n\t"; + buf += "__continue_label_"; + buf += utostr(ObjCBcLabelNo.back()); + buf += ": ;"; + buf += "\n\t\t"; + buf += "} while (counter < limit);\n\t"; + buf += "} while ((limit = "; + SynthCountByEnumWithState(buf); + buf += "));\n\t"; + buf += elementName; + buf += " = (("; + buf += elementTypeAsString; + buf += ")0);\n\t"; + buf += "__break_label_"; + buf += utostr(ObjCBcLabelNo.back()); + buf += ": ;\n\t"; + buf += "}\n\t"; + buf += "else\n\t\t"; + buf += elementName; + buf += " = (("; + buf += elementTypeAsString; + buf += ")0);\n\t"; + buf += "}\n"; + + // Insert all these *after* the statement body. + // FIXME: If this should support Obj-C++, support CXXTryStmt + if (isa<CompoundStmt>(S->getBody())) { + SourceLocation endBodyLoc = OrigEnd.getLocWithOffset(1); + InsertText(endBodyLoc, buf); + } else { + /* Need to treat single statements specially. For example: + * + * for (A *a in b) if (stuff()) break; + * for (A *a in b) xxxyy; + * + * The following code simply scans ahead to the semi to find the actual end. + */ + const char *stmtBuf = SM->getCharacterData(OrigEnd); + const char *semiBuf = strchr(stmtBuf, ';'); + assert(semiBuf && "Can't find ';'"); + SourceLocation endBodyLoc = OrigEnd.getLocWithOffset(semiBuf-stmtBuf+1); + InsertText(endBodyLoc, buf); + } + Stmts.pop_back(); + ObjCBcLabelNo.pop_back(); + return nullptr; +} + +static void Write_RethrowObject(std::string &buf) { + buf += "{ struct _FIN { _FIN(id reth) : rethrow(reth) {}\n"; + buf += "\t~_FIN() { if (rethrow) objc_exception_throw(rethrow); }\n"; + buf += "\tid rethrow;\n"; + buf += "\t} _fin_force_rethow(_rethrow);"; +} + +/// RewriteObjCSynchronizedStmt - +/// This routine rewrites @synchronized(expr) stmt; +/// into: +/// objc_sync_enter(expr); +/// @try stmt @finally { objc_sync_exit(expr); } +/// +Stmt *RewriteModernObjC::RewriteObjCSynchronizedStmt(ObjCAtSynchronizedStmt *S) { + // Get the start location and compute the semi location. + SourceLocation startLoc = S->getBeginLoc(); + const char *startBuf = SM->getCharacterData(startLoc); + + assert((*startBuf == '@') && "bogus @synchronized location"); + + std::string buf; + SourceLocation SynchLoc = S->getAtSynchronizedLoc(); + ConvertSourceLocationToLineDirective(SynchLoc, buf); + buf += "{ id _rethrow = 0; id _sync_obj = (id)"; + + const char *lparenBuf = startBuf; + while (*lparenBuf != '(') lparenBuf++; + ReplaceText(startLoc, lparenBuf-startBuf+1, buf); + + buf = "; objc_sync_enter(_sync_obj);\n"; + buf += "try {\n\tstruct _SYNC_EXIT { _SYNC_EXIT(id arg) : sync_exit(arg) {}"; + buf += "\n\t~_SYNC_EXIT() {objc_sync_exit(sync_exit);}"; + buf += "\n\tid sync_exit;"; + buf += "\n\t} _sync_exit(_sync_obj);\n"; + + // We can't use S->getSynchExpr()->getEndLoc() to find the end location, since + // the sync expression is typically a message expression that's already + // been rewritten! (which implies the SourceLocation's are invalid). + SourceLocation RParenExprLoc = S->getSynchBody()->getBeginLoc(); + const char *RParenExprLocBuf = SM->getCharacterData(RParenExprLoc); + while (*RParenExprLocBuf != ')') RParenExprLocBuf--; + RParenExprLoc = startLoc.getLocWithOffset(RParenExprLocBuf-startBuf); + + SourceLocation LBranceLoc = S->getSynchBody()->getBeginLoc(); + const char *LBraceLocBuf = SM->getCharacterData(LBranceLoc); + assert (*LBraceLocBuf == '{'); + ReplaceText(RParenExprLoc, (LBraceLocBuf - SM->getCharacterData(RParenExprLoc) + 1), buf); + + SourceLocation startRBraceLoc = S->getSynchBody()->getEndLoc(); + assert((*SM->getCharacterData(startRBraceLoc) == '}') && + "bogus @synchronized block"); + + buf = "} catch (id e) {_rethrow = e;}\n"; + Write_RethrowObject(buf); + buf += "}\n"; + buf += "}\n"; + + ReplaceText(startRBraceLoc, 1, buf); + + return nullptr; +} + +void RewriteModernObjC::WarnAboutReturnGotoStmts(Stmt *S) +{ + // Perform a bottom up traversal of all children. + for (Stmt *SubStmt : S->children()) + if (SubStmt) + WarnAboutReturnGotoStmts(SubStmt); + + if (isa<ReturnStmt>(S) || isa<GotoStmt>(S)) { + Diags.Report(Context->getFullLoc(S->getBeginLoc()), + TryFinallyContainsReturnDiag); + } +} + +Stmt *RewriteModernObjC::RewriteObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S) { + SourceLocation startLoc = S->getAtLoc(); + ReplaceText(startLoc, strlen("@autoreleasepool"), "/* @autoreleasepool */"); + ReplaceText(S->getSubStmt()->getBeginLoc(), 1, + "{ __AtAutoreleasePool __autoreleasepool; "); + + return nullptr; +} + +Stmt *RewriteModernObjC::RewriteObjCTryStmt(ObjCAtTryStmt *S) { + ObjCAtFinallyStmt *finalStmt = S->getFinallyStmt(); + bool noCatch = S->getNumCatchStmts() == 0; + std::string buf; + SourceLocation TryLocation = S->getAtTryLoc(); + ConvertSourceLocationToLineDirective(TryLocation, buf); + + if (finalStmt) { + if (noCatch) + buf += "{ id volatile _rethrow = 0;\n"; + else { + buf += "{ id volatile _rethrow = 0;\ntry {\n"; + } + } + // Get the start location and compute the semi location. + SourceLocation startLoc = S->getBeginLoc(); + const char *startBuf = SM->getCharacterData(startLoc); + + assert((*startBuf == '@') && "bogus @try location"); + if (finalStmt) + ReplaceText(startLoc, 1, buf); + else + // @try -> try + ReplaceText(startLoc, 1, ""); + + for (unsigned I = 0, N = S->getNumCatchStmts(); I != N; ++I) { + ObjCAtCatchStmt *Catch = S->getCatchStmt(I); + VarDecl *catchDecl = Catch->getCatchParamDecl(); + + startLoc = Catch->getBeginLoc(); + bool AtRemoved = false; + if (catchDecl) { + QualType t = catchDecl->getType(); + if (const ObjCObjectPointerType *Ptr = t->getAs<ObjCObjectPointerType>()) { + // Should be a pointer to a class. + ObjCInterfaceDecl *IDecl = Ptr->getObjectType()->getInterface(); + if (IDecl) { + std::string Result; + ConvertSourceLocationToLineDirective(Catch->getBeginLoc(), Result); + + startBuf = SM->getCharacterData(startLoc); + assert((*startBuf == '@') && "bogus @catch location"); + SourceLocation rParenLoc = Catch->getRParenLoc(); + const char *rParenBuf = SM->getCharacterData(rParenLoc); + + // _objc_exc_Foo *_e as argument to catch. + Result += "catch (_objc_exc_"; Result += IDecl->getNameAsString(); + Result += " *_"; Result += catchDecl->getNameAsString(); + Result += ")"; + ReplaceText(startLoc, rParenBuf-startBuf+1, Result); + // Foo *e = (Foo *)_e; + Result.clear(); + Result = "{ "; + Result += IDecl->getNameAsString(); + Result += " *"; Result += catchDecl->getNameAsString(); + Result += " = ("; Result += IDecl->getNameAsString(); Result += "*)"; + Result += "_"; Result += catchDecl->getNameAsString(); + + Result += "; "; + SourceLocation lBraceLoc = Catch->getCatchBody()->getBeginLoc(); + ReplaceText(lBraceLoc, 1, Result); + AtRemoved = true; + } + } + } + if (!AtRemoved) + // @catch -> catch + ReplaceText(startLoc, 1, ""); + + } + if (finalStmt) { + buf.clear(); + SourceLocation FinallyLoc = finalStmt->getBeginLoc(); + + if (noCatch) { + ConvertSourceLocationToLineDirective(FinallyLoc, buf); + buf += "catch (id e) {_rethrow = e;}\n"; + } + else { + buf += "}\n"; + ConvertSourceLocationToLineDirective(FinallyLoc, buf); + buf += "catch (id e) {_rethrow = e;}\n"; + } + + SourceLocation startFinalLoc = finalStmt->getBeginLoc(); + ReplaceText(startFinalLoc, 8, buf); + Stmt *body = finalStmt->getFinallyBody(); + SourceLocation startFinalBodyLoc = body->getBeginLoc(); + buf.clear(); + Write_RethrowObject(buf); + ReplaceText(startFinalBodyLoc, 1, buf); + + SourceLocation endFinalBodyLoc = body->getEndLoc(); + ReplaceText(endFinalBodyLoc, 1, "}\n}"); + // Now check for any return/continue/go statements within the @try. + WarnAboutReturnGotoStmts(S->getTryBody()); + } + + return nullptr; +} + +// This can't be done with ReplaceStmt(S, ThrowExpr), since +// the throw expression is typically a message expression that's already +// been rewritten! (which implies the SourceLocation's are invalid). +Stmt *RewriteModernObjC::RewriteObjCThrowStmt(ObjCAtThrowStmt *S) { + // Get the start location and compute the semi location. + SourceLocation startLoc = S->getBeginLoc(); + const char *startBuf = SM->getCharacterData(startLoc); + + assert((*startBuf == '@') && "bogus @throw location"); + + std::string buf; + /* void objc_exception_throw(id) __attribute__((noreturn)); */ + if (S->getThrowExpr()) + buf = "objc_exception_throw("; + else + buf = "throw"; + + // handle "@ throw" correctly. + const char *wBuf = strchr(startBuf, 'w'); + assert((*wBuf == 'w') && "@throw: can't find 'w'"); + ReplaceText(startLoc, wBuf-startBuf+1, buf); + + SourceLocation endLoc = S->getEndLoc(); + const char *endBuf = SM->getCharacterData(endLoc); + const char *semiBuf = strchr(endBuf, ';'); + assert((*semiBuf == ';') && "@throw: can't find ';'"); + SourceLocation semiLoc = startLoc.getLocWithOffset(semiBuf-startBuf); + if (S->getThrowExpr()) + ReplaceText(semiLoc, 1, ");"); + return nullptr; +} + +Stmt *RewriteModernObjC::RewriteAtEncode(ObjCEncodeExpr *Exp) { + // Create a new string expression. + std::string StrEncoding; + Context->getObjCEncodingForType(Exp->getEncodedType(), StrEncoding); + Expr *Replacement = getStringLiteral(StrEncoding); + ReplaceStmt(Exp, Replacement); + + // Replace this subexpr in the parent. + // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. + return Replacement; +} + +Stmt *RewriteModernObjC::RewriteAtSelector(ObjCSelectorExpr *Exp) { + if (!SelGetUidFunctionDecl) + SynthSelGetUidFunctionDecl(); + assert(SelGetUidFunctionDecl && "Can't find sel_registerName() decl"); + // Create a call to sel_registerName("selName"). + SmallVector<Expr*, 8> SelExprs; + SelExprs.push_back(getStringLiteral(Exp->getSelector().getAsString())); + CallExpr *SelExp = SynthesizeCallToFunctionDecl(SelGetUidFunctionDecl, + SelExprs); + ReplaceStmt(Exp, SelExp); + // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. + return SelExp; +} + +CallExpr * +RewriteModernObjC::SynthesizeCallToFunctionDecl(FunctionDecl *FD, + ArrayRef<Expr *> Args, + SourceLocation StartLoc, + SourceLocation EndLoc) { + // Get the type, we will need to reference it in a couple spots. + QualType msgSendType = FD->getType(); + + // Create a reference to the objc_msgSend() declaration. + DeclRefExpr *DRE = new (Context) DeclRefExpr(*Context, FD, false, msgSendType, + VK_LValue, SourceLocation()); + + // Now, we cast the reference to a pointer to the objc_msgSend type. + QualType pToFunc = Context->getPointerType(msgSendType); + ImplicitCastExpr *ICE = + ImplicitCastExpr::Create(*Context, pToFunc, CK_FunctionToPointerDecay, + DRE, nullptr, VK_RValue); + + const FunctionType *FT = msgSendType->getAs<FunctionType>(); + + CallExpr *Exp = CallExpr::Create( + *Context, ICE, Args, FT->getCallResultType(*Context), VK_RValue, EndLoc); + return Exp; +} + +static bool scanForProtocolRefs(const char *startBuf, const char *endBuf, + const char *&startRef, const char *&endRef) { + while (startBuf < endBuf) { + if (*startBuf == '<') + startRef = startBuf; // mark the start. + if (*startBuf == '>') { + if (startRef && *startRef == '<') { + endRef = startBuf; // mark the end. + return true; + } + return false; + } + startBuf++; + } + return false; +} + +static void scanToNextArgument(const char *&argRef) { + int angle = 0; + while (*argRef != ')' && (*argRef != ',' || angle > 0)) { + if (*argRef == '<') + angle++; + else if (*argRef == '>') + angle--; + argRef++; + } + assert(angle == 0 && "scanToNextArgument - bad protocol type syntax"); +} + +bool RewriteModernObjC::needToScanForQualifiers(QualType T) { + if (T->isObjCQualifiedIdType()) + return true; + if (const PointerType *PT = T->getAs<PointerType>()) { + if (PT->getPointeeType()->isObjCQualifiedIdType()) + return true; + } + if (T->isObjCObjectPointerType()) { + T = T->getPointeeType(); + return T->isObjCQualifiedInterfaceType(); + } + if (T->isArrayType()) { + QualType ElemTy = Context->getBaseElementType(T); + return needToScanForQualifiers(ElemTy); + } + return false; +} + +void RewriteModernObjC::RewriteObjCQualifiedInterfaceTypes(Expr *E) { + QualType Type = E->getType(); + if (needToScanForQualifiers(Type)) { + SourceLocation Loc, EndLoc; + + if (const CStyleCastExpr *ECE = dyn_cast<CStyleCastExpr>(E)) { + Loc = ECE->getLParenLoc(); + EndLoc = ECE->getRParenLoc(); + } else { + Loc = E->getBeginLoc(); + EndLoc = E->getEndLoc(); + } + // This will defend against trying to rewrite synthesized expressions. + if (Loc.isInvalid() || EndLoc.isInvalid()) + return; + + const char *startBuf = SM->getCharacterData(Loc); + const char *endBuf = SM->getCharacterData(EndLoc); + const char *startRef = nullptr, *endRef = nullptr; + if (scanForProtocolRefs(startBuf, endBuf, startRef, endRef)) { + // Get the locations of the startRef, endRef. + SourceLocation LessLoc = Loc.getLocWithOffset(startRef-startBuf); + SourceLocation GreaterLoc = Loc.getLocWithOffset(endRef-startBuf+1); + // Comment out the protocol references. + InsertText(LessLoc, "/*"); + InsertText(GreaterLoc, "*/"); + } + } +} + +void RewriteModernObjC::RewriteObjCQualifiedInterfaceTypes(Decl *Dcl) { + SourceLocation Loc; + QualType Type; + const FunctionProtoType *proto = nullptr; + if (VarDecl *VD = dyn_cast<VarDecl>(Dcl)) { + Loc = VD->getLocation(); + Type = VD->getType(); + } + else if (FunctionDecl *FD = dyn_cast<FunctionDecl>(Dcl)) { + Loc = FD->getLocation(); + // Check for ObjC 'id' and class types that have been adorned with protocol + // information (id<p>, C<p>*). The protocol references need to be rewritten! + const FunctionType *funcType = FD->getType()->getAs<FunctionType>(); + assert(funcType && "missing function type"); + proto = dyn_cast<FunctionProtoType>(funcType); + if (!proto) + return; + Type = proto->getReturnType(); + } + else if (FieldDecl *FD = dyn_cast<FieldDecl>(Dcl)) { + Loc = FD->getLocation(); + Type = FD->getType(); + } + else if (TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(Dcl)) { + Loc = TD->getLocation(); + Type = TD->getUnderlyingType(); + } + else + return; + + if (needToScanForQualifiers(Type)) { + // Since types are unique, we need to scan the buffer. + + const char *endBuf = SM->getCharacterData(Loc); + const char *startBuf = endBuf; + while (*startBuf != ';' && *startBuf != '<' && startBuf != MainFileStart) + startBuf--; // scan backward (from the decl location) for return type. + const char *startRef = nullptr, *endRef = nullptr; + if (scanForProtocolRefs(startBuf, endBuf, startRef, endRef)) { + // Get the locations of the startRef, endRef. + SourceLocation LessLoc = Loc.getLocWithOffset(startRef-endBuf); + SourceLocation GreaterLoc = Loc.getLocWithOffset(endRef-endBuf+1); + // Comment out the protocol references. + InsertText(LessLoc, "/*"); + InsertText(GreaterLoc, "*/"); + } + } + if (!proto) + return; // most likely, was a variable + // Now check arguments. + const char *startBuf = SM->getCharacterData(Loc); + const char *startFuncBuf = startBuf; + for (unsigned i = 0; i < proto->getNumParams(); i++) { + if (needToScanForQualifiers(proto->getParamType(i))) { + // Since types are unique, we need to scan the buffer. + + const char *endBuf = startBuf; + // scan forward (from the decl location) for argument types. + scanToNextArgument(endBuf); + const char *startRef = nullptr, *endRef = nullptr; + if (scanForProtocolRefs(startBuf, endBuf, startRef, endRef)) { + // Get the locations of the startRef, endRef. + SourceLocation LessLoc = + Loc.getLocWithOffset(startRef-startFuncBuf); + SourceLocation GreaterLoc = + Loc.getLocWithOffset(endRef-startFuncBuf+1); + // Comment out the protocol references. + InsertText(LessLoc, "/*"); + InsertText(GreaterLoc, "*/"); + } + startBuf = ++endBuf; + } + else { + // If the function name is derived from a macro expansion, then the + // argument buffer will not follow the name. Need to speak with Chris. + while (*startBuf && *startBuf != ')' && *startBuf != ',') + startBuf++; // scan forward (from the decl location) for argument types. + startBuf++; + } + } +} + +void RewriteModernObjC::RewriteTypeOfDecl(VarDecl *ND) { + QualType QT = ND->getType(); + const Type* TypePtr = QT->getAs<Type>(); + if (!isa<TypeOfExprType>(TypePtr)) + return; + while (isa<TypeOfExprType>(TypePtr)) { + const TypeOfExprType *TypeOfExprTypePtr = cast<TypeOfExprType>(TypePtr); + QT = TypeOfExprTypePtr->getUnderlyingExpr()->getType(); + TypePtr = QT->getAs<Type>(); + } + // FIXME. This will not work for multiple declarators; as in: + // __typeof__(a) b,c,d; + std::string TypeAsString(QT.getAsString(Context->getPrintingPolicy())); + SourceLocation DeclLoc = ND->getTypeSpecStartLoc(); + const char *startBuf = SM->getCharacterData(DeclLoc); + if (ND->getInit()) { + std::string Name(ND->getNameAsString()); + TypeAsString += " " + Name + " = "; + Expr *E = ND->getInit(); + SourceLocation startLoc; + if (const CStyleCastExpr *ECE = dyn_cast<CStyleCastExpr>(E)) + startLoc = ECE->getLParenLoc(); + else + startLoc = E->getBeginLoc(); + startLoc = SM->getExpansionLoc(startLoc); + const char *endBuf = SM->getCharacterData(startLoc); + ReplaceText(DeclLoc, endBuf-startBuf-1, TypeAsString); + } + else { + SourceLocation X = ND->getEndLoc(); + X = SM->getExpansionLoc(X); + const char *endBuf = SM->getCharacterData(X); + ReplaceText(DeclLoc, endBuf-startBuf-1, TypeAsString); + } +} + +// SynthSelGetUidFunctionDecl - SEL sel_registerName(const char *str); +void RewriteModernObjC::SynthSelGetUidFunctionDecl() { + IdentifierInfo *SelGetUidIdent = &Context->Idents.get("sel_registerName"); + SmallVector<QualType, 16> ArgTys; + ArgTys.push_back(Context->getPointerType(Context->CharTy.withConst())); + QualType getFuncType = + getSimpleFunctionType(Context->getObjCSelType(), ArgTys); + SelGetUidFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + SelGetUidIdent, getFuncType, + nullptr, SC_Extern); +} + +void RewriteModernObjC::RewriteFunctionDecl(FunctionDecl *FD) { + // declared in <objc/objc.h> + if (FD->getIdentifier() && + FD->getName() == "sel_registerName") { + SelGetUidFunctionDecl = FD; + return; + } + RewriteObjCQualifiedInterfaceTypes(FD); +} + +void RewriteModernObjC::RewriteBlockPointerType(std::string& Str, QualType Type) { + std::string TypeString(Type.getAsString(Context->getPrintingPolicy())); + const char *argPtr = TypeString.c_str(); + if (!strchr(argPtr, '^')) { + Str += TypeString; + return; + } + while (*argPtr) { + Str += (*argPtr == '^' ? '*' : *argPtr); + argPtr++; + } +} + +// FIXME. Consolidate this routine with RewriteBlockPointerType. +void RewriteModernObjC::RewriteBlockPointerTypeVariable(std::string& Str, + ValueDecl *VD) { + QualType Type = VD->getType(); + std::string TypeString(Type.getAsString(Context->getPrintingPolicy())); + const char *argPtr = TypeString.c_str(); + int paren = 0; + while (*argPtr) { + switch (*argPtr) { + case '(': + Str += *argPtr; + paren++; + break; + case ')': + Str += *argPtr; + paren--; + break; + case '^': + Str += '*'; + if (paren == 1) + Str += VD->getNameAsString(); + break; + default: + Str += *argPtr; + break; + } + argPtr++; + } +} + +void RewriteModernObjC::RewriteBlockLiteralFunctionDecl(FunctionDecl *FD) { + SourceLocation FunLocStart = FD->getTypeSpecStartLoc(); + const FunctionType *funcType = FD->getType()->getAs<FunctionType>(); + const FunctionProtoType *proto = dyn_cast<FunctionProtoType>(funcType); + if (!proto) + return; + QualType Type = proto->getReturnType(); + std::string FdStr = Type.getAsString(Context->getPrintingPolicy()); + FdStr += " "; + FdStr += FD->getName(); + FdStr += "("; + unsigned numArgs = proto->getNumParams(); + for (unsigned i = 0; i < numArgs; i++) { + QualType ArgType = proto->getParamType(i); + RewriteBlockPointerType(FdStr, ArgType); + if (i+1 < numArgs) + FdStr += ", "; + } + if (FD->isVariadic()) { + FdStr += (numArgs > 0) ? ", ...);\n" : "...);\n"; + } + else + FdStr += ");\n"; + InsertText(FunLocStart, FdStr); +} + +// SynthSuperConstructorFunctionDecl - id __rw_objc_super(id obj, id super); +void RewriteModernObjC::SynthSuperConstructorFunctionDecl() { + if (SuperConstructorFunctionDecl) + return; + IdentifierInfo *msgSendIdent = &Context->Idents.get("__rw_objc_super"); + SmallVector<QualType, 16> ArgTys; + QualType argT = Context->getObjCIdType(); + assert(!argT.isNull() && "Can't find 'id' type"); + ArgTys.push_back(argT); + ArgTys.push_back(argT); + QualType msgSendType = getSimpleFunctionType(Context->getObjCIdType(), + ArgTys); + SuperConstructorFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + msgSendIdent, msgSendType, + nullptr, SC_Extern); +} + +// SynthMsgSendFunctionDecl - id objc_msgSend(id self, SEL op, ...); +void RewriteModernObjC::SynthMsgSendFunctionDecl() { + IdentifierInfo *msgSendIdent = &Context->Idents.get("objc_msgSend"); + SmallVector<QualType, 16> ArgTys; + QualType argT = Context->getObjCIdType(); + assert(!argT.isNull() && "Can't find 'id' type"); + ArgTys.push_back(argT); + argT = Context->getObjCSelType(); + assert(!argT.isNull() && "Can't find 'SEL' type"); + ArgTys.push_back(argT); + QualType msgSendType = getSimpleFunctionType(Context->getObjCIdType(), + ArgTys, /*isVariadic=*/true); + MsgSendFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + msgSendIdent, msgSendType, nullptr, + SC_Extern); +} + +// SynthMsgSendSuperFunctionDecl - id objc_msgSendSuper(void); +void RewriteModernObjC::SynthMsgSendSuperFunctionDecl() { + IdentifierInfo *msgSendIdent = &Context->Idents.get("objc_msgSendSuper"); + SmallVector<QualType, 2> ArgTys; + ArgTys.push_back(Context->VoidTy); + QualType msgSendType = getSimpleFunctionType(Context->getObjCIdType(), + ArgTys, /*isVariadic=*/true); + MsgSendSuperFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + msgSendIdent, msgSendType, + nullptr, SC_Extern); +} + +// SynthMsgSendStretFunctionDecl - id objc_msgSend_stret(id self, SEL op, ...); +void RewriteModernObjC::SynthMsgSendStretFunctionDecl() { + IdentifierInfo *msgSendIdent = &Context->Idents.get("objc_msgSend_stret"); + SmallVector<QualType, 16> ArgTys; + QualType argT = Context->getObjCIdType(); + assert(!argT.isNull() && "Can't find 'id' type"); + ArgTys.push_back(argT); + argT = Context->getObjCSelType(); + assert(!argT.isNull() && "Can't find 'SEL' type"); + ArgTys.push_back(argT); + QualType msgSendType = getSimpleFunctionType(Context->getObjCIdType(), + ArgTys, /*isVariadic=*/true); + MsgSendStretFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + msgSendIdent, msgSendType, + nullptr, SC_Extern); +} + +// SynthMsgSendSuperStretFunctionDecl - +// id objc_msgSendSuper_stret(void); +void RewriteModernObjC::SynthMsgSendSuperStretFunctionDecl() { + IdentifierInfo *msgSendIdent = + &Context->Idents.get("objc_msgSendSuper_stret"); + SmallVector<QualType, 2> ArgTys; + ArgTys.push_back(Context->VoidTy); + QualType msgSendType = getSimpleFunctionType(Context->getObjCIdType(), + ArgTys, /*isVariadic=*/true); + MsgSendSuperStretFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + msgSendIdent, + msgSendType, nullptr, + SC_Extern); +} + +// SynthMsgSendFpretFunctionDecl - double objc_msgSend_fpret(id self, SEL op, ...); +void RewriteModernObjC::SynthMsgSendFpretFunctionDecl() { + IdentifierInfo *msgSendIdent = &Context->Idents.get("objc_msgSend_fpret"); + SmallVector<QualType, 16> ArgTys; + QualType argT = Context->getObjCIdType(); + assert(!argT.isNull() && "Can't find 'id' type"); + ArgTys.push_back(argT); + argT = Context->getObjCSelType(); + assert(!argT.isNull() && "Can't find 'SEL' type"); + ArgTys.push_back(argT); + QualType msgSendType = getSimpleFunctionType(Context->DoubleTy, + ArgTys, /*isVariadic=*/true); + MsgSendFpretFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + msgSendIdent, msgSendType, + nullptr, SC_Extern); +} + +// SynthGetClassFunctionDecl - Class objc_getClass(const char *name); +void RewriteModernObjC::SynthGetClassFunctionDecl() { + IdentifierInfo *getClassIdent = &Context->Idents.get("objc_getClass"); + SmallVector<QualType, 16> ArgTys; + ArgTys.push_back(Context->getPointerType(Context->CharTy.withConst())); + QualType getClassType = getSimpleFunctionType(Context->getObjCClassType(), + ArgTys); + GetClassFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + getClassIdent, getClassType, + nullptr, SC_Extern); +} + +// SynthGetSuperClassFunctionDecl - Class class_getSuperclass(Class cls); +void RewriteModernObjC::SynthGetSuperClassFunctionDecl() { + IdentifierInfo *getSuperClassIdent = + &Context->Idents.get("class_getSuperclass"); + SmallVector<QualType, 16> ArgTys; + ArgTys.push_back(Context->getObjCClassType()); + QualType getClassType = getSimpleFunctionType(Context->getObjCClassType(), + ArgTys); + GetSuperClassFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + getSuperClassIdent, + getClassType, nullptr, + SC_Extern); +} + +// SynthGetMetaClassFunctionDecl - Class objc_getMetaClass(const char *name); +void RewriteModernObjC::SynthGetMetaClassFunctionDecl() { + IdentifierInfo *getClassIdent = &Context->Idents.get("objc_getMetaClass"); + SmallVector<QualType, 16> ArgTys; + ArgTys.push_back(Context->getPointerType(Context->CharTy.withConst())); + QualType getClassType = getSimpleFunctionType(Context->getObjCClassType(), + ArgTys); + GetMetaClassFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + getClassIdent, getClassType, + nullptr, SC_Extern); +} + +Stmt *RewriteModernObjC::RewriteObjCStringLiteral(ObjCStringLiteral *Exp) { + assert (Exp != nullptr && "Expected non-null ObjCStringLiteral"); + QualType strType = getConstantStringStructType(); + + std::string S = "__NSConstantStringImpl_"; + + std::string tmpName = InFileName; + unsigned i; + for (i=0; i < tmpName.length(); i++) { + char c = tmpName.at(i); + // replace any non-alphanumeric characters with '_'. + if (!isAlphanumeric(c)) + tmpName[i] = '_'; + } + S += tmpName; + S += "_"; + S += utostr(NumObjCStringLiterals++); + + Preamble += "static __NSConstantStringImpl " + S; + Preamble += " __attribute__ ((section (\"__DATA, __cfstring\"))) = {__CFConstantStringClassReference,"; + Preamble += "0x000007c8,"; // utf8_str + // The pretty printer for StringLiteral handles escape characters properly. + std::string prettyBufS; + llvm::raw_string_ostream prettyBuf(prettyBufS); + Exp->getString()->printPretty(prettyBuf, nullptr, PrintingPolicy(LangOpts)); + Preamble += prettyBuf.str(); + Preamble += ","; + Preamble += utostr(Exp->getString()->getByteLength()) + "};\n"; + + VarDecl *NewVD = VarDecl::Create(*Context, TUDecl, SourceLocation(), + SourceLocation(), &Context->Idents.get(S), + strType, nullptr, SC_Static); + DeclRefExpr *DRE = new (Context) + DeclRefExpr(*Context, NewVD, false, strType, VK_LValue, SourceLocation()); + Expr *Unop = new (Context) + UnaryOperator(DRE, UO_AddrOf, Context->getPointerType(DRE->getType()), + VK_RValue, OK_Ordinary, SourceLocation(), false); + // cast to NSConstantString * + CastExpr *cast = NoTypeInfoCStyleCastExpr(Context, Exp->getType(), + CK_CPointerToObjCPointerCast, Unop); + ReplaceStmt(Exp, cast); + // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. + return cast; +} + +Stmt *RewriteModernObjC::RewriteObjCBoolLiteralExpr(ObjCBoolLiteralExpr *Exp) { + unsigned IntSize = + static_cast<unsigned>(Context->getTypeSize(Context->IntTy)); + + Expr *FlagExp = IntegerLiteral::Create(*Context, + llvm::APInt(IntSize, Exp->getValue()), + Context->IntTy, Exp->getLocation()); + CastExpr *cast = NoTypeInfoCStyleCastExpr(Context, Context->ObjCBuiltinBoolTy, + CK_BitCast, FlagExp); + ParenExpr *PE = new (Context) ParenExpr(Exp->getLocation(), Exp->getExprLoc(), + cast); + ReplaceStmt(Exp, PE); + return PE; +} + +Stmt *RewriteModernObjC::RewriteObjCBoxedExpr(ObjCBoxedExpr *Exp) { + // synthesize declaration of helper functions needed in this routine. + if (!SelGetUidFunctionDecl) + SynthSelGetUidFunctionDecl(); + // use objc_msgSend() for all. + if (!MsgSendFunctionDecl) + SynthMsgSendFunctionDecl(); + if (!GetClassFunctionDecl) + SynthGetClassFunctionDecl(); + + FunctionDecl *MsgSendFlavor = MsgSendFunctionDecl; + SourceLocation StartLoc = Exp->getBeginLoc(); + SourceLocation EndLoc = Exp->getEndLoc(); + + // Synthesize a call to objc_msgSend(). + SmallVector<Expr*, 4> MsgExprs; + SmallVector<Expr*, 4> ClsExprs; + + // Create a call to objc_getClass("<BoxingClass>"). It will be the 1st argument. + ObjCMethodDecl *BoxingMethod = Exp->getBoxingMethod(); + ObjCInterfaceDecl *BoxingClass = BoxingMethod->getClassInterface(); + + IdentifierInfo *clsName = BoxingClass->getIdentifier(); + ClsExprs.push_back(getStringLiteral(clsName->getName())); + CallExpr *Cls = SynthesizeCallToFunctionDecl(GetClassFunctionDecl, ClsExprs, + StartLoc, EndLoc); + MsgExprs.push_back(Cls); + + // Create a call to sel_registerName("<BoxingMethod>:"), etc. + // it will be the 2nd argument. + SmallVector<Expr*, 4> SelExprs; + SelExprs.push_back( + getStringLiteral(BoxingMethod->getSelector().getAsString())); + CallExpr *SelExp = SynthesizeCallToFunctionDecl(SelGetUidFunctionDecl, + SelExprs, StartLoc, EndLoc); + MsgExprs.push_back(SelExp); + + // User provided sub-expression is the 3rd, and last, argument. + Expr *subExpr = Exp->getSubExpr(); + if (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(subExpr)) { + QualType type = ICE->getType(); + const Expr *SubExpr = ICE->IgnoreParenImpCasts(); + CastKind CK = CK_BitCast; + if (SubExpr->getType()->isIntegralType(*Context) && type->isBooleanType()) + CK = CK_IntegralToBoolean; + subExpr = NoTypeInfoCStyleCastExpr(Context, type, CK, subExpr); + } + MsgExprs.push_back(subExpr); + + SmallVector<QualType, 4> ArgTypes; + ArgTypes.push_back(Context->getObjCClassType()); + ArgTypes.push_back(Context->getObjCSelType()); + for (const auto PI : BoxingMethod->parameters()) + ArgTypes.push_back(PI->getType()); + + QualType returnType = Exp->getType(); + // Get the type, we will need to reference it in a couple spots. + QualType msgSendType = MsgSendFlavor->getType(); + + // Create a reference to the objc_msgSend() declaration. + DeclRefExpr *DRE = new (Context) DeclRefExpr( + *Context, MsgSendFlavor, false, msgSendType, VK_LValue, SourceLocation()); + + CastExpr *cast = NoTypeInfoCStyleCastExpr( + Context, Context->getPointerType(Context->VoidTy), CK_BitCast, DRE); + + // Now do the "normal" pointer to function cast. + QualType castType = + getSimpleFunctionType(returnType, ArgTypes, BoxingMethod->isVariadic()); + castType = Context->getPointerType(castType); + cast = NoTypeInfoCStyleCastExpr(Context, castType, CK_BitCast, + cast); + + // Don't forget the parens to enforce the proper binding. + ParenExpr *PE = new (Context) ParenExpr(StartLoc, EndLoc, cast); + + const FunctionType *FT = msgSendType->getAs<FunctionType>(); + CallExpr *CE = CallExpr::Create(*Context, PE, MsgExprs, FT->getReturnType(), + VK_RValue, EndLoc); + ReplaceStmt(Exp, CE); + return CE; +} + +Stmt *RewriteModernObjC::RewriteObjCArrayLiteralExpr(ObjCArrayLiteral *Exp) { + // synthesize declaration of helper functions needed in this routine. + if (!SelGetUidFunctionDecl) + SynthSelGetUidFunctionDecl(); + // use objc_msgSend() for all. + if (!MsgSendFunctionDecl) + SynthMsgSendFunctionDecl(); + if (!GetClassFunctionDecl) + SynthGetClassFunctionDecl(); + + FunctionDecl *MsgSendFlavor = MsgSendFunctionDecl; + SourceLocation StartLoc = Exp->getBeginLoc(); + SourceLocation EndLoc = Exp->getEndLoc(); + + // Build the expression: __NSContainer_literal(int, ...).arr + QualType IntQT = Context->IntTy; + QualType NSArrayFType = + getSimpleFunctionType(Context->VoidTy, IntQT, true); + std::string NSArrayFName("__NSContainer_literal"); + FunctionDecl *NSArrayFD = SynthBlockInitFunctionDecl(NSArrayFName); + DeclRefExpr *NSArrayDRE = new (Context) DeclRefExpr( + *Context, NSArrayFD, false, NSArrayFType, VK_RValue, SourceLocation()); + + SmallVector<Expr*, 16> InitExprs; + unsigned NumElements = Exp->getNumElements(); + unsigned UnsignedIntSize = + static_cast<unsigned>(Context->getTypeSize(Context->UnsignedIntTy)); + Expr *count = IntegerLiteral::Create(*Context, + llvm::APInt(UnsignedIntSize, NumElements), + Context->UnsignedIntTy, SourceLocation()); + InitExprs.push_back(count); + for (unsigned i = 0; i < NumElements; i++) + InitExprs.push_back(Exp->getElement(i)); + Expr *NSArrayCallExpr = + CallExpr::Create(*Context, NSArrayDRE, InitExprs, NSArrayFType, VK_LValue, + SourceLocation()); + + FieldDecl *ARRFD = FieldDecl::Create(*Context, nullptr, SourceLocation(), + SourceLocation(), + &Context->Idents.get("arr"), + Context->getPointerType(Context->VoidPtrTy), + nullptr, /*BitWidth=*/nullptr, + /*Mutable=*/true, ICIS_NoInit); + MemberExpr *ArrayLiteralME = new (Context) + MemberExpr(NSArrayCallExpr, false, SourceLocation(), ARRFD, + SourceLocation(), ARRFD->getType(), VK_LValue, OK_Ordinary); + QualType ConstIdT = Context->getObjCIdType().withConst(); + CStyleCastExpr * ArrayLiteralObjects = + NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(ConstIdT), + CK_BitCast, + ArrayLiteralME); + + // Synthesize a call to objc_msgSend(). + SmallVector<Expr*, 32> MsgExprs; + SmallVector<Expr*, 4> ClsExprs; + QualType expType = Exp->getType(); + + // Create a call to objc_getClass("NSArray"). It will be th 1st argument. + ObjCInterfaceDecl *Class = + expType->getPointeeType()->getAs<ObjCObjectType>()->getInterface(); + + IdentifierInfo *clsName = Class->getIdentifier(); + ClsExprs.push_back(getStringLiteral(clsName->getName())); + CallExpr *Cls = SynthesizeCallToFunctionDecl(GetClassFunctionDecl, ClsExprs, + StartLoc, EndLoc); + MsgExprs.push_back(Cls); + + // Create a call to sel_registerName("arrayWithObjects:count:"). + // it will be the 2nd argument. + SmallVector<Expr*, 4> SelExprs; + ObjCMethodDecl *ArrayMethod = Exp->getArrayWithObjectsMethod(); + SelExprs.push_back( + getStringLiteral(ArrayMethod->getSelector().getAsString())); + CallExpr *SelExp = SynthesizeCallToFunctionDecl(SelGetUidFunctionDecl, + SelExprs, StartLoc, EndLoc); + MsgExprs.push_back(SelExp); + + // (const id [])objects + MsgExprs.push_back(ArrayLiteralObjects); + + // (NSUInteger)cnt + Expr *cnt = IntegerLiteral::Create(*Context, + llvm::APInt(UnsignedIntSize, NumElements), + Context->UnsignedIntTy, SourceLocation()); + MsgExprs.push_back(cnt); + + SmallVector<QualType, 4> ArgTypes; + ArgTypes.push_back(Context->getObjCClassType()); + ArgTypes.push_back(Context->getObjCSelType()); + for (const auto *PI : ArrayMethod->parameters()) + ArgTypes.push_back(PI->getType()); + + QualType returnType = Exp->getType(); + // Get the type, we will need to reference it in a couple spots. + QualType msgSendType = MsgSendFlavor->getType(); + + // Create a reference to the objc_msgSend() declaration. + DeclRefExpr *DRE = new (Context) DeclRefExpr( + *Context, MsgSendFlavor, false, msgSendType, VK_LValue, SourceLocation()); + + CastExpr *cast = NoTypeInfoCStyleCastExpr( + Context, Context->getPointerType(Context->VoidTy), CK_BitCast, DRE); + + // Now do the "normal" pointer to function cast. + QualType castType = + getSimpleFunctionType(returnType, ArgTypes, ArrayMethod->isVariadic()); + castType = Context->getPointerType(castType); + cast = NoTypeInfoCStyleCastExpr(Context, castType, CK_BitCast, + cast); + + // Don't forget the parens to enforce the proper binding. + ParenExpr *PE = new (Context) ParenExpr(StartLoc, EndLoc, cast); + + const FunctionType *FT = msgSendType->getAs<FunctionType>(); + CallExpr *CE = CallExpr::Create(*Context, PE, MsgExprs, FT->getReturnType(), + VK_RValue, EndLoc); + ReplaceStmt(Exp, CE); + return CE; +} + +Stmt *RewriteModernObjC::RewriteObjCDictionaryLiteralExpr(ObjCDictionaryLiteral *Exp) { + // synthesize declaration of helper functions needed in this routine. + if (!SelGetUidFunctionDecl) + SynthSelGetUidFunctionDecl(); + // use objc_msgSend() for all. + if (!MsgSendFunctionDecl) + SynthMsgSendFunctionDecl(); + if (!GetClassFunctionDecl) + SynthGetClassFunctionDecl(); + + FunctionDecl *MsgSendFlavor = MsgSendFunctionDecl; + SourceLocation StartLoc = Exp->getBeginLoc(); + SourceLocation EndLoc = Exp->getEndLoc(); + + // Build the expression: __NSContainer_literal(int, ...).arr + QualType IntQT = Context->IntTy; + QualType NSDictFType = + getSimpleFunctionType(Context->VoidTy, IntQT, true); + std::string NSDictFName("__NSContainer_literal"); + FunctionDecl *NSDictFD = SynthBlockInitFunctionDecl(NSDictFName); + DeclRefExpr *NSDictDRE = new (Context) DeclRefExpr( + *Context, NSDictFD, false, NSDictFType, VK_RValue, SourceLocation()); + + SmallVector<Expr*, 16> KeyExprs; + SmallVector<Expr*, 16> ValueExprs; + + unsigned NumElements = Exp->getNumElements(); + unsigned UnsignedIntSize = + static_cast<unsigned>(Context->getTypeSize(Context->UnsignedIntTy)); + Expr *count = IntegerLiteral::Create(*Context, + llvm::APInt(UnsignedIntSize, NumElements), + Context->UnsignedIntTy, SourceLocation()); + KeyExprs.push_back(count); + ValueExprs.push_back(count); + for (unsigned i = 0; i < NumElements; i++) { + ObjCDictionaryElement Element = Exp->getKeyValueElement(i); + KeyExprs.push_back(Element.Key); + ValueExprs.push_back(Element.Value); + } + + // (const id [])objects + Expr *NSValueCallExpr = + CallExpr::Create(*Context, NSDictDRE, ValueExprs, NSDictFType, VK_LValue, + SourceLocation()); + + FieldDecl *ARRFD = FieldDecl::Create(*Context, nullptr, SourceLocation(), + SourceLocation(), + &Context->Idents.get("arr"), + Context->getPointerType(Context->VoidPtrTy), + nullptr, /*BitWidth=*/nullptr, + /*Mutable=*/true, ICIS_NoInit); + MemberExpr *DictLiteralValueME = new (Context) + MemberExpr(NSValueCallExpr, false, SourceLocation(), ARRFD, + SourceLocation(), ARRFD->getType(), VK_LValue, OK_Ordinary); + QualType ConstIdT = Context->getObjCIdType().withConst(); + CStyleCastExpr * DictValueObjects = + NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(ConstIdT), + CK_BitCast, + DictLiteralValueME); + // (const id <NSCopying> [])keys + Expr *NSKeyCallExpr = CallExpr::Create( + *Context, NSDictDRE, KeyExprs, NSDictFType, VK_LValue, SourceLocation()); + + MemberExpr *DictLiteralKeyME = new (Context) + MemberExpr(NSKeyCallExpr, false, SourceLocation(), ARRFD, + SourceLocation(), ARRFD->getType(), VK_LValue, OK_Ordinary); + + CStyleCastExpr * DictKeyObjects = + NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(ConstIdT), + CK_BitCast, + DictLiteralKeyME); + + // Synthesize a call to objc_msgSend(). + SmallVector<Expr*, 32> MsgExprs; + SmallVector<Expr*, 4> ClsExprs; + QualType expType = Exp->getType(); + + // Create a call to objc_getClass("NSArray"). It will be th 1st argument. + ObjCInterfaceDecl *Class = + expType->getPointeeType()->getAs<ObjCObjectType>()->getInterface(); + + IdentifierInfo *clsName = Class->getIdentifier(); + ClsExprs.push_back(getStringLiteral(clsName->getName())); + CallExpr *Cls = SynthesizeCallToFunctionDecl(GetClassFunctionDecl, ClsExprs, + StartLoc, EndLoc); + MsgExprs.push_back(Cls); + + // Create a call to sel_registerName("arrayWithObjects:count:"). + // it will be the 2nd argument. + SmallVector<Expr*, 4> SelExprs; + ObjCMethodDecl *DictMethod = Exp->getDictWithObjectsMethod(); + SelExprs.push_back(getStringLiteral(DictMethod->getSelector().getAsString())); + CallExpr *SelExp = SynthesizeCallToFunctionDecl(SelGetUidFunctionDecl, + SelExprs, StartLoc, EndLoc); + MsgExprs.push_back(SelExp); + + // (const id [])objects + MsgExprs.push_back(DictValueObjects); + + // (const id <NSCopying> [])keys + MsgExprs.push_back(DictKeyObjects); + + // (NSUInteger)cnt + Expr *cnt = IntegerLiteral::Create(*Context, + llvm::APInt(UnsignedIntSize, NumElements), + Context->UnsignedIntTy, SourceLocation()); + MsgExprs.push_back(cnt); + + SmallVector<QualType, 8> ArgTypes; + ArgTypes.push_back(Context->getObjCClassType()); + ArgTypes.push_back(Context->getObjCSelType()); + for (const auto *PI : DictMethod->parameters()) { + QualType T = PI->getType(); + if (const PointerType* PT = T->getAs<PointerType>()) { + QualType PointeeTy = PT->getPointeeType(); + convertToUnqualifiedObjCType(PointeeTy); + T = Context->getPointerType(PointeeTy); + } + ArgTypes.push_back(T); + } + + QualType returnType = Exp->getType(); + // Get the type, we will need to reference it in a couple spots. + QualType msgSendType = MsgSendFlavor->getType(); + + // Create a reference to the objc_msgSend() declaration. + DeclRefExpr *DRE = new (Context) DeclRefExpr( + *Context, MsgSendFlavor, false, msgSendType, VK_LValue, SourceLocation()); + + CastExpr *cast = NoTypeInfoCStyleCastExpr( + Context, Context->getPointerType(Context->VoidTy), CK_BitCast, DRE); + + // Now do the "normal" pointer to function cast. + QualType castType = + getSimpleFunctionType(returnType, ArgTypes, DictMethod->isVariadic()); + castType = Context->getPointerType(castType); + cast = NoTypeInfoCStyleCastExpr(Context, castType, CK_BitCast, + cast); + + // Don't forget the parens to enforce the proper binding. + ParenExpr *PE = new (Context) ParenExpr(StartLoc, EndLoc, cast); + + const FunctionType *FT = msgSendType->getAs<FunctionType>(); + CallExpr *CE = CallExpr::Create(*Context, PE, MsgExprs, FT->getReturnType(), + VK_RValue, EndLoc); + ReplaceStmt(Exp, CE); + return CE; +} + +// struct __rw_objc_super { +// struct objc_object *object; struct objc_object *superClass; +// }; +QualType RewriteModernObjC::getSuperStructType() { + if (!SuperStructDecl) { + SuperStructDecl = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), SourceLocation(), + &Context->Idents.get("__rw_objc_super")); + QualType FieldTypes[2]; + + // struct objc_object *object; + FieldTypes[0] = Context->getObjCIdType(); + // struct objc_object *superClass; + FieldTypes[1] = Context->getObjCIdType(); + + // Create fields + for (unsigned i = 0; i < 2; ++i) { + SuperStructDecl->addDecl(FieldDecl::Create(*Context, SuperStructDecl, + SourceLocation(), + SourceLocation(), nullptr, + FieldTypes[i], nullptr, + /*BitWidth=*/nullptr, + /*Mutable=*/false, + ICIS_NoInit)); + } + + SuperStructDecl->completeDefinition(); + } + return Context->getTagDeclType(SuperStructDecl); +} + +QualType RewriteModernObjC::getConstantStringStructType() { + if (!ConstantStringDecl) { + ConstantStringDecl = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), SourceLocation(), + &Context->Idents.get("__NSConstantStringImpl")); + QualType FieldTypes[4]; + + // struct objc_object *receiver; + FieldTypes[0] = Context->getObjCIdType(); + // int flags; + FieldTypes[1] = Context->IntTy; + // char *str; + FieldTypes[2] = Context->getPointerType(Context->CharTy); + // long length; + FieldTypes[3] = Context->LongTy; + + // Create fields + for (unsigned i = 0; i < 4; ++i) { + ConstantStringDecl->addDecl(FieldDecl::Create(*Context, + ConstantStringDecl, + SourceLocation(), + SourceLocation(), nullptr, + FieldTypes[i], nullptr, + /*BitWidth=*/nullptr, + /*Mutable=*/true, + ICIS_NoInit)); + } + + ConstantStringDecl->completeDefinition(); + } + return Context->getTagDeclType(ConstantStringDecl); +} + +/// getFunctionSourceLocation - returns start location of a function +/// definition. Complication arises when function has declared as +/// extern "C" or extern "C" {...} +static SourceLocation getFunctionSourceLocation (RewriteModernObjC &R, + FunctionDecl *FD) { + if (FD->isExternC() && !FD->isMain()) { + const DeclContext *DC = FD->getDeclContext(); + if (const LinkageSpecDecl *LSD = dyn_cast<LinkageSpecDecl>(DC)) + // if it is extern "C" {...}, return function decl's own location. + if (!LSD->getRBraceLoc().isValid()) + return LSD->getExternLoc(); + } + if (FD->getStorageClass() != SC_None) + R.RewriteBlockLiteralFunctionDecl(FD); + return FD->getTypeSpecStartLoc(); +} + +void RewriteModernObjC::RewriteLineDirective(const Decl *D) { + + SourceLocation Location = D->getLocation(); + + if (Location.isFileID() && GenerateLineInfo) { + std::string LineString("\n#line "); + PresumedLoc PLoc = SM->getPresumedLoc(Location); + LineString += utostr(PLoc.getLine()); + LineString += " \""; + LineString += Lexer::Stringify(PLoc.getFilename()); + if (isa<ObjCMethodDecl>(D)) + LineString += "\""; + else LineString += "\"\n"; + + Location = D->getBeginLoc(); + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + if (FD->isExternC() && !FD->isMain()) { + const DeclContext *DC = FD->getDeclContext(); + if (const LinkageSpecDecl *LSD = dyn_cast<LinkageSpecDecl>(DC)) + // if it is extern "C" {...}, return function decl's own location. + if (!LSD->getRBraceLoc().isValid()) + Location = LSD->getExternLoc(); + } + } + InsertText(Location, LineString); + } +} + +/// SynthMsgSendStretCallExpr - This routine translates message expression +/// into a call to objc_msgSend_stret() entry point. Tricky part is that +/// nil check on receiver must be performed before calling objc_msgSend_stret. +/// MsgSendStretFlavor - function declaration objc_msgSend_stret(...) +/// msgSendType - function type of objc_msgSend_stret(...) +/// returnType - Result type of the method being synthesized. +/// ArgTypes - type of the arguments passed to objc_msgSend_stret, starting with receiver type. +/// MsgExprs - list of argument expressions being passed to objc_msgSend_stret, +/// starting with receiver. +/// Method - Method being rewritten. +Expr *RewriteModernObjC::SynthMsgSendStretCallExpr(FunctionDecl *MsgSendStretFlavor, + QualType returnType, + SmallVectorImpl<QualType> &ArgTypes, + SmallVectorImpl<Expr*> &MsgExprs, + ObjCMethodDecl *Method) { + // Now do the "normal" pointer to function cast. + QualType FuncType = getSimpleFunctionType( + returnType, ArgTypes, Method ? Method->isVariadic() : false); + QualType castType = Context->getPointerType(FuncType); + + // build type for containing the objc_msgSend_stret object. + static unsigned stretCount=0; + std::string name = "__Stret"; name += utostr(stretCount); + std::string str = + "extern \"C\" void * __cdecl memset(void *_Dst, int _Val, size_t _Size);\n"; + str += "namespace {\n"; + str += "struct "; str += name; + str += " {\n\t"; + str += name; + str += "(id receiver, SEL sel"; + for (unsigned i = 2; i < ArgTypes.size(); i++) { + std::string ArgName = "arg"; ArgName += utostr(i); + ArgTypes[i].getAsStringInternal(ArgName, Context->getPrintingPolicy()); + str += ", "; str += ArgName; + } + // could be vararg. + for (unsigned i = ArgTypes.size(); i < MsgExprs.size(); i++) { + std::string ArgName = "arg"; ArgName += utostr(i); + MsgExprs[i]->getType().getAsStringInternal(ArgName, + Context->getPrintingPolicy()); + str += ", "; str += ArgName; + } + + str += ") {\n"; + str += "\t unsigned size = sizeof("; + str += returnType.getAsString(Context->getPrintingPolicy()); str += ");\n"; + + str += "\t if (size == 1 || size == 2 || size == 4 || size == 8)\n"; + + str += "\t s = (("; str += castType.getAsString(Context->getPrintingPolicy()); + str += ")(void *)objc_msgSend)(receiver, sel"; + for (unsigned i = 2; i < ArgTypes.size(); i++) { + str += ", arg"; str += utostr(i); + } + // could be vararg. + for (unsigned i = ArgTypes.size(); i < MsgExprs.size(); i++) { + str += ", arg"; str += utostr(i); + } + str+= ");\n"; + + str += "\t else if (receiver == 0)\n"; + str += "\t memset((void*)&s, 0, sizeof(s));\n"; + str += "\t else\n"; + + str += "\t s = (("; str += castType.getAsString(Context->getPrintingPolicy()); + str += ")(void *)objc_msgSend_stret)(receiver, sel"; + for (unsigned i = 2; i < ArgTypes.size(); i++) { + str += ", arg"; str += utostr(i); + } + // could be vararg. + for (unsigned i = ArgTypes.size(); i < MsgExprs.size(); i++) { + str += ", arg"; str += utostr(i); + } + str += ");\n"; + + str += "\t}\n"; + str += "\t"; str += returnType.getAsString(Context->getPrintingPolicy()); + str += " s;\n"; + str += "};\n};\n\n"; + SourceLocation FunLocStart; + if (CurFunctionDef) + FunLocStart = getFunctionSourceLocation(*this, CurFunctionDef); + else { + assert(CurMethodDef && "SynthMsgSendStretCallExpr - CurMethodDef is null"); + FunLocStart = CurMethodDef->getBeginLoc(); + } + + InsertText(FunLocStart, str); + ++stretCount; + + // AST for __Stretn(receiver, args).s; + IdentifierInfo *ID = &Context->Idents.get(name); + FunctionDecl *FD = + FunctionDecl::Create(*Context, TUDecl, SourceLocation(), SourceLocation(), + ID, FuncType, nullptr, SC_Extern, false, false); + DeclRefExpr *DRE = new (Context) + DeclRefExpr(*Context, FD, false, castType, VK_RValue, SourceLocation()); + CallExpr *STCE = CallExpr::Create(*Context, DRE, MsgExprs, castType, + VK_LValue, SourceLocation()); + + FieldDecl *FieldD = FieldDecl::Create(*Context, nullptr, SourceLocation(), + SourceLocation(), + &Context->Idents.get("s"), + returnType, nullptr, + /*BitWidth=*/nullptr, + /*Mutable=*/true, ICIS_NoInit); + MemberExpr *ME = new (Context) + MemberExpr(STCE, false, SourceLocation(), FieldD, SourceLocation(), + FieldD->getType(), VK_LValue, OK_Ordinary); + + return ME; +} + +Stmt *RewriteModernObjC::SynthMessageExpr(ObjCMessageExpr *Exp, + SourceLocation StartLoc, + SourceLocation EndLoc) { + if (!SelGetUidFunctionDecl) + SynthSelGetUidFunctionDecl(); + if (!MsgSendFunctionDecl) + SynthMsgSendFunctionDecl(); + if (!MsgSendSuperFunctionDecl) + SynthMsgSendSuperFunctionDecl(); + if (!MsgSendStretFunctionDecl) + SynthMsgSendStretFunctionDecl(); + if (!MsgSendSuperStretFunctionDecl) + SynthMsgSendSuperStretFunctionDecl(); + if (!MsgSendFpretFunctionDecl) + SynthMsgSendFpretFunctionDecl(); + if (!GetClassFunctionDecl) + SynthGetClassFunctionDecl(); + if (!GetSuperClassFunctionDecl) + SynthGetSuperClassFunctionDecl(); + if (!GetMetaClassFunctionDecl) + SynthGetMetaClassFunctionDecl(); + + // default to objc_msgSend(). + FunctionDecl *MsgSendFlavor = MsgSendFunctionDecl; + // May need to use objc_msgSend_stret() as well. + FunctionDecl *MsgSendStretFlavor = nullptr; + if (ObjCMethodDecl *mDecl = Exp->getMethodDecl()) { + QualType resultType = mDecl->getReturnType(); + if (resultType->isRecordType()) + MsgSendStretFlavor = MsgSendStretFunctionDecl; + else if (resultType->isRealFloatingType()) + MsgSendFlavor = MsgSendFpretFunctionDecl; + } + + // Synthesize a call to objc_msgSend(). + SmallVector<Expr*, 8> MsgExprs; + switch (Exp->getReceiverKind()) { + case ObjCMessageExpr::SuperClass: { + MsgSendFlavor = MsgSendSuperFunctionDecl; + if (MsgSendStretFlavor) + MsgSendStretFlavor = MsgSendSuperStretFunctionDecl; + assert(MsgSendFlavor && "MsgSendFlavor is NULL!"); + + ObjCInterfaceDecl *ClassDecl = CurMethodDef->getClassInterface(); + + SmallVector<Expr*, 4> InitExprs; + + // set the receiver to self, the first argument to all methods. + InitExprs.push_back( + NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), + CK_BitCast, + new (Context) DeclRefExpr(*Context, + CurMethodDef->getSelfDecl(), + false, + Context->getObjCIdType(), + VK_RValue, + SourceLocation())) + ); // set the 'receiver'. + + // (id)class_getSuperclass((Class)objc_getClass("CurrentClass")) + SmallVector<Expr*, 8> ClsExprs; + ClsExprs.push_back(getStringLiteral(ClassDecl->getIdentifier()->getName())); + // (Class)objc_getClass("CurrentClass") + CallExpr *Cls = SynthesizeCallToFunctionDecl(GetMetaClassFunctionDecl, + ClsExprs, StartLoc, EndLoc); + ClsExprs.clear(); + ClsExprs.push_back(Cls); + Cls = SynthesizeCallToFunctionDecl(GetSuperClassFunctionDecl, ClsExprs, + StartLoc, EndLoc); + + // (id)class_getSuperclass((Class)objc_getClass("CurrentClass")) + // To turn off a warning, type-cast to 'id' + InitExprs.push_back( // set 'super class', using class_getSuperclass(). + NoTypeInfoCStyleCastExpr(Context, + Context->getObjCIdType(), + CK_BitCast, Cls)); + // struct __rw_objc_super + QualType superType = getSuperStructType(); + Expr *SuperRep; + + if (LangOpts.MicrosoftExt) { + SynthSuperConstructorFunctionDecl(); + // Simulate a constructor call... + DeclRefExpr *DRE = new (Context) + DeclRefExpr(*Context, SuperConstructorFunctionDecl, false, superType, + VK_LValue, SourceLocation()); + SuperRep = CallExpr::Create(*Context, DRE, InitExprs, superType, + VK_LValue, SourceLocation()); + // The code for super is a little tricky to prevent collision with + // the structure definition in the header. The rewriter has it's own + // internal definition (__rw_objc_super) that is uses. This is why + // we need the cast below. For example: + // (struct __rw_objc_super *)&__rw_objc_super((id)self, (id)objc_getClass("SUPER")) + // + SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf, + Context->getPointerType(SuperRep->getType()), + VK_RValue, OK_Ordinary, + SourceLocation(), false); + SuperRep = NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(superType), + CK_BitCast, SuperRep); + } else { + // (struct __rw_objc_super) { <exprs from above> } + InitListExpr *ILE = + new (Context) InitListExpr(*Context, SourceLocation(), InitExprs, + SourceLocation()); + TypeSourceInfo *superTInfo + = Context->getTrivialTypeSourceInfo(superType); + SuperRep = new (Context) CompoundLiteralExpr(SourceLocation(), superTInfo, + superType, VK_LValue, + ILE, false); + // struct __rw_objc_super * + SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf, + Context->getPointerType(SuperRep->getType()), + VK_RValue, OK_Ordinary, + SourceLocation(), false); + } + MsgExprs.push_back(SuperRep); + break; + } + + case ObjCMessageExpr::Class: { + SmallVector<Expr*, 8> ClsExprs; + ObjCInterfaceDecl *Class + = Exp->getClassReceiver()->getAs<ObjCObjectType>()->getInterface(); + IdentifierInfo *clsName = Class->getIdentifier(); + ClsExprs.push_back(getStringLiteral(clsName->getName())); + CallExpr *Cls = SynthesizeCallToFunctionDecl(GetClassFunctionDecl, ClsExprs, + StartLoc, EndLoc); + CastExpr *ArgExpr = NoTypeInfoCStyleCastExpr(Context, + Context->getObjCIdType(), + CK_BitCast, Cls); + MsgExprs.push_back(ArgExpr); + break; + } + + case ObjCMessageExpr::SuperInstance:{ + MsgSendFlavor = MsgSendSuperFunctionDecl; + if (MsgSendStretFlavor) + MsgSendStretFlavor = MsgSendSuperStretFunctionDecl; + assert(MsgSendFlavor && "MsgSendFlavor is NULL!"); + ObjCInterfaceDecl *ClassDecl = CurMethodDef->getClassInterface(); + SmallVector<Expr*, 4> InitExprs; + + InitExprs.push_back( + NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), + CK_BitCast, + new (Context) DeclRefExpr(*Context, + CurMethodDef->getSelfDecl(), + false, + Context->getObjCIdType(), + VK_RValue, SourceLocation())) + ); // set the 'receiver'. + + // (id)class_getSuperclass((Class)objc_getClass("CurrentClass")) + SmallVector<Expr*, 8> ClsExprs; + ClsExprs.push_back(getStringLiteral(ClassDecl->getIdentifier()->getName())); + // (Class)objc_getClass("CurrentClass") + CallExpr *Cls = SynthesizeCallToFunctionDecl(GetClassFunctionDecl, ClsExprs, + StartLoc, EndLoc); + ClsExprs.clear(); + ClsExprs.push_back(Cls); + Cls = SynthesizeCallToFunctionDecl(GetSuperClassFunctionDecl, ClsExprs, + StartLoc, EndLoc); + + // (id)class_getSuperclass((Class)objc_getClass("CurrentClass")) + // To turn off a warning, type-cast to 'id' + InitExprs.push_back( + // set 'super class', using class_getSuperclass(). + NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), + CK_BitCast, Cls)); + // struct __rw_objc_super + QualType superType = getSuperStructType(); + Expr *SuperRep; + + if (LangOpts.MicrosoftExt) { + SynthSuperConstructorFunctionDecl(); + // Simulate a constructor call... + DeclRefExpr *DRE = new (Context) + DeclRefExpr(*Context, SuperConstructorFunctionDecl, false, superType, + VK_LValue, SourceLocation()); + SuperRep = CallExpr::Create(*Context, DRE, InitExprs, superType, + VK_LValue, SourceLocation()); + // The code for super is a little tricky to prevent collision with + // the structure definition in the header. The rewriter has it's own + // internal definition (__rw_objc_super) that is uses. This is why + // we need the cast below. For example: + // (struct __rw_objc_super *)&__rw_objc_super((id)self, (id)objc_getClass("SUPER")) + // + SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf, + Context->getPointerType(SuperRep->getType()), + VK_RValue, OK_Ordinary, + SourceLocation(), false); + SuperRep = NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(superType), + CK_BitCast, SuperRep); + } else { + // (struct __rw_objc_super) { <exprs from above> } + InitListExpr *ILE = + new (Context) InitListExpr(*Context, SourceLocation(), InitExprs, + SourceLocation()); + TypeSourceInfo *superTInfo + = Context->getTrivialTypeSourceInfo(superType); + SuperRep = new (Context) CompoundLiteralExpr(SourceLocation(), superTInfo, + superType, VK_RValue, ILE, + false); + } + MsgExprs.push_back(SuperRep); + break; + } + + case ObjCMessageExpr::Instance: { + // Remove all type-casts because it may contain objc-style types; e.g. + // Foo<Proto> *. + Expr *recExpr = Exp->getInstanceReceiver(); + while (CStyleCastExpr *CE = dyn_cast<CStyleCastExpr>(recExpr)) + recExpr = CE->getSubExpr(); + CastKind CK = recExpr->getType()->isObjCObjectPointerType() + ? CK_BitCast : recExpr->getType()->isBlockPointerType() + ? CK_BlockPointerToObjCPointerCast + : CK_CPointerToObjCPointerCast; + + recExpr = NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), + CK, recExpr); + MsgExprs.push_back(recExpr); + break; + } + } + + // Create a call to sel_registerName("selName"), it will be the 2nd argument. + SmallVector<Expr*, 8> SelExprs; + SelExprs.push_back(getStringLiteral(Exp->getSelector().getAsString())); + CallExpr *SelExp = SynthesizeCallToFunctionDecl(SelGetUidFunctionDecl, + SelExprs, StartLoc, EndLoc); + MsgExprs.push_back(SelExp); + + // Now push any user supplied arguments. + for (unsigned i = 0; i < Exp->getNumArgs(); i++) { + Expr *userExpr = Exp->getArg(i); + // Make all implicit casts explicit...ICE comes in handy:-) + if (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(userExpr)) { + // Reuse the ICE type, it is exactly what the doctor ordered. + QualType type = ICE->getType(); + if (needToScanForQualifiers(type)) + type = Context->getObjCIdType(); + // Make sure we convert "type (^)(...)" to "type (*)(...)". + (void)convertBlockPointerToFunctionPointer(type); + const Expr *SubExpr = ICE->IgnoreParenImpCasts(); + CastKind CK; + if (SubExpr->getType()->isIntegralType(*Context) && + type->isBooleanType()) { + CK = CK_IntegralToBoolean; + } else if (type->isObjCObjectPointerType()) { + if (SubExpr->getType()->isBlockPointerType()) { + CK = CK_BlockPointerToObjCPointerCast; + } else if (SubExpr->getType()->isPointerType()) { + CK = CK_CPointerToObjCPointerCast; + } else { + CK = CK_BitCast; + } + } else { + CK = CK_BitCast; + } + + userExpr = NoTypeInfoCStyleCastExpr(Context, type, CK, userExpr); + } + // Make id<P...> cast into an 'id' cast. + else if (CStyleCastExpr *CE = dyn_cast<CStyleCastExpr>(userExpr)) { + if (CE->getType()->isObjCQualifiedIdType()) { + while ((CE = dyn_cast<CStyleCastExpr>(userExpr))) + userExpr = CE->getSubExpr(); + CastKind CK; + if (userExpr->getType()->isIntegralType(*Context)) { + CK = CK_IntegralToPointer; + } else if (userExpr->getType()->isBlockPointerType()) { + CK = CK_BlockPointerToObjCPointerCast; + } else if (userExpr->getType()->isPointerType()) { + CK = CK_CPointerToObjCPointerCast; + } else { + CK = CK_BitCast; + } + userExpr = NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), + CK, userExpr); + } + } + MsgExprs.push_back(userExpr); + // We've transferred the ownership to MsgExprs. For now, we *don't* null + // out the argument in the original expression (since we aren't deleting + // the ObjCMessageExpr). See RewritePropertyOrImplicitSetter() usage for more info. + //Exp->setArg(i, 0); + } + // Generate the funky cast. + CastExpr *cast; + SmallVector<QualType, 8> ArgTypes; + QualType returnType; + + // Push 'id' and 'SEL', the 2 implicit arguments. + if (MsgSendFlavor == MsgSendSuperFunctionDecl) + ArgTypes.push_back(Context->getPointerType(getSuperStructType())); + else + ArgTypes.push_back(Context->getObjCIdType()); + ArgTypes.push_back(Context->getObjCSelType()); + if (ObjCMethodDecl *OMD = Exp->getMethodDecl()) { + // Push any user argument types. + for (const auto *PI : OMD->parameters()) { + QualType t = PI->getType()->isObjCQualifiedIdType() + ? Context->getObjCIdType() + : PI->getType(); + // Make sure we convert "t (^)(...)" to "t (*)(...)". + (void)convertBlockPointerToFunctionPointer(t); + ArgTypes.push_back(t); + } + returnType = Exp->getType(); + convertToUnqualifiedObjCType(returnType); + (void)convertBlockPointerToFunctionPointer(returnType); + } else { + returnType = Context->getObjCIdType(); + } + // Get the type, we will need to reference it in a couple spots. + QualType msgSendType = MsgSendFlavor->getType(); + + // Create a reference to the objc_msgSend() declaration. + DeclRefExpr *DRE = new (Context) DeclRefExpr( + *Context, MsgSendFlavor, false, msgSendType, VK_LValue, SourceLocation()); + + // Need to cast objc_msgSend to "void *" (to workaround a GCC bandaid). + // If we don't do this cast, we get the following bizarre warning/note: + // xx.m:13: warning: function called through a non-compatible type + // xx.m:13: note: if this code is reached, the program will abort + cast = NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(Context->VoidTy), + CK_BitCast, DRE); + + // Now do the "normal" pointer to function cast. + // If we don't have a method decl, force a variadic cast. + const ObjCMethodDecl *MD = Exp->getMethodDecl(); + QualType castType = + getSimpleFunctionType(returnType, ArgTypes, MD ? MD->isVariadic() : true); + castType = Context->getPointerType(castType); + cast = NoTypeInfoCStyleCastExpr(Context, castType, CK_BitCast, + cast); + + // Don't forget the parens to enforce the proper binding. + ParenExpr *PE = new (Context) ParenExpr(StartLoc, EndLoc, cast); + + const FunctionType *FT = msgSendType->getAs<FunctionType>(); + CallExpr *CE = CallExpr::Create(*Context, PE, MsgExprs, FT->getReturnType(), + VK_RValue, EndLoc); + Stmt *ReplacingStmt = CE; + if (MsgSendStretFlavor) { + // We have the method which returns a struct/union. Must also generate + // call to objc_msgSend_stret and hang both varieties on a conditional + // expression which dictate which one to envoke depending on size of + // method's return type. + + Expr *STCE = SynthMsgSendStretCallExpr(MsgSendStretFlavor, + returnType, + ArgTypes, MsgExprs, + Exp->getMethodDecl()); + ReplacingStmt = STCE; + } + // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. + return ReplacingStmt; +} + +Stmt *RewriteModernObjC::RewriteMessageExpr(ObjCMessageExpr *Exp) { + Stmt *ReplacingStmt = + SynthMessageExpr(Exp, Exp->getBeginLoc(), Exp->getEndLoc()); + + // Now do the actual rewrite. + ReplaceStmt(Exp, ReplacingStmt); + + // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. + return ReplacingStmt; +} + +// typedef struct objc_object Protocol; +QualType RewriteModernObjC::getProtocolType() { + if (!ProtocolTypeDecl) { + TypeSourceInfo *TInfo + = Context->getTrivialTypeSourceInfo(Context->getObjCIdType()); + ProtocolTypeDecl = TypedefDecl::Create(*Context, TUDecl, + SourceLocation(), SourceLocation(), + &Context->Idents.get("Protocol"), + TInfo); + } + return Context->getTypeDeclType(ProtocolTypeDecl); +} + +/// RewriteObjCProtocolExpr - Rewrite a protocol expression into +/// a synthesized/forward data reference (to the protocol's metadata). +/// The forward references (and metadata) are generated in +/// RewriteModernObjC::HandleTranslationUnit(). +Stmt *RewriteModernObjC::RewriteObjCProtocolExpr(ObjCProtocolExpr *Exp) { + std::string Name = "_OBJC_PROTOCOL_REFERENCE_$_" + + Exp->getProtocol()->getNameAsString(); + IdentifierInfo *ID = &Context->Idents.get(Name); + VarDecl *VD = VarDecl::Create(*Context, TUDecl, SourceLocation(), + SourceLocation(), ID, getProtocolType(), + nullptr, SC_Extern); + DeclRefExpr *DRE = new (Context) DeclRefExpr( + *Context, VD, false, getProtocolType(), VK_LValue, SourceLocation()); + CastExpr *castExpr = NoTypeInfoCStyleCastExpr( + Context, Context->getPointerType(DRE->getType()), CK_BitCast, DRE); + ReplaceStmt(Exp, castExpr); + ProtocolExprDecls.insert(Exp->getProtocol()->getCanonicalDecl()); + // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. + return castExpr; +} + +/// IsTagDefinedInsideClass - This routine checks that a named tagged type +/// is defined inside an objective-c class. If so, it returns true. +bool RewriteModernObjC::IsTagDefinedInsideClass(ObjCContainerDecl *IDecl, + TagDecl *Tag, + bool &IsNamedDefinition) { + if (!IDecl) + return false; + SourceLocation TagLocation; + if (RecordDecl *RD = dyn_cast<RecordDecl>(Tag)) { + RD = RD->getDefinition(); + if (!RD || !RD->getDeclName().getAsIdentifierInfo()) + return false; + IsNamedDefinition = true; + TagLocation = RD->getLocation(); + return Context->getSourceManager().isBeforeInTranslationUnit( + IDecl->getLocation(), TagLocation); + } + if (EnumDecl *ED = dyn_cast<EnumDecl>(Tag)) { + if (!ED || !ED->getDeclName().getAsIdentifierInfo()) + return false; + IsNamedDefinition = true; + TagLocation = ED->getLocation(); + return Context->getSourceManager().isBeforeInTranslationUnit( + IDecl->getLocation(), TagLocation); + } + return false; +} + +/// RewriteObjCFieldDeclType - This routine rewrites a type into the buffer. +/// It handles elaborated types, as well as enum types in the process. +bool RewriteModernObjC::RewriteObjCFieldDeclType(QualType &Type, + std::string &Result) { + if (isa<TypedefType>(Type)) { + Result += "\t"; + return false; + } + + if (Type->isArrayType()) { + QualType ElemTy = Context->getBaseElementType(Type); + return RewriteObjCFieldDeclType(ElemTy, Result); + } + else if (Type->isRecordType()) { + RecordDecl *RD = Type->getAs<RecordType>()->getDecl(); + if (RD->isCompleteDefinition()) { + if (RD->isStruct()) + Result += "\n\tstruct "; + else if (RD->isUnion()) + Result += "\n\tunion "; + else + assert(false && "class not allowed as an ivar type"); + + Result += RD->getName(); + if (GlobalDefinedTags.count(RD)) { + // struct/union is defined globally, use it. + Result += " "; + return true; + } + Result += " {\n"; + for (auto *FD : RD->fields()) + RewriteObjCFieldDecl(FD, Result); + Result += "\t} "; + return true; + } + } + else if (Type->isEnumeralType()) { + EnumDecl *ED = Type->getAs<EnumType>()->getDecl(); + if (ED->isCompleteDefinition()) { + Result += "\n\tenum "; + Result += ED->getName(); + if (GlobalDefinedTags.count(ED)) { + // Enum is globall defined, use it. + Result += " "; + return true; + } + + Result += " {\n"; + for (const auto *EC : ED->enumerators()) { + Result += "\t"; Result += EC->getName(); Result += " = "; + llvm::APSInt Val = EC->getInitVal(); + Result += Val.toString(10); + Result += ",\n"; + } + Result += "\t} "; + return true; + } + } + + Result += "\t"; + convertObjCTypeToCStyleType(Type); + return false; +} + + +/// RewriteObjCFieldDecl - This routine rewrites a field into the buffer. +/// It handles elaborated types, as well as enum types in the process. +void RewriteModernObjC::RewriteObjCFieldDecl(FieldDecl *fieldDecl, + std::string &Result) { + QualType Type = fieldDecl->getType(); + std::string Name = fieldDecl->getNameAsString(); + + bool EleboratedType = RewriteObjCFieldDeclType(Type, Result); + if (!EleboratedType) + Type.getAsStringInternal(Name, Context->getPrintingPolicy()); + Result += Name; + if (fieldDecl->isBitField()) { + Result += " : "; Result += utostr(fieldDecl->getBitWidthValue(*Context)); + } + else if (EleboratedType && Type->isArrayType()) { + const ArrayType *AT = Context->getAsArrayType(Type); + do { + if (const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(AT)) { + Result += "["; + llvm::APInt Dim = CAT->getSize(); + Result += utostr(Dim.getZExtValue()); + Result += "]"; + } + AT = Context->getAsArrayType(AT->getElementType()); + } while (AT); + } + + Result += ";\n"; +} + +/// RewriteLocallyDefinedNamedAggregates - This routine rewrites locally defined +/// named aggregate types into the input buffer. +void RewriteModernObjC::RewriteLocallyDefinedNamedAggregates(FieldDecl *fieldDecl, + std::string &Result) { + QualType Type = fieldDecl->getType(); + if (isa<TypedefType>(Type)) + return; + if (Type->isArrayType()) + Type = Context->getBaseElementType(Type); + ObjCContainerDecl *IDecl = + dyn_cast<ObjCContainerDecl>(fieldDecl->getDeclContext()); + + TagDecl *TD = nullptr; + if (Type->isRecordType()) { + TD = Type->getAs<RecordType>()->getDecl(); + } + else if (Type->isEnumeralType()) { + TD = Type->getAs<EnumType>()->getDecl(); + } + + if (TD) { + if (GlobalDefinedTags.count(TD)) + return; + + bool IsNamedDefinition = false; + if (IsTagDefinedInsideClass(IDecl, TD, IsNamedDefinition)) { + RewriteObjCFieldDeclType(Type, Result); + Result += ";"; + } + if (IsNamedDefinition) + GlobalDefinedTags.insert(TD); + } +} + +unsigned RewriteModernObjC::ObjCIvarBitfieldGroupNo(ObjCIvarDecl *IV) { + const ObjCInterfaceDecl *CDecl = IV->getContainingInterface(); + if (ObjCInterefaceHasBitfieldGroups.count(CDecl)) { + return IvarGroupNumber[IV]; + } + unsigned GroupNo = 0; + SmallVector<const ObjCIvarDecl *, 8> IVars; + for (const ObjCIvarDecl *IVD = CDecl->all_declared_ivar_begin(); + IVD; IVD = IVD->getNextIvar()) + IVars.push_back(IVD); + + for (unsigned i = 0, e = IVars.size(); i < e; i++) + if (IVars[i]->isBitField()) { + IvarGroupNumber[IVars[i++]] = ++GroupNo; + while (i < e && IVars[i]->isBitField()) + IvarGroupNumber[IVars[i++]] = GroupNo; + if (i < e) + --i; + } + + ObjCInterefaceHasBitfieldGroups.insert(CDecl); + return IvarGroupNumber[IV]; +} + +QualType RewriteModernObjC::SynthesizeBitfieldGroupStructType( + ObjCIvarDecl *IV, + SmallVectorImpl<ObjCIvarDecl *> &IVars) { + std::string StructTagName; + ObjCIvarBitfieldGroupType(IV, StructTagName); + RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, + Context->getTranslationUnitDecl(), + SourceLocation(), SourceLocation(), + &Context->Idents.get(StructTagName)); + for (unsigned i=0, e = IVars.size(); i < e; i++) { + ObjCIvarDecl *Ivar = IVars[i]; + RD->addDecl(FieldDecl::Create(*Context, RD, SourceLocation(), SourceLocation(), + &Context->Idents.get(Ivar->getName()), + Ivar->getType(), + nullptr, /*Expr *BW */Ivar->getBitWidth(), + false, ICIS_NoInit)); + } + RD->completeDefinition(); + return Context->getTagDeclType(RD); +} + +QualType RewriteModernObjC::GetGroupRecordTypeForObjCIvarBitfield(ObjCIvarDecl *IV) { + const ObjCInterfaceDecl *CDecl = IV->getContainingInterface(); + unsigned GroupNo = ObjCIvarBitfieldGroupNo(IV); + std::pair<const ObjCInterfaceDecl*, unsigned> tuple = std::make_pair(CDecl, GroupNo); + if (GroupRecordType.count(tuple)) + return GroupRecordType[tuple]; + + SmallVector<ObjCIvarDecl *, 8> IVars; + for (const ObjCIvarDecl *IVD = CDecl->all_declared_ivar_begin(); + IVD; IVD = IVD->getNextIvar()) { + if (IVD->isBitField()) + IVars.push_back(const_cast<ObjCIvarDecl *>(IVD)); + else { + if (!IVars.empty()) { + unsigned GroupNo = ObjCIvarBitfieldGroupNo(IVars[0]); + // Generate the struct type for this group of bitfield ivars. + GroupRecordType[std::make_pair(CDecl, GroupNo)] = + SynthesizeBitfieldGroupStructType(IVars[0], IVars); + IVars.clear(); + } + } + } + if (!IVars.empty()) { + // Do the last one. + unsigned GroupNo = ObjCIvarBitfieldGroupNo(IVars[0]); + GroupRecordType[std::make_pair(CDecl, GroupNo)] = + SynthesizeBitfieldGroupStructType(IVars[0], IVars); + } + QualType RetQT = GroupRecordType[tuple]; + assert(!RetQT.isNull() && "GetGroupRecordTypeForObjCIvarBitfield struct type is NULL"); + + return RetQT; +} + +/// ObjCIvarBitfieldGroupDecl - Names field decl. for ivar bitfield group. +/// Name would be: classname__GRBF_n where n is the group number for this ivar. +void RewriteModernObjC::ObjCIvarBitfieldGroupDecl(ObjCIvarDecl *IV, + std::string &Result) { + const ObjCInterfaceDecl *CDecl = IV->getContainingInterface(); + Result += CDecl->getName(); + Result += "__GRBF_"; + unsigned GroupNo = ObjCIvarBitfieldGroupNo(IV); + Result += utostr(GroupNo); +} + +/// ObjCIvarBitfieldGroupType - Names struct type for ivar bitfield group. +/// Name of the struct would be: classname__T_n where n is the group number for +/// this ivar. +void RewriteModernObjC::ObjCIvarBitfieldGroupType(ObjCIvarDecl *IV, + std::string &Result) { + const ObjCInterfaceDecl *CDecl = IV->getContainingInterface(); + Result += CDecl->getName(); + Result += "__T_"; + unsigned GroupNo = ObjCIvarBitfieldGroupNo(IV); + Result += utostr(GroupNo); +} + +/// ObjCIvarBitfieldGroupOffset - Names symbol for ivar bitfield group field offset. +/// Name would be: OBJC_IVAR_$_classname__GRBF_n where n is the group number for +/// this ivar. +void RewriteModernObjC::ObjCIvarBitfieldGroupOffset(ObjCIvarDecl *IV, + std::string &Result) { + Result += "OBJC_IVAR_$_"; + ObjCIvarBitfieldGroupDecl(IV, Result); +} + +#define SKIP_BITFIELDS(IX, ENDIX, VEC) { \ + while ((IX < ENDIX) && VEC[IX]->isBitField()) \ + ++IX; \ + if (IX < ENDIX) \ + --IX; \ +} + +/// RewriteObjCInternalStruct - Rewrite one internal struct corresponding to +/// an objective-c class with ivars. +void RewriteModernObjC::RewriteObjCInternalStruct(ObjCInterfaceDecl *CDecl, + std::string &Result) { + assert(CDecl && "Class missing in SynthesizeObjCInternalStruct"); + assert(CDecl->getName() != "" && + "Name missing in SynthesizeObjCInternalStruct"); + ObjCInterfaceDecl *RCDecl = CDecl->getSuperClass(); + SmallVector<ObjCIvarDecl *, 8> IVars; + for (ObjCIvarDecl *IVD = CDecl->all_declared_ivar_begin(); + IVD; IVD = IVD->getNextIvar()) + IVars.push_back(IVD); + + SourceLocation LocStart = CDecl->getBeginLoc(); + SourceLocation LocEnd = CDecl->getEndOfDefinitionLoc(); + + const char *startBuf = SM->getCharacterData(LocStart); + const char *endBuf = SM->getCharacterData(LocEnd); + + // If no ivars and no root or if its root, directly or indirectly, + // have no ivars (thus not synthesized) then no need to synthesize this class. + if ((!CDecl->isThisDeclarationADefinition() || IVars.size() == 0) && + (!RCDecl || !ObjCSynthesizedStructs.count(RCDecl))) { + endBuf += Lexer::MeasureTokenLength(LocEnd, *SM, LangOpts); + ReplaceText(LocStart, endBuf-startBuf, Result); + return; + } + + // Insert named struct/union definitions inside class to + // outer scope. This follows semantics of locally defined + // struct/unions in objective-c classes. + for (unsigned i = 0, e = IVars.size(); i < e; i++) + RewriteLocallyDefinedNamedAggregates(IVars[i], Result); + + // Insert named structs which are syntheized to group ivar bitfields + // to outer scope as well. + for (unsigned i = 0, e = IVars.size(); i < e; i++) + if (IVars[i]->isBitField()) { + ObjCIvarDecl *IV = IVars[i]; + QualType QT = GetGroupRecordTypeForObjCIvarBitfield(IV); + RewriteObjCFieldDeclType(QT, Result); + Result += ";"; + // skip over ivar bitfields in this group. + SKIP_BITFIELDS(i , e, IVars); + } + + Result += "\nstruct "; + Result += CDecl->getNameAsString(); + Result += "_IMPL {\n"; + + if (RCDecl && ObjCSynthesizedStructs.count(RCDecl)) { + Result += "\tstruct "; Result += RCDecl->getNameAsString(); + Result += "_IMPL "; Result += RCDecl->getNameAsString(); + Result += "_IVARS;\n"; + } + + for (unsigned i = 0, e = IVars.size(); i < e; i++) { + if (IVars[i]->isBitField()) { + ObjCIvarDecl *IV = IVars[i]; + Result += "\tstruct "; + ObjCIvarBitfieldGroupType(IV, Result); Result += " "; + ObjCIvarBitfieldGroupDecl(IV, Result); Result += ";\n"; + // skip over ivar bitfields in this group. + SKIP_BITFIELDS(i , e, IVars); + } + else + RewriteObjCFieldDecl(IVars[i], Result); + } + + Result += "};\n"; + endBuf += Lexer::MeasureTokenLength(LocEnd, *SM, LangOpts); + ReplaceText(LocStart, endBuf-startBuf, Result); + // Mark this struct as having been generated. + if (!ObjCSynthesizedStructs.insert(CDecl).second) + llvm_unreachable("struct already synthesize- RewriteObjCInternalStruct"); +} + +/// RewriteIvarOffsetSymbols - Rewrite ivar offset symbols of those ivars which +/// have been referenced in an ivar access expression. +void RewriteModernObjC::RewriteIvarOffsetSymbols(ObjCInterfaceDecl *CDecl, + std::string &Result) { + // write out ivar offset symbols which have been referenced in an ivar + // access expression. + llvm::SmallSetVector<ObjCIvarDecl *, 8> Ivars = ReferencedIvars[CDecl]; + + if (Ivars.empty()) + return; + + llvm::DenseSet<std::pair<const ObjCInterfaceDecl*, unsigned> > GroupSymbolOutput; + for (ObjCIvarDecl *IvarDecl : Ivars) { + const ObjCInterfaceDecl *IDecl = IvarDecl->getContainingInterface(); + unsigned GroupNo = 0; + if (IvarDecl->isBitField()) { + GroupNo = ObjCIvarBitfieldGroupNo(IvarDecl); + if (GroupSymbolOutput.count(std::make_pair(IDecl, GroupNo))) + continue; + } + Result += "\n"; + if (LangOpts.MicrosoftExt) + Result += "__declspec(allocate(\".objc_ivar$B\")) "; + Result += "extern \"C\" "; + if (LangOpts.MicrosoftExt && + IvarDecl->getAccessControl() != ObjCIvarDecl::Private && + IvarDecl->getAccessControl() != ObjCIvarDecl::Package) + Result += "__declspec(dllimport) "; + + Result += "unsigned long "; + if (IvarDecl->isBitField()) { + ObjCIvarBitfieldGroupOffset(IvarDecl, Result); + GroupSymbolOutput.insert(std::make_pair(IDecl, GroupNo)); + } + else + WriteInternalIvarName(CDecl, IvarDecl, Result); + Result += ";"; + } +} + +//===----------------------------------------------------------------------===// +// Meta Data Emission +//===----------------------------------------------------------------------===// + +/// RewriteImplementations - This routine rewrites all method implementations +/// and emits meta-data. + +void RewriteModernObjC::RewriteImplementations() { + int ClsDefCount = ClassImplementation.size(); + int CatDefCount = CategoryImplementation.size(); + + // Rewrite implemented methods + for (int i = 0; i < ClsDefCount; i++) { + ObjCImplementationDecl *OIMP = ClassImplementation[i]; + ObjCInterfaceDecl *CDecl = OIMP->getClassInterface(); + if (CDecl->isImplicitInterfaceDecl()) + assert(false && + "Legacy implicit interface rewriting not supported in moder abi"); + RewriteImplementationDecl(OIMP); + } + + for (int i = 0; i < CatDefCount; i++) { + ObjCCategoryImplDecl *CIMP = CategoryImplementation[i]; + ObjCInterfaceDecl *CDecl = CIMP->getClassInterface(); + if (CDecl->isImplicitInterfaceDecl()) + assert(false && + "Legacy implicit interface rewriting not supported in moder abi"); + RewriteImplementationDecl(CIMP); + } +} + +void RewriteModernObjC::RewriteByRefString(std::string &ResultStr, + const std::string &Name, + ValueDecl *VD, bool def) { + assert(BlockByRefDeclNo.count(VD) && + "RewriteByRefString: ByRef decl missing"); + if (def) + ResultStr += "struct "; + ResultStr += "__Block_byref_" + Name + + "_" + utostr(BlockByRefDeclNo[VD]) ; +} + +static bool HasLocalVariableExternalStorage(ValueDecl *VD) { + if (VarDecl *Var = dyn_cast<VarDecl>(VD)) + return (Var->isFunctionOrMethodVarDecl() && !Var->hasLocalStorage()); + return false; +} + +std::string RewriteModernObjC::SynthesizeBlockFunc(BlockExpr *CE, int i, + StringRef funcName, + std::string Tag) { + const FunctionType *AFT = CE->getFunctionType(); + QualType RT = AFT->getReturnType(); + std::string StructRef = "struct " + Tag; + SourceLocation BlockLoc = CE->getExprLoc(); + std::string S; + ConvertSourceLocationToLineDirective(BlockLoc, S); + + S += "static " + RT.getAsString(Context->getPrintingPolicy()) + " __" + + funcName.str() + "_block_func_" + utostr(i); + + BlockDecl *BD = CE->getBlockDecl(); + + if (isa<FunctionNoProtoType>(AFT)) { + // No user-supplied arguments. Still need to pass in a pointer to the + // block (to reference imported block decl refs). + S += "(" + StructRef + " *__cself)"; + } else if (BD->param_empty()) { + S += "(" + StructRef + " *__cself)"; + } else { + const FunctionProtoType *FT = cast<FunctionProtoType>(AFT); + assert(FT && "SynthesizeBlockFunc: No function proto"); + S += '('; + // first add the implicit argument. + S += StructRef + " *__cself, "; + std::string ParamStr; + for (BlockDecl::param_iterator AI = BD->param_begin(), + E = BD->param_end(); AI != E; ++AI) { + if (AI != BD->param_begin()) S += ", "; + ParamStr = (*AI)->getNameAsString(); + QualType QT = (*AI)->getType(); + (void)convertBlockPointerToFunctionPointer(QT); + QT.getAsStringInternal(ParamStr, Context->getPrintingPolicy()); + S += ParamStr; + } + if (FT->isVariadic()) { + if (!BD->param_empty()) S += ", "; + S += "..."; + } + S += ')'; + } + S += " {\n"; + + // Create local declarations to avoid rewriting all closure decl ref exprs. + // First, emit a declaration for all "by ref" decls. + for (SmallVectorImpl<ValueDecl *>::iterator I = BlockByRefDecls.begin(), + E = BlockByRefDecls.end(); I != E; ++I) { + S += " "; + std::string Name = (*I)->getNameAsString(); + std::string TypeString; + RewriteByRefString(TypeString, Name, (*I)); + TypeString += " *"; + Name = TypeString + Name; + S += Name + " = __cself->" + (*I)->getNameAsString() + "; // bound by ref\n"; + } + // Next, emit a declaration for all "by copy" declarations. + for (SmallVectorImpl<ValueDecl *>::iterator I = BlockByCopyDecls.begin(), + E = BlockByCopyDecls.end(); I != E; ++I) { + S += " "; + // Handle nested closure invocation. For example: + // + // void (^myImportedClosure)(void); + // myImportedClosure = ^(void) { setGlobalInt(x + y); }; + // + // void (^anotherClosure)(void); + // anotherClosure = ^(void) { + // myImportedClosure(); // import and invoke the closure + // }; + // + if (isTopLevelBlockPointerType((*I)->getType())) { + RewriteBlockPointerTypeVariable(S, (*I)); + S += " = ("; + RewriteBlockPointerType(S, (*I)->getType()); + S += ")"; + S += "__cself->" + (*I)->getNameAsString() + "; // bound by copy\n"; + } + else { + std::string Name = (*I)->getNameAsString(); + QualType QT = (*I)->getType(); + if (HasLocalVariableExternalStorage(*I)) + QT = Context->getPointerType(QT); + QT.getAsStringInternal(Name, Context->getPrintingPolicy()); + S += Name + " = __cself->" + + (*I)->getNameAsString() + "; // bound by copy\n"; + } + } + std::string RewrittenStr = RewrittenBlockExprs[CE]; + const char *cstr = RewrittenStr.c_str(); + while (*cstr++ != '{') ; + S += cstr; + S += "\n"; + return S; +} + +std::string RewriteModernObjC::SynthesizeBlockHelperFuncs(BlockExpr *CE, int i, + StringRef funcName, + std::string Tag) { + std::string StructRef = "struct " + Tag; + std::string S = "static void __"; + + S += funcName; + S += "_block_copy_" + utostr(i); + S += "(" + StructRef; + S += "*dst, " + StructRef; + S += "*src) {"; + for (ValueDecl *VD : ImportedBlockDecls) { + S += "_Block_object_assign((void*)&dst->"; + S += VD->getNameAsString(); + S += ", (void*)src->"; + S += VD->getNameAsString(); + if (BlockByRefDeclsPtrSet.count(VD)) + S += ", " + utostr(BLOCK_FIELD_IS_BYREF) + "/*BLOCK_FIELD_IS_BYREF*/);"; + else if (VD->getType()->isBlockPointerType()) + S += ", " + utostr(BLOCK_FIELD_IS_BLOCK) + "/*BLOCK_FIELD_IS_BLOCK*/);"; + else + S += ", " + utostr(BLOCK_FIELD_IS_OBJECT) + "/*BLOCK_FIELD_IS_OBJECT*/);"; + } + S += "}\n"; + + S += "\nstatic void __"; + S += funcName; + S += "_block_dispose_" + utostr(i); + S += "(" + StructRef; + S += "*src) {"; + for (ValueDecl *VD : ImportedBlockDecls) { + S += "_Block_object_dispose((void*)src->"; + S += VD->getNameAsString(); + if (BlockByRefDeclsPtrSet.count(VD)) + S += ", " + utostr(BLOCK_FIELD_IS_BYREF) + "/*BLOCK_FIELD_IS_BYREF*/);"; + else if (VD->getType()->isBlockPointerType()) + S += ", " + utostr(BLOCK_FIELD_IS_BLOCK) + "/*BLOCK_FIELD_IS_BLOCK*/);"; + else + S += ", " + utostr(BLOCK_FIELD_IS_OBJECT) + "/*BLOCK_FIELD_IS_OBJECT*/);"; + } + S += "}\n"; + return S; +} + +std::string RewriteModernObjC::SynthesizeBlockImpl(BlockExpr *CE, std::string Tag, + std::string Desc) { + std::string S = "\nstruct " + Tag; + std::string Constructor = " " + Tag; + + S += " {\n struct __block_impl impl;\n"; + S += " struct " + Desc; + S += "* Desc;\n"; + + Constructor += "(void *fp, "; // Invoke function pointer. + Constructor += "struct " + Desc; // Descriptor pointer. + Constructor += " *desc"; + + if (BlockDeclRefs.size()) { + // Output all "by copy" declarations. + for (SmallVectorImpl<ValueDecl *>::iterator I = BlockByCopyDecls.begin(), + E = BlockByCopyDecls.end(); I != E; ++I) { + S += " "; + std::string FieldName = (*I)->getNameAsString(); + std::string ArgName = "_" + FieldName; + // Handle nested closure invocation. For example: + // + // void (^myImportedBlock)(void); + // myImportedBlock = ^(void) { setGlobalInt(x + y); }; + // + // void (^anotherBlock)(void); + // anotherBlock = ^(void) { + // myImportedBlock(); // import and invoke the closure + // }; + // + if (isTopLevelBlockPointerType((*I)->getType())) { + S += "struct __block_impl *"; + Constructor += ", void *" + ArgName; + } else { + QualType QT = (*I)->getType(); + if (HasLocalVariableExternalStorage(*I)) + QT = Context->getPointerType(QT); + QT.getAsStringInternal(FieldName, Context->getPrintingPolicy()); + QT.getAsStringInternal(ArgName, Context->getPrintingPolicy()); + Constructor += ", " + ArgName; + } + S += FieldName + ";\n"; + } + // Output all "by ref" declarations. + for (SmallVectorImpl<ValueDecl *>::iterator I = BlockByRefDecls.begin(), + E = BlockByRefDecls.end(); I != E; ++I) { + S += " "; + std::string FieldName = (*I)->getNameAsString(); + std::string ArgName = "_" + FieldName; + { + std::string TypeString; + RewriteByRefString(TypeString, FieldName, (*I)); + TypeString += " *"; + FieldName = TypeString + FieldName; + ArgName = TypeString + ArgName; + Constructor += ", " + ArgName; + } + S += FieldName + "; // by ref\n"; + } + // Finish writing the constructor. + Constructor += ", int flags=0)"; + // Initialize all "by copy" arguments. + bool firsTime = true; + for (SmallVectorImpl<ValueDecl *>::iterator I = BlockByCopyDecls.begin(), + E = BlockByCopyDecls.end(); I != E; ++I) { + std::string Name = (*I)->getNameAsString(); + if (firsTime) { + Constructor += " : "; + firsTime = false; + } + else + Constructor += ", "; + if (isTopLevelBlockPointerType((*I)->getType())) + Constructor += Name + "((struct __block_impl *)_" + Name + ")"; + else + Constructor += Name + "(_" + Name + ")"; + } + // Initialize all "by ref" arguments. + for (SmallVectorImpl<ValueDecl *>::iterator I = BlockByRefDecls.begin(), + E = BlockByRefDecls.end(); I != E; ++I) { + std::string Name = (*I)->getNameAsString(); + if (firsTime) { + Constructor += " : "; + firsTime = false; + } + else + Constructor += ", "; + Constructor += Name + "(_" + Name + "->__forwarding)"; + } + + Constructor += " {\n"; + if (GlobalVarDecl) + Constructor += " impl.isa = &_NSConcreteGlobalBlock;\n"; + else + Constructor += " impl.isa = &_NSConcreteStackBlock;\n"; + Constructor += " impl.Flags = flags;\n impl.FuncPtr = fp;\n"; + + Constructor += " Desc = desc;\n"; + } else { + // Finish writing the constructor. + Constructor += ", int flags=0) {\n"; + if (GlobalVarDecl) + Constructor += " impl.isa = &_NSConcreteGlobalBlock;\n"; + else + Constructor += " impl.isa = &_NSConcreteStackBlock;\n"; + Constructor += " impl.Flags = flags;\n impl.FuncPtr = fp;\n"; + Constructor += " Desc = desc;\n"; + } + Constructor += " "; + Constructor += "}\n"; + S += Constructor; + S += "};\n"; + return S; +} + +std::string RewriteModernObjC::SynthesizeBlockDescriptor(std::string DescTag, + std::string ImplTag, int i, + StringRef FunName, + unsigned hasCopy) { + std::string S = "\nstatic struct " + DescTag; + + S += " {\n size_t reserved;\n"; + S += " size_t Block_size;\n"; + if (hasCopy) { + S += " void (*copy)(struct "; + S += ImplTag; S += "*, struct "; + S += ImplTag; S += "*);\n"; + + S += " void (*dispose)(struct "; + S += ImplTag; S += "*);\n"; + } + S += "} "; + + S += DescTag + "_DATA = { 0, sizeof(struct "; + S += ImplTag + ")"; + if (hasCopy) { + S += ", __" + FunName.str() + "_block_copy_" + utostr(i); + S += ", __" + FunName.str() + "_block_dispose_" + utostr(i); + } + S += "};\n"; + return S; +} + +void RewriteModernObjC::SynthesizeBlockLiterals(SourceLocation FunLocStart, + StringRef FunName) { + bool RewriteSC = (GlobalVarDecl && + !Blocks.empty() && + GlobalVarDecl->getStorageClass() == SC_Static && + GlobalVarDecl->getType().getCVRQualifiers()); + if (RewriteSC) { + std::string SC(" void __"); + SC += GlobalVarDecl->getNameAsString(); + SC += "() {}"; + InsertText(FunLocStart, SC); + } + + // Insert closures that were part of the function. + for (unsigned i = 0, count=0; i < Blocks.size(); i++) { + CollectBlockDeclRefInfo(Blocks[i]); + // Need to copy-in the inner copied-in variables not actually used in this + // block. + for (int j = 0; j < InnerDeclRefsCount[i]; j++) { + DeclRefExpr *Exp = InnerDeclRefs[count++]; + ValueDecl *VD = Exp->getDecl(); + BlockDeclRefs.push_back(Exp); + if (!VD->hasAttr<BlocksAttr>()) { + if (!BlockByCopyDeclsPtrSet.count(VD)) { + BlockByCopyDeclsPtrSet.insert(VD); + BlockByCopyDecls.push_back(VD); + } + continue; + } + + if (!BlockByRefDeclsPtrSet.count(VD)) { + BlockByRefDeclsPtrSet.insert(VD); + BlockByRefDecls.push_back(VD); + } + + // imported objects in the inner blocks not used in the outer + // blocks must be copied/disposed in the outer block as well. + if (VD->getType()->isObjCObjectPointerType() || + VD->getType()->isBlockPointerType()) + ImportedBlockDecls.insert(VD); + } + + std::string ImplTag = "__" + FunName.str() + "_block_impl_" + utostr(i); + std::string DescTag = "__" + FunName.str() + "_block_desc_" + utostr(i); + + std::string CI = SynthesizeBlockImpl(Blocks[i], ImplTag, DescTag); + + InsertText(FunLocStart, CI); + + std::string CF = SynthesizeBlockFunc(Blocks[i], i, FunName, ImplTag); + + InsertText(FunLocStart, CF); + + if (ImportedBlockDecls.size()) { + std::string HF = SynthesizeBlockHelperFuncs(Blocks[i], i, FunName, ImplTag); + InsertText(FunLocStart, HF); + } + std::string BD = SynthesizeBlockDescriptor(DescTag, ImplTag, i, FunName, + ImportedBlockDecls.size() > 0); + InsertText(FunLocStart, BD); + + BlockDeclRefs.clear(); + BlockByRefDecls.clear(); + BlockByRefDeclsPtrSet.clear(); + BlockByCopyDecls.clear(); + BlockByCopyDeclsPtrSet.clear(); + ImportedBlockDecls.clear(); + } + if (RewriteSC) { + // Must insert any 'const/volatile/static here. Since it has been + // removed as result of rewriting of block literals. + std::string SC; + if (GlobalVarDecl->getStorageClass() == SC_Static) + SC = "static "; + if (GlobalVarDecl->getType().isConstQualified()) + SC += "const "; + if (GlobalVarDecl->getType().isVolatileQualified()) + SC += "volatile "; + if (GlobalVarDecl->getType().isRestrictQualified()) + SC += "restrict "; + InsertText(FunLocStart, SC); + } + if (GlobalConstructionExp) { + // extra fancy dance for global literal expression. + + // Always the latest block expression on the block stack. + std::string Tag = "__"; + Tag += FunName; + Tag += "_block_impl_"; + Tag += utostr(Blocks.size()-1); + std::string globalBuf = "static "; + globalBuf += Tag; globalBuf += " "; + std::string SStr; + + llvm::raw_string_ostream constructorExprBuf(SStr); + GlobalConstructionExp->printPretty(constructorExprBuf, nullptr, + PrintingPolicy(LangOpts)); + globalBuf += constructorExprBuf.str(); + globalBuf += ";\n"; + InsertText(FunLocStart, globalBuf); + GlobalConstructionExp = nullptr; + } + + Blocks.clear(); + InnerDeclRefsCount.clear(); + InnerDeclRefs.clear(); + RewrittenBlockExprs.clear(); +} + +void RewriteModernObjC::InsertBlockLiteralsWithinFunction(FunctionDecl *FD) { + SourceLocation FunLocStart = + (!Blocks.empty()) ? getFunctionSourceLocation(*this, FD) + : FD->getTypeSpecStartLoc(); + StringRef FuncName = FD->getName(); + + SynthesizeBlockLiterals(FunLocStart, FuncName); +} + +static void BuildUniqueMethodName(std::string &Name, + ObjCMethodDecl *MD) { + ObjCInterfaceDecl *IFace = MD->getClassInterface(); + Name = IFace->getName(); + Name += "__" + MD->getSelector().getAsString(); + // Convert colons to underscores. + std::string::size_type loc = 0; + while ((loc = Name.find(':', loc)) != std::string::npos) + Name.replace(loc, 1, "_"); +} + +void RewriteModernObjC::InsertBlockLiteralsWithinMethod(ObjCMethodDecl *MD) { + // fprintf(stderr,"In InsertBlockLiteralsWitinMethod\n"); + // SourceLocation FunLocStart = MD->getBeginLoc(); + SourceLocation FunLocStart = MD->getBeginLoc(); + std::string FuncName; + BuildUniqueMethodName(FuncName, MD); + SynthesizeBlockLiterals(FunLocStart, FuncName); +} + +void RewriteModernObjC::GetBlockDeclRefExprs(Stmt *S) { + for (Stmt *SubStmt : S->children()) + if (SubStmt) { + if (BlockExpr *CBE = dyn_cast<BlockExpr>(SubStmt)) + GetBlockDeclRefExprs(CBE->getBody()); + else + GetBlockDeclRefExprs(SubStmt); + } + // Handle specific things. + if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(S)) + if (DRE->refersToEnclosingVariableOrCapture() || + HasLocalVariableExternalStorage(DRE->getDecl())) + // FIXME: Handle enums. + BlockDeclRefs.push_back(DRE); +} + +void RewriteModernObjC::GetInnerBlockDeclRefExprs(Stmt *S, + SmallVectorImpl<DeclRefExpr *> &InnerBlockDeclRefs, + llvm::SmallPtrSetImpl<const DeclContext *> &InnerContexts) { + for (Stmt *SubStmt : S->children()) + if (SubStmt) { + if (BlockExpr *CBE = dyn_cast<BlockExpr>(SubStmt)) { + InnerContexts.insert(cast<DeclContext>(CBE->getBlockDecl())); + GetInnerBlockDeclRefExprs(CBE->getBody(), + InnerBlockDeclRefs, + InnerContexts); + } + else + GetInnerBlockDeclRefExprs(SubStmt, InnerBlockDeclRefs, InnerContexts); + } + // Handle specific things. + if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(S)) { + if (DRE->refersToEnclosingVariableOrCapture() || + HasLocalVariableExternalStorage(DRE->getDecl())) { + if (!InnerContexts.count(DRE->getDecl()->getDeclContext())) + InnerBlockDeclRefs.push_back(DRE); + if (VarDecl *Var = cast<VarDecl>(DRE->getDecl())) + if (Var->isFunctionOrMethodVarDecl()) + ImportedLocalExternalDecls.insert(Var); + } + } +} + +/// convertObjCTypeToCStyleType - This routine converts such objc types +/// as qualified objects, and blocks to their closest c/c++ types that +/// it can. It returns true if input type was modified. +bool RewriteModernObjC::convertObjCTypeToCStyleType(QualType &T) { + QualType oldT = T; + convertBlockPointerToFunctionPointer(T); + if (T->isFunctionPointerType()) { + QualType PointeeTy; + if (const PointerType* PT = T->getAs<PointerType>()) { + PointeeTy = PT->getPointeeType(); + if (const FunctionType *FT = PointeeTy->getAs<FunctionType>()) { + T = convertFunctionTypeOfBlocks(FT); + T = Context->getPointerType(T); + } + } + } + + convertToUnqualifiedObjCType(T); + return T != oldT; +} + +/// convertFunctionTypeOfBlocks - This routine converts a function type +/// whose result type may be a block pointer or whose argument type(s) +/// might be block pointers to an equivalent function type replacing +/// all block pointers to function pointers. +QualType RewriteModernObjC::convertFunctionTypeOfBlocks(const FunctionType *FT) { + const FunctionProtoType *FTP = dyn_cast<FunctionProtoType>(FT); + // FTP will be null for closures that don't take arguments. + // Generate a funky cast. + SmallVector<QualType, 8> ArgTypes; + QualType Res = FT->getReturnType(); + bool modified = convertObjCTypeToCStyleType(Res); + + if (FTP) { + for (auto &I : FTP->param_types()) { + QualType t = I; + // Make sure we convert "t (^)(...)" to "t (*)(...)". + if (convertObjCTypeToCStyleType(t)) + modified = true; + ArgTypes.push_back(t); + } + } + QualType FuncType; + if (modified) + FuncType = getSimpleFunctionType(Res, ArgTypes); + else FuncType = QualType(FT, 0); + return FuncType; +} + +Stmt *RewriteModernObjC::SynthesizeBlockCall(CallExpr *Exp, const Expr *BlockExp) { + // Navigate to relevant type information. + const BlockPointerType *CPT = nullptr; + + if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(BlockExp)) { + CPT = DRE->getType()->getAs<BlockPointerType>(); + } else if (const MemberExpr *MExpr = dyn_cast<MemberExpr>(BlockExp)) { + CPT = MExpr->getType()->getAs<BlockPointerType>(); + } + else if (const ParenExpr *PRE = dyn_cast<ParenExpr>(BlockExp)) { + return SynthesizeBlockCall(Exp, PRE->getSubExpr()); + } + else if (const ImplicitCastExpr *IEXPR = dyn_cast<ImplicitCastExpr>(BlockExp)) + CPT = IEXPR->getType()->getAs<BlockPointerType>(); + else if (const ConditionalOperator *CEXPR = + dyn_cast<ConditionalOperator>(BlockExp)) { + Expr *LHSExp = CEXPR->getLHS(); + Stmt *LHSStmt = SynthesizeBlockCall(Exp, LHSExp); + Expr *RHSExp = CEXPR->getRHS(); + Stmt *RHSStmt = SynthesizeBlockCall(Exp, RHSExp); + Expr *CONDExp = CEXPR->getCond(); + ConditionalOperator *CondExpr = + new (Context) ConditionalOperator(CONDExp, + SourceLocation(), cast<Expr>(LHSStmt), + SourceLocation(), cast<Expr>(RHSStmt), + Exp->getType(), VK_RValue, OK_Ordinary); + return CondExpr; + } else if (const ObjCIvarRefExpr *IRE = dyn_cast<ObjCIvarRefExpr>(BlockExp)) { + CPT = IRE->getType()->getAs<BlockPointerType>(); + } else if (const PseudoObjectExpr *POE + = dyn_cast<PseudoObjectExpr>(BlockExp)) { + CPT = POE->getType()->castAs<BlockPointerType>(); + } else { + assert(false && "RewriteBlockClass: Bad type"); + } + assert(CPT && "RewriteBlockClass: Bad type"); + const FunctionType *FT = CPT->getPointeeType()->getAs<FunctionType>(); + assert(FT && "RewriteBlockClass: Bad type"); + const FunctionProtoType *FTP = dyn_cast<FunctionProtoType>(FT); + // FTP will be null for closures that don't take arguments. + + RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), SourceLocation(), + &Context->Idents.get("__block_impl")); + QualType PtrBlock = Context->getPointerType(Context->getTagDeclType(RD)); + + // Generate a funky cast. + SmallVector<QualType, 8> ArgTypes; + + // Push the block argument type. + ArgTypes.push_back(PtrBlock); + if (FTP) { + for (auto &I : FTP->param_types()) { + QualType t = I; + // Make sure we convert "t (^)(...)" to "t (*)(...)". + if (!convertBlockPointerToFunctionPointer(t)) + convertToUnqualifiedObjCType(t); + ArgTypes.push_back(t); + } + } + // Now do the pointer to function cast. + QualType PtrToFuncCastType = getSimpleFunctionType(Exp->getType(), ArgTypes); + + PtrToFuncCastType = Context->getPointerType(PtrToFuncCastType); + + CastExpr *BlkCast = NoTypeInfoCStyleCastExpr(Context, PtrBlock, + CK_BitCast, + const_cast<Expr*>(BlockExp)); + // Don't forget the parens to enforce the proper binding. + ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), + BlkCast); + //PE->dump(); + + FieldDecl *FD = FieldDecl::Create(*Context, nullptr, SourceLocation(), + SourceLocation(), + &Context->Idents.get("FuncPtr"), + Context->VoidPtrTy, nullptr, + /*BitWidth=*/nullptr, /*Mutable=*/true, + ICIS_NoInit); + MemberExpr *ME = + new (Context) MemberExpr(PE, true, SourceLocation(), FD, SourceLocation(), + FD->getType(), VK_LValue, OK_Ordinary); + + CastExpr *FunkCast = NoTypeInfoCStyleCastExpr(Context, PtrToFuncCastType, + CK_BitCast, ME); + PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), FunkCast); + + SmallVector<Expr*, 8> BlkExprs; + // Add the implicit argument. + BlkExprs.push_back(BlkCast); + // Add the user arguments. + for (CallExpr::arg_iterator I = Exp->arg_begin(), + E = Exp->arg_end(); I != E; ++I) { + BlkExprs.push_back(*I); + } + CallExpr *CE = CallExpr::Create(*Context, PE, BlkExprs, Exp->getType(), + VK_RValue, SourceLocation()); + return CE; +} + +// We need to return the rewritten expression to handle cases where the +// DeclRefExpr is embedded in another expression being rewritten. +// For example: +// +// int main() { +// __block Foo *f; +// __block int i; +// +// void (^myblock)() = ^() { +// [f test]; // f is a DeclRefExpr embedded in a message (which is being rewritten). +// i = 77; +// }; +//} +Stmt *RewriteModernObjC::RewriteBlockDeclRefExpr(DeclRefExpr *DeclRefExp) { + // Rewrite the byref variable into BYREFVAR->__forwarding->BYREFVAR + // for each DeclRefExp where BYREFVAR is name of the variable. + ValueDecl *VD = DeclRefExp->getDecl(); + bool isArrow = DeclRefExp->refersToEnclosingVariableOrCapture() || + HasLocalVariableExternalStorage(DeclRefExp->getDecl()); + + FieldDecl *FD = FieldDecl::Create(*Context, nullptr, SourceLocation(), + SourceLocation(), + &Context->Idents.get("__forwarding"), + Context->VoidPtrTy, nullptr, + /*BitWidth=*/nullptr, /*Mutable=*/true, + ICIS_NoInit); + MemberExpr *ME = new (Context) + MemberExpr(DeclRefExp, isArrow, SourceLocation(), FD, SourceLocation(), + FD->getType(), VK_LValue, OK_Ordinary); + + StringRef Name = VD->getName(); + FD = FieldDecl::Create(*Context, nullptr, SourceLocation(), SourceLocation(), + &Context->Idents.get(Name), + Context->VoidPtrTy, nullptr, + /*BitWidth=*/nullptr, /*Mutable=*/true, + ICIS_NoInit); + ME = + new (Context) MemberExpr(ME, true, SourceLocation(), FD, SourceLocation(), + DeclRefExp->getType(), VK_LValue, OK_Ordinary); + + // Need parens to enforce precedence. + ParenExpr *PE = new (Context) ParenExpr(DeclRefExp->getExprLoc(), + DeclRefExp->getExprLoc(), + ME); + ReplaceStmt(DeclRefExp, PE); + return PE; +} + +// Rewrites the imported local variable V with external storage +// (static, extern, etc.) as *V +// +Stmt *RewriteModernObjC::RewriteLocalVariableExternalStorage(DeclRefExpr *DRE) { + ValueDecl *VD = DRE->getDecl(); + if (VarDecl *Var = dyn_cast<VarDecl>(VD)) + if (!ImportedLocalExternalDecls.count(Var)) + return DRE; + Expr *Exp = new (Context) UnaryOperator(DRE, UO_Deref, DRE->getType(), + VK_LValue, OK_Ordinary, + DRE->getLocation(), false); + // Need parens to enforce precedence. + ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), + Exp); + ReplaceStmt(DRE, PE); + return PE; +} + +void RewriteModernObjC::RewriteCastExpr(CStyleCastExpr *CE) { + SourceLocation LocStart = CE->getLParenLoc(); + SourceLocation LocEnd = CE->getRParenLoc(); + + // Need to avoid trying to rewrite synthesized casts. + if (LocStart.isInvalid()) + return; + // Need to avoid trying to rewrite casts contained in macros. + if (!Rewriter::isRewritable(LocStart) || !Rewriter::isRewritable(LocEnd)) + return; + + const char *startBuf = SM->getCharacterData(LocStart); + const char *endBuf = SM->getCharacterData(LocEnd); + QualType QT = CE->getType(); + const Type* TypePtr = QT->getAs<Type>(); + if (isa<TypeOfExprType>(TypePtr)) { + const TypeOfExprType *TypeOfExprTypePtr = cast<TypeOfExprType>(TypePtr); + QT = TypeOfExprTypePtr->getUnderlyingExpr()->getType(); + std::string TypeAsString = "("; + RewriteBlockPointerType(TypeAsString, QT); + TypeAsString += ")"; + ReplaceText(LocStart, endBuf-startBuf+1, TypeAsString); + return; + } + // advance the location to startArgList. + const char *argPtr = startBuf; + + while (*argPtr++ && (argPtr < endBuf)) { + switch (*argPtr) { + case '^': + // Replace the '^' with '*'. + LocStart = LocStart.getLocWithOffset(argPtr-startBuf); + ReplaceText(LocStart, 1, "*"); + break; + } + } +} + +void RewriteModernObjC::RewriteImplicitCastObjCExpr(CastExpr *IC) { + CastKind CastKind = IC->getCastKind(); + if (CastKind != CK_BlockPointerToObjCPointerCast && + CastKind != CK_AnyPointerToBlockPointerCast) + return; + + QualType QT = IC->getType(); + (void)convertBlockPointerToFunctionPointer(QT); + std::string TypeString(QT.getAsString(Context->getPrintingPolicy())); + std::string Str = "("; + Str += TypeString; + Str += ")"; + InsertText(IC->getSubExpr()->getBeginLoc(), Str); +} + +void RewriteModernObjC::RewriteBlockPointerFunctionArgs(FunctionDecl *FD) { + SourceLocation DeclLoc = FD->getLocation(); + unsigned parenCount = 0; + + // We have 1 or more arguments that have closure pointers. + const char *startBuf = SM->getCharacterData(DeclLoc); + const char *startArgList = strchr(startBuf, '('); + + assert((*startArgList == '(') && "Rewriter fuzzy parser confused"); + + parenCount++; + // advance the location to startArgList. + DeclLoc = DeclLoc.getLocWithOffset(startArgList-startBuf); + assert((DeclLoc.isValid()) && "Invalid DeclLoc"); + + const char *argPtr = startArgList; + + while (*argPtr++ && parenCount) { + switch (*argPtr) { + case '^': + // Replace the '^' with '*'. + DeclLoc = DeclLoc.getLocWithOffset(argPtr-startArgList); + ReplaceText(DeclLoc, 1, "*"); + break; + case '(': + parenCount++; + break; + case ')': + parenCount--; + break; + } + } +} + +bool RewriteModernObjC::PointerTypeTakesAnyBlockArguments(QualType QT) { + const FunctionProtoType *FTP; + const PointerType *PT = QT->getAs<PointerType>(); + if (PT) { + FTP = PT->getPointeeType()->getAs<FunctionProtoType>(); + } else { + const BlockPointerType *BPT = QT->getAs<BlockPointerType>(); + assert(BPT && "BlockPointerTypeTakeAnyBlockArguments(): not a block pointer type"); + FTP = BPT->getPointeeType()->getAs<FunctionProtoType>(); + } + if (FTP) { + for (const auto &I : FTP->param_types()) + if (isTopLevelBlockPointerType(I)) + return true; + } + return false; +} + +bool RewriteModernObjC::PointerTypeTakesAnyObjCQualifiedType(QualType QT) { + const FunctionProtoType *FTP; + const PointerType *PT = QT->getAs<PointerType>(); + if (PT) { + FTP = PT->getPointeeType()->getAs<FunctionProtoType>(); + } else { + const BlockPointerType *BPT = QT->getAs<BlockPointerType>(); + assert(BPT && "BlockPointerTypeTakeAnyBlockArguments(): not a block pointer type"); + FTP = BPT->getPointeeType()->getAs<FunctionProtoType>(); + } + if (FTP) { + for (const auto &I : FTP->param_types()) { + if (I->isObjCQualifiedIdType()) + return true; + if (I->isObjCObjectPointerType() && + I->getPointeeType()->isObjCQualifiedInterfaceType()) + return true; + } + + } + return false; +} + +void RewriteModernObjC::GetExtentOfArgList(const char *Name, const char *&LParen, + const char *&RParen) { + const char *argPtr = strchr(Name, '('); + assert((*argPtr == '(') && "Rewriter fuzzy parser confused"); + + LParen = argPtr; // output the start. + argPtr++; // skip past the left paren. + unsigned parenCount = 1; + + while (*argPtr && parenCount) { + switch (*argPtr) { + case '(': parenCount++; break; + case ')': parenCount--; break; + default: break; + } + if (parenCount) argPtr++; + } + assert((*argPtr == ')') && "Rewriter fuzzy parser confused"); + RParen = argPtr; // output the end +} + +void RewriteModernObjC::RewriteBlockPointerDecl(NamedDecl *ND) { + if (FunctionDecl *FD = dyn_cast<FunctionDecl>(ND)) { + RewriteBlockPointerFunctionArgs(FD); + return; + } + // Handle Variables and Typedefs. + SourceLocation DeclLoc = ND->getLocation(); + QualType DeclT; + if (VarDecl *VD = dyn_cast<VarDecl>(ND)) + DeclT = VD->getType(); + else if (TypedefNameDecl *TDD = dyn_cast<TypedefNameDecl>(ND)) + DeclT = TDD->getUnderlyingType(); + else if (FieldDecl *FD = dyn_cast<FieldDecl>(ND)) + DeclT = FD->getType(); + else + llvm_unreachable("RewriteBlockPointerDecl(): Decl type not yet handled"); + + const char *startBuf = SM->getCharacterData(DeclLoc); + const char *endBuf = startBuf; + // scan backward (from the decl location) for the end of the previous decl. + while (*startBuf != '^' && *startBuf != ';' && startBuf != MainFileStart) + startBuf--; + SourceLocation Start = DeclLoc.getLocWithOffset(startBuf-endBuf); + std::string buf; + unsigned OrigLength=0; + // *startBuf != '^' if we are dealing with a pointer to function that + // may take block argument types (which will be handled below). + if (*startBuf == '^') { + // Replace the '^' with '*', computing a negative offset. + buf = '*'; + startBuf++; + OrigLength++; + } + while (*startBuf != ')') { + buf += *startBuf; + startBuf++; + OrigLength++; + } + buf += ')'; + OrigLength++; + + if (PointerTypeTakesAnyBlockArguments(DeclT) || + PointerTypeTakesAnyObjCQualifiedType(DeclT)) { + // Replace the '^' with '*' for arguments. + // Replace id<P> with id/*<>*/ + DeclLoc = ND->getLocation(); + startBuf = SM->getCharacterData(DeclLoc); + const char *argListBegin, *argListEnd; + GetExtentOfArgList(startBuf, argListBegin, argListEnd); + while (argListBegin < argListEnd) { + if (*argListBegin == '^') + buf += '*'; + else if (*argListBegin == '<') { + buf += "/*"; + buf += *argListBegin++; + OrigLength++; + while (*argListBegin != '>') { + buf += *argListBegin++; + OrigLength++; + } + buf += *argListBegin; + buf += "*/"; + } + else + buf += *argListBegin; + argListBegin++; + OrigLength++; + } + buf += ')'; + OrigLength++; + } + ReplaceText(Start, OrigLength, buf); +} + +/// SynthesizeByrefCopyDestroyHelper - This routine synthesizes: +/// void __Block_byref_id_object_copy(struct Block_byref_id_object *dst, +/// struct Block_byref_id_object *src) { +/// _Block_object_assign (&_dest->object, _src->object, +/// BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT +/// [|BLOCK_FIELD_IS_WEAK]) // object +/// _Block_object_assign(&_dest->object, _src->object, +/// BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK +/// [|BLOCK_FIELD_IS_WEAK]) // block +/// } +/// And: +/// void __Block_byref_id_object_dispose(struct Block_byref_id_object *_src) { +/// _Block_object_dispose(_src->object, +/// BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT +/// [|BLOCK_FIELD_IS_WEAK]) // object +/// _Block_object_dispose(_src->object, +/// BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK +/// [|BLOCK_FIELD_IS_WEAK]) // block +/// } + +std::string RewriteModernObjC::SynthesizeByrefCopyDestroyHelper(VarDecl *VD, + int flag) { + std::string S; + if (CopyDestroyCache.count(flag)) + return S; + CopyDestroyCache.insert(flag); + S = "static void __Block_byref_id_object_copy_"; + S += utostr(flag); + S += "(void *dst, void *src) {\n"; + + // offset into the object pointer is computed as: + // void * + void* + int + int + void* + void * + unsigned IntSize = + static_cast<unsigned>(Context->getTypeSize(Context->IntTy)); + unsigned VoidPtrSize = + static_cast<unsigned>(Context->getTypeSize(Context->VoidPtrTy)); + + unsigned offset = (VoidPtrSize*4 + IntSize + IntSize)/Context->getCharWidth(); + S += " _Block_object_assign((char*)dst + "; + S += utostr(offset); + S += ", *(void * *) ((char*)src + "; + S += utostr(offset); + S += "), "; + S += utostr(flag); + S += ");\n}\n"; + + S += "static void __Block_byref_id_object_dispose_"; + S += utostr(flag); + S += "(void *src) {\n"; + S += " _Block_object_dispose(*(void * *) ((char*)src + "; + S += utostr(offset); + S += "), "; + S += utostr(flag); + S += ");\n}\n"; + return S; +} + +/// RewriteByRefVar - For each __block typex ND variable this routine transforms +/// the declaration into: +/// struct __Block_byref_ND { +/// void *__isa; // NULL for everything except __weak pointers +/// struct __Block_byref_ND *__forwarding; +/// int32_t __flags; +/// int32_t __size; +/// void *__Block_byref_id_object_copy; // If variable is __block ObjC object +/// void *__Block_byref_id_object_dispose; // If variable is __block ObjC object +/// typex ND; +/// }; +/// +/// It then replaces declaration of ND variable with: +/// struct __Block_byref_ND ND = {__isa=0B, __forwarding=&ND, __flags=some_flag, +/// __size=sizeof(struct __Block_byref_ND), +/// ND=initializer-if-any}; +/// +/// +void RewriteModernObjC::RewriteByRefVar(VarDecl *ND, bool firstDecl, + bool lastDecl) { + int flag = 0; + int isa = 0; + SourceLocation DeclLoc = ND->getTypeSpecStartLoc(); + if (DeclLoc.isInvalid()) + // If type location is missing, it is because of missing type (a warning). + // Use variable's location which is good for this case. + DeclLoc = ND->getLocation(); + const char *startBuf = SM->getCharacterData(DeclLoc); + SourceLocation X = ND->getEndLoc(); + X = SM->getExpansionLoc(X); + const char *endBuf = SM->getCharacterData(X); + std::string Name(ND->getNameAsString()); + std::string ByrefType; + RewriteByRefString(ByrefType, Name, ND, true); + ByrefType += " {\n"; + ByrefType += " void *__isa;\n"; + RewriteByRefString(ByrefType, Name, ND); + ByrefType += " *__forwarding;\n"; + ByrefType += " int __flags;\n"; + ByrefType += " int __size;\n"; + // Add void *__Block_byref_id_object_copy; + // void *__Block_byref_id_object_dispose; if needed. + QualType Ty = ND->getType(); + bool HasCopyAndDispose = Context->BlockRequiresCopying(Ty, ND); + if (HasCopyAndDispose) { + ByrefType += " void (*__Block_byref_id_object_copy)(void*, void*);\n"; + ByrefType += " void (*__Block_byref_id_object_dispose)(void*);\n"; + } + + QualType T = Ty; + (void)convertBlockPointerToFunctionPointer(T); + T.getAsStringInternal(Name, Context->getPrintingPolicy()); + + ByrefType += " " + Name + ";\n"; + ByrefType += "};\n"; + // Insert this type in global scope. It is needed by helper function. + SourceLocation FunLocStart; + if (CurFunctionDef) + FunLocStart = getFunctionSourceLocation(*this, CurFunctionDef); + else { + assert(CurMethodDef && "RewriteByRefVar - CurMethodDef is null"); + FunLocStart = CurMethodDef->getBeginLoc(); + } + InsertText(FunLocStart, ByrefType); + + if (Ty.isObjCGCWeak()) { + flag |= BLOCK_FIELD_IS_WEAK; + isa = 1; + } + if (HasCopyAndDispose) { + flag = BLOCK_BYREF_CALLER; + QualType Ty = ND->getType(); + // FIXME. Handle __weak variable (BLOCK_FIELD_IS_WEAK) as well. + if (Ty->isBlockPointerType()) + flag |= BLOCK_FIELD_IS_BLOCK; + else + flag |= BLOCK_FIELD_IS_OBJECT; + std::string HF = SynthesizeByrefCopyDestroyHelper(ND, flag); + if (!HF.empty()) + Preamble += HF; + } + + // struct __Block_byref_ND ND = + // {0, &ND, some_flag, __size=sizeof(struct __Block_byref_ND), + // initializer-if-any}; + bool hasInit = (ND->getInit() != nullptr); + // FIXME. rewriter does not support __block c++ objects which + // require construction. + if (hasInit) + if (CXXConstructExpr *CExp = dyn_cast<CXXConstructExpr>(ND->getInit())) { + CXXConstructorDecl *CXXDecl = CExp->getConstructor(); + if (CXXDecl && CXXDecl->isDefaultConstructor()) + hasInit = false; + } + + unsigned flags = 0; + if (HasCopyAndDispose) + flags |= BLOCK_HAS_COPY_DISPOSE; + Name = ND->getNameAsString(); + ByrefType.clear(); + RewriteByRefString(ByrefType, Name, ND); + std::string ForwardingCastType("("); + ForwardingCastType += ByrefType + " *)"; + ByrefType += " " + Name + " = {(void*)"; + ByrefType += utostr(isa); + ByrefType += "," + ForwardingCastType + "&" + Name + ", "; + ByrefType += utostr(flags); + ByrefType += ", "; + ByrefType += "sizeof("; + RewriteByRefString(ByrefType, Name, ND); + ByrefType += ")"; + if (HasCopyAndDispose) { + ByrefType += ", __Block_byref_id_object_copy_"; + ByrefType += utostr(flag); + ByrefType += ", __Block_byref_id_object_dispose_"; + ByrefType += utostr(flag); + } + + if (!firstDecl) { + // In multiple __block declarations, and for all but 1st declaration, + // find location of the separating comma. This would be start location + // where new text is to be inserted. + DeclLoc = ND->getLocation(); + const char *startDeclBuf = SM->getCharacterData(DeclLoc); + const char *commaBuf = startDeclBuf; + while (*commaBuf != ',') + commaBuf--; + assert((*commaBuf == ',') && "RewriteByRefVar: can't find ','"); + DeclLoc = DeclLoc.getLocWithOffset(commaBuf - startDeclBuf); + startBuf = commaBuf; + } + + if (!hasInit) { + ByrefType += "};\n"; + unsigned nameSize = Name.size(); + // for block or function pointer declaration. Name is already + // part of the declaration. + if (Ty->isBlockPointerType() || Ty->isFunctionPointerType()) + nameSize = 1; + ReplaceText(DeclLoc, endBuf-startBuf+nameSize, ByrefType); + } + else { + ByrefType += ", "; + SourceLocation startLoc; + Expr *E = ND->getInit(); + if (const CStyleCastExpr *ECE = dyn_cast<CStyleCastExpr>(E)) + startLoc = ECE->getLParenLoc(); + else + startLoc = E->getBeginLoc(); + startLoc = SM->getExpansionLoc(startLoc); + endBuf = SM->getCharacterData(startLoc); + ReplaceText(DeclLoc, endBuf-startBuf, ByrefType); + + const char separator = lastDecl ? ';' : ','; + const char *startInitializerBuf = SM->getCharacterData(startLoc); + const char *separatorBuf = strchr(startInitializerBuf, separator); + assert((*separatorBuf == separator) && + "RewriteByRefVar: can't find ';' or ','"); + SourceLocation separatorLoc = + startLoc.getLocWithOffset(separatorBuf-startInitializerBuf); + + InsertText(separatorLoc, lastDecl ? "}" : "};\n"); + } +} + +void RewriteModernObjC::CollectBlockDeclRefInfo(BlockExpr *Exp) { + // Add initializers for any closure decl refs. + GetBlockDeclRefExprs(Exp->getBody()); + if (BlockDeclRefs.size()) { + // Unique all "by copy" declarations. + for (unsigned i = 0; i < BlockDeclRefs.size(); i++) + if (!BlockDeclRefs[i]->getDecl()->hasAttr<BlocksAttr>()) { + if (!BlockByCopyDeclsPtrSet.count(BlockDeclRefs[i]->getDecl())) { + BlockByCopyDeclsPtrSet.insert(BlockDeclRefs[i]->getDecl()); + BlockByCopyDecls.push_back(BlockDeclRefs[i]->getDecl()); + } + } + // Unique all "by ref" declarations. + for (unsigned i = 0; i < BlockDeclRefs.size(); i++) + if (BlockDeclRefs[i]->getDecl()->hasAttr<BlocksAttr>()) { + if (!BlockByRefDeclsPtrSet.count(BlockDeclRefs[i]->getDecl())) { + BlockByRefDeclsPtrSet.insert(BlockDeclRefs[i]->getDecl()); + BlockByRefDecls.push_back(BlockDeclRefs[i]->getDecl()); + } + } + // Find any imported blocks...they will need special attention. + for (unsigned i = 0; i < BlockDeclRefs.size(); i++) + if (BlockDeclRefs[i]->getDecl()->hasAttr<BlocksAttr>() || + BlockDeclRefs[i]->getType()->isObjCObjectPointerType() || + BlockDeclRefs[i]->getType()->isBlockPointerType()) + ImportedBlockDecls.insert(BlockDeclRefs[i]->getDecl()); + } +} + +FunctionDecl *RewriteModernObjC::SynthBlockInitFunctionDecl(StringRef name) { + IdentifierInfo *ID = &Context->Idents.get(name); + QualType FType = Context->getFunctionNoProtoType(Context->VoidPtrTy); + return FunctionDecl::Create(*Context, TUDecl, SourceLocation(), + SourceLocation(), ID, FType, nullptr, SC_Extern, + false, false); +} + +Stmt *RewriteModernObjC::SynthBlockInitExpr(BlockExpr *Exp, + const SmallVectorImpl<DeclRefExpr *> &InnerBlockDeclRefs) { + const BlockDecl *block = Exp->getBlockDecl(); + + Blocks.push_back(Exp); + + CollectBlockDeclRefInfo(Exp); + + // Add inner imported variables now used in current block. + int countOfInnerDecls = 0; + if (!InnerBlockDeclRefs.empty()) { + for (unsigned i = 0; i < InnerBlockDeclRefs.size(); i++) { + DeclRefExpr *Exp = InnerBlockDeclRefs[i]; + ValueDecl *VD = Exp->getDecl(); + if (!VD->hasAttr<BlocksAttr>() && !BlockByCopyDeclsPtrSet.count(VD)) { + // We need to save the copied-in variables in nested + // blocks because it is needed at the end for some of the API generations. + // See SynthesizeBlockLiterals routine. + InnerDeclRefs.push_back(Exp); countOfInnerDecls++; + BlockDeclRefs.push_back(Exp); + BlockByCopyDeclsPtrSet.insert(VD); + BlockByCopyDecls.push_back(VD); + } + if (VD->hasAttr<BlocksAttr>() && !BlockByRefDeclsPtrSet.count(VD)) { + InnerDeclRefs.push_back(Exp); countOfInnerDecls++; + BlockDeclRefs.push_back(Exp); + BlockByRefDeclsPtrSet.insert(VD); + BlockByRefDecls.push_back(VD); + } + } + // Find any imported blocks...they will need special attention. + for (unsigned i = 0; i < InnerBlockDeclRefs.size(); i++) + if (InnerBlockDeclRefs[i]->getDecl()->hasAttr<BlocksAttr>() || + InnerBlockDeclRefs[i]->getType()->isObjCObjectPointerType() || + InnerBlockDeclRefs[i]->getType()->isBlockPointerType()) + ImportedBlockDecls.insert(InnerBlockDeclRefs[i]->getDecl()); + } + InnerDeclRefsCount.push_back(countOfInnerDecls); + + std::string FuncName; + + if (CurFunctionDef) + FuncName = CurFunctionDef->getNameAsString(); + else if (CurMethodDef) + BuildUniqueMethodName(FuncName, CurMethodDef); + else if (GlobalVarDecl) + FuncName = std::string(GlobalVarDecl->getNameAsString()); + + bool GlobalBlockExpr = + block->getDeclContext()->getRedeclContext()->isFileContext(); + + if (GlobalBlockExpr && !GlobalVarDecl) { + Diags.Report(block->getLocation(), GlobalBlockRewriteFailedDiag); + GlobalBlockExpr = false; + } + + std::string BlockNumber = utostr(Blocks.size()-1); + + std::string Func = "__" + FuncName + "_block_func_" + BlockNumber; + + // Get a pointer to the function type so we can cast appropriately. + QualType BFT = convertFunctionTypeOfBlocks(Exp->getFunctionType()); + QualType FType = Context->getPointerType(BFT); + + FunctionDecl *FD; + Expr *NewRep; + + // Simulate a constructor call... + std::string Tag; + + if (GlobalBlockExpr) + Tag = "__global_"; + else + Tag = "__"; + Tag += FuncName + "_block_impl_" + BlockNumber; + + FD = SynthBlockInitFunctionDecl(Tag); + DeclRefExpr *DRE = new (Context) + DeclRefExpr(*Context, FD, false, FType, VK_RValue, SourceLocation()); + + SmallVector<Expr*, 4> InitExprs; + + // Initialize the block function. + FD = SynthBlockInitFunctionDecl(Func); + DeclRefExpr *Arg = new (Context) DeclRefExpr( + *Context, FD, false, FD->getType(), VK_LValue, SourceLocation()); + CastExpr *castExpr = NoTypeInfoCStyleCastExpr(Context, Context->VoidPtrTy, + CK_BitCast, Arg); + InitExprs.push_back(castExpr); + + // Initialize the block descriptor. + std::string DescData = "__" + FuncName + "_block_desc_" + BlockNumber + "_DATA"; + + VarDecl *NewVD = VarDecl::Create( + *Context, TUDecl, SourceLocation(), SourceLocation(), + &Context->Idents.get(DescData), Context->VoidPtrTy, nullptr, SC_Static); + UnaryOperator *DescRefExpr = new (Context) UnaryOperator( + new (Context) DeclRefExpr(*Context, NewVD, false, Context->VoidPtrTy, + VK_LValue, SourceLocation()), + UO_AddrOf, Context->getPointerType(Context->VoidPtrTy), VK_RValue, + OK_Ordinary, SourceLocation(), false); + InitExprs.push_back(DescRefExpr); + + // Add initializers for any closure decl refs. + if (BlockDeclRefs.size()) { + Expr *Exp; + // Output all "by copy" declarations. + for (SmallVectorImpl<ValueDecl *>::iterator I = BlockByCopyDecls.begin(), + E = BlockByCopyDecls.end(); I != E; ++I) { + if (isObjCType((*I)->getType())) { + // FIXME: Conform to ABI ([[obj retain] autorelease]). + FD = SynthBlockInitFunctionDecl((*I)->getName()); + Exp = new (Context) DeclRefExpr(*Context, FD, false, FD->getType(), + VK_LValue, SourceLocation()); + if (HasLocalVariableExternalStorage(*I)) { + QualType QT = (*I)->getType(); + QT = Context->getPointerType(QT); + Exp = new (Context) UnaryOperator(Exp, UO_AddrOf, QT, VK_RValue, + OK_Ordinary, SourceLocation(), + false); + } + } else if (isTopLevelBlockPointerType((*I)->getType())) { + FD = SynthBlockInitFunctionDecl((*I)->getName()); + Arg = new (Context) DeclRefExpr(*Context, FD, false, FD->getType(), + VK_LValue, SourceLocation()); + Exp = NoTypeInfoCStyleCastExpr(Context, Context->VoidPtrTy, + CK_BitCast, Arg); + } else { + FD = SynthBlockInitFunctionDecl((*I)->getName()); + Exp = new (Context) DeclRefExpr(*Context, FD, false, FD->getType(), + VK_LValue, SourceLocation()); + if (HasLocalVariableExternalStorage(*I)) { + QualType QT = (*I)->getType(); + QT = Context->getPointerType(QT); + Exp = new (Context) UnaryOperator(Exp, UO_AddrOf, QT, VK_RValue, + OK_Ordinary, SourceLocation(), + false); + } + + } + InitExprs.push_back(Exp); + } + // Output all "by ref" declarations. + for (SmallVectorImpl<ValueDecl *>::iterator I = BlockByRefDecls.begin(), + E = BlockByRefDecls.end(); I != E; ++I) { + ValueDecl *ND = (*I); + std::string Name(ND->getNameAsString()); + std::string RecName; + RewriteByRefString(RecName, Name, ND, true); + IdentifierInfo *II = &Context->Idents.get(RecName.c_str() + + sizeof("struct")); + RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), SourceLocation(), + II); + assert(RD && "SynthBlockInitExpr(): Can't find RecordDecl"); + QualType castT = Context->getPointerType(Context->getTagDeclType(RD)); + + FD = SynthBlockInitFunctionDecl((*I)->getName()); + Exp = new (Context) DeclRefExpr(*Context, FD, false, FD->getType(), + VK_LValue, SourceLocation()); + bool isNestedCapturedVar = false; + if (block) + for (const auto &CI : block->captures()) { + const VarDecl *variable = CI.getVariable(); + if (variable == ND && CI.isNested()) { + assert (CI.isByRef() && + "SynthBlockInitExpr - captured block variable is not byref"); + isNestedCapturedVar = true; + break; + } + } + // captured nested byref variable has its address passed. Do not take + // its address again. + if (!isNestedCapturedVar) + Exp = new (Context) UnaryOperator(Exp, UO_AddrOf, + Context->getPointerType(Exp->getType()), + VK_RValue, OK_Ordinary, SourceLocation(), + false); + Exp = NoTypeInfoCStyleCastExpr(Context, castT, CK_BitCast, Exp); + InitExprs.push_back(Exp); + } + } + if (ImportedBlockDecls.size()) { + // generate BLOCK_HAS_COPY_DISPOSE(have helper funcs) | BLOCK_HAS_DESCRIPTOR + int flag = (BLOCK_HAS_COPY_DISPOSE | BLOCK_HAS_DESCRIPTOR); + unsigned IntSize = + static_cast<unsigned>(Context->getTypeSize(Context->IntTy)); + Expr *FlagExp = IntegerLiteral::Create(*Context, llvm::APInt(IntSize, flag), + Context->IntTy, SourceLocation()); + InitExprs.push_back(FlagExp); + } + NewRep = CallExpr::Create(*Context, DRE, InitExprs, FType, VK_LValue, + SourceLocation()); + + if (GlobalBlockExpr) { + assert (!GlobalConstructionExp && + "SynthBlockInitExpr - GlobalConstructionExp must be null"); + GlobalConstructionExp = NewRep; + NewRep = DRE; + } + + NewRep = new (Context) UnaryOperator(NewRep, UO_AddrOf, + Context->getPointerType(NewRep->getType()), + VK_RValue, OK_Ordinary, SourceLocation(), false); + NewRep = NoTypeInfoCStyleCastExpr(Context, FType, CK_BitCast, + NewRep); + // Put Paren around the call. + NewRep = new (Context) ParenExpr(SourceLocation(), SourceLocation(), + NewRep); + + BlockDeclRefs.clear(); + BlockByRefDecls.clear(); + BlockByRefDeclsPtrSet.clear(); + BlockByCopyDecls.clear(); + BlockByCopyDeclsPtrSet.clear(); + ImportedBlockDecls.clear(); + return NewRep; +} + +bool RewriteModernObjC::IsDeclStmtInForeachHeader(DeclStmt *DS) { + if (const ObjCForCollectionStmt * CS = + dyn_cast<ObjCForCollectionStmt>(Stmts.back())) + return CS->getElement() == DS; + return false; +} + +//===----------------------------------------------------------------------===// +// Function Body / Expression rewriting +//===----------------------------------------------------------------------===// + +Stmt *RewriteModernObjC::RewriteFunctionBodyOrGlobalInitializer(Stmt *S) { + if (isa<SwitchStmt>(S) || isa<WhileStmt>(S) || + isa<DoStmt>(S) || isa<ForStmt>(S)) + Stmts.push_back(S); + else if (isa<ObjCForCollectionStmt>(S)) { + Stmts.push_back(S); + ObjCBcLabelNo.push_back(++BcLabelCount); + } + + // Pseudo-object operations and ivar references need special + // treatment because we're going to recursively rewrite them. + if (PseudoObjectExpr *PseudoOp = dyn_cast<PseudoObjectExpr>(S)) { + if (isa<BinaryOperator>(PseudoOp->getSyntacticForm())) { + return RewritePropertyOrImplicitSetter(PseudoOp); + } else { + return RewritePropertyOrImplicitGetter(PseudoOp); + } + } else if (ObjCIvarRefExpr *IvarRefExpr = dyn_cast<ObjCIvarRefExpr>(S)) { + return RewriteObjCIvarRefExpr(IvarRefExpr); + } + else if (isa<OpaqueValueExpr>(S)) + S = cast<OpaqueValueExpr>(S)->getSourceExpr(); + + SourceRange OrigStmtRange = S->getSourceRange(); + + // Perform a bottom up rewrite of all children. + for (Stmt *&childStmt : S->children()) + if (childStmt) { + Stmt *newStmt = RewriteFunctionBodyOrGlobalInitializer(childStmt); + if (newStmt) { + childStmt = newStmt; + } + } + + if (BlockExpr *BE = dyn_cast<BlockExpr>(S)) { + SmallVector<DeclRefExpr *, 8> InnerBlockDeclRefs; + llvm::SmallPtrSet<const DeclContext *, 8> InnerContexts; + InnerContexts.insert(BE->getBlockDecl()); + ImportedLocalExternalDecls.clear(); + GetInnerBlockDeclRefExprs(BE->getBody(), + InnerBlockDeclRefs, InnerContexts); + // Rewrite the block body in place. + Stmt *SaveCurrentBody = CurrentBody; + CurrentBody = BE->getBody(); + PropParentMap = nullptr; + // block literal on rhs of a property-dot-sytax assignment + // must be replaced by its synthesize ast so getRewrittenText + // works as expected. In this case, what actually ends up on RHS + // is the blockTranscribed which is the helper function for the + // block literal; as in: self.c = ^() {[ace ARR];}; + bool saveDisableReplaceStmt = DisableReplaceStmt; + DisableReplaceStmt = false; + RewriteFunctionBodyOrGlobalInitializer(BE->getBody()); + DisableReplaceStmt = saveDisableReplaceStmt; + CurrentBody = SaveCurrentBody; + PropParentMap = nullptr; + ImportedLocalExternalDecls.clear(); + // Now we snarf the rewritten text and stash it away for later use. + std::string Str = Rewrite.getRewrittenText(BE->getSourceRange()); + RewrittenBlockExprs[BE] = Str; + + Stmt *blockTranscribed = SynthBlockInitExpr(BE, InnerBlockDeclRefs); + + //blockTranscribed->dump(); + ReplaceStmt(S, blockTranscribed); + return blockTranscribed; + } + // Handle specific things. + if (ObjCEncodeExpr *AtEncode = dyn_cast<ObjCEncodeExpr>(S)) + return RewriteAtEncode(AtEncode); + + if (ObjCSelectorExpr *AtSelector = dyn_cast<ObjCSelectorExpr>(S)) + return RewriteAtSelector(AtSelector); + + if (ObjCStringLiteral *AtString = dyn_cast<ObjCStringLiteral>(S)) + return RewriteObjCStringLiteral(AtString); + + if (ObjCBoolLiteralExpr *BoolLitExpr = dyn_cast<ObjCBoolLiteralExpr>(S)) + return RewriteObjCBoolLiteralExpr(BoolLitExpr); + + if (ObjCBoxedExpr *BoxedExpr = dyn_cast<ObjCBoxedExpr>(S)) + return RewriteObjCBoxedExpr(BoxedExpr); + + if (ObjCArrayLiteral *ArrayLitExpr = dyn_cast<ObjCArrayLiteral>(S)) + return RewriteObjCArrayLiteralExpr(ArrayLitExpr); + + if (ObjCDictionaryLiteral *DictionaryLitExpr = + dyn_cast<ObjCDictionaryLiteral>(S)) + return RewriteObjCDictionaryLiteralExpr(DictionaryLitExpr); + + if (ObjCMessageExpr *MessExpr = dyn_cast<ObjCMessageExpr>(S)) { +#if 0 + // Before we rewrite it, put the original message expression in a comment. + SourceLocation startLoc = MessExpr->getBeginLoc(); + SourceLocation endLoc = MessExpr->getEndLoc(); + + const char *startBuf = SM->getCharacterData(startLoc); + const char *endBuf = SM->getCharacterData(endLoc); + + std::string messString; + messString += "// "; + messString.append(startBuf, endBuf-startBuf+1); + messString += "\n"; + + // FIXME: Missing definition of + // InsertText(clang::SourceLocation, char const*, unsigned int). + // InsertText(startLoc, messString); + // Tried this, but it didn't work either... + // ReplaceText(startLoc, 0, messString.c_str(), messString.size()); +#endif + return RewriteMessageExpr(MessExpr); + } + + if (ObjCAutoreleasePoolStmt *StmtAutoRelease = + dyn_cast<ObjCAutoreleasePoolStmt>(S)) { + return RewriteObjCAutoreleasePoolStmt(StmtAutoRelease); + } + + if (ObjCAtTryStmt *StmtTry = dyn_cast<ObjCAtTryStmt>(S)) + return RewriteObjCTryStmt(StmtTry); + + if (ObjCAtSynchronizedStmt *StmtTry = dyn_cast<ObjCAtSynchronizedStmt>(S)) + return RewriteObjCSynchronizedStmt(StmtTry); + + if (ObjCAtThrowStmt *StmtThrow = dyn_cast<ObjCAtThrowStmt>(S)) + return RewriteObjCThrowStmt(StmtThrow); + + if (ObjCProtocolExpr *ProtocolExp = dyn_cast<ObjCProtocolExpr>(S)) + return RewriteObjCProtocolExpr(ProtocolExp); + + if (ObjCForCollectionStmt *StmtForCollection = + dyn_cast<ObjCForCollectionStmt>(S)) + return RewriteObjCForCollectionStmt(StmtForCollection, + OrigStmtRange.getEnd()); + if (BreakStmt *StmtBreakStmt = + dyn_cast<BreakStmt>(S)) + return RewriteBreakStmt(StmtBreakStmt); + if (ContinueStmt *StmtContinueStmt = + dyn_cast<ContinueStmt>(S)) + return RewriteContinueStmt(StmtContinueStmt); + + // Need to check for protocol refs (id <P>, Foo <P> *) in variable decls + // and cast exprs. + if (DeclStmt *DS = dyn_cast<DeclStmt>(S)) { + // FIXME: What we're doing here is modifying the type-specifier that + // precedes the first Decl. In the future the DeclGroup should have + // a separate type-specifier that we can rewrite. + // NOTE: We need to avoid rewriting the DeclStmt if it is within + // the context of an ObjCForCollectionStmt. For example: + // NSArray *someArray; + // for (id <FooProtocol> index in someArray) ; + // This is because RewriteObjCForCollectionStmt() does textual rewriting + // and it depends on the original text locations/positions. + if (Stmts.empty() || !IsDeclStmtInForeachHeader(DS)) + RewriteObjCQualifiedInterfaceTypes(*DS->decl_begin()); + + // Blocks rewrite rules. + for (DeclStmt::decl_iterator DI = DS->decl_begin(), DE = DS->decl_end(); + DI != DE; ++DI) { + Decl *SD = *DI; + if (ValueDecl *ND = dyn_cast<ValueDecl>(SD)) { + if (isTopLevelBlockPointerType(ND->getType())) + RewriteBlockPointerDecl(ND); + else if (ND->getType()->isFunctionPointerType()) + CheckFunctionPointerDecl(ND->getType(), ND); + if (VarDecl *VD = dyn_cast<VarDecl>(SD)) { + if (VD->hasAttr<BlocksAttr>()) { + static unsigned uniqueByrefDeclCount = 0; + assert(!BlockByRefDeclNo.count(ND) && + "RewriteFunctionBodyOrGlobalInitializer: Duplicate byref decl"); + BlockByRefDeclNo[ND] = uniqueByrefDeclCount++; + RewriteByRefVar(VD, (DI == DS->decl_begin()), ((DI+1) == DE)); + } + else + RewriteTypeOfDecl(VD); + } + } + if (TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(SD)) { + if (isTopLevelBlockPointerType(TD->getUnderlyingType())) + RewriteBlockPointerDecl(TD); + else if (TD->getUnderlyingType()->isFunctionPointerType()) + CheckFunctionPointerDecl(TD->getUnderlyingType(), TD); + } + } + } + + if (CStyleCastExpr *CE = dyn_cast<CStyleCastExpr>(S)) + RewriteObjCQualifiedInterfaceTypes(CE); + + if (isa<SwitchStmt>(S) || isa<WhileStmt>(S) || + isa<DoStmt>(S) || isa<ForStmt>(S)) { + assert(!Stmts.empty() && "Statement stack is empty"); + assert ((isa<SwitchStmt>(Stmts.back()) || isa<WhileStmt>(Stmts.back()) || + isa<DoStmt>(Stmts.back()) || isa<ForStmt>(Stmts.back())) + && "Statement stack mismatch"); + Stmts.pop_back(); + } + // Handle blocks rewriting. + if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(S)) { + ValueDecl *VD = DRE->getDecl(); + if (VD->hasAttr<BlocksAttr>()) + return RewriteBlockDeclRefExpr(DRE); + if (HasLocalVariableExternalStorage(VD)) + return RewriteLocalVariableExternalStorage(DRE); + } + + if (CallExpr *CE = dyn_cast<CallExpr>(S)) { + if (CE->getCallee()->getType()->isBlockPointerType()) { + Stmt *BlockCall = SynthesizeBlockCall(CE, CE->getCallee()); + ReplaceStmt(S, BlockCall); + return BlockCall; + } + } + if (CStyleCastExpr *CE = dyn_cast<CStyleCastExpr>(S)) { + RewriteCastExpr(CE); + } + if (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(S)) { + RewriteImplicitCastObjCExpr(ICE); + } +#if 0 + + if (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(S)) { + CastExpr *Replacement = new (Context) CastExpr(ICE->getType(), + ICE->getSubExpr(), + SourceLocation()); + // Get the new text. + std::string SStr; + llvm::raw_string_ostream Buf(SStr); + Replacement->printPretty(Buf); + const std::string &Str = Buf.str(); + + printf("CAST = %s\n", &Str[0]); + InsertText(ICE->getSubExpr()->getBeginLoc(), Str); + delete S; + return Replacement; + } +#endif + // Return this stmt unmodified. + return S; +} + +void RewriteModernObjC::RewriteRecordBody(RecordDecl *RD) { + for (auto *FD : RD->fields()) { + if (isTopLevelBlockPointerType(FD->getType())) + RewriteBlockPointerDecl(FD); + if (FD->getType()->isObjCQualifiedIdType() || + FD->getType()->isObjCQualifiedInterfaceType()) + RewriteObjCQualifiedInterfaceTypes(FD); + } +} + +/// HandleDeclInMainFile - This is called for each top-level decl defined in the +/// main file of the input. +void RewriteModernObjC::HandleDeclInMainFile(Decl *D) { + switch (D->getKind()) { + case Decl::Function: { + FunctionDecl *FD = cast<FunctionDecl>(D); + if (FD->isOverloadedOperator()) + return; + + // Since function prototypes don't have ParmDecl's, we check the function + // prototype. This enables us to rewrite function declarations and + // definitions using the same code. + RewriteBlocksInFunctionProtoType(FD->getType(), FD); + + if (!FD->isThisDeclarationADefinition()) + break; + + // FIXME: If this should support Obj-C++, support CXXTryStmt + if (CompoundStmt *Body = dyn_cast_or_null<CompoundStmt>(FD->getBody())) { + CurFunctionDef = FD; + CurrentBody = Body; + Body = + cast_or_null<CompoundStmt>(RewriteFunctionBodyOrGlobalInitializer(Body)); + FD->setBody(Body); + CurrentBody = nullptr; + if (PropParentMap) { + delete PropParentMap; + PropParentMap = nullptr; + } + // This synthesizes and inserts the block "impl" struct, invoke function, + // and any copy/dispose helper functions. + InsertBlockLiteralsWithinFunction(FD); + RewriteLineDirective(D); + CurFunctionDef = nullptr; + } + break; + } + case Decl::ObjCMethod: { + ObjCMethodDecl *MD = cast<ObjCMethodDecl>(D); + if (CompoundStmt *Body = MD->getCompoundBody()) { + CurMethodDef = MD; + CurrentBody = Body; + Body = + cast_or_null<CompoundStmt>(RewriteFunctionBodyOrGlobalInitializer(Body)); + MD->setBody(Body); + CurrentBody = nullptr; + if (PropParentMap) { + delete PropParentMap; + PropParentMap = nullptr; + } + InsertBlockLiteralsWithinMethod(MD); + RewriteLineDirective(D); + CurMethodDef = nullptr; + } + break; + } + case Decl::ObjCImplementation: { + ObjCImplementationDecl *CI = cast<ObjCImplementationDecl>(D); + ClassImplementation.push_back(CI); + break; + } + case Decl::ObjCCategoryImpl: { + ObjCCategoryImplDecl *CI = cast<ObjCCategoryImplDecl>(D); + CategoryImplementation.push_back(CI); + break; + } + case Decl::Var: { + VarDecl *VD = cast<VarDecl>(D); + RewriteObjCQualifiedInterfaceTypes(VD); + if (isTopLevelBlockPointerType(VD->getType())) + RewriteBlockPointerDecl(VD); + else if (VD->getType()->isFunctionPointerType()) { + CheckFunctionPointerDecl(VD->getType(), VD); + if (VD->getInit()) { + if (CStyleCastExpr *CE = dyn_cast<CStyleCastExpr>(VD->getInit())) { + RewriteCastExpr(CE); + } + } + } else if (VD->getType()->isRecordType()) { + RecordDecl *RD = VD->getType()->getAs<RecordType>()->getDecl(); + if (RD->isCompleteDefinition()) + RewriteRecordBody(RD); + } + if (VD->getInit()) { + GlobalVarDecl = VD; + CurrentBody = VD->getInit(); + RewriteFunctionBodyOrGlobalInitializer(VD->getInit()); + CurrentBody = nullptr; + if (PropParentMap) { + delete PropParentMap; + PropParentMap = nullptr; + } + SynthesizeBlockLiterals(VD->getTypeSpecStartLoc(), VD->getName()); + GlobalVarDecl = nullptr; + + // This is needed for blocks. + if (CStyleCastExpr *CE = dyn_cast<CStyleCastExpr>(VD->getInit())) { + RewriteCastExpr(CE); + } + } + break; + } + case Decl::TypeAlias: + case Decl::Typedef: { + if (TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(D)) { + if (isTopLevelBlockPointerType(TD->getUnderlyingType())) + RewriteBlockPointerDecl(TD); + else if (TD->getUnderlyingType()->isFunctionPointerType()) + CheckFunctionPointerDecl(TD->getUnderlyingType(), TD); + else + RewriteObjCQualifiedInterfaceTypes(TD); + } + break; + } + case Decl::CXXRecord: + case Decl::Record: { + RecordDecl *RD = cast<RecordDecl>(D); + if (RD->isCompleteDefinition()) + RewriteRecordBody(RD); + break; + } + default: + break; + } + // Nothing yet. +} + +/// Write_ProtocolExprReferencedMetadata - This routine writer out the +/// protocol reference symbols in the for of: +/// struct _protocol_t *PROTOCOL_REF = &PROTOCOL_METADATA. +static void Write_ProtocolExprReferencedMetadata(ASTContext *Context, + ObjCProtocolDecl *PDecl, + std::string &Result) { + // Also output .objc_protorefs$B section and its meta-data. + if (Context->getLangOpts().MicrosoftExt) + Result += "static "; + Result += "struct _protocol_t *"; + Result += "_OBJC_PROTOCOL_REFERENCE_$_"; + Result += PDecl->getNameAsString(); + Result += " = &"; + Result += "_OBJC_PROTOCOL_"; Result += PDecl->getNameAsString(); + Result += ";\n"; +} + +void RewriteModernObjC::HandleTranslationUnit(ASTContext &C) { + if (Diags.hasErrorOccurred()) + return; + + RewriteInclude(); + + for (unsigned i = 0, e = FunctionDefinitionsSeen.size(); i < e; i++) { + // translation of function bodies were postponed until all class and + // their extensions and implementations are seen. This is because, we + // cannot build grouping structs for bitfields until they are all seen. + FunctionDecl *FDecl = FunctionDefinitionsSeen[i]; + HandleTopLevelSingleDecl(FDecl); + } + + // Here's a great place to add any extra declarations that may be needed. + // Write out meta data for each @protocol(<expr>). + for (ObjCProtocolDecl *ProtDecl : ProtocolExprDecls) { + RewriteObjCProtocolMetaData(ProtDecl, Preamble); + Write_ProtocolExprReferencedMetadata(Context, ProtDecl, Preamble); + } + + InsertText(SM->getLocForStartOfFile(MainFileID), Preamble, false); + + if (ClassImplementation.size() || CategoryImplementation.size()) + RewriteImplementations(); + + for (unsigned i = 0, e = ObjCInterfacesSeen.size(); i < e; i++) { + ObjCInterfaceDecl *CDecl = ObjCInterfacesSeen[i]; + // Write struct declaration for the class matching its ivar declarations. + // Note that for modern abi, this is postponed until the end of TU + // because class extensions and the implementation might declare their own + // private ivars. + RewriteInterfaceDecl(CDecl); + } + + // Get the buffer corresponding to MainFileID. If we haven't changed it, then + // we are done. + if (const RewriteBuffer *RewriteBuf = + Rewrite.getRewriteBufferFor(MainFileID)) { + //printf("Changed:\n"); + *OutFile << std::string(RewriteBuf->begin(), RewriteBuf->end()); + } else { + llvm::errs() << "No changes\n"; + } + + if (ClassImplementation.size() || CategoryImplementation.size() || + ProtocolExprDecls.size()) { + // Rewrite Objective-c meta data* + std::string ResultStr; + RewriteMetaDataIntoBuffer(ResultStr); + // Emit metadata. + *OutFile << ResultStr; + } + // Emit ImageInfo; + { + std::string ResultStr; + WriteImageInfo(ResultStr); + *OutFile << ResultStr; + } + OutFile->flush(); +} + +void RewriteModernObjC::Initialize(ASTContext &context) { + InitializeCommon(context); + + Preamble += "#ifndef __OBJC2__\n"; + Preamble += "#define __OBJC2__\n"; + Preamble += "#endif\n"; + + // declaring objc_selector outside the parameter list removes a silly + // scope related warning... + if (IsHeader) + Preamble = "#pragma once\n"; + Preamble += "struct objc_selector; struct objc_class;\n"; + Preamble += "struct __rw_objc_super { \n\tstruct objc_object *object; "; + Preamble += "\n\tstruct objc_object *superClass; "; + // Add a constructor for creating temporary objects. + Preamble += "\n\t__rw_objc_super(struct objc_object *o, struct objc_object *s) "; + Preamble += ": object(o), superClass(s) {} "; + Preamble += "\n};\n"; + + if (LangOpts.MicrosoftExt) { + // Define all sections using syntax that makes sense. + // These are currently generated. + Preamble += "\n#pragma section(\".objc_classlist$B\", long, read, write)\n"; + Preamble += "#pragma section(\".objc_catlist$B\", long, read, write)\n"; + Preamble += "#pragma section(\".objc_imageinfo$B\", long, read, write)\n"; + Preamble += "#pragma section(\".objc_nlclslist$B\", long, read, write)\n"; + Preamble += "#pragma section(\".objc_nlcatlist$B\", long, read, write)\n"; + // These are generated but not necessary for functionality. + Preamble += "#pragma section(\".cat_cls_meth$B\", long, read, write)\n"; + Preamble += "#pragma section(\".inst_meth$B\", long, read, write)\n"; + Preamble += "#pragma section(\".cls_meth$B\", long, read, write)\n"; + Preamble += "#pragma section(\".objc_ivar$B\", long, read, write)\n"; + + // These need be generated for performance. Currently they are not, + // using API calls instead. + Preamble += "#pragma section(\".objc_selrefs$B\", long, read, write)\n"; + Preamble += "#pragma section(\".objc_classrefs$B\", long, read, write)\n"; + Preamble += "#pragma section(\".objc_superrefs$B\", long, read, write)\n"; + + } + Preamble += "#ifndef _REWRITER_typedef_Protocol\n"; + Preamble += "typedef struct objc_object Protocol;\n"; + Preamble += "#define _REWRITER_typedef_Protocol\n"; + Preamble += "#endif\n"; + if (LangOpts.MicrosoftExt) { + Preamble += "#define __OBJC_RW_DLLIMPORT extern \"C\" __declspec(dllimport)\n"; + Preamble += "#define __OBJC_RW_STATICIMPORT extern \"C\"\n"; + } + else + Preamble += "#define __OBJC_RW_DLLIMPORT extern\n"; + + Preamble += "__OBJC_RW_DLLIMPORT void objc_msgSend(void);\n"; + Preamble += "__OBJC_RW_DLLIMPORT void objc_msgSendSuper(void);\n"; + Preamble += "__OBJC_RW_DLLIMPORT void objc_msgSend_stret(void);\n"; + Preamble += "__OBJC_RW_DLLIMPORT void objc_msgSendSuper_stret(void);\n"; + Preamble += "__OBJC_RW_DLLIMPORT void objc_msgSend_fpret(void);\n"; + + Preamble += "__OBJC_RW_DLLIMPORT struct objc_class *objc_getClass"; + Preamble += "(const char *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT struct objc_class *class_getSuperclass"; + Preamble += "(struct objc_class *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT struct objc_class *objc_getMetaClass"; + Preamble += "(const char *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT void objc_exception_throw( struct objc_object *);\n"; + // @synchronized hooks. + Preamble += "__OBJC_RW_DLLIMPORT int objc_sync_enter( struct objc_object *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT int objc_sync_exit( struct objc_object *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT Protocol *objc_getProtocol(const char *);\n"; + Preamble += "#ifdef _WIN64\n"; + Preamble += "typedef unsigned long long _WIN_NSUInteger;\n"; + Preamble += "#else\n"; + Preamble += "typedef unsigned int _WIN_NSUInteger;\n"; + Preamble += "#endif\n"; + Preamble += "#ifndef __FASTENUMERATIONSTATE\n"; + Preamble += "struct __objcFastEnumerationState {\n\t"; + Preamble += "unsigned long state;\n\t"; + Preamble += "void **itemsPtr;\n\t"; + Preamble += "unsigned long *mutationsPtr;\n\t"; + Preamble += "unsigned long extra[5];\n};\n"; + Preamble += "__OBJC_RW_DLLIMPORT void objc_enumerationMutation(struct objc_object *);\n"; + Preamble += "#define __FASTENUMERATIONSTATE\n"; + Preamble += "#endif\n"; + Preamble += "#ifndef __NSCONSTANTSTRINGIMPL\n"; + Preamble += "struct __NSConstantStringImpl {\n"; + Preamble += " int *isa;\n"; + Preamble += " int flags;\n"; + Preamble += " char *str;\n"; + Preamble += "#if _WIN64\n"; + Preamble += " long long length;\n"; + Preamble += "#else\n"; + Preamble += " long length;\n"; + Preamble += "#endif\n"; + Preamble += "};\n"; + Preamble += "#ifdef CF_EXPORT_CONSTANT_STRING\n"; + Preamble += "extern \"C\" __declspec(dllexport) int __CFConstantStringClassReference[];\n"; + Preamble += "#else\n"; + Preamble += "__OBJC_RW_DLLIMPORT int __CFConstantStringClassReference[];\n"; + Preamble += "#endif\n"; + Preamble += "#define __NSCONSTANTSTRINGIMPL\n"; + Preamble += "#endif\n"; + // Blocks preamble. + Preamble += "#ifndef BLOCK_IMPL\n"; + Preamble += "#define BLOCK_IMPL\n"; + Preamble += "struct __block_impl {\n"; + Preamble += " void *isa;\n"; + Preamble += " int Flags;\n"; + Preamble += " int Reserved;\n"; + Preamble += " void *FuncPtr;\n"; + Preamble += "};\n"; + Preamble += "// Runtime copy/destroy helper functions (from Block_private.h)\n"; + Preamble += "#ifdef __OBJC_EXPORT_BLOCKS\n"; + Preamble += "extern \"C\" __declspec(dllexport) " + "void _Block_object_assign(void *, const void *, const int);\n"; + Preamble += "extern \"C\" __declspec(dllexport) void _Block_object_dispose(const void *, const int);\n"; + Preamble += "extern \"C\" __declspec(dllexport) void *_NSConcreteGlobalBlock[32];\n"; + Preamble += "extern \"C\" __declspec(dllexport) void *_NSConcreteStackBlock[32];\n"; + Preamble += "#else\n"; + Preamble += "__OBJC_RW_DLLIMPORT void _Block_object_assign(void *, const void *, const int);\n"; + Preamble += "__OBJC_RW_DLLIMPORT void _Block_object_dispose(const void *, const int);\n"; + Preamble += "__OBJC_RW_DLLIMPORT void *_NSConcreteGlobalBlock[32];\n"; + Preamble += "__OBJC_RW_DLLIMPORT void *_NSConcreteStackBlock[32];\n"; + Preamble += "#endif\n"; + Preamble += "#endif\n"; + if (LangOpts.MicrosoftExt) { + Preamble += "#undef __OBJC_RW_DLLIMPORT\n"; + Preamble += "#undef __OBJC_RW_STATICIMPORT\n"; + Preamble += "#ifndef KEEP_ATTRIBUTES\n"; // We use this for clang tests. + Preamble += "#define __attribute__(X)\n"; + Preamble += "#endif\n"; + Preamble += "#ifndef __weak\n"; + Preamble += "#define __weak\n"; + Preamble += "#endif\n"; + Preamble += "#ifndef __block\n"; + Preamble += "#define __block\n"; + Preamble += "#endif\n"; + } + else { + Preamble += "#define __block\n"; + Preamble += "#define __weak\n"; + } + + // Declarations required for modern objective-c array and dictionary literals. + Preamble += "\n#include <stdarg.h>\n"; + Preamble += "struct __NSContainer_literal {\n"; + Preamble += " void * *arr;\n"; + Preamble += " __NSContainer_literal (unsigned int count, ...) {\n"; + Preamble += "\tva_list marker;\n"; + Preamble += "\tva_start(marker, count);\n"; + Preamble += "\tarr = new void *[count];\n"; + Preamble += "\tfor (unsigned i = 0; i < count; i++)\n"; + Preamble += "\t arr[i] = va_arg(marker, void *);\n"; + Preamble += "\tva_end( marker );\n"; + Preamble += " };\n"; + Preamble += " ~__NSContainer_literal() {\n"; + Preamble += "\tdelete[] arr;\n"; + Preamble += " }\n"; + Preamble += "};\n"; + + // Declaration required for implementation of @autoreleasepool statement. + Preamble += "extern \"C\" __declspec(dllimport) void * objc_autoreleasePoolPush(void);\n"; + Preamble += "extern \"C\" __declspec(dllimport) void objc_autoreleasePoolPop(void *);\n\n"; + Preamble += "struct __AtAutoreleasePool {\n"; + Preamble += " __AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}\n"; + Preamble += " ~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}\n"; + Preamble += " void * atautoreleasepoolobj;\n"; + Preamble += "};\n"; + + // NOTE! Windows uses LLP64 for 64bit mode. So, cast pointer to long long + // as this avoids warning in any 64bit/32bit compilation model. + Preamble += "\n#define __OFFSETOFIVAR__(TYPE, MEMBER) ((long long) &((TYPE *)0)->MEMBER)\n"; +} + +/// RewriteIvarOffsetComputation - This routine synthesizes computation of +/// ivar offset. +void RewriteModernObjC::RewriteIvarOffsetComputation(ObjCIvarDecl *ivar, + std::string &Result) { + Result += "__OFFSETOFIVAR__(struct "; + Result += ivar->getContainingInterface()->getNameAsString(); + if (LangOpts.MicrosoftExt) + Result += "_IMPL"; + Result += ", "; + if (ivar->isBitField()) + ObjCIvarBitfieldGroupDecl(ivar, Result); + else + Result += ivar->getNameAsString(); + Result += ")"; +} + +/// WriteModernMetadataDeclarations - Writes out metadata declarations for modern ABI. +/// struct _prop_t { +/// const char *name; +/// char *attributes; +/// } + +/// struct _prop_list_t { +/// uint32_t entsize; // sizeof(struct _prop_t) +/// uint32_t count_of_properties; +/// struct _prop_t prop_list[count_of_properties]; +/// } + +/// struct _protocol_t; + +/// struct _protocol_list_t { +/// long protocol_count; // Note, this is 32/64 bit +/// struct _protocol_t * protocol_list[protocol_count]; +/// } + +/// struct _objc_method { +/// SEL _cmd; +/// const char *method_type; +/// char *_imp; +/// } + +/// struct _method_list_t { +/// uint32_t entsize; // sizeof(struct _objc_method) +/// uint32_t method_count; +/// struct _objc_method method_list[method_count]; +/// } + +/// struct _protocol_t { +/// id isa; // NULL +/// const char *protocol_name; +/// const struct _protocol_list_t * protocol_list; // super protocols +/// const struct method_list_t *instance_methods; +/// const struct method_list_t *class_methods; +/// const struct method_list_t *optionalInstanceMethods; +/// const struct method_list_t *optionalClassMethods; +/// const struct _prop_list_t * properties; +/// const uint32_t size; // sizeof(struct _protocol_t) +/// const uint32_t flags; // = 0 +/// const char ** extendedMethodTypes; +/// } + +/// struct _ivar_t { +/// unsigned long int *offset; // pointer to ivar offset location +/// const char *name; +/// const char *type; +/// uint32_t alignment; +/// uint32_t size; +/// } + +/// struct _ivar_list_t { +/// uint32 entsize; // sizeof(struct _ivar_t) +/// uint32 count; +/// struct _ivar_t list[count]; +/// } + +/// struct _class_ro_t { +/// uint32_t flags; +/// uint32_t instanceStart; +/// uint32_t instanceSize; +/// uint32_t reserved; // only when building for 64bit targets +/// const uint8_t *ivarLayout; +/// const char *name; +/// const struct _method_list_t *baseMethods; +/// const struct _protocol_list_t *baseProtocols; +/// const struct _ivar_list_t *ivars; +/// const uint8_t *weakIvarLayout; +/// const struct _prop_list_t *properties; +/// } + +/// struct _class_t { +/// struct _class_t *isa; +/// struct _class_t *superclass; +/// void *cache; +/// IMP *vtable; +/// struct _class_ro_t *ro; +/// } + +/// struct _category_t { +/// const char *name; +/// struct _class_t *cls; +/// const struct _method_list_t *instance_methods; +/// const struct _method_list_t *class_methods; +/// const struct _protocol_list_t *protocols; +/// const struct _prop_list_t *properties; +/// } + +/// MessageRefTy - LLVM for: +/// struct _message_ref_t { +/// IMP messenger; +/// SEL name; +/// }; + +/// SuperMessageRefTy - LLVM for: +/// struct _super_message_ref_t { +/// SUPER_IMP messenger; +/// SEL name; +/// }; + +static void WriteModernMetadataDeclarations(ASTContext *Context, std::string &Result) { + static bool meta_data_declared = false; + if (meta_data_declared) + return; + + Result += "\nstruct _prop_t {\n"; + Result += "\tconst char *name;\n"; + Result += "\tconst char *attributes;\n"; + Result += "};\n"; + + Result += "\nstruct _protocol_t;\n"; + + Result += "\nstruct _objc_method {\n"; + Result += "\tstruct objc_selector * _cmd;\n"; + Result += "\tconst char *method_type;\n"; + Result += "\tvoid *_imp;\n"; + Result += "};\n"; + + Result += "\nstruct _protocol_t {\n"; + Result += "\tvoid * isa; // NULL\n"; + Result += "\tconst char *protocol_name;\n"; + Result += "\tconst struct _protocol_list_t * protocol_list; // super protocols\n"; + Result += "\tconst struct method_list_t *instance_methods;\n"; + Result += "\tconst struct method_list_t *class_methods;\n"; + Result += "\tconst struct method_list_t *optionalInstanceMethods;\n"; + Result += "\tconst struct method_list_t *optionalClassMethods;\n"; + Result += "\tconst struct _prop_list_t * properties;\n"; + Result += "\tconst unsigned int size; // sizeof(struct _protocol_t)\n"; + Result += "\tconst unsigned int flags; // = 0\n"; + Result += "\tconst char ** extendedMethodTypes;\n"; + Result += "};\n"; + + Result += "\nstruct _ivar_t {\n"; + Result += "\tunsigned long int *offset; // pointer to ivar offset location\n"; + Result += "\tconst char *name;\n"; + Result += "\tconst char *type;\n"; + Result += "\tunsigned int alignment;\n"; + Result += "\tunsigned int size;\n"; + Result += "};\n"; + + Result += "\nstruct _class_ro_t {\n"; + Result += "\tunsigned int flags;\n"; + Result += "\tunsigned int instanceStart;\n"; + Result += "\tunsigned int instanceSize;\n"; + const llvm::Triple &Triple(Context->getTargetInfo().getTriple()); + if (Triple.getArch() == llvm::Triple::x86_64) + Result += "\tunsigned int reserved;\n"; + Result += "\tconst unsigned char *ivarLayout;\n"; + Result += "\tconst char *name;\n"; + Result += "\tconst struct _method_list_t *baseMethods;\n"; + Result += "\tconst struct _objc_protocol_list *baseProtocols;\n"; + Result += "\tconst struct _ivar_list_t *ivars;\n"; + Result += "\tconst unsigned char *weakIvarLayout;\n"; + Result += "\tconst struct _prop_list_t *properties;\n"; + Result += "};\n"; + + Result += "\nstruct _class_t {\n"; + Result += "\tstruct _class_t *isa;\n"; + Result += "\tstruct _class_t *superclass;\n"; + Result += "\tvoid *cache;\n"; + Result += "\tvoid *vtable;\n"; + Result += "\tstruct _class_ro_t *ro;\n"; + Result += "};\n"; + + Result += "\nstruct _category_t {\n"; + Result += "\tconst char *name;\n"; + Result += "\tstruct _class_t *cls;\n"; + Result += "\tconst struct _method_list_t *instance_methods;\n"; + Result += "\tconst struct _method_list_t *class_methods;\n"; + Result += "\tconst struct _protocol_list_t *protocols;\n"; + Result += "\tconst struct _prop_list_t *properties;\n"; + Result += "};\n"; + + Result += "extern \"C\" __declspec(dllimport) struct objc_cache _objc_empty_cache;\n"; + Result += "#pragma warning(disable:4273)\n"; + meta_data_declared = true; +} + +static void Write_protocol_list_t_TypeDecl(std::string &Result, + long super_protocol_count) { + Result += "struct /*_protocol_list_t*/"; Result += " {\n"; + Result += "\tlong protocol_count; // Note, this is 32/64 bit\n"; + Result += "\tstruct _protocol_t *super_protocols["; + Result += utostr(super_protocol_count); Result += "];\n"; + Result += "}"; +} + +static void Write_method_list_t_TypeDecl(std::string &Result, + unsigned int method_count) { + Result += "struct /*_method_list_t*/"; Result += " {\n"; + Result += "\tunsigned int entsize; // sizeof(struct _objc_method)\n"; + Result += "\tunsigned int method_count;\n"; + Result += "\tstruct _objc_method method_list["; + Result += utostr(method_count); Result += "];\n"; + Result += "}"; +} + +static void Write__prop_list_t_TypeDecl(std::string &Result, + unsigned int property_count) { + Result += "struct /*_prop_list_t*/"; Result += " {\n"; + Result += "\tunsigned int entsize; // sizeof(struct _prop_t)\n"; + Result += "\tunsigned int count_of_properties;\n"; + Result += "\tstruct _prop_t prop_list["; + Result += utostr(property_count); Result += "];\n"; + Result += "}"; +} + +static void Write__ivar_list_t_TypeDecl(std::string &Result, + unsigned int ivar_count) { + Result += "struct /*_ivar_list_t*/"; Result += " {\n"; + Result += "\tunsigned int entsize; // sizeof(struct _prop_t)\n"; + Result += "\tunsigned int count;\n"; + Result += "\tstruct _ivar_t ivar_list["; + Result += utostr(ivar_count); Result += "];\n"; + Result += "}"; +} + +static void Write_protocol_list_initializer(ASTContext *Context, std::string &Result, + ArrayRef<ObjCProtocolDecl *> SuperProtocols, + StringRef VarName, + StringRef ProtocolName) { + if (SuperProtocols.size() > 0) { + Result += "\nstatic "; + Write_protocol_list_t_TypeDecl(Result, SuperProtocols.size()); + Result += " "; Result += VarName; + Result += ProtocolName; + Result += " __attribute__ ((used, section (\"__DATA,__objc_const\"))) = {\n"; + Result += "\t"; Result += utostr(SuperProtocols.size()); Result += ",\n"; + for (unsigned i = 0, e = SuperProtocols.size(); i < e; i++) { + ObjCProtocolDecl *SuperPD = SuperProtocols[i]; + Result += "\t&"; Result += "_OBJC_PROTOCOL_"; + Result += SuperPD->getNameAsString(); + if (i == e-1) + Result += "\n};\n"; + else + Result += ",\n"; + } + } +} + +static void Write_method_list_t_initializer(RewriteModernObjC &RewriteObj, + ASTContext *Context, std::string &Result, + ArrayRef<ObjCMethodDecl *> Methods, + StringRef VarName, + StringRef TopLevelDeclName, + bool MethodImpl) { + if (Methods.size() > 0) { + Result += "\nstatic "; + Write_method_list_t_TypeDecl(Result, Methods.size()); + Result += " "; Result += VarName; + Result += TopLevelDeclName; + Result += " __attribute__ ((used, section (\"__DATA,__objc_const\"))) = {\n"; + Result += "\t"; Result += "sizeof(_objc_method)"; Result += ",\n"; + Result += "\t"; Result += utostr(Methods.size()); Result += ",\n"; + for (unsigned i = 0, e = Methods.size(); i < e; i++) { + ObjCMethodDecl *MD = Methods[i]; + if (i == 0) + Result += "\t{{(struct objc_selector *)\""; + else + Result += "\t{(struct objc_selector *)\""; + Result += (MD)->getSelector().getAsString(); Result += "\""; + Result += ", "; + std::string MethodTypeString = Context->getObjCEncodingForMethodDecl(MD); + Result += "\""; Result += MethodTypeString; Result += "\""; + Result += ", "; + if (!MethodImpl) + Result += "0"; + else { + Result += "(void *)"; + Result += RewriteObj.MethodInternalNames[MD]; + } + if (i == e-1) + Result += "}}\n"; + else + Result += "},\n"; + } + Result += "};\n"; + } +} + +static void Write_prop_list_t_initializer(RewriteModernObjC &RewriteObj, + ASTContext *Context, std::string &Result, + ArrayRef<ObjCPropertyDecl *> Properties, + const Decl *Container, + StringRef VarName, + StringRef ProtocolName) { + if (Properties.size() > 0) { + Result += "\nstatic "; + Write__prop_list_t_TypeDecl(Result, Properties.size()); + Result += " "; Result += VarName; + Result += ProtocolName; + Result += " __attribute__ ((used, section (\"__DATA,__objc_const\"))) = {\n"; + Result += "\t"; Result += "sizeof(_prop_t)"; Result += ",\n"; + Result += "\t"; Result += utostr(Properties.size()); Result += ",\n"; + for (unsigned i = 0, e = Properties.size(); i < e; i++) { + ObjCPropertyDecl *PropDecl = Properties[i]; + if (i == 0) + Result += "\t{{\""; + else + Result += "\t{\""; + Result += PropDecl->getName(); Result += "\","; + std::string PropertyTypeString = + Context->getObjCEncodingForPropertyDecl(PropDecl, Container); + std::string QuotePropertyTypeString; + RewriteObj.QuoteDoublequotes(PropertyTypeString, QuotePropertyTypeString); + Result += "\""; Result += QuotePropertyTypeString; Result += "\""; + if (i == e-1) + Result += "}}\n"; + else + Result += "},\n"; + } + Result += "};\n"; + } +} + +// Metadata flags +enum MetaDataDlags { + CLS = 0x0, + CLS_META = 0x1, + CLS_ROOT = 0x2, + OBJC2_CLS_HIDDEN = 0x10, + CLS_EXCEPTION = 0x20, + + /// (Obsolete) ARC-specific: this class has a .release_ivars method + CLS_HAS_IVAR_RELEASER = 0x40, + /// class was compiled with -fobjc-arr + CLS_COMPILED_BY_ARC = 0x80 // (1<<7) +}; + +static void Write__class_ro_t_initializer(ASTContext *Context, std::string &Result, + unsigned int flags, + const std::string &InstanceStart, + const std::string &InstanceSize, + ArrayRef<ObjCMethodDecl *>baseMethods, + ArrayRef<ObjCProtocolDecl *>baseProtocols, + ArrayRef<ObjCIvarDecl *>ivars, + ArrayRef<ObjCPropertyDecl *>Properties, + StringRef VarName, + StringRef ClassName) { + Result += "\nstatic struct _class_ro_t "; + Result += VarName; Result += ClassName; + Result += " __attribute__ ((used, section (\"__DATA,__objc_const\"))) = {\n"; + Result += "\t"; + Result += llvm::utostr(flags); Result += ", "; + Result += InstanceStart; Result += ", "; + Result += InstanceSize; Result += ", \n"; + Result += "\t"; + const llvm::Triple &Triple(Context->getTargetInfo().getTriple()); + if (Triple.getArch() == llvm::Triple::x86_64) + // uint32_t const reserved; // only when building for 64bit targets + Result += "(unsigned int)0, \n\t"; + // const uint8_t * const ivarLayout; + Result += "0, \n\t"; + Result += "\""; Result += ClassName; Result += "\",\n\t"; + bool metaclass = ((flags & CLS_META) != 0); + if (baseMethods.size() > 0) { + Result += "(const struct _method_list_t *)&"; + if (metaclass) + Result += "_OBJC_$_CLASS_METHODS_"; + else + Result += "_OBJC_$_INSTANCE_METHODS_"; + Result += ClassName; + Result += ",\n\t"; + } + else + Result += "0, \n\t"; + + if (!metaclass && baseProtocols.size() > 0) { + Result += "(const struct _objc_protocol_list *)&"; + Result += "_OBJC_CLASS_PROTOCOLS_$_"; Result += ClassName; + Result += ",\n\t"; + } + else + Result += "0, \n\t"; + + if (!metaclass && ivars.size() > 0) { + Result += "(const struct _ivar_list_t *)&"; + Result += "_OBJC_$_INSTANCE_VARIABLES_"; Result += ClassName; + Result += ",\n\t"; + } + else + Result += "0, \n\t"; + + // weakIvarLayout + Result += "0, \n\t"; + if (!metaclass && Properties.size() > 0) { + Result += "(const struct _prop_list_t *)&"; + Result += "_OBJC_$_PROP_LIST_"; Result += ClassName; + Result += ",\n"; + } + else + Result += "0, \n"; + + Result += "};\n"; +} + +static void Write_class_t(ASTContext *Context, std::string &Result, + StringRef VarName, + const ObjCInterfaceDecl *CDecl, bool metaclass) { + bool rootClass = (!CDecl->getSuperClass()); + const ObjCInterfaceDecl *RootClass = CDecl; + + if (!rootClass) { + // Find the Root class + RootClass = CDecl->getSuperClass(); + while (RootClass->getSuperClass()) { + RootClass = RootClass->getSuperClass(); + } + } + + if (metaclass && rootClass) { + // Need to handle a case of use of forward declaration. + Result += "\n"; + Result += "extern \"C\" "; + if (CDecl->getImplementation()) + Result += "__declspec(dllexport) "; + else + Result += "__declspec(dllimport) "; + + Result += "struct _class_t OBJC_CLASS_$_"; + Result += CDecl->getNameAsString(); + Result += ";\n"; + } + // Also, for possibility of 'super' metadata class not having been defined yet. + if (!rootClass) { + ObjCInterfaceDecl *SuperClass = CDecl->getSuperClass(); + Result += "\n"; + Result += "extern \"C\" "; + if (SuperClass->getImplementation()) + Result += "__declspec(dllexport) "; + else + Result += "__declspec(dllimport) "; + + Result += "struct _class_t "; + Result += VarName; + Result += SuperClass->getNameAsString(); + Result += ";\n"; + + if (metaclass && RootClass != SuperClass) { + Result += "extern \"C\" "; + if (RootClass->getImplementation()) + Result += "__declspec(dllexport) "; + else + Result += "__declspec(dllimport) "; + + Result += "struct _class_t "; + Result += VarName; + Result += RootClass->getNameAsString(); + Result += ";\n"; + } + } + + Result += "\nextern \"C\" __declspec(dllexport) struct _class_t "; + Result += VarName; Result += CDecl->getNameAsString(); + Result += " __attribute__ ((used, section (\"__DATA,__objc_data\"))) = {\n"; + Result += "\t"; + if (metaclass) { + if (!rootClass) { + Result += "0, // &"; Result += VarName; + Result += RootClass->getNameAsString(); + Result += ",\n\t"; + Result += "0, // &"; Result += VarName; + Result += CDecl->getSuperClass()->getNameAsString(); + Result += ",\n\t"; + } + else { + Result += "0, // &"; Result += VarName; + Result += CDecl->getNameAsString(); + Result += ",\n\t"; + Result += "0, // &OBJC_CLASS_$_"; Result += CDecl->getNameAsString(); + Result += ",\n\t"; + } + } + else { + Result += "0, // &OBJC_METACLASS_$_"; + Result += CDecl->getNameAsString(); + Result += ",\n\t"; + if (!rootClass) { + Result += "0, // &"; Result += VarName; + Result += CDecl->getSuperClass()->getNameAsString(); + Result += ",\n\t"; + } + else + Result += "0,\n\t"; + } + Result += "0, // (void *)&_objc_empty_cache,\n\t"; + Result += "0, // unused, was (void *)&_objc_empty_vtable,\n\t"; + if (metaclass) + Result += "&_OBJC_METACLASS_RO_$_"; + else + Result += "&_OBJC_CLASS_RO_$_"; + Result += CDecl->getNameAsString(); + Result += ",\n};\n"; + + // Add static function to initialize some of the meta-data fields. + // avoid doing it twice. + if (metaclass) + return; + + const ObjCInterfaceDecl *SuperClass = + rootClass ? CDecl : CDecl->getSuperClass(); + + Result += "static void OBJC_CLASS_SETUP_$_"; + Result += CDecl->getNameAsString(); + Result += "(void ) {\n"; + Result += "\tOBJC_METACLASS_$_"; Result += CDecl->getNameAsString(); + Result += ".isa = "; Result += "&OBJC_METACLASS_$_"; + Result += RootClass->getNameAsString(); Result += ";\n"; + + Result += "\tOBJC_METACLASS_$_"; Result += CDecl->getNameAsString(); + Result += ".superclass = "; + if (rootClass) + Result += "&OBJC_CLASS_$_"; + else + Result += "&OBJC_METACLASS_$_"; + + Result += SuperClass->getNameAsString(); Result += ";\n"; + + Result += "\tOBJC_METACLASS_$_"; Result += CDecl->getNameAsString(); + Result += ".cache = "; Result += "&_objc_empty_cache"; Result += ";\n"; + + Result += "\tOBJC_CLASS_$_"; Result += CDecl->getNameAsString(); + Result += ".isa = "; Result += "&OBJC_METACLASS_$_"; + Result += CDecl->getNameAsString(); Result += ";\n"; + + if (!rootClass) { + Result += "\tOBJC_CLASS_$_"; Result += CDecl->getNameAsString(); + Result += ".superclass = "; Result += "&OBJC_CLASS_$_"; + Result += SuperClass->getNameAsString(); Result += ";\n"; + } + + Result += "\tOBJC_CLASS_$_"; Result += CDecl->getNameAsString(); + Result += ".cache = "; Result += "&_objc_empty_cache"; Result += ";\n"; + Result += "}\n"; +} + +static void Write_category_t(RewriteModernObjC &RewriteObj, ASTContext *Context, + std::string &Result, + ObjCCategoryDecl *CatDecl, + ObjCInterfaceDecl *ClassDecl, + ArrayRef<ObjCMethodDecl *> InstanceMethods, + ArrayRef<ObjCMethodDecl *> ClassMethods, + ArrayRef<ObjCProtocolDecl *> RefedProtocols, + ArrayRef<ObjCPropertyDecl *> ClassProperties) { + StringRef CatName = CatDecl->getName(); + StringRef ClassName = ClassDecl->getName(); + // must declare an extern class object in case this class is not implemented + // in this TU. + Result += "\n"; + Result += "extern \"C\" "; + if (ClassDecl->getImplementation()) + Result += "__declspec(dllexport) "; + else + Result += "__declspec(dllimport) "; + + Result += "struct _class_t "; + Result += "OBJC_CLASS_$_"; Result += ClassName; + Result += ";\n"; + + Result += "\nstatic struct _category_t "; + Result += "_OBJC_$_CATEGORY_"; + Result += ClassName; Result += "_$_"; Result += CatName; + Result += " __attribute__ ((used, section (\"__DATA,__objc_const\"))) = \n"; + Result += "{\n"; + Result += "\t\""; Result += ClassName; Result += "\",\n"; + Result += "\t0, // &"; Result += "OBJC_CLASS_$_"; Result += ClassName; + Result += ",\n"; + if (InstanceMethods.size() > 0) { + Result += "\t(const struct _method_list_t *)&"; + Result += "_OBJC_$_CATEGORY_INSTANCE_METHODS_"; + Result += ClassName; Result += "_$_"; Result += CatName; + Result += ",\n"; + } + else + Result += "\t0,\n"; + + if (ClassMethods.size() > 0) { + Result += "\t(const struct _method_list_t *)&"; + Result += "_OBJC_$_CATEGORY_CLASS_METHODS_"; + Result += ClassName; Result += "_$_"; Result += CatName; + Result += ",\n"; + } + else + Result += "\t0,\n"; + + if (RefedProtocols.size() > 0) { + Result += "\t(const struct _protocol_list_t *)&"; + Result += "_OBJC_CATEGORY_PROTOCOLS_$_"; + Result += ClassName; Result += "_$_"; Result += CatName; + Result += ",\n"; + } + else + Result += "\t0,\n"; + + if (ClassProperties.size() > 0) { + Result += "\t(const struct _prop_list_t *)&"; Result += "_OBJC_$_PROP_LIST_"; + Result += ClassName; Result += "_$_"; Result += CatName; + Result += ",\n"; + } + else + Result += "\t0,\n"; + + Result += "};\n"; + + // Add static function to initialize the class pointer in the category structure. + Result += "static void OBJC_CATEGORY_SETUP_$_"; + Result += ClassDecl->getNameAsString(); + Result += "_$_"; + Result += CatName; + Result += "(void ) {\n"; + Result += "\t_OBJC_$_CATEGORY_"; + Result += ClassDecl->getNameAsString(); + Result += "_$_"; + Result += CatName; + Result += ".cls = "; Result += "&OBJC_CLASS_$_"; Result += ClassName; + Result += ";\n}\n"; +} + +static void Write__extendedMethodTypes_initializer(RewriteModernObjC &RewriteObj, + ASTContext *Context, std::string &Result, + ArrayRef<ObjCMethodDecl *> Methods, + StringRef VarName, + StringRef ProtocolName) { + if (Methods.size() == 0) + return; + + Result += "\nstatic const char *"; + Result += VarName; Result += ProtocolName; + Result += " [] __attribute__ ((used, section (\"__DATA,__objc_const\"))) = \n"; + Result += "{\n"; + for (unsigned i = 0, e = Methods.size(); i < e; i++) { + ObjCMethodDecl *MD = Methods[i]; + std::string MethodTypeString = + Context->getObjCEncodingForMethodDecl(MD, true); + std::string QuoteMethodTypeString; + RewriteObj.QuoteDoublequotes(MethodTypeString, QuoteMethodTypeString); + Result += "\t\""; Result += QuoteMethodTypeString; Result += "\""; + if (i == e-1) + Result += "\n};\n"; + else { + Result += ",\n"; + } + } +} + +static void Write_IvarOffsetVar(RewriteModernObjC &RewriteObj, + ASTContext *Context, + std::string &Result, + ArrayRef<ObjCIvarDecl *> Ivars, + ObjCInterfaceDecl *CDecl) { + // FIXME. visibilty of offset symbols may have to be set; for Darwin + // this is what happens: + /** + if (Ivar->getAccessControl() == ObjCIvarDecl::Private || + Ivar->getAccessControl() == ObjCIvarDecl::Package || + Class->getVisibility() == HiddenVisibility) + Visibility should be: HiddenVisibility; + else + Visibility should be: DefaultVisibility; + */ + + Result += "\n"; + for (unsigned i =0, e = Ivars.size(); i < e; i++) { + ObjCIvarDecl *IvarDecl = Ivars[i]; + if (Context->getLangOpts().MicrosoftExt) + Result += "__declspec(allocate(\".objc_ivar$B\")) "; + + if (!Context->getLangOpts().MicrosoftExt || + IvarDecl->getAccessControl() == ObjCIvarDecl::Private || + IvarDecl->getAccessControl() == ObjCIvarDecl::Package) + Result += "extern \"C\" unsigned long int "; + else + Result += "extern \"C\" __declspec(dllexport) unsigned long int "; + if (Ivars[i]->isBitField()) + RewriteObj.ObjCIvarBitfieldGroupOffset(IvarDecl, Result); + else + WriteInternalIvarName(CDecl, IvarDecl, Result); + Result += " __attribute__ ((used, section (\"__DATA,__objc_ivar\")))"; + Result += " = "; + RewriteObj.RewriteIvarOffsetComputation(IvarDecl, Result); + Result += ";\n"; + if (Ivars[i]->isBitField()) { + // skip over rest of the ivar bitfields. + SKIP_BITFIELDS(i , e, Ivars); + } + } +} + +static void Write__ivar_list_t_initializer(RewriteModernObjC &RewriteObj, + ASTContext *Context, std::string &Result, + ArrayRef<ObjCIvarDecl *> OriginalIvars, + StringRef VarName, + ObjCInterfaceDecl *CDecl) { + if (OriginalIvars.size() > 0) { + Write_IvarOffsetVar(RewriteObj, Context, Result, OriginalIvars, CDecl); + SmallVector<ObjCIvarDecl *, 8> Ivars; + // strip off all but the first ivar bitfield from each group of ivars. + // Such ivars in the ivar list table will be replaced by their grouping struct + // 'ivar'. + for (unsigned i = 0, e = OriginalIvars.size(); i < e; i++) { + if (OriginalIvars[i]->isBitField()) { + Ivars.push_back(OriginalIvars[i]); + // skip over rest of the ivar bitfields. + SKIP_BITFIELDS(i , e, OriginalIvars); + } + else + Ivars.push_back(OriginalIvars[i]); + } + + Result += "\nstatic "; + Write__ivar_list_t_TypeDecl(Result, Ivars.size()); + Result += " "; Result += VarName; + Result += CDecl->getNameAsString(); + Result += " __attribute__ ((used, section (\"__DATA,__objc_const\"))) = {\n"; + Result += "\t"; Result += "sizeof(_ivar_t)"; Result += ",\n"; + Result += "\t"; Result += utostr(Ivars.size()); Result += ",\n"; + for (unsigned i =0, e = Ivars.size(); i < e; i++) { + ObjCIvarDecl *IvarDecl = Ivars[i]; + if (i == 0) + Result += "\t{{"; + else + Result += "\t {"; + Result += "(unsigned long int *)&"; + if (Ivars[i]->isBitField()) + RewriteObj.ObjCIvarBitfieldGroupOffset(IvarDecl, Result); + else + WriteInternalIvarName(CDecl, IvarDecl, Result); + Result += ", "; + + Result += "\""; + if (Ivars[i]->isBitField()) + RewriteObj.ObjCIvarBitfieldGroupDecl(Ivars[i], Result); + else + Result += IvarDecl->getName(); + Result += "\", "; + + QualType IVQT = IvarDecl->getType(); + if (IvarDecl->isBitField()) + IVQT = RewriteObj.GetGroupRecordTypeForObjCIvarBitfield(IvarDecl); + + std::string IvarTypeString, QuoteIvarTypeString; + Context->getObjCEncodingForType(IVQT, IvarTypeString, + IvarDecl); + RewriteObj.QuoteDoublequotes(IvarTypeString, QuoteIvarTypeString); + Result += "\""; Result += QuoteIvarTypeString; Result += "\", "; + + // FIXME. this alignment represents the host alignment and need be changed to + // represent the target alignment. + unsigned Align = Context->getTypeAlign(IVQT)/8; + Align = llvm::Log2_32(Align); + Result += llvm::utostr(Align); Result += ", "; + CharUnits Size = Context->getTypeSizeInChars(IVQT); + Result += llvm::utostr(Size.getQuantity()); + if (i == e-1) + Result += "}}\n"; + else + Result += "},\n"; + } + Result += "};\n"; + } +} + +/// RewriteObjCProtocolMetaData - Rewrite protocols meta-data. +void RewriteModernObjC::RewriteObjCProtocolMetaData(ObjCProtocolDecl *PDecl, + std::string &Result) { + + // Do not synthesize the protocol more than once. + if (ObjCSynthesizedProtocols.count(PDecl->getCanonicalDecl())) + return; + WriteModernMetadataDeclarations(Context, Result); + + if (ObjCProtocolDecl *Def = PDecl->getDefinition()) + PDecl = Def; + // Must write out all protocol definitions in current qualifier list, + // and in their nested qualifiers before writing out current definition. + for (auto *I : PDecl->protocols()) + RewriteObjCProtocolMetaData(I, Result); + + // Construct method lists. + std::vector<ObjCMethodDecl *> InstanceMethods, ClassMethods; + std::vector<ObjCMethodDecl *> OptInstanceMethods, OptClassMethods; + for (auto *MD : PDecl->instance_methods()) { + if (MD->getImplementationControl() == ObjCMethodDecl::Optional) { + OptInstanceMethods.push_back(MD); + } else { + InstanceMethods.push_back(MD); + } + } + + for (auto *MD : PDecl->class_methods()) { + if (MD->getImplementationControl() == ObjCMethodDecl::Optional) { + OptClassMethods.push_back(MD); + } else { + ClassMethods.push_back(MD); + } + } + std::vector<ObjCMethodDecl *> AllMethods; + for (unsigned i = 0, e = InstanceMethods.size(); i < e; i++) + AllMethods.push_back(InstanceMethods[i]); + for (unsigned i = 0, e = ClassMethods.size(); i < e; i++) + AllMethods.push_back(ClassMethods[i]); + for (unsigned i = 0, e = OptInstanceMethods.size(); i < e; i++) + AllMethods.push_back(OptInstanceMethods[i]); + for (unsigned i = 0, e = OptClassMethods.size(); i < e; i++) + AllMethods.push_back(OptClassMethods[i]); + + Write__extendedMethodTypes_initializer(*this, Context, Result, + AllMethods, + "_OBJC_PROTOCOL_METHOD_TYPES_", + PDecl->getNameAsString()); + // Protocol's super protocol list + SmallVector<ObjCProtocolDecl *, 8> SuperProtocols(PDecl->protocols()); + Write_protocol_list_initializer(Context, Result, SuperProtocols, + "_OBJC_PROTOCOL_REFS_", + PDecl->getNameAsString()); + + Write_method_list_t_initializer(*this, Context, Result, InstanceMethods, + "_OBJC_PROTOCOL_INSTANCE_METHODS_", + PDecl->getNameAsString(), false); + + Write_method_list_t_initializer(*this, Context, Result, ClassMethods, + "_OBJC_PROTOCOL_CLASS_METHODS_", + PDecl->getNameAsString(), false); + + Write_method_list_t_initializer(*this, Context, Result, OptInstanceMethods, + "_OBJC_PROTOCOL_OPT_INSTANCE_METHODS_", + PDecl->getNameAsString(), false); + + Write_method_list_t_initializer(*this, Context, Result, OptClassMethods, + "_OBJC_PROTOCOL_OPT_CLASS_METHODS_", + PDecl->getNameAsString(), false); + + // Protocol's property metadata. + SmallVector<ObjCPropertyDecl *, 8> ProtocolProperties( + PDecl->instance_properties()); + Write_prop_list_t_initializer(*this, Context, Result, ProtocolProperties, + /* Container */nullptr, + "_OBJC_PROTOCOL_PROPERTIES_", + PDecl->getNameAsString()); + + // Writer out root metadata for current protocol: struct _protocol_t + Result += "\n"; + if (LangOpts.MicrosoftExt) + Result += "static "; + Result += "struct _protocol_t _OBJC_PROTOCOL_"; + Result += PDecl->getNameAsString(); + Result += " __attribute__ ((used)) = {\n"; + Result += "\t0,\n"; // id is; is null + Result += "\t\""; Result += PDecl->getNameAsString(); Result += "\",\n"; + if (SuperProtocols.size() > 0) { + Result += "\t(const struct _protocol_list_t *)&"; Result += "_OBJC_PROTOCOL_REFS_"; + Result += PDecl->getNameAsString(); Result += ",\n"; + } + else + Result += "\t0,\n"; + if (InstanceMethods.size() > 0) { + Result += "\t(const struct method_list_t *)&_OBJC_PROTOCOL_INSTANCE_METHODS_"; + Result += PDecl->getNameAsString(); Result += ",\n"; + } + else + Result += "\t0,\n"; + + if (ClassMethods.size() > 0) { + Result += "\t(const struct method_list_t *)&_OBJC_PROTOCOL_CLASS_METHODS_"; + Result += PDecl->getNameAsString(); Result += ",\n"; + } + else + Result += "\t0,\n"; + + if (OptInstanceMethods.size() > 0) { + Result += "\t(const struct method_list_t *)&_OBJC_PROTOCOL_OPT_INSTANCE_METHODS_"; + Result += PDecl->getNameAsString(); Result += ",\n"; + } + else + Result += "\t0,\n"; + + if (OptClassMethods.size() > 0) { + Result += "\t(const struct method_list_t *)&_OBJC_PROTOCOL_OPT_CLASS_METHODS_"; + Result += PDecl->getNameAsString(); Result += ",\n"; + } + else + Result += "\t0,\n"; + + if (ProtocolProperties.size() > 0) { + Result += "\t(const struct _prop_list_t *)&_OBJC_PROTOCOL_PROPERTIES_"; + Result += PDecl->getNameAsString(); Result += ",\n"; + } + else + Result += "\t0,\n"; + + Result += "\t"; Result += "sizeof(_protocol_t)"; Result += ",\n"; + Result += "\t0,\n"; + + if (AllMethods.size() > 0) { + Result += "\t(const char **)&"; Result += "_OBJC_PROTOCOL_METHOD_TYPES_"; + Result += PDecl->getNameAsString(); + Result += "\n};\n"; + } + else + Result += "\t0\n};\n"; + + if (LangOpts.MicrosoftExt) + Result += "static "; + Result += "struct _protocol_t *"; + Result += "_OBJC_LABEL_PROTOCOL_$_"; Result += PDecl->getNameAsString(); + Result += " = &_OBJC_PROTOCOL_"; Result += PDecl->getNameAsString(); + Result += ";\n"; + + // Mark this protocol as having been generated. + if (!ObjCSynthesizedProtocols.insert(PDecl->getCanonicalDecl()).second) + llvm_unreachable("protocol already synthesized"); +} + +/// hasObjCExceptionAttribute - Return true if this class or any super +/// class has the __objc_exception__ attribute. +/// FIXME. Move this to ASTContext.cpp as it is also used for IRGen. +static bool hasObjCExceptionAttribute(ASTContext &Context, + const ObjCInterfaceDecl *OID) { + if (OID->hasAttr<ObjCExceptionAttr>()) + return true; + if (const ObjCInterfaceDecl *Super = OID->getSuperClass()) + return hasObjCExceptionAttribute(Context, Super); + return false; +} + +void RewriteModernObjC::RewriteObjCClassMetaData(ObjCImplementationDecl *IDecl, + std::string &Result) { + ObjCInterfaceDecl *CDecl = IDecl->getClassInterface(); + + // Explicitly declared @interface's are already synthesized. + if (CDecl->isImplicitInterfaceDecl()) + assert(false && + "Legacy implicit interface rewriting not supported in moder abi"); + + WriteModernMetadataDeclarations(Context, Result); + SmallVector<ObjCIvarDecl *, 8> IVars; + + for (ObjCIvarDecl *IVD = CDecl->all_declared_ivar_begin(); + IVD; IVD = IVD->getNextIvar()) { + // Ignore unnamed bit-fields. + if (!IVD->getDeclName()) + continue; + IVars.push_back(IVD); + } + + Write__ivar_list_t_initializer(*this, Context, Result, IVars, + "_OBJC_$_INSTANCE_VARIABLES_", + CDecl); + + // Build _objc_method_list for class's instance methods if needed + SmallVector<ObjCMethodDecl *, 32> InstanceMethods(IDecl->instance_methods()); + + // If any of our property implementations have associated getters or + // setters, produce metadata for them as well. + for (const auto *Prop : IDecl->property_impls()) { + if (Prop->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) + continue; + if (!Prop->getPropertyIvarDecl()) + continue; + ObjCPropertyDecl *PD = Prop->getPropertyDecl(); + if (!PD) + continue; + if (ObjCMethodDecl *Getter = PD->getGetterMethodDecl()) + if (mustSynthesizeSetterGetterMethod(IDecl, PD, true /*getter*/)) + InstanceMethods.push_back(Getter); + if (PD->isReadOnly()) + continue; + if (ObjCMethodDecl *Setter = PD->getSetterMethodDecl()) + if (mustSynthesizeSetterGetterMethod(IDecl, PD, false /*setter*/)) + InstanceMethods.push_back(Setter); + } + + Write_method_list_t_initializer(*this, Context, Result, InstanceMethods, + "_OBJC_$_INSTANCE_METHODS_", + IDecl->getNameAsString(), true); + + SmallVector<ObjCMethodDecl *, 32> ClassMethods(IDecl->class_methods()); + + Write_method_list_t_initializer(*this, Context, Result, ClassMethods, + "_OBJC_$_CLASS_METHODS_", + IDecl->getNameAsString(), true); + + // Protocols referenced in class declaration? + // Protocol's super protocol list + std::vector<ObjCProtocolDecl *> RefedProtocols; + const ObjCList<ObjCProtocolDecl> &Protocols = CDecl->getReferencedProtocols(); + for (ObjCList<ObjCProtocolDecl>::iterator I = Protocols.begin(), + E = Protocols.end(); + I != E; ++I) { + RefedProtocols.push_back(*I); + // Must write out all protocol definitions in current qualifier list, + // and in their nested qualifiers before writing out current definition. + RewriteObjCProtocolMetaData(*I, Result); + } + + Write_protocol_list_initializer(Context, Result, + RefedProtocols, + "_OBJC_CLASS_PROTOCOLS_$_", + IDecl->getNameAsString()); + + // Protocol's property metadata. + SmallVector<ObjCPropertyDecl *, 8> ClassProperties( + CDecl->instance_properties()); + Write_prop_list_t_initializer(*this, Context, Result, ClassProperties, + /* Container */IDecl, + "_OBJC_$_PROP_LIST_", + CDecl->getNameAsString()); + + // Data for initializing _class_ro_t metaclass meta-data + uint32_t flags = CLS_META; + std::string InstanceSize; + std::string InstanceStart; + + bool classIsHidden = CDecl->getVisibility() == HiddenVisibility; + if (classIsHidden) + flags |= OBJC2_CLS_HIDDEN; + + if (!CDecl->getSuperClass()) + // class is root + flags |= CLS_ROOT; + InstanceSize = "sizeof(struct _class_t)"; + InstanceStart = InstanceSize; + Write__class_ro_t_initializer(Context, Result, flags, + InstanceStart, InstanceSize, + ClassMethods, + nullptr, + nullptr, + nullptr, + "_OBJC_METACLASS_RO_$_", + CDecl->getNameAsString()); + + // Data for initializing _class_ro_t meta-data + flags = CLS; + if (classIsHidden) + flags |= OBJC2_CLS_HIDDEN; + + if (hasObjCExceptionAttribute(*Context, CDecl)) + flags |= CLS_EXCEPTION; + + if (!CDecl->getSuperClass()) + // class is root + flags |= CLS_ROOT; + + InstanceSize.clear(); + InstanceStart.clear(); + if (!ObjCSynthesizedStructs.count(CDecl)) { + InstanceSize = "0"; + InstanceStart = "0"; + } + else { + InstanceSize = "sizeof(struct "; + InstanceSize += CDecl->getNameAsString(); + InstanceSize += "_IMPL)"; + + ObjCIvarDecl *IVD = CDecl->all_declared_ivar_begin(); + if (IVD) { + RewriteIvarOffsetComputation(IVD, InstanceStart); + } + else + InstanceStart = InstanceSize; + } + Write__class_ro_t_initializer(Context, Result, flags, + InstanceStart, InstanceSize, + InstanceMethods, + RefedProtocols, + IVars, + ClassProperties, + "_OBJC_CLASS_RO_$_", + CDecl->getNameAsString()); + + Write_class_t(Context, Result, + "OBJC_METACLASS_$_", + CDecl, /*metaclass*/true); + + Write_class_t(Context, Result, + "OBJC_CLASS_$_", + CDecl, /*metaclass*/false); + + if (ImplementationIsNonLazy(IDecl)) + DefinedNonLazyClasses.push_back(CDecl); +} + +void RewriteModernObjC::RewriteClassSetupInitHook(std::string &Result) { + int ClsDefCount = ClassImplementation.size(); + if (!ClsDefCount) + return; + Result += "#pragma section(\".objc_inithooks$B\", long, read, write)\n"; + Result += "__declspec(allocate(\".objc_inithooks$B\")) "; + Result += "static void *OBJC_CLASS_SETUP[] = {\n"; + for (int i = 0; i < ClsDefCount; i++) { + ObjCImplementationDecl *IDecl = ClassImplementation[i]; + ObjCInterfaceDecl *CDecl = IDecl->getClassInterface(); + Result += "\t(void *)&OBJC_CLASS_SETUP_$_"; + Result += CDecl->getName(); Result += ",\n"; + } + Result += "};\n"; +} + +void RewriteModernObjC::RewriteMetaDataIntoBuffer(std::string &Result) { + int ClsDefCount = ClassImplementation.size(); + int CatDefCount = CategoryImplementation.size(); + + // For each implemented class, write out all its meta data. + for (int i = 0; i < ClsDefCount; i++) + RewriteObjCClassMetaData(ClassImplementation[i], Result); + + RewriteClassSetupInitHook(Result); + + // For each implemented category, write out all its meta data. + for (int i = 0; i < CatDefCount; i++) + RewriteObjCCategoryImplDecl(CategoryImplementation[i], Result); + + RewriteCategorySetupInitHook(Result); + + if (ClsDefCount > 0) { + if (LangOpts.MicrosoftExt) + Result += "__declspec(allocate(\".objc_classlist$B\")) "; + Result += "static struct _class_t *L_OBJC_LABEL_CLASS_$ ["; + Result += llvm::utostr(ClsDefCount); Result += "]"; + Result += + " __attribute__((used, section (\"__DATA, __objc_classlist," + "regular,no_dead_strip\")))= {\n"; + for (int i = 0; i < ClsDefCount; i++) { + Result += "\t&OBJC_CLASS_$_"; + Result += ClassImplementation[i]->getNameAsString(); + Result += ",\n"; + } + Result += "};\n"; + + if (!DefinedNonLazyClasses.empty()) { + if (LangOpts.MicrosoftExt) + Result += "__declspec(allocate(\".objc_nlclslist$B\")) \n"; + Result += "static struct _class_t *_OBJC_LABEL_NONLAZY_CLASS_$[] = {\n\t"; + for (unsigned i = 0, e = DefinedNonLazyClasses.size(); i < e; i++) { + Result += "\t&OBJC_CLASS_$_"; Result += DefinedNonLazyClasses[i]->getNameAsString(); + Result += ",\n"; + } + Result += "};\n"; + } + } + + if (CatDefCount > 0) { + if (LangOpts.MicrosoftExt) + Result += "__declspec(allocate(\".objc_catlist$B\")) "; + Result += "static struct _category_t *L_OBJC_LABEL_CATEGORY_$ ["; + Result += llvm::utostr(CatDefCount); Result += "]"; + Result += + " __attribute__((used, section (\"__DATA, __objc_catlist," + "regular,no_dead_strip\")))= {\n"; + for (int i = 0; i < CatDefCount; i++) { + Result += "\t&_OBJC_$_CATEGORY_"; + Result += + CategoryImplementation[i]->getClassInterface()->getNameAsString(); + Result += "_$_"; + Result += CategoryImplementation[i]->getNameAsString(); + Result += ",\n"; + } + Result += "};\n"; + } + + if (!DefinedNonLazyCategories.empty()) { + if (LangOpts.MicrosoftExt) + Result += "__declspec(allocate(\".objc_nlcatlist$B\")) \n"; + Result += "static struct _category_t *_OBJC_LABEL_NONLAZY_CATEGORY_$[] = {\n\t"; + for (unsigned i = 0, e = DefinedNonLazyCategories.size(); i < e; i++) { + Result += "\t&_OBJC_$_CATEGORY_"; + Result += + DefinedNonLazyCategories[i]->getClassInterface()->getNameAsString(); + Result += "_$_"; + Result += DefinedNonLazyCategories[i]->getNameAsString(); + Result += ",\n"; + } + Result += "};\n"; + } +} + +void RewriteModernObjC::WriteImageInfo(std::string &Result) { + if (LangOpts.MicrosoftExt) + Result += "__declspec(allocate(\".objc_imageinfo$B\")) \n"; + + Result += "static struct IMAGE_INFO { unsigned version; unsigned flag; } "; + // version 0, ObjCABI is 2 + Result += "_OBJC_IMAGE_INFO = { 0, 2 };\n"; +} + +/// RewriteObjCCategoryImplDecl - Rewrite metadata for each category +/// implementation. +void RewriteModernObjC::RewriteObjCCategoryImplDecl(ObjCCategoryImplDecl *IDecl, + std::string &Result) { + WriteModernMetadataDeclarations(Context, Result); + ObjCInterfaceDecl *ClassDecl = IDecl->getClassInterface(); + // Find category declaration for this implementation. + ObjCCategoryDecl *CDecl + = ClassDecl->FindCategoryDeclaration(IDecl->getIdentifier()); + + std::string FullCategoryName = ClassDecl->getNameAsString(); + FullCategoryName += "_$_"; + FullCategoryName += CDecl->getNameAsString(); + + // Build _objc_method_list for class's instance methods if needed + SmallVector<ObjCMethodDecl *, 32> InstanceMethods(IDecl->instance_methods()); + + // If any of our property implementations have associated getters or + // setters, produce metadata for them as well. + for (const auto *Prop : IDecl->property_impls()) { + if (Prop->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) + continue; + if (!Prop->getPropertyIvarDecl()) + continue; + ObjCPropertyDecl *PD = Prop->getPropertyDecl(); + if (!PD) + continue; + if (ObjCMethodDecl *Getter = PD->getGetterMethodDecl()) + InstanceMethods.push_back(Getter); + if (PD->isReadOnly()) + continue; + if (ObjCMethodDecl *Setter = PD->getSetterMethodDecl()) + InstanceMethods.push_back(Setter); + } + + Write_method_list_t_initializer(*this, Context, Result, InstanceMethods, + "_OBJC_$_CATEGORY_INSTANCE_METHODS_", + FullCategoryName, true); + + SmallVector<ObjCMethodDecl *, 32> ClassMethods(IDecl->class_methods()); + + Write_method_list_t_initializer(*this, Context, Result, ClassMethods, + "_OBJC_$_CATEGORY_CLASS_METHODS_", + FullCategoryName, true); + + // Protocols referenced in class declaration? + // Protocol's super protocol list + SmallVector<ObjCProtocolDecl *, 8> RefedProtocols(CDecl->protocols()); + for (auto *I : CDecl->protocols()) + // Must write out all protocol definitions in current qualifier list, + // and in their nested qualifiers before writing out current definition. + RewriteObjCProtocolMetaData(I, Result); + + Write_protocol_list_initializer(Context, Result, + RefedProtocols, + "_OBJC_CATEGORY_PROTOCOLS_$_", + FullCategoryName); + + // Protocol's property metadata. + SmallVector<ObjCPropertyDecl *, 8> ClassProperties( + CDecl->instance_properties()); + Write_prop_list_t_initializer(*this, Context, Result, ClassProperties, + /* Container */IDecl, + "_OBJC_$_PROP_LIST_", + FullCategoryName); + + Write_category_t(*this, Context, Result, + CDecl, + ClassDecl, + InstanceMethods, + ClassMethods, + RefedProtocols, + ClassProperties); + + // Determine if this category is also "non-lazy". + if (ImplementationIsNonLazy(IDecl)) + DefinedNonLazyCategories.push_back(CDecl); +} + +void RewriteModernObjC::RewriteCategorySetupInitHook(std::string &Result) { + int CatDefCount = CategoryImplementation.size(); + if (!CatDefCount) + return; + Result += "#pragma section(\".objc_inithooks$B\", long, read, write)\n"; + Result += "__declspec(allocate(\".objc_inithooks$B\")) "; + Result += "static void *OBJC_CATEGORY_SETUP[] = {\n"; + for (int i = 0; i < CatDefCount; i++) { + ObjCCategoryImplDecl *IDecl = CategoryImplementation[i]; + ObjCCategoryDecl *CatDecl= IDecl->getCategoryDecl(); + ObjCInterfaceDecl *ClassDecl = IDecl->getClassInterface(); + Result += "\t(void *)&OBJC_CATEGORY_SETUP_$_"; + Result += ClassDecl->getName(); + Result += "_$_"; + Result += CatDecl->getName(); + Result += ",\n"; + } + Result += "};\n"; +} + +// RewriteObjCMethodsMetaData - Rewrite methods metadata for instance or +/// class methods. +template<typename MethodIterator> +void RewriteModernObjC::RewriteObjCMethodsMetaData(MethodIterator MethodBegin, + MethodIterator MethodEnd, + bool IsInstanceMethod, + StringRef prefix, + StringRef ClassName, + std::string &Result) { + if (MethodBegin == MethodEnd) return; + + if (!objc_impl_method) { + /* struct _objc_method { + SEL _cmd; + char *method_types; + void *_imp; + } + */ + Result += "\nstruct _objc_method {\n"; + Result += "\tSEL _cmd;\n"; + Result += "\tchar *method_types;\n"; + Result += "\tvoid *_imp;\n"; + Result += "};\n"; + + objc_impl_method = true; + } + + // Build _objc_method_list for class's methods if needed + + /* struct { + struct _objc_method_list *next_method; + int method_count; + struct _objc_method method_list[]; + } + */ + unsigned NumMethods = std::distance(MethodBegin, MethodEnd); + Result += "\n"; + if (LangOpts.MicrosoftExt) { + if (IsInstanceMethod) + Result += "__declspec(allocate(\".inst_meth$B\")) "; + else + Result += "__declspec(allocate(\".cls_meth$B\")) "; + } + Result += "static struct {\n"; + Result += "\tstruct _objc_method_list *next_method;\n"; + Result += "\tint method_count;\n"; + Result += "\tstruct _objc_method method_list["; + Result += utostr(NumMethods); + Result += "];\n} _OBJC_"; + Result += prefix; + Result += IsInstanceMethod ? "INSTANCE" : "CLASS"; + Result += "_METHODS_"; + Result += ClassName; + Result += " __attribute__ ((used, section (\"__OBJC, __"; + Result += IsInstanceMethod ? "inst" : "cls"; + Result += "_meth\")))= "; + Result += "{\n\t0, " + utostr(NumMethods) + "\n"; + + Result += "\t,{{(SEL)\""; + Result += (*MethodBegin)->getSelector().getAsString().c_str(); + std::string MethodTypeString; + Context->getObjCEncodingForMethodDecl(*MethodBegin, MethodTypeString); + Result += "\", \""; + Result += MethodTypeString; + Result += "\", (void *)"; + Result += MethodInternalNames[*MethodBegin]; + Result += "}\n"; + for (++MethodBegin; MethodBegin != MethodEnd; ++MethodBegin) { + Result += "\t ,{(SEL)\""; + Result += (*MethodBegin)->getSelector().getAsString().c_str(); + std::string MethodTypeString; + Context->getObjCEncodingForMethodDecl(*MethodBegin, MethodTypeString); + Result += "\", \""; + Result += MethodTypeString; + Result += "\", (void *)"; + Result += MethodInternalNames[*MethodBegin]; + Result += "}\n"; + } + Result += "\t }\n};\n"; +} + +Stmt *RewriteModernObjC::RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV) { + SourceRange OldRange = IV->getSourceRange(); + Expr *BaseExpr = IV->getBase(); + + // Rewrite the base, but without actually doing replaces. + { + DisableReplaceStmtScope S(*this); + BaseExpr = cast<Expr>(RewriteFunctionBodyOrGlobalInitializer(BaseExpr)); + IV->setBase(BaseExpr); + } + + ObjCIvarDecl *D = IV->getDecl(); + + Expr *Replacement = IV; + + if (BaseExpr->getType()->isObjCObjectPointerType()) { + const ObjCInterfaceType *iFaceDecl = + dyn_cast<ObjCInterfaceType>(BaseExpr->getType()->getPointeeType()); + assert(iFaceDecl && "RewriteObjCIvarRefExpr - iFaceDecl is null"); + // lookup which class implements the instance variable. + ObjCInterfaceDecl *clsDeclared = nullptr; + iFaceDecl->getDecl()->lookupInstanceVariable(D->getIdentifier(), + clsDeclared); + assert(clsDeclared && "RewriteObjCIvarRefExpr(): Can't find class"); + + // Build name of symbol holding ivar offset. + std::string IvarOffsetName; + if (D->isBitField()) + ObjCIvarBitfieldGroupOffset(D, IvarOffsetName); + else + WriteInternalIvarName(clsDeclared, D, IvarOffsetName); + + ReferencedIvars[clsDeclared].insert(D); + + // cast offset to "char *". + CastExpr *castExpr = NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(Context->CharTy), + CK_BitCast, + BaseExpr); + VarDecl *NewVD = VarDecl::Create(*Context, TUDecl, SourceLocation(), + SourceLocation(), &Context->Idents.get(IvarOffsetName), + Context->UnsignedLongTy, nullptr, + SC_Extern); + DeclRefExpr *DRE = new (Context) + DeclRefExpr(*Context, NewVD, false, Context->UnsignedLongTy, + VK_LValue, SourceLocation()); + BinaryOperator *addExpr = + new (Context) BinaryOperator(castExpr, DRE, BO_Add, + Context->getPointerType(Context->CharTy), + VK_RValue, OK_Ordinary, SourceLocation(), FPOptions()); + // Don't forget the parens to enforce the proper binding. + ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), + SourceLocation(), + addExpr); + QualType IvarT = D->getType(); + if (D->isBitField()) + IvarT = GetGroupRecordTypeForObjCIvarBitfield(D); + + if (!isa<TypedefType>(IvarT) && IvarT->isRecordType()) { + RecordDecl *RD = IvarT->getAs<RecordType>()->getDecl(); + RD = RD->getDefinition(); + if (RD && !RD->getDeclName().getAsIdentifierInfo()) { + // decltype(((Foo_IMPL*)0)->bar) * + ObjCContainerDecl *CDecl = + dyn_cast<ObjCContainerDecl>(D->getDeclContext()); + // ivar in class extensions requires special treatment. + if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl)) + CDecl = CatDecl->getClassInterface(); + std::string RecName = CDecl->getName(); + RecName += "_IMPL"; + RecordDecl *RD = RecordDecl::Create( + *Context, TTK_Struct, TUDecl, SourceLocation(), SourceLocation(), + &Context->Idents.get(RecName)); + QualType PtrStructIMPL = Context->getPointerType(Context->getTagDeclType(RD)); + unsigned UnsignedIntSize = + static_cast<unsigned>(Context->getTypeSize(Context->UnsignedIntTy)); + Expr *Zero = IntegerLiteral::Create(*Context, + llvm::APInt(UnsignedIntSize, 0), + Context->UnsignedIntTy, SourceLocation()); + Zero = NoTypeInfoCStyleCastExpr(Context, PtrStructIMPL, CK_BitCast, Zero); + ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), + Zero); + FieldDecl *FD = FieldDecl::Create(*Context, nullptr, SourceLocation(), + SourceLocation(), + &Context->Idents.get(D->getNameAsString()), + IvarT, nullptr, + /*BitWidth=*/nullptr, + /*Mutable=*/true, ICIS_NoInit); + MemberExpr *ME = new (Context) + MemberExpr(PE, true, SourceLocation(), FD, SourceLocation(), + FD->getType(), VK_LValue, OK_Ordinary); + IvarT = Context->getDecltypeType(ME, ME->getType()); + } + } + convertObjCTypeToCStyleType(IvarT); + QualType castT = Context->getPointerType(IvarT); + + castExpr = NoTypeInfoCStyleCastExpr(Context, + castT, + CK_BitCast, + PE); + + + Expr *Exp = new (Context) UnaryOperator(castExpr, UO_Deref, IvarT, + VK_LValue, OK_Ordinary, + SourceLocation(), false); + PE = new (Context) ParenExpr(OldRange.getBegin(), + OldRange.getEnd(), + Exp); + + if (D->isBitField()) { + FieldDecl *FD = FieldDecl::Create(*Context, nullptr, SourceLocation(), + SourceLocation(), + &Context->Idents.get(D->getNameAsString()), + D->getType(), nullptr, + /*BitWidth=*/D->getBitWidth(), + /*Mutable=*/true, ICIS_NoInit); + MemberExpr *ME = new (Context) + MemberExpr(PE, /*isArrow*/ false, SourceLocation(), FD, + SourceLocation(), FD->getType(), VK_LValue, OK_Ordinary); + Replacement = ME; + + } + else + Replacement = PE; + } + + ReplaceStmtWithRange(IV, Replacement, OldRange); + return Replacement; +} + +#endif // CLANG_ENABLE_OBJC_REWRITER diff --git a/contrib/llvm/tools/clang/lib/Frontend/Rewrite/RewriteObjC.cpp b/contrib/llvm/tools/clang/lib/Frontend/Rewrite/RewriteObjC.cpp new file mode 100644 index 000000000000..3e018800b909 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/Rewrite/RewriteObjC.cpp @@ -0,0 +1,5883 @@ +//===--- RewriteObjC.cpp - Playground for the code rewriter ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Hacks and fun related to the code rewriter. +// +//===----------------------------------------------------------------------===// + +#include "clang/Rewrite/Frontend/ASTConsumers.h" +#include "clang/AST/AST.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/Attr.h" +#include "clang/AST/ParentMap.h" +#include "clang/Basic/CharInfo.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Config/config.h" +#include "clang/Lex/Lexer.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include <memory> + +#if CLANG_ENABLE_OBJC_REWRITER + +using namespace clang; +using llvm::utostr; + +namespace { + class RewriteObjC : public ASTConsumer { + protected: + enum { + BLOCK_FIELD_IS_OBJECT = 3, /* id, NSObject, __attribute__((NSObject)), + block, ... */ + BLOCK_FIELD_IS_BLOCK = 7, /* a block variable */ + BLOCK_FIELD_IS_BYREF = 8, /* the on stack structure holding the + __block variable */ + BLOCK_FIELD_IS_WEAK = 16, /* declared __weak, only used in byref copy + helpers */ + BLOCK_BYREF_CALLER = 128, /* called from __block (byref) copy/dispose + support routines */ + BLOCK_BYREF_CURRENT_MAX = 256 + }; + + enum { + BLOCK_NEEDS_FREE = (1 << 24), + BLOCK_HAS_COPY_DISPOSE = (1 << 25), + BLOCK_HAS_CXX_OBJ = (1 << 26), + BLOCK_IS_GC = (1 << 27), + BLOCK_IS_GLOBAL = (1 << 28), + BLOCK_HAS_DESCRIPTOR = (1 << 29) + }; + static const int OBJC_ABI_VERSION = 7; + + Rewriter Rewrite; + DiagnosticsEngine &Diags; + const LangOptions &LangOpts; + ASTContext *Context; + SourceManager *SM; + TranslationUnitDecl *TUDecl; + FileID MainFileID; + const char *MainFileStart, *MainFileEnd; + Stmt *CurrentBody; + ParentMap *PropParentMap; // created lazily. + std::string InFileName; + std::unique_ptr<raw_ostream> OutFile; + std::string Preamble; + + TypeDecl *ProtocolTypeDecl; + VarDecl *GlobalVarDecl; + unsigned RewriteFailedDiag; + // ObjC string constant support. + unsigned NumObjCStringLiterals; + VarDecl *ConstantStringClassReference; + RecordDecl *NSStringRecord; + + // ObjC foreach break/continue generation support. + int BcLabelCount; + + unsigned TryFinallyContainsReturnDiag; + // Needed for super. + ObjCMethodDecl *CurMethodDef; + RecordDecl *SuperStructDecl; + RecordDecl *ConstantStringDecl; + + FunctionDecl *MsgSendFunctionDecl; + FunctionDecl *MsgSendSuperFunctionDecl; + FunctionDecl *MsgSendStretFunctionDecl; + FunctionDecl *MsgSendSuperStretFunctionDecl; + FunctionDecl *MsgSendFpretFunctionDecl; + FunctionDecl *GetClassFunctionDecl; + FunctionDecl *GetMetaClassFunctionDecl; + FunctionDecl *GetSuperClassFunctionDecl; + FunctionDecl *SelGetUidFunctionDecl; + FunctionDecl *CFStringFunctionDecl; + FunctionDecl *SuperConstructorFunctionDecl; + FunctionDecl *CurFunctionDef; + FunctionDecl *CurFunctionDeclToDeclareForBlock; + + /* Misc. containers needed for meta-data rewrite. */ + SmallVector<ObjCImplementationDecl *, 8> ClassImplementation; + SmallVector<ObjCCategoryImplDecl *, 8> CategoryImplementation; + llvm::SmallPtrSet<ObjCInterfaceDecl*, 8> ObjCSynthesizedStructs; + llvm::SmallPtrSet<ObjCProtocolDecl*, 8> ObjCSynthesizedProtocols; + llvm::SmallPtrSet<ObjCInterfaceDecl*, 8> ObjCForwardDecls; + llvm::DenseMap<ObjCMethodDecl*, std::string> MethodInternalNames; + SmallVector<Stmt *, 32> Stmts; + SmallVector<int, 8> ObjCBcLabelNo; + // Remember all the @protocol(<expr>) expressions. + llvm::SmallPtrSet<ObjCProtocolDecl *, 32> ProtocolExprDecls; + + llvm::DenseSet<uint64_t> CopyDestroyCache; + + // Block expressions. + SmallVector<BlockExpr *, 32> Blocks; + SmallVector<int, 32> InnerDeclRefsCount; + SmallVector<DeclRefExpr *, 32> InnerDeclRefs; + + SmallVector<DeclRefExpr *, 32> BlockDeclRefs; + + // Block related declarations. + SmallVector<ValueDecl *, 8> BlockByCopyDecls; + llvm::SmallPtrSet<ValueDecl *, 8> BlockByCopyDeclsPtrSet; + SmallVector<ValueDecl *, 8> BlockByRefDecls; + llvm::SmallPtrSet<ValueDecl *, 8> BlockByRefDeclsPtrSet; + llvm::DenseMap<ValueDecl *, unsigned> BlockByRefDeclNo; + llvm::SmallPtrSet<ValueDecl *, 8> ImportedBlockDecls; + llvm::SmallPtrSet<VarDecl *, 8> ImportedLocalExternalDecls; + + llvm::DenseMap<BlockExpr *, std::string> RewrittenBlockExprs; + + // This maps an original source AST to it's rewritten form. This allows + // us to avoid rewriting the same node twice (which is very uncommon). + // This is needed to support some of the exotic property rewriting. + llvm::DenseMap<Stmt *, Stmt *> ReplacedNodes; + + // Needed for header files being rewritten + bool IsHeader; + bool SilenceRewriteMacroWarning; + bool objc_impl_method; + + bool DisableReplaceStmt; + class DisableReplaceStmtScope { + RewriteObjC &R; + bool SavedValue; + + public: + DisableReplaceStmtScope(RewriteObjC &R) + : R(R), SavedValue(R.DisableReplaceStmt) { + R.DisableReplaceStmt = true; + } + + ~DisableReplaceStmtScope() { + R.DisableReplaceStmt = SavedValue; + } + }; + + void InitializeCommon(ASTContext &context); + + public: + // Top Level Driver code. + bool HandleTopLevelDecl(DeclGroupRef D) override { + for (DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; ++I) { + if (ObjCInterfaceDecl *Class = dyn_cast<ObjCInterfaceDecl>(*I)) { + if (!Class->isThisDeclarationADefinition()) { + RewriteForwardClassDecl(D); + break; + } + } + + if (ObjCProtocolDecl *Proto = dyn_cast<ObjCProtocolDecl>(*I)) { + if (!Proto->isThisDeclarationADefinition()) { + RewriteForwardProtocolDecl(D); + break; + } + } + + HandleTopLevelSingleDecl(*I); + } + return true; + } + + void HandleTopLevelSingleDecl(Decl *D); + void HandleDeclInMainFile(Decl *D); + RewriteObjC(std::string inFile, std::unique_ptr<raw_ostream> OS, + DiagnosticsEngine &D, const LangOptions &LOpts, + bool silenceMacroWarn); + + ~RewriteObjC() override {} + + void HandleTranslationUnit(ASTContext &C) override; + + void ReplaceStmt(Stmt *Old, Stmt *New) { + ReplaceStmtWithRange(Old, New, Old->getSourceRange()); + } + + void ReplaceStmtWithRange(Stmt *Old, Stmt *New, SourceRange SrcRange) { + assert(Old != nullptr && New != nullptr && "Expected non-null Stmt's"); + + Stmt *ReplacingStmt = ReplacedNodes[Old]; + if (ReplacingStmt) + return; // We can't rewrite the same node twice. + + if (DisableReplaceStmt) + return; + + // Measure the old text. + int Size = Rewrite.getRangeSize(SrcRange); + if (Size == -1) { + Diags.Report(Context->getFullLoc(Old->getBeginLoc()), RewriteFailedDiag) + << Old->getSourceRange(); + return; + } + // Get the new text. + std::string SStr; + llvm::raw_string_ostream S(SStr); + New->printPretty(S, nullptr, PrintingPolicy(LangOpts)); + const std::string &Str = S.str(); + + // If replacement succeeded or warning disabled return with no warning. + if (!Rewrite.ReplaceText(SrcRange.getBegin(), Size, Str)) { + ReplacedNodes[Old] = New; + return; + } + if (SilenceRewriteMacroWarning) + return; + Diags.Report(Context->getFullLoc(Old->getBeginLoc()), RewriteFailedDiag) + << Old->getSourceRange(); + } + + void InsertText(SourceLocation Loc, StringRef Str, + bool InsertAfter = true) { + // If insertion succeeded or warning disabled return with no warning. + if (!Rewrite.InsertText(Loc, Str, InsertAfter) || + SilenceRewriteMacroWarning) + return; + + Diags.Report(Context->getFullLoc(Loc), RewriteFailedDiag); + } + + void ReplaceText(SourceLocation Start, unsigned OrigLength, + StringRef Str) { + // If removal succeeded or warning disabled return with no warning. + if (!Rewrite.ReplaceText(Start, OrigLength, Str) || + SilenceRewriteMacroWarning) + return; + + Diags.Report(Context->getFullLoc(Start), RewriteFailedDiag); + } + + // Syntactic Rewriting. + void RewriteRecordBody(RecordDecl *RD); + void RewriteInclude(); + void RewriteForwardClassDecl(DeclGroupRef D); + void RewriteForwardClassDecl(const SmallVectorImpl<Decl *> &DG); + void RewriteForwardClassEpilogue(ObjCInterfaceDecl *ClassDecl, + const std::string &typedefString); + void RewriteImplementations(); + void RewritePropertyImplDecl(ObjCPropertyImplDecl *PID, + ObjCImplementationDecl *IMD, + ObjCCategoryImplDecl *CID); + void RewriteInterfaceDecl(ObjCInterfaceDecl *Dcl); + void RewriteImplementationDecl(Decl *Dcl); + void RewriteObjCMethodDecl(const ObjCInterfaceDecl *IDecl, + ObjCMethodDecl *MDecl, std::string &ResultStr); + void RewriteTypeIntoString(QualType T, std::string &ResultStr, + const FunctionType *&FPRetType); + void RewriteByRefString(std::string &ResultStr, const std::string &Name, + ValueDecl *VD, bool def=false); + void RewriteCategoryDecl(ObjCCategoryDecl *Dcl); + void RewriteProtocolDecl(ObjCProtocolDecl *Dcl); + void RewriteForwardProtocolDecl(DeclGroupRef D); + void RewriteForwardProtocolDecl(const SmallVectorImpl<Decl *> &DG); + void RewriteMethodDeclaration(ObjCMethodDecl *Method); + void RewriteProperty(ObjCPropertyDecl *prop); + void RewriteFunctionDecl(FunctionDecl *FD); + void RewriteBlockPointerType(std::string& Str, QualType Type); + void RewriteBlockPointerTypeVariable(std::string& Str, ValueDecl *VD); + void RewriteBlockLiteralFunctionDecl(FunctionDecl *FD); + void RewriteObjCQualifiedInterfaceTypes(Decl *Dcl); + void RewriteTypeOfDecl(VarDecl *VD); + void RewriteObjCQualifiedInterfaceTypes(Expr *E); + + // Expression Rewriting. + Stmt *RewriteFunctionBodyOrGlobalInitializer(Stmt *S); + Stmt *RewriteAtEncode(ObjCEncodeExpr *Exp); + Stmt *RewritePropertyOrImplicitGetter(PseudoObjectExpr *Pseudo); + Stmt *RewritePropertyOrImplicitSetter(PseudoObjectExpr *Pseudo); + Stmt *RewriteAtSelector(ObjCSelectorExpr *Exp); + Stmt *RewriteMessageExpr(ObjCMessageExpr *Exp); + Stmt *RewriteObjCStringLiteral(ObjCStringLiteral *Exp); + Stmt *RewriteObjCProtocolExpr(ObjCProtocolExpr *Exp); + void RewriteTryReturnStmts(Stmt *S); + void RewriteSyncReturnStmts(Stmt *S, std::string buf); + Stmt *RewriteObjCTryStmt(ObjCAtTryStmt *S); + Stmt *RewriteObjCSynchronizedStmt(ObjCAtSynchronizedStmt *S); + Stmt *RewriteObjCThrowStmt(ObjCAtThrowStmt *S); + Stmt *RewriteObjCForCollectionStmt(ObjCForCollectionStmt *S, + SourceLocation OrigEnd); + Stmt *RewriteBreakStmt(BreakStmt *S); + Stmt *RewriteContinueStmt(ContinueStmt *S); + void RewriteCastExpr(CStyleCastExpr *CE); + + // Block rewriting. + void RewriteBlocksInFunctionProtoType(QualType funcType, NamedDecl *D); + + // Block specific rewrite rules. + void RewriteBlockPointerDecl(NamedDecl *VD); + void RewriteByRefVar(VarDecl *VD); + Stmt *RewriteBlockDeclRefExpr(DeclRefExpr *VD); + Stmt *RewriteLocalVariableExternalStorage(DeclRefExpr *DRE); + void RewriteBlockPointerFunctionArgs(FunctionDecl *FD); + + void RewriteObjCInternalStruct(ObjCInterfaceDecl *CDecl, + std::string &Result); + + void Initialize(ASTContext &context) override = 0; + + // Metadata Rewriting. + virtual void RewriteMetaDataIntoBuffer(std::string &Result) = 0; + virtual void RewriteObjCProtocolListMetaData(const ObjCList<ObjCProtocolDecl> &Prots, + StringRef prefix, + StringRef ClassName, + std::string &Result) = 0; + virtual void RewriteObjCCategoryImplDecl(ObjCCategoryImplDecl *CDecl, + std::string &Result) = 0; + virtual void RewriteObjCProtocolMetaData(ObjCProtocolDecl *Protocol, + StringRef prefix, + StringRef ClassName, + std::string &Result) = 0; + virtual void RewriteObjCClassMetaData(ObjCImplementationDecl *IDecl, + std::string &Result) = 0; + + // Rewriting ivar access + virtual Stmt *RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV) = 0; + virtual void RewriteIvarOffsetComputation(ObjCIvarDecl *ivar, + std::string &Result) = 0; + + // Misc. AST transformation routines. Sometimes they end up calling + // rewriting routines on the new ASTs. + CallExpr *SynthesizeCallToFunctionDecl(FunctionDecl *FD, + ArrayRef<Expr *> Args, + SourceLocation StartLoc=SourceLocation(), + SourceLocation EndLoc=SourceLocation()); + CallExpr *SynthMsgSendStretCallExpr(FunctionDecl *MsgSendStretFlavor, + QualType msgSendType, + QualType returnType, + SmallVectorImpl<QualType> &ArgTypes, + SmallVectorImpl<Expr*> &MsgExprs, + ObjCMethodDecl *Method); + Stmt *SynthMessageExpr(ObjCMessageExpr *Exp, + SourceLocation StartLoc=SourceLocation(), + SourceLocation EndLoc=SourceLocation()); + + void SynthCountByEnumWithState(std::string &buf); + void SynthMsgSendFunctionDecl(); + void SynthMsgSendSuperFunctionDecl(); + void SynthMsgSendStretFunctionDecl(); + void SynthMsgSendFpretFunctionDecl(); + void SynthMsgSendSuperStretFunctionDecl(); + void SynthGetClassFunctionDecl(); + void SynthGetMetaClassFunctionDecl(); + void SynthGetSuperClassFunctionDecl(); + void SynthSelGetUidFunctionDecl(); + void SynthSuperConstructorFunctionDecl(); + + std::string SynthesizeByrefCopyDestroyHelper(VarDecl *VD, int flag); + std::string SynthesizeBlockHelperFuncs(BlockExpr *CE, int i, + StringRef funcName, std::string Tag); + std::string SynthesizeBlockFunc(BlockExpr *CE, int i, + StringRef funcName, std::string Tag); + std::string SynthesizeBlockImpl(BlockExpr *CE, + std::string Tag, std::string Desc); + std::string SynthesizeBlockDescriptor(std::string DescTag, + std::string ImplTag, + int i, StringRef funcName, + unsigned hasCopy); + Stmt *SynthesizeBlockCall(CallExpr *Exp, const Expr* BlockExp); + void SynthesizeBlockLiterals(SourceLocation FunLocStart, + StringRef FunName); + FunctionDecl *SynthBlockInitFunctionDecl(StringRef name); + Stmt *SynthBlockInitExpr(BlockExpr *Exp, + const SmallVectorImpl<DeclRefExpr *> &InnerBlockDeclRefs); + + // Misc. helper routines. + QualType getProtocolType(); + void WarnAboutReturnGotoStmts(Stmt *S); + void HasReturnStmts(Stmt *S, bool &hasReturns); + void CheckFunctionPointerDecl(QualType dType, NamedDecl *ND); + void InsertBlockLiteralsWithinFunction(FunctionDecl *FD); + void InsertBlockLiteralsWithinMethod(ObjCMethodDecl *MD); + + bool IsDeclStmtInForeachHeader(DeclStmt *DS); + void CollectBlockDeclRefInfo(BlockExpr *Exp); + void GetBlockDeclRefExprs(Stmt *S); + void GetInnerBlockDeclRefExprs(Stmt *S, + SmallVectorImpl<DeclRefExpr *> &InnerBlockDeclRefs, + llvm::SmallPtrSetImpl<const DeclContext *> &InnerContexts); + + // We avoid calling Type::isBlockPointerType(), since it operates on the + // canonical type. We only care if the top-level type is a closure pointer. + bool isTopLevelBlockPointerType(QualType T) { + return isa<BlockPointerType>(T); + } + + /// convertBlockPointerToFunctionPointer - Converts a block-pointer type + /// to a function pointer type and upon success, returns true; false + /// otherwise. + bool convertBlockPointerToFunctionPointer(QualType &T) { + if (isTopLevelBlockPointerType(T)) { + const BlockPointerType *BPT = T->getAs<BlockPointerType>(); + T = Context->getPointerType(BPT->getPointeeType()); + return true; + } + return false; + } + + bool needToScanForQualifiers(QualType T); + QualType getSuperStructType(); + QualType getConstantStringStructType(); + QualType convertFunctionTypeOfBlocks(const FunctionType *FT); + bool BufferContainsPPDirectives(const char *startBuf, const char *endBuf); + + void convertToUnqualifiedObjCType(QualType &T) { + if (T->isObjCQualifiedIdType()) + T = Context->getObjCIdType(); + else if (T->isObjCQualifiedClassType()) + T = Context->getObjCClassType(); + else if (T->isObjCObjectPointerType() && + T->getPointeeType()->isObjCQualifiedInterfaceType()) { + if (const ObjCObjectPointerType * OBJPT = + T->getAsObjCInterfacePointerType()) { + const ObjCInterfaceType *IFaceT = OBJPT->getInterfaceType(); + T = QualType(IFaceT, 0); + T = Context->getPointerType(T); + } + } + } + + // FIXME: This predicate seems like it would be useful to add to ASTContext. + bool isObjCType(QualType T) { + if (!LangOpts.ObjC) + return false; + + QualType OCT = Context->getCanonicalType(T).getUnqualifiedType(); + + if (OCT == Context->getCanonicalType(Context->getObjCIdType()) || + OCT == Context->getCanonicalType(Context->getObjCClassType())) + return true; + + if (const PointerType *PT = OCT->getAs<PointerType>()) { + if (isa<ObjCInterfaceType>(PT->getPointeeType()) || + PT->getPointeeType()->isObjCQualifiedIdType()) + return true; + } + return false; + } + bool PointerTypeTakesAnyBlockArguments(QualType QT); + bool PointerTypeTakesAnyObjCQualifiedType(QualType QT); + void GetExtentOfArgList(const char *Name, const char *&LParen, + const char *&RParen); + + void QuoteDoublequotes(std::string &From, std::string &To) { + for (unsigned i = 0; i < From.length(); i++) { + if (From[i] == '"') + To += "\\\""; + else + To += From[i]; + } + } + + QualType getSimpleFunctionType(QualType result, + ArrayRef<QualType> args, + bool variadic = false) { + if (result == Context->getObjCInstanceType()) + result = Context->getObjCIdType(); + FunctionProtoType::ExtProtoInfo fpi; + fpi.Variadic = variadic; + return Context->getFunctionType(result, args, fpi); + } + + // Helper function: create a CStyleCastExpr with trivial type source info. + CStyleCastExpr* NoTypeInfoCStyleCastExpr(ASTContext *Ctx, QualType Ty, + CastKind Kind, Expr *E) { + TypeSourceInfo *TInfo = Ctx->getTrivialTypeSourceInfo(Ty, SourceLocation()); + return CStyleCastExpr::Create(*Ctx, Ty, VK_RValue, Kind, E, nullptr, + TInfo, SourceLocation(), SourceLocation()); + } + + StringLiteral *getStringLiteral(StringRef Str) { + QualType StrType = Context->getConstantArrayType( + Context->CharTy, llvm::APInt(32, Str.size() + 1), ArrayType::Normal, + 0); + return StringLiteral::Create(*Context, Str, StringLiteral::Ascii, + /*Pascal=*/false, StrType, SourceLocation()); + } + }; + + class RewriteObjCFragileABI : public RewriteObjC { + public: + RewriteObjCFragileABI(std::string inFile, std::unique_ptr<raw_ostream> OS, + DiagnosticsEngine &D, const LangOptions &LOpts, + bool silenceMacroWarn) + : RewriteObjC(inFile, std::move(OS), D, LOpts, silenceMacroWarn) {} + + ~RewriteObjCFragileABI() override {} + void Initialize(ASTContext &context) override; + + // Rewriting metadata + template<typename MethodIterator> + void RewriteObjCMethodsMetaData(MethodIterator MethodBegin, + MethodIterator MethodEnd, + bool IsInstanceMethod, + StringRef prefix, + StringRef ClassName, + std::string &Result); + void RewriteObjCProtocolMetaData(ObjCProtocolDecl *Protocol, + StringRef prefix, StringRef ClassName, + std::string &Result) override; + void RewriteObjCProtocolListMetaData( + const ObjCList<ObjCProtocolDecl> &Prots, + StringRef prefix, StringRef ClassName, std::string &Result) override; + void RewriteObjCClassMetaData(ObjCImplementationDecl *IDecl, + std::string &Result) override; + void RewriteMetaDataIntoBuffer(std::string &Result) override; + void RewriteObjCCategoryImplDecl(ObjCCategoryImplDecl *CDecl, + std::string &Result) override; + + // Rewriting ivar + void RewriteIvarOffsetComputation(ObjCIvarDecl *ivar, + std::string &Result) override; + Stmt *RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV) override; + }; +} // end anonymous namespace + +void RewriteObjC::RewriteBlocksInFunctionProtoType(QualType funcType, + NamedDecl *D) { + if (const FunctionProtoType *fproto + = dyn_cast<FunctionProtoType>(funcType.IgnoreParens())) { + for (const auto &I : fproto->param_types()) + if (isTopLevelBlockPointerType(I)) { + // All the args are checked/rewritten. Don't call twice! + RewriteBlockPointerDecl(D); + break; + } + } +} + +void RewriteObjC::CheckFunctionPointerDecl(QualType funcType, NamedDecl *ND) { + const PointerType *PT = funcType->getAs<PointerType>(); + if (PT && PointerTypeTakesAnyBlockArguments(funcType)) + RewriteBlocksInFunctionProtoType(PT->getPointeeType(), ND); +} + +static bool IsHeaderFile(const std::string &Filename) { + std::string::size_type DotPos = Filename.rfind('.'); + + if (DotPos == std::string::npos) { + // no file extension + return false; + } + + std::string Ext = std::string(Filename.begin()+DotPos+1, Filename.end()); + // C header: .h + // C++ header: .hh or .H; + return Ext == "h" || Ext == "hh" || Ext == "H"; +} + +RewriteObjC::RewriteObjC(std::string inFile, std::unique_ptr<raw_ostream> OS, + DiagnosticsEngine &D, const LangOptions &LOpts, + bool silenceMacroWarn) + : Diags(D), LangOpts(LOpts), InFileName(inFile), OutFile(std::move(OS)), + SilenceRewriteMacroWarning(silenceMacroWarn) { + IsHeader = IsHeaderFile(inFile); + RewriteFailedDiag = Diags.getCustomDiagID(DiagnosticsEngine::Warning, + "rewriting sub-expression within a macro (may not be correct)"); + TryFinallyContainsReturnDiag = Diags.getCustomDiagID( + DiagnosticsEngine::Warning, + "rewriter doesn't support user-specified control flow semantics " + "for @try/@finally (code may not execute properly)"); +} + +std::unique_ptr<ASTConsumer> +clang::CreateObjCRewriter(const std::string &InFile, + std::unique_ptr<raw_ostream> OS, + DiagnosticsEngine &Diags, const LangOptions &LOpts, + bool SilenceRewriteMacroWarning) { + return llvm::make_unique<RewriteObjCFragileABI>( + InFile, std::move(OS), Diags, LOpts, SilenceRewriteMacroWarning); +} + +void RewriteObjC::InitializeCommon(ASTContext &context) { + Context = &context; + SM = &Context->getSourceManager(); + TUDecl = Context->getTranslationUnitDecl(); + MsgSendFunctionDecl = nullptr; + MsgSendSuperFunctionDecl = nullptr; + MsgSendStretFunctionDecl = nullptr; + MsgSendSuperStretFunctionDecl = nullptr; + MsgSendFpretFunctionDecl = nullptr; + GetClassFunctionDecl = nullptr; + GetMetaClassFunctionDecl = nullptr; + GetSuperClassFunctionDecl = nullptr; + SelGetUidFunctionDecl = nullptr; + CFStringFunctionDecl = nullptr; + ConstantStringClassReference = nullptr; + NSStringRecord = nullptr; + CurMethodDef = nullptr; + CurFunctionDef = nullptr; + CurFunctionDeclToDeclareForBlock = nullptr; + GlobalVarDecl = nullptr; + SuperStructDecl = nullptr; + ProtocolTypeDecl = nullptr; + ConstantStringDecl = nullptr; + BcLabelCount = 0; + SuperConstructorFunctionDecl = nullptr; + NumObjCStringLiterals = 0; + PropParentMap = nullptr; + CurrentBody = nullptr; + DisableReplaceStmt = false; + objc_impl_method = false; + + // Get the ID and start/end of the main file. + MainFileID = SM->getMainFileID(); + const llvm::MemoryBuffer *MainBuf = SM->getBuffer(MainFileID); + MainFileStart = MainBuf->getBufferStart(); + MainFileEnd = MainBuf->getBufferEnd(); + + Rewrite.setSourceMgr(Context->getSourceManager(), Context->getLangOpts()); +} + +//===----------------------------------------------------------------------===// +// Top Level Driver Code +//===----------------------------------------------------------------------===// + +void RewriteObjC::HandleTopLevelSingleDecl(Decl *D) { + if (Diags.hasErrorOccurred()) + return; + + // Two cases: either the decl could be in the main file, or it could be in a + // #included file. If the former, rewrite it now. If the later, check to see + // if we rewrote the #include/#import. + SourceLocation Loc = D->getLocation(); + Loc = SM->getExpansionLoc(Loc); + + // If this is for a builtin, ignore it. + if (Loc.isInvalid()) return; + + // Look for built-in declarations that we need to refer during the rewrite. + if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + RewriteFunctionDecl(FD); + } else if (VarDecl *FVD = dyn_cast<VarDecl>(D)) { + // declared in <Foundation/NSString.h> + if (FVD->getName() == "_NSConstantStringClassReference") { + ConstantStringClassReference = FVD; + return; + } + } else if (ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(D)) { + if (ID->isThisDeclarationADefinition()) + RewriteInterfaceDecl(ID); + } else if (ObjCCategoryDecl *CD = dyn_cast<ObjCCategoryDecl>(D)) { + RewriteCategoryDecl(CD); + } else if (ObjCProtocolDecl *PD = dyn_cast<ObjCProtocolDecl>(D)) { + if (PD->isThisDeclarationADefinition()) + RewriteProtocolDecl(PD); + } else if (LinkageSpecDecl *LSD = dyn_cast<LinkageSpecDecl>(D)) { + // Recurse into linkage specifications + for (DeclContext::decl_iterator DI = LSD->decls_begin(), + DIEnd = LSD->decls_end(); + DI != DIEnd; ) { + if (ObjCInterfaceDecl *IFace = dyn_cast<ObjCInterfaceDecl>((*DI))) { + if (!IFace->isThisDeclarationADefinition()) { + SmallVector<Decl *, 8> DG; + SourceLocation StartLoc = IFace->getBeginLoc(); + do { + if (isa<ObjCInterfaceDecl>(*DI) && + !cast<ObjCInterfaceDecl>(*DI)->isThisDeclarationADefinition() && + StartLoc == (*DI)->getBeginLoc()) + DG.push_back(*DI); + else + break; + + ++DI; + } while (DI != DIEnd); + RewriteForwardClassDecl(DG); + continue; + } + } + + if (ObjCProtocolDecl *Proto = dyn_cast<ObjCProtocolDecl>((*DI))) { + if (!Proto->isThisDeclarationADefinition()) { + SmallVector<Decl *, 8> DG; + SourceLocation StartLoc = Proto->getBeginLoc(); + do { + if (isa<ObjCProtocolDecl>(*DI) && + !cast<ObjCProtocolDecl>(*DI)->isThisDeclarationADefinition() && + StartLoc == (*DI)->getBeginLoc()) + DG.push_back(*DI); + else + break; + + ++DI; + } while (DI != DIEnd); + RewriteForwardProtocolDecl(DG); + continue; + } + } + + HandleTopLevelSingleDecl(*DI); + ++DI; + } + } + // If we have a decl in the main file, see if we should rewrite it. + if (SM->isWrittenInMainFile(Loc)) + return HandleDeclInMainFile(D); +} + +//===----------------------------------------------------------------------===// +// Syntactic (non-AST) Rewriting Code +//===----------------------------------------------------------------------===// + +void RewriteObjC::RewriteInclude() { + SourceLocation LocStart = SM->getLocForStartOfFile(MainFileID); + StringRef MainBuf = SM->getBufferData(MainFileID); + const char *MainBufStart = MainBuf.begin(); + const char *MainBufEnd = MainBuf.end(); + size_t ImportLen = strlen("import"); + + // Loop over the whole file, looking for includes. + for (const char *BufPtr = MainBufStart; BufPtr < MainBufEnd; ++BufPtr) { + if (*BufPtr == '#') { + if (++BufPtr == MainBufEnd) + return; + while (*BufPtr == ' ' || *BufPtr == '\t') + if (++BufPtr == MainBufEnd) + return; + if (!strncmp(BufPtr, "import", ImportLen)) { + // replace import with include + SourceLocation ImportLoc = + LocStart.getLocWithOffset(BufPtr-MainBufStart); + ReplaceText(ImportLoc, ImportLen, "include"); + BufPtr += ImportLen; + } + } + } +} + +static std::string getIvarAccessString(ObjCIvarDecl *OID) { + const ObjCInterfaceDecl *ClassDecl = OID->getContainingInterface(); + std::string S; + S = "((struct "; + S += ClassDecl->getIdentifier()->getName(); + S += "_IMPL *)self)->"; + S += OID->getName(); + return S; +} + +void RewriteObjC::RewritePropertyImplDecl(ObjCPropertyImplDecl *PID, + ObjCImplementationDecl *IMD, + ObjCCategoryImplDecl *CID) { + static bool objcGetPropertyDefined = false; + static bool objcSetPropertyDefined = false; + SourceLocation startLoc = PID->getBeginLoc(); + InsertText(startLoc, "// "); + const char *startBuf = SM->getCharacterData(startLoc); + assert((*startBuf == '@') && "bogus @synthesize location"); + const char *semiBuf = strchr(startBuf, ';'); + assert((*semiBuf == ';') && "@synthesize: can't find ';'"); + SourceLocation onePastSemiLoc = + startLoc.getLocWithOffset(semiBuf-startBuf+1); + + if (PID->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) + return; // FIXME: is this correct? + + // Generate the 'getter' function. + ObjCPropertyDecl *PD = PID->getPropertyDecl(); + ObjCIvarDecl *OID = PID->getPropertyIvarDecl(); + + if (!OID) + return; + unsigned Attributes = PD->getPropertyAttributes(); + if (!PD->getGetterMethodDecl()->isDefined()) { + bool GenGetProperty = !(Attributes & ObjCPropertyDecl::OBJC_PR_nonatomic) && + (Attributes & (ObjCPropertyDecl::OBJC_PR_retain | + ObjCPropertyDecl::OBJC_PR_copy)); + std::string Getr; + if (GenGetProperty && !objcGetPropertyDefined) { + objcGetPropertyDefined = true; + // FIXME. Is this attribute correct in all cases? + Getr = "\nextern \"C\" __declspec(dllimport) " + "id objc_getProperty(id, SEL, long, bool);\n"; + } + RewriteObjCMethodDecl(OID->getContainingInterface(), + PD->getGetterMethodDecl(), Getr); + Getr += "{ "; + // Synthesize an explicit cast to gain access to the ivar. + // See objc-act.c:objc_synthesize_new_getter() for details. + if (GenGetProperty) { + // return objc_getProperty(self, _cmd, offsetof(ClassDecl, OID), 1) + Getr += "typedef "; + const FunctionType *FPRetType = nullptr; + RewriteTypeIntoString(PD->getGetterMethodDecl()->getReturnType(), Getr, + FPRetType); + Getr += " _TYPE"; + if (FPRetType) { + Getr += ")"; // close the precedence "scope" for "*". + + // Now, emit the argument types (if any). + if (const FunctionProtoType *FT = dyn_cast<FunctionProtoType>(FPRetType)){ + Getr += "("; + for (unsigned i = 0, e = FT->getNumParams(); i != e; ++i) { + if (i) Getr += ", "; + std::string ParamStr = + FT->getParamType(i).getAsString(Context->getPrintingPolicy()); + Getr += ParamStr; + } + if (FT->isVariadic()) { + if (FT->getNumParams()) + Getr += ", "; + Getr += "..."; + } + Getr += ")"; + } else + Getr += "()"; + } + Getr += ";\n"; + Getr += "return (_TYPE)"; + Getr += "objc_getProperty(self, _cmd, "; + RewriteIvarOffsetComputation(OID, Getr); + Getr += ", 1)"; + } + else + Getr += "return " + getIvarAccessString(OID); + Getr += "; }"; + InsertText(onePastSemiLoc, Getr); + } + + if (PD->isReadOnly() || PD->getSetterMethodDecl()->isDefined()) + return; + + // Generate the 'setter' function. + std::string Setr; + bool GenSetProperty = Attributes & (ObjCPropertyDecl::OBJC_PR_retain | + ObjCPropertyDecl::OBJC_PR_copy); + if (GenSetProperty && !objcSetPropertyDefined) { + objcSetPropertyDefined = true; + // FIXME. Is this attribute correct in all cases? + Setr = "\nextern \"C\" __declspec(dllimport) " + "void objc_setProperty (id, SEL, long, id, bool, bool);\n"; + } + + RewriteObjCMethodDecl(OID->getContainingInterface(), + PD->getSetterMethodDecl(), Setr); + Setr += "{ "; + // Synthesize an explicit cast to initialize the ivar. + // See objc-act.c:objc_synthesize_new_setter() for details. + if (GenSetProperty) { + Setr += "objc_setProperty (self, _cmd, "; + RewriteIvarOffsetComputation(OID, Setr); + Setr += ", (id)"; + Setr += PD->getName(); + Setr += ", "; + if (Attributes & ObjCPropertyDecl::OBJC_PR_nonatomic) + Setr += "0, "; + else + Setr += "1, "; + if (Attributes & ObjCPropertyDecl::OBJC_PR_copy) + Setr += "1)"; + else + Setr += "0)"; + } + else { + Setr += getIvarAccessString(OID) + " = "; + Setr += PD->getName(); + } + Setr += "; }"; + InsertText(onePastSemiLoc, Setr); +} + +static void RewriteOneForwardClassDecl(ObjCInterfaceDecl *ForwardDecl, + std::string &typedefString) { + typedefString += "#ifndef _REWRITER_typedef_"; + typedefString += ForwardDecl->getNameAsString(); + typedefString += "\n"; + typedefString += "#define _REWRITER_typedef_"; + typedefString += ForwardDecl->getNameAsString(); + typedefString += "\n"; + typedefString += "typedef struct objc_object "; + typedefString += ForwardDecl->getNameAsString(); + typedefString += ";\n#endif\n"; +} + +void RewriteObjC::RewriteForwardClassEpilogue(ObjCInterfaceDecl *ClassDecl, + const std::string &typedefString) { + SourceLocation startLoc = ClassDecl->getBeginLoc(); + const char *startBuf = SM->getCharacterData(startLoc); + const char *semiPtr = strchr(startBuf, ';'); + // Replace the @class with typedefs corresponding to the classes. + ReplaceText(startLoc, semiPtr - startBuf + 1, typedefString); +} + +void RewriteObjC::RewriteForwardClassDecl(DeclGroupRef D) { + std::string typedefString; + for (DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; ++I) { + ObjCInterfaceDecl *ForwardDecl = cast<ObjCInterfaceDecl>(*I); + if (I == D.begin()) { + // Translate to typedef's that forward reference structs with the same name + // as the class. As a convenience, we include the original declaration + // as a comment. + typedefString += "// @class "; + typedefString += ForwardDecl->getNameAsString(); + typedefString += ";\n"; + } + RewriteOneForwardClassDecl(ForwardDecl, typedefString); + } + DeclGroupRef::iterator I = D.begin(); + RewriteForwardClassEpilogue(cast<ObjCInterfaceDecl>(*I), typedefString); +} + +void RewriteObjC::RewriteForwardClassDecl(const SmallVectorImpl<Decl *> &D) { + std::string typedefString; + for (unsigned i = 0; i < D.size(); i++) { + ObjCInterfaceDecl *ForwardDecl = cast<ObjCInterfaceDecl>(D[i]); + if (i == 0) { + typedefString += "// @class "; + typedefString += ForwardDecl->getNameAsString(); + typedefString += ";\n"; + } + RewriteOneForwardClassDecl(ForwardDecl, typedefString); + } + RewriteForwardClassEpilogue(cast<ObjCInterfaceDecl>(D[0]), typedefString); +} + +void RewriteObjC::RewriteMethodDeclaration(ObjCMethodDecl *Method) { + // When method is a synthesized one, such as a getter/setter there is + // nothing to rewrite. + if (Method->isImplicit()) + return; + SourceLocation LocStart = Method->getBeginLoc(); + SourceLocation LocEnd = Method->getEndLoc(); + + if (SM->getExpansionLineNumber(LocEnd) > + SM->getExpansionLineNumber(LocStart)) { + InsertText(LocStart, "#if 0\n"); + ReplaceText(LocEnd, 1, ";\n#endif\n"); + } else { + InsertText(LocStart, "// "); + } +} + +void RewriteObjC::RewriteProperty(ObjCPropertyDecl *prop) { + SourceLocation Loc = prop->getAtLoc(); + + ReplaceText(Loc, 0, "// "); + // FIXME: handle properties that are declared across multiple lines. +} + +void RewriteObjC::RewriteCategoryDecl(ObjCCategoryDecl *CatDecl) { + SourceLocation LocStart = CatDecl->getBeginLoc(); + + // FIXME: handle category headers that are declared across multiple lines. + ReplaceText(LocStart, 0, "// "); + + for (auto *I : CatDecl->instance_properties()) + RewriteProperty(I); + for (auto *I : CatDecl->instance_methods()) + RewriteMethodDeclaration(I); + for (auto *I : CatDecl->class_methods()) + RewriteMethodDeclaration(I); + + // Lastly, comment out the @end. + ReplaceText(CatDecl->getAtEndRange().getBegin(), + strlen("@end"), "/* @end */"); +} + +void RewriteObjC::RewriteProtocolDecl(ObjCProtocolDecl *PDecl) { + SourceLocation LocStart = PDecl->getBeginLoc(); + assert(PDecl->isThisDeclarationADefinition()); + + // FIXME: handle protocol headers that are declared across multiple lines. + ReplaceText(LocStart, 0, "// "); + + for (auto *I : PDecl->instance_methods()) + RewriteMethodDeclaration(I); + for (auto *I : PDecl->class_methods()) + RewriteMethodDeclaration(I); + for (auto *I : PDecl->instance_properties()) + RewriteProperty(I); + + // Lastly, comment out the @end. + SourceLocation LocEnd = PDecl->getAtEndRange().getBegin(); + ReplaceText(LocEnd, strlen("@end"), "/* @end */"); + + // Must comment out @optional/@required + const char *startBuf = SM->getCharacterData(LocStart); + const char *endBuf = SM->getCharacterData(LocEnd); + for (const char *p = startBuf; p < endBuf; p++) { + if (*p == '@' && !strncmp(p+1, "optional", strlen("optional"))) { + SourceLocation OptionalLoc = LocStart.getLocWithOffset(p-startBuf); + ReplaceText(OptionalLoc, strlen("@optional"), "/* @optional */"); + + } + else if (*p == '@' && !strncmp(p+1, "required", strlen("required"))) { + SourceLocation OptionalLoc = LocStart.getLocWithOffset(p-startBuf); + ReplaceText(OptionalLoc, strlen("@required"), "/* @required */"); + + } + } +} + +void RewriteObjC::RewriteForwardProtocolDecl(DeclGroupRef D) { + SourceLocation LocStart = (*D.begin())->getBeginLoc(); + if (LocStart.isInvalid()) + llvm_unreachable("Invalid SourceLocation"); + // FIXME: handle forward protocol that are declared across multiple lines. + ReplaceText(LocStart, 0, "// "); +} + +void +RewriteObjC::RewriteForwardProtocolDecl(const SmallVectorImpl<Decl *> &DG) { + SourceLocation LocStart = DG[0]->getBeginLoc(); + if (LocStart.isInvalid()) + llvm_unreachable("Invalid SourceLocation"); + // FIXME: handle forward protocol that are declared across multiple lines. + ReplaceText(LocStart, 0, "// "); +} + +void RewriteObjC::RewriteTypeIntoString(QualType T, std::string &ResultStr, + const FunctionType *&FPRetType) { + if (T->isObjCQualifiedIdType()) + ResultStr += "id"; + else if (T->isFunctionPointerType() || + T->isBlockPointerType()) { + // needs special handling, since pointer-to-functions have special + // syntax (where a decaration models use). + QualType retType = T; + QualType PointeeTy; + if (const PointerType* PT = retType->getAs<PointerType>()) + PointeeTy = PT->getPointeeType(); + else if (const BlockPointerType *BPT = retType->getAs<BlockPointerType>()) + PointeeTy = BPT->getPointeeType(); + if ((FPRetType = PointeeTy->getAs<FunctionType>())) { + ResultStr += + FPRetType->getReturnType().getAsString(Context->getPrintingPolicy()); + ResultStr += "(*"; + } + } else + ResultStr += T.getAsString(Context->getPrintingPolicy()); +} + +void RewriteObjC::RewriteObjCMethodDecl(const ObjCInterfaceDecl *IDecl, + ObjCMethodDecl *OMD, + std::string &ResultStr) { + //fprintf(stderr,"In RewriteObjCMethodDecl\n"); + const FunctionType *FPRetType = nullptr; + ResultStr += "\nstatic "; + RewriteTypeIntoString(OMD->getReturnType(), ResultStr, FPRetType); + ResultStr += " "; + + // Unique method name + std::string NameStr; + + if (OMD->isInstanceMethod()) + NameStr += "_I_"; + else + NameStr += "_C_"; + + NameStr += IDecl->getNameAsString(); + NameStr += "_"; + + if (ObjCCategoryImplDecl *CID = + dyn_cast<ObjCCategoryImplDecl>(OMD->getDeclContext())) { + NameStr += CID->getNameAsString(); + NameStr += "_"; + } + // Append selector names, replacing ':' with '_' + { + std::string selString = OMD->getSelector().getAsString(); + int len = selString.size(); + for (int i = 0; i < len; i++) + if (selString[i] == ':') + selString[i] = '_'; + NameStr += selString; + } + // Remember this name for metadata emission + MethodInternalNames[OMD] = NameStr; + ResultStr += NameStr; + + // Rewrite arguments + ResultStr += "("; + + // invisible arguments + if (OMD->isInstanceMethod()) { + QualType selfTy = Context->getObjCInterfaceType(IDecl); + selfTy = Context->getPointerType(selfTy); + if (!LangOpts.MicrosoftExt) { + if (ObjCSynthesizedStructs.count(const_cast<ObjCInterfaceDecl*>(IDecl))) + ResultStr += "struct "; + } + // When rewriting for Microsoft, explicitly omit the structure name. + ResultStr += IDecl->getNameAsString(); + ResultStr += " *"; + } + else + ResultStr += Context->getObjCClassType().getAsString( + Context->getPrintingPolicy()); + + ResultStr += " self, "; + ResultStr += Context->getObjCSelType().getAsString(Context->getPrintingPolicy()); + ResultStr += " _cmd"; + + // Method arguments. + for (const auto *PDecl : OMD->parameters()) { + ResultStr += ", "; + if (PDecl->getType()->isObjCQualifiedIdType()) { + ResultStr += "id "; + ResultStr += PDecl->getNameAsString(); + } else { + std::string Name = PDecl->getNameAsString(); + QualType QT = PDecl->getType(); + // Make sure we convert "t (^)(...)" to "t (*)(...)". + (void)convertBlockPointerToFunctionPointer(QT); + QT.getAsStringInternal(Name, Context->getPrintingPolicy()); + ResultStr += Name; + } + } + if (OMD->isVariadic()) + ResultStr += ", ..."; + ResultStr += ") "; + + if (FPRetType) { + ResultStr += ")"; // close the precedence "scope" for "*". + + // Now, emit the argument types (if any). + if (const FunctionProtoType *FT = dyn_cast<FunctionProtoType>(FPRetType)) { + ResultStr += "("; + for (unsigned i = 0, e = FT->getNumParams(); i != e; ++i) { + if (i) ResultStr += ", "; + std::string ParamStr = + FT->getParamType(i).getAsString(Context->getPrintingPolicy()); + ResultStr += ParamStr; + } + if (FT->isVariadic()) { + if (FT->getNumParams()) + ResultStr += ", "; + ResultStr += "..."; + } + ResultStr += ")"; + } else { + ResultStr += "()"; + } + } +} + +void RewriteObjC::RewriteImplementationDecl(Decl *OID) { + ObjCImplementationDecl *IMD = dyn_cast<ObjCImplementationDecl>(OID); + ObjCCategoryImplDecl *CID = dyn_cast<ObjCCategoryImplDecl>(OID); + + InsertText(IMD ? IMD->getBeginLoc() : CID->getBeginLoc(), "// "); + + for (auto *OMD : IMD ? IMD->instance_methods() : CID->instance_methods()) { + std::string ResultStr; + RewriteObjCMethodDecl(OMD->getClassInterface(), OMD, ResultStr); + SourceLocation LocStart = OMD->getBeginLoc(); + SourceLocation LocEnd = OMD->getCompoundBody()->getBeginLoc(); + + const char *startBuf = SM->getCharacterData(LocStart); + const char *endBuf = SM->getCharacterData(LocEnd); + ReplaceText(LocStart, endBuf-startBuf, ResultStr); + } + + for (auto *OMD : IMD ? IMD->class_methods() : CID->class_methods()) { + std::string ResultStr; + RewriteObjCMethodDecl(OMD->getClassInterface(), OMD, ResultStr); + SourceLocation LocStart = OMD->getBeginLoc(); + SourceLocation LocEnd = OMD->getCompoundBody()->getBeginLoc(); + + const char *startBuf = SM->getCharacterData(LocStart); + const char *endBuf = SM->getCharacterData(LocEnd); + ReplaceText(LocStart, endBuf-startBuf, ResultStr); + } + for (auto *I : IMD ? IMD->property_impls() : CID->property_impls()) + RewritePropertyImplDecl(I, IMD, CID); + + InsertText(IMD ? IMD->getEndLoc() : CID->getEndLoc(), "// "); +} + +void RewriteObjC::RewriteInterfaceDecl(ObjCInterfaceDecl *ClassDecl) { + std::string ResultStr; + if (!ObjCForwardDecls.count(ClassDecl->getCanonicalDecl())) { + // we haven't seen a forward decl - generate a typedef. + ResultStr = "#ifndef _REWRITER_typedef_"; + ResultStr += ClassDecl->getNameAsString(); + ResultStr += "\n"; + ResultStr += "#define _REWRITER_typedef_"; + ResultStr += ClassDecl->getNameAsString(); + ResultStr += "\n"; + ResultStr += "typedef struct objc_object "; + ResultStr += ClassDecl->getNameAsString(); + ResultStr += ";\n#endif\n"; + // Mark this typedef as having been generated. + ObjCForwardDecls.insert(ClassDecl->getCanonicalDecl()); + } + RewriteObjCInternalStruct(ClassDecl, ResultStr); + + for (auto *I : ClassDecl->instance_properties()) + RewriteProperty(I); + for (auto *I : ClassDecl->instance_methods()) + RewriteMethodDeclaration(I); + for (auto *I : ClassDecl->class_methods()) + RewriteMethodDeclaration(I); + + // Lastly, comment out the @end. + ReplaceText(ClassDecl->getAtEndRange().getBegin(), strlen("@end"), + "/* @end */"); +} + +Stmt *RewriteObjC::RewritePropertyOrImplicitSetter(PseudoObjectExpr *PseudoOp) { + SourceRange OldRange = PseudoOp->getSourceRange(); + + // We just magically know some things about the structure of this + // expression. + ObjCMessageExpr *OldMsg = + cast<ObjCMessageExpr>(PseudoOp->getSemanticExpr( + PseudoOp->getNumSemanticExprs() - 1)); + + // Because the rewriter doesn't allow us to rewrite rewritten code, + // we need to suppress rewriting the sub-statements. + Expr *Base, *RHS; + { + DisableReplaceStmtScope S(*this); + + // Rebuild the base expression if we have one. + Base = nullptr; + if (OldMsg->getReceiverKind() == ObjCMessageExpr::Instance) { + Base = OldMsg->getInstanceReceiver(); + Base = cast<OpaqueValueExpr>(Base)->getSourceExpr(); + Base = cast<Expr>(RewriteFunctionBodyOrGlobalInitializer(Base)); + } + + // Rebuild the RHS. + RHS = cast<BinaryOperator>(PseudoOp->getSyntacticForm())->getRHS(); + RHS = cast<OpaqueValueExpr>(RHS)->getSourceExpr(); + RHS = cast<Expr>(RewriteFunctionBodyOrGlobalInitializer(RHS)); + } + + // TODO: avoid this copy. + SmallVector<SourceLocation, 1> SelLocs; + OldMsg->getSelectorLocs(SelLocs); + + ObjCMessageExpr *NewMsg = nullptr; + switch (OldMsg->getReceiverKind()) { + case ObjCMessageExpr::Class: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + OldMsg->getClassReceiverTypeInfo(), + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + RHS, + OldMsg->getRightLoc(), + OldMsg->isImplicit()); + break; + + case ObjCMessageExpr::Instance: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + Base, + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + RHS, + OldMsg->getRightLoc(), + OldMsg->isImplicit()); + break; + + case ObjCMessageExpr::SuperClass: + case ObjCMessageExpr::SuperInstance: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + OldMsg->getSuperLoc(), + OldMsg->getReceiverKind() == ObjCMessageExpr::SuperInstance, + OldMsg->getSuperType(), + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + RHS, + OldMsg->getRightLoc(), + OldMsg->isImplicit()); + break; + } + + Stmt *Replacement = SynthMessageExpr(NewMsg); + ReplaceStmtWithRange(PseudoOp, Replacement, OldRange); + return Replacement; +} + +Stmt *RewriteObjC::RewritePropertyOrImplicitGetter(PseudoObjectExpr *PseudoOp) { + SourceRange OldRange = PseudoOp->getSourceRange(); + + // We just magically know some things about the structure of this + // expression. + ObjCMessageExpr *OldMsg = + cast<ObjCMessageExpr>(PseudoOp->getResultExpr()->IgnoreImplicit()); + + // Because the rewriter doesn't allow us to rewrite rewritten code, + // we need to suppress rewriting the sub-statements. + Expr *Base = nullptr; + { + DisableReplaceStmtScope S(*this); + + // Rebuild the base expression if we have one. + if (OldMsg->getReceiverKind() == ObjCMessageExpr::Instance) { + Base = OldMsg->getInstanceReceiver(); + Base = cast<OpaqueValueExpr>(Base)->getSourceExpr(); + Base = cast<Expr>(RewriteFunctionBodyOrGlobalInitializer(Base)); + } + } + + // Intentionally empty. + SmallVector<SourceLocation, 1> SelLocs; + SmallVector<Expr*, 1> Args; + + ObjCMessageExpr *NewMsg = nullptr; + switch (OldMsg->getReceiverKind()) { + case ObjCMessageExpr::Class: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + OldMsg->getClassReceiverTypeInfo(), + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + Args, + OldMsg->getRightLoc(), + OldMsg->isImplicit()); + break; + + case ObjCMessageExpr::Instance: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + Base, + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + Args, + OldMsg->getRightLoc(), + OldMsg->isImplicit()); + break; + + case ObjCMessageExpr::SuperClass: + case ObjCMessageExpr::SuperInstance: + NewMsg = ObjCMessageExpr::Create(*Context, OldMsg->getType(), + OldMsg->getValueKind(), + OldMsg->getLeftLoc(), + OldMsg->getSuperLoc(), + OldMsg->getReceiverKind() == ObjCMessageExpr::SuperInstance, + OldMsg->getSuperType(), + OldMsg->getSelector(), + SelLocs, + OldMsg->getMethodDecl(), + Args, + OldMsg->getRightLoc(), + OldMsg->isImplicit()); + break; + } + + Stmt *Replacement = SynthMessageExpr(NewMsg); + ReplaceStmtWithRange(PseudoOp, Replacement, OldRange); + return Replacement; +} + +/// SynthCountByEnumWithState - To print: +/// ((unsigned int (*) +/// (id, SEL, struct __objcFastEnumerationState *, id *, unsigned int)) +/// (void *)objc_msgSend)((id)l_collection, +/// sel_registerName( +/// "countByEnumeratingWithState:objects:count:"), +/// &enumState, +/// (id *)__rw_items, (unsigned int)16) +/// +void RewriteObjC::SynthCountByEnumWithState(std::string &buf) { + buf += "((unsigned int (*) (id, SEL, struct __objcFastEnumerationState *, " + "id *, unsigned int))(void *)objc_msgSend)"; + buf += "\n\t\t"; + buf += "((id)l_collection,\n\t\t"; + buf += "sel_registerName(\"countByEnumeratingWithState:objects:count:\"),"; + buf += "\n\t\t"; + buf += "&enumState, " + "(id *)__rw_items, (unsigned int)16)"; +} + +/// RewriteBreakStmt - Rewrite for a break-stmt inside an ObjC2's foreach +/// statement to exit to its outer synthesized loop. +/// +Stmt *RewriteObjC::RewriteBreakStmt(BreakStmt *S) { + if (Stmts.empty() || !isa<ObjCForCollectionStmt>(Stmts.back())) + return S; + // replace break with goto __break_label + std::string buf; + + SourceLocation startLoc = S->getBeginLoc(); + buf = "goto __break_label_"; + buf += utostr(ObjCBcLabelNo.back()); + ReplaceText(startLoc, strlen("break"), buf); + + return nullptr; +} + +/// RewriteContinueStmt - Rewrite for a continue-stmt inside an ObjC2's foreach +/// statement to continue with its inner synthesized loop. +/// +Stmt *RewriteObjC::RewriteContinueStmt(ContinueStmt *S) { + if (Stmts.empty() || !isa<ObjCForCollectionStmt>(Stmts.back())) + return S; + // replace continue with goto __continue_label + std::string buf; + + SourceLocation startLoc = S->getBeginLoc(); + buf = "goto __continue_label_"; + buf += utostr(ObjCBcLabelNo.back()); + ReplaceText(startLoc, strlen("continue"), buf); + + return nullptr; +} + +/// RewriteObjCForCollectionStmt - Rewriter for ObjC2's foreach statement. +/// It rewrites: +/// for ( type elem in collection) { stmts; } + +/// Into: +/// { +/// type elem; +/// struct __objcFastEnumerationState enumState = { 0 }; +/// id __rw_items[16]; +/// id l_collection = (id)collection; +/// unsigned long limit = [l_collection countByEnumeratingWithState:&enumState +/// objects:__rw_items count:16]; +/// if (limit) { +/// unsigned long startMutations = *enumState.mutationsPtr; +/// do { +/// unsigned long counter = 0; +/// do { +/// if (startMutations != *enumState.mutationsPtr) +/// objc_enumerationMutation(l_collection); +/// elem = (type)enumState.itemsPtr[counter++]; +/// stmts; +/// __continue_label: ; +/// } while (counter < limit); +/// } while (limit = [l_collection countByEnumeratingWithState:&enumState +/// objects:__rw_items count:16]); +/// elem = nil; +/// __break_label: ; +/// } +/// else +/// elem = nil; +/// } +/// +Stmt *RewriteObjC::RewriteObjCForCollectionStmt(ObjCForCollectionStmt *S, + SourceLocation OrigEnd) { + assert(!Stmts.empty() && "ObjCForCollectionStmt - Statement stack empty"); + assert(isa<ObjCForCollectionStmt>(Stmts.back()) && + "ObjCForCollectionStmt Statement stack mismatch"); + assert(!ObjCBcLabelNo.empty() && + "ObjCForCollectionStmt - Label No stack empty"); + + SourceLocation startLoc = S->getBeginLoc(); + const char *startBuf = SM->getCharacterData(startLoc); + StringRef elementName; + std::string elementTypeAsString; + std::string buf; + buf = "\n{\n\t"; + if (DeclStmt *DS = dyn_cast<DeclStmt>(S->getElement())) { + // type elem; + NamedDecl* D = cast<NamedDecl>(DS->getSingleDecl()); + QualType ElementType = cast<ValueDecl>(D)->getType(); + if (ElementType->isObjCQualifiedIdType() || + ElementType->isObjCQualifiedInterfaceType()) + // Simply use 'id' for all qualified types. + elementTypeAsString = "id"; + else + elementTypeAsString = ElementType.getAsString(Context->getPrintingPolicy()); + buf += elementTypeAsString; + buf += " "; + elementName = D->getName(); + buf += elementName; + buf += ";\n\t"; + } + else { + DeclRefExpr *DR = cast<DeclRefExpr>(S->getElement()); + elementName = DR->getDecl()->getName(); + ValueDecl *VD = DR->getDecl(); + if (VD->getType()->isObjCQualifiedIdType() || + VD->getType()->isObjCQualifiedInterfaceType()) + // Simply use 'id' for all qualified types. + elementTypeAsString = "id"; + else + elementTypeAsString = VD->getType().getAsString(Context->getPrintingPolicy()); + } + + // struct __objcFastEnumerationState enumState = { 0 }; + buf += "struct __objcFastEnumerationState enumState = { 0 };\n\t"; + // id __rw_items[16]; + buf += "id __rw_items[16];\n\t"; + // id l_collection = (id) + buf += "id l_collection = (id)"; + // Find start location of 'collection' the hard way! + const char *startCollectionBuf = startBuf; + startCollectionBuf += 3; // skip 'for' + startCollectionBuf = strchr(startCollectionBuf, '('); + startCollectionBuf++; // skip '(' + // find 'in' and skip it. + while (*startCollectionBuf != ' ' || + *(startCollectionBuf+1) != 'i' || *(startCollectionBuf+2) != 'n' || + (*(startCollectionBuf+3) != ' ' && + *(startCollectionBuf+3) != '[' && *(startCollectionBuf+3) != '(')) + startCollectionBuf++; + startCollectionBuf += 3; + + // Replace: "for (type element in" with string constructed thus far. + ReplaceText(startLoc, startCollectionBuf - startBuf, buf); + // Replace ')' in for '(' type elem in collection ')' with ';' + SourceLocation rightParenLoc = S->getRParenLoc(); + const char *rparenBuf = SM->getCharacterData(rightParenLoc); + SourceLocation lparenLoc = startLoc.getLocWithOffset(rparenBuf-startBuf); + buf = ";\n\t"; + + // unsigned long limit = [l_collection countByEnumeratingWithState:&enumState + // objects:__rw_items count:16]; + // which is synthesized into: + // unsigned int limit = + // ((unsigned int (*) + // (id, SEL, struct __objcFastEnumerationState *, id *, unsigned int)) + // (void *)objc_msgSend)((id)l_collection, + // sel_registerName( + // "countByEnumeratingWithState:objects:count:"), + // (struct __objcFastEnumerationState *)&state, + // (id *)__rw_items, (unsigned int)16); + buf += "unsigned long limit =\n\t\t"; + SynthCountByEnumWithState(buf); + buf += ";\n\t"; + /// if (limit) { + /// unsigned long startMutations = *enumState.mutationsPtr; + /// do { + /// unsigned long counter = 0; + /// do { + /// if (startMutations != *enumState.mutationsPtr) + /// objc_enumerationMutation(l_collection); + /// elem = (type)enumState.itemsPtr[counter++]; + buf += "if (limit) {\n\t"; + buf += "unsigned long startMutations = *enumState.mutationsPtr;\n\t"; + buf += "do {\n\t\t"; + buf += "unsigned long counter = 0;\n\t\t"; + buf += "do {\n\t\t\t"; + buf += "if (startMutations != *enumState.mutationsPtr)\n\t\t\t\t"; + buf += "objc_enumerationMutation(l_collection);\n\t\t\t"; + buf += elementName; + buf += " = ("; + buf += elementTypeAsString; + buf += ")enumState.itemsPtr[counter++];"; + // Replace ')' in for '(' type elem in collection ')' with all of these. + ReplaceText(lparenLoc, 1, buf); + + /// __continue_label: ; + /// } while (counter < limit); + /// } while (limit = [l_collection countByEnumeratingWithState:&enumState + /// objects:__rw_items count:16]); + /// elem = nil; + /// __break_label: ; + /// } + /// else + /// elem = nil; + /// } + /// + buf = ";\n\t"; + buf += "__continue_label_"; + buf += utostr(ObjCBcLabelNo.back()); + buf += ": ;"; + buf += "\n\t\t"; + buf += "} while (counter < limit);\n\t"; + buf += "} while (limit = "; + SynthCountByEnumWithState(buf); + buf += ");\n\t"; + buf += elementName; + buf += " = (("; + buf += elementTypeAsString; + buf += ")0);\n\t"; + buf += "__break_label_"; + buf += utostr(ObjCBcLabelNo.back()); + buf += ": ;\n\t"; + buf += "}\n\t"; + buf += "else\n\t\t"; + buf += elementName; + buf += " = (("; + buf += elementTypeAsString; + buf += ")0);\n\t"; + buf += "}\n"; + + // Insert all these *after* the statement body. + // FIXME: If this should support Obj-C++, support CXXTryStmt + if (isa<CompoundStmt>(S->getBody())) { + SourceLocation endBodyLoc = OrigEnd.getLocWithOffset(1); + InsertText(endBodyLoc, buf); + } else { + /* Need to treat single statements specially. For example: + * + * for (A *a in b) if (stuff()) break; + * for (A *a in b) xxxyy; + * + * The following code simply scans ahead to the semi to find the actual end. + */ + const char *stmtBuf = SM->getCharacterData(OrigEnd); + const char *semiBuf = strchr(stmtBuf, ';'); + assert(semiBuf && "Can't find ';'"); + SourceLocation endBodyLoc = OrigEnd.getLocWithOffset(semiBuf-stmtBuf+1); + InsertText(endBodyLoc, buf); + } + Stmts.pop_back(); + ObjCBcLabelNo.pop_back(); + return nullptr; +} + +/// RewriteObjCSynchronizedStmt - +/// This routine rewrites @synchronized(expr) stmt; +/// into: +/// objc_sync_enter(expr); +/// @try stmt @finally { objc_sync_exit(expr); } +/// +Stmt *RewriteObjC::RewriteObjCSynchronizedStmt(ObjCAtSynchronizedStmt *S) { + // Get the start location and compute the semi location. + SourceLocation startLoc = S->getBeginLoc(); + const char *startBuf = SM->getCharacterData(startLoc); + + assert((*startBuf == '@') && "bogus @synchronized location"); + + std::string buf; + buf = "objc_sync_enter((id)"; + const char *lparenBuf = startBuf; + while (*lparenBuf != '(') lparenBuf++; + ReplaceText(startLoc, lparenBuf-startBuf+1, buf); + // We can't use S->getSynchExpr()->getEndLoc() to find the end location, since + // the sync expression is typically a message expression that's already + // been rewritten! (which implies the SourceLocation's are invalid). + SourceLocation endLoc = S->getSynchBody()->getBeginLoc(); + const char *endBuf = SM->getCharacterData(endLoc); + while (*endBuf != ')') endBuf--; + SourceLocation rparenLoc = startLoc.getLocWithOffset(endBuf-startBuf); + buf = ");\n"; + // declare a new scope with two variables, _stack and _rethrow. + buf += "/* @try scope begin */ \n{ struct _objc_exception_data {\n"; + buf += "int buf[18/*32-bit i386*/];\n"; + buf += "char *pointers[4];} _stack;\n"; + buf += "id volatile _rethrow = 0;\n"; + buf += "objc_exception_try_enter(&_stack);\n"; + buf += "if (!_setjmp(_stack.buf)) /* @try block continue */\n"; + ReplaceText(rparenLoc, 1, buf); + startLoc = S->getSynchBody()->getEndLoc(); + startBuf = SM->getCharacterData(startLoc); + + assert((*startBuf == '}') && "bogus @synchronized block"); + SourceLocation lastCurlyLoc = startLoc; + buf = "}\nelse {\n"; + buf += " _rethrow = objc_exception_extract(&_stack);\n"; + buf += "}\n"; + buf += "{ /* implicit finally clause */\n"; + buf += " if (!_rethrow) objc_exception_try_exit(&_stack);\n"; + + std::string syncBuf; + syncBuf += " objc_sync_exit("; + + Expr *syncExpr = S->getSynchExpr(); + CastKind CK = syncExpr->getType()->isObjCObjectPointerType() + ? CK_BitCast : + syncExpr->getType()->isBlockPointerType() + ? CK_BlockPointerToObjCPointerCast + : CK_CPointerToObjCPointerCast; + syncExpr = NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), + CK, syncExpr); + std::string syncExprBufS; + llvm::raw_string_ostream syncExprBuf(syncExprBufS); + assert(syncExpr != nullptr && "Expected non-null Expr"); + syncExpr->printPretty(syncExprBuf, nullptr, PrintingPolicy(LangOpts)); + syncBuf += syncExprBuf.str(); + syncBuf += ");"; + + buf += syncBuf; + buf += "\n if (_rethrow) objc_exception_throw(_rethrow);\n"; + buf += "}\n"; + buf += "}"; + + ReplaceText(lastCurlyLoc, 1, buf); + + bool hasReturns = false; + HasReturnStmts(S->getSynchBody(), hasReturns); + if (hasReturns) + RewriteSyncReturnStmts(S->getSynchBody(), syncBuf); + + return nullptr; +} + +void RewriteObjC::WarnAboutReturnGotoStmts(Stmt *S) +{ + // Perform a bottom up traversal of all children. + for (Stmt *SubStmt : S->children()) + if (SubStmt) + WarnAboutReturnGotoStmts(SubStmt); + + if (isa<ReturnStmt>(S) || isa<GotoStmt>(S)) { + Diags.Report(Context->getFullLoc(S->getBeginLoc()), + TryFinallyContainsReturnDiag); + } +} + +void RewriteObjC::HasReturnStmts(Stmt *S, bool &hasReturns) +{ + // Perform a bottom up traversal of all children. + for (Stmt *SubStmt : S->children()) + if (SubStmt) + HasReturnStmts(SubStmt, hasReturns); + + if (isa<ReturnStmt>(S)) + hasReturns = true; +} + +void RewriteObjC::RewriteTryReturnStmts(Stmt *S) { + // Perform a bottom up traversal of all children. + for (Stmt *SubStmt : S->children()) + if (SubStmt) { + RewriteTryReturnStmts(SubStmt); + } + if (isa<ReturnStmt>(S)) { + SourceLocation startLoc = S->getBeginLoc(); + const char *startBuf = SM->getCharacterData(startLoc); + const char *semiBuf = strchr(startBuf, ';'); + assert((*semiBuf == ';') && "RewriteTryReturnStmts: can't find ';'"); + SourceLocation onePastSemiLoc = startLoc.getLocWithOffset(semiBuf-startBuf+1); + + std::string buf; + buf = "{ objc_exception_try_exit(&_stack); return"; + + ReplaceText(startLoc, 6, buf); + InsertText(onePastSemiLoc, "}"); + } +} + +void RewriteObjC::RewriteSyncReturnStmts(Stmt *S, std::string syncExitBuf) { + // Perform a bottom up traversal of all children. + for (Stmt *SubStmt : S->children()) + if (SubStmt) { + RewriteSyncReturnStmts(SubStmt, syncExitBuf); + } + if (isa<ReturnStmt>(S)) { + SourceLocation startLoc = S->getBeginLoc(); + const char *startBuf = SM->getCharacterData(startLoc); + + const char *semiBuf = strchr(startBuf, ';'); + assert((*semiBuf == ';') && "RewriteSyncReturnStmts: can't find ';'"); + SourceLocation onePastSemiLoc = startLoc.getLocWithOffset(semiBuf-startBuf+1); + + std::string buf; + buf = "{ objc_exception_try_exit(&_stack);"; + buf += syncExitBuf; + buf += " return"; + + ReplaceText(startLoc, 6, buf); + InsertText(onePastSemiLoc, "}"); + } +} + +Stmt *RewriteObjC::RewriteObjCTryStmt(ObjCAtTryStmt *S) { + // Get the start location and compute the semi location. + SourceLocation startLoc = S->getBeginLoc(); + const char *startBuf = SM->getCharacterData(startLoc); + + assert((*startBuf == '@') && "bogus @try location"); + + std::string buf; + // declare a new scope with two variables, _stack and _rethrow. + buf = "/* @try scope begin */ { struct _objc_exception_data {\n"; + buf += "int buf[18/*32-bit i386*/];\n"; + buf += "char *pointers[4];} _stack;\n"; + buf += "id volatile _rethrow = 0;\n"; + buf += "objc_exception_try_enter(&_stack);\n"; + buf += "if (!_setjmp(_stack.buf)) /* @try block continue */\n"; + + ReplaceText(startLoc, 4, buf); + + startLoc = S->getTryBody()->getEndLoc(); + startBuf = SM->getCharacterData(startLoc); + + assert((*startBuf == '}') && "bogus @try block"); + + SourceLocation lastCurlyLoc = startLoc; + if (S->getNumCatchStmts()) { + startLoc = startLoc.getLocWithOffset(1); + buf = " /* @catch begin */ else {\n"; + buf += " id _caught = objc_exception_extract(&_stack);\n"; + buf += " objc_exception_try_enter (&_stack);\n"; + buf += " if (_setjmp(_stack.buf))\n"; + buf += " _rethrow = objc_exception_extract(&_stack);\n"; + buf += " else { /* @catch continue */"; + + InsertText(startLoc, buf); + } else { /* no catch list */ + buf = "}\nelse {\n"; + buf += " _rethrow = objc_exception_extract(&_stack);\n"; + buf += "}"; + ReplaceText(lastCurlyLoc, 1, buf); + } + Stmt *lastCatchBody = nullptr; + for (unsigned I = 0, N = S->getNumCatchStmts(); I != N; ++I) { + ObjCAtCatchStmt *Catch = S->getCatchStmt(I); + VarDecl *catchDecl = Catch->getCatchParamDecl(); + + if (I == 0) + buf = "if ("; // we are generating code for the first catch clause + else + buf = "else if ("; + startLoc = Catch->getBeginLoc(); + startBuf = SM->getCharacterData(startLoc); + + assert((*startBuf == '@') && "bogus @catch location"); + + const char *lParenLoc = strchr(startBuf, '('); + + if (Catch->hasEllipsis()) { + // Now rewrite the body... + lastCatchBody = Catch->getCatchBody(); + SourceLocation bodyLoc = lastCatchBody->getBeginLoc(); + const char *bodyBuf = SM->getCharacterData(bodyLoc); + assert(*SM->getCharacterData(Catch->getRParenLoc()) == ')' && + "bogus @catch paren location"); + assert((*bodyBuf == '{') && "bogus @catch body location"); + + buf += "1) { id _tmp = _caught;"; + Rewrite.ReplaceText(startLoc, bodyBuf-startBuf+1, buf); + } else if (catchDecl) { + QualType t = catchDecl->getType(); + if (t == Context->getObjCIdType()) { + buf += "1) { "; + ReplaceText(startLoc, lParenLoc-startBuf+1, buf); + } else if (const ObjCObjectPointerType *Ptr = + t->getAs<ObjCObjectPointerType>()) { + // Should be a pointer to a class. + ObjCInterfaceDecl *IDecl = Ptr->getObjectType()->getInterface(); + if (IDecl) { + buf += "objc_exception_match((struct objc_class *)objc_getClass(\""; + buf += IDecl->getNameAsString(); + buf += "\"), (struct objc_object *)_caught)) { "; + ReplaceText(startLoc, lParenLoc-startBuf+1, buf); + } + } + // Now rewrite the body... + lastCatchBody = Catch->getCatchBody(); + SourceLocation rParenLoc = Catch->getRParenLoc(); + SourceLocation bodyLoc = lastCatchBody->getBeginLoc(); + const char *bodyBuf = SM->getCharacterData(bodyLoc); + const char *rParenBuf = SM->getCharacterData(rParenLoc); + assert((*rParenBuf == ')') && "bogus @catch paren location"); + assert((*bodyBuf == '{') && "bogus @catch body location"); + + // Here we replace ") {" with "= _caught;" (which initializes and + // declares the @catch parameter). + ReplaceText(rParenLoc, bodyBuf-rParenBuf+1, " = _caught;"); + } else { + llvm_unreachable("@catch rewrite bug"); + } + } + // Complete the catch list... + if (lastCatchBody) { + SourceLocation bodyLoc = lastCatchBody->getEndLoc(); + assert(*SM->getCharacterData(bodyLoc) == '}' && + "bogus @catch body location"); + + // Insert the last (implicit) else clause *before* the right curly brace. + bodyLoc = bodyLoc.getLocWithOffset(-1); + buf = "} /* last catch end */\n"; + buf += "else {\n"; + buf += " _rethrow = _caught;\n"; + buf += " objc_exception_try_exit(&_stack);\n"; + buf += "} } /* @catch end */\n"; + if (!S->getFinallyStmt()) + buf += "}\n"; + InsertText(bodyLoc, buf); + + // Set lastCurlyLoc + lastCurlyLoc = lastCatchBody->getEndLoc(); + } + if (ObjCAtFinallyStmt *finalStmt = S->getFinallyStmt()) { + startLoc = finalStmt->getBeginLoc(); + startBuf = SM->getCharacterData(startLoc); + assert((*startBuf == '@') && "bogus @finally start"); + + ReplaceText(startLoc, 8, "/* @finally */"); + + Stmt *body = finalStmt->getFinallyBody(); + SourceLocation startLoc = body->getBeginLoc(); + SourceLocation endLoc = body->getEndLoc(); + assert(*SM->getCharacterData(startLoc) == '{' && + "bogus @finally body location"); + assert(*SM->getCharacterData(endLoc) == '}' && + "bogus @finally body location"); + + startLoc = startLoc.getLocWithOffset(1); + InsertText(startLoc, " if (!_rethrow) objc_exception_try_exit(&_stack);\n"); + endLoc = endLoc.getLocWithOffset(-1); + InsertText(endLoc, " if (_rethrow) objc_exception_throw(_rethrow);\n"); + + // Set lastCurlyLoc + lastCurlyLoc = body->getEndLoc(); + + // Now check for any return/continue/go statements within the @try. + WarnAboutReturnGotoStmts(S->getTryBody()); + } else { /* no finally clause - make sure we synthesize an implicit one */ + buf = "{ /* implicit finally clause */\n"; + buf += " if (!_rethrow) objc_exception_try_exit(&_stack);\n"; + buf += " if (_rethrow) objc_exception_throw(_rethrow);\n"; + buf += "}"; + ReplaceText(lastCurlyLoc, 1, buf); + + // Now check for any return/continue/go statements within the @try. + // The implicit finally clause won't called if the @try contains any + // jump statements. + bool hasReturns = false; + HasReturnStmts(S->getTryBody(), hasReturns); + if (hasReturns) + RewriteTryReturnStmts(S->getTryBody()); + } + // Now emit the final closing curly brace... + lastCurlyLoc = lastCurlyLoc.getLocWithOffset(1); + InsertText(lastCurlyLoc, " } /* @try scope end */\n"); + return nullptr; +} + +// This can't be done with ReplaceStmt(S, ThrowExpr), since +// the throw expression is typically a message expression that's already +// been rewritten! (which implies the SourceLocation's are invalid). +Stmt *RewriteObjC::RewriteObjCThrowStmt(ObjCAtThrowStmt *S) { + // Get the start location and compute the semi location. + SourceLocation startLoc = S->getBeginLoc(); + const char *startBuf = SM->getCharacterData(startLoc); + + assert((*startBuf == '@') && "bogus @throw location"); + + std::string buf; + /* void objc_exception_throw(id) __attribute__((noreturn)); */ + if (S->getThrowExpr()) + buf = "objc_exception_throw("; + else // add an implicit argument + buf = "objc_exception_throw(_caught"; + + // handle "@ throw" correctly. + const char *wBuf = strchr(startBuf, 'w'); + assert((*wBuf == 'w') && "@throw: can't find 'w'"); + ReplaceText(startLoc, wBuf-startBuf+1, buf); + + const char *semiBuf = strchr(startBuf, ';'); + assert((*semiBuf == ';') && "@throw: can't find ';'"); + SourceLocation semiLoc = startLoc.getLocWithOffset(semiBuf-startBuf); + ReplaceText(semiLoc, 1, ");"); + return nullptr; +} + +Stmt *RewriteObjC::RewriteAtEncode(ObjCEncodeExpr *Exp) { + // Create a new string expression. + std::string StrEncoding; + Context->getObjCEncodingForType(Exp->getEncodedType(), StrEncoding); + Expr *Replacement = getStringLiteral(StrEncoding); + ReplaceStmt(Exp, Replacement); + + // Replace this subexpr in the parent. + // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. + return Replacement; +} + +Stmt *RewriteObjC::RewriteAtSelector(ObjCSelectorExpr *Exp) { + if (!SelGetUidFunctionDecl) + SynthSelGetUidFunctionDecl(); + assert(SelGetUidFunctionDecl && "Can't find sel_registerName() decl"); + // Create a call to sel_registerName("selName"). + SmallVector<Expr*, 8> SelExprs; + SelExprs.push_back(getStringLiteral(Exp->getSelector().getAsString())); + CallExpr *SelExp = SynthesizeCallToFunctionDecl(SelGetUidFunctionDecl, + SelExprs); + ReplaceStmt(Exp, SelExp); + // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. + return SelExp; +} + +CallExpr * +RewriteObjC::SynthesizeCallToFunctionDecl(FunctionDecl *FD, + ArrayRef<Expr *> Args, + SourceLocation StartLoc, + SourceLocation EndLoc) { + // Get the type, we will need to reference it in a couple spots. + QualType msgSendType = FD->getType(); + + // Create a reference to the objc_msgSend() declaration. + DeclRefExpr *DRE = new (Context) DeclRefExpr(*Context, FD, false, msgSendType, + VK_LValue, SourceLocation()); + + // Now, we cast the reference to a pointer to the objc_msgSend type. + QualType pToFunc = Context->getPointerType(msgSendType); + ImplicitCastExpr *ICE = + ImplicitCastExpr::Create(*Context, pToFunc, CK_FunctionToPointerDecay, + DRE, nullptr, VK_RValue); + + const FunctionType *FT = msgSendType->getAs<FunctionType>(); + + CallExpr *Exp = CallExpr::Create( + *Context, ICE, Args, FT->getCallResultType(*Context), VK_RValue, EndLoc); + return Exp; +} + +static bool scanForProtocolRefs(const char *startBuf, const char *endBuf, + const char *&startRef, const char *&endRef) { + while (startBuf < endBuf) { + if (*startBuf == '<') + startRef = startBuf; // mark the start. + if (*startBuf == '>') { + if (startRef && *startRef == '<') { + endRef = startBuf; // mark the end. + return true; + } + return false; + } + startBuf++; + } + return false; +} + +static void scanToNextArgument(const char *&argRef) { + int angle = 0; + while (*argRef != ')' && (*argRef != ',' || angle > 0)) { + if (*argRef == '<') + angle++; + else if (*argRef == '>') + angle--; + argRef++; + } + assert(angle == 0 && "scanToNextArgument - bad protocol type syntax"); +} + +bool RewriteObjC::needToScanForQualifiers(QualType T) { + if (T->isObjCQualifiedIdType()) + return true; + if (const PointerType *PT = T->getAs<PointerType>()) { + if (PT->getPointeeType()->isObjCQualifiedIdType()) + return true; + } + if (T->isObjCObjectPointerType()) { + T = T->getPointeeType(); + return T->isObjCQualifiedInterfaceType(); + } + if (T->isArrayType()) { + QualType ElemTy = Context->getBaseElementType(T); + return needToScanForQualifiers(ElemTy); + } + return false; +} + +void RewriteObjC::RewriteObjCQualifiedInterfaceTypes(Expr *E) { + QualType Type = E->getType(); + if (needToScanForQualifiers(Type)) { + SourceLocation Loc, EndLoc; + + if (const CStyleCastExpr *ECE = dyn_cast<CStyleCastExpr>(E)) { + Loc = ECE->getLParenLoc(); + EndLoc = ECE->getRParenLoc(); + } else { + Loc = E->getBeginLoc(); + EndLoc = E->getEndLoc(); + } + // This will defend against trying to rewrite synthesized expressions. + if (Loc.isInvalid() || EndLoc.isInvalid()) + return; + + const char *startBuf = SM->getCharacterData(Loc); + const char *endBuf = SM->getCharacterData(EndLoc); + const char *startRef = nullptr, *endRef = nullptr; + if (scanForProtocolRefs(startBuf, endBuf, startRef, endRef)) { + // Get the locations of the startRef, endRef. + SourceLocation LessLoc = Loc.getLocWithOffset(startRef-startBuf); + SourceLocation GreaterLoc = Loc.getLocWithOffset(endRef-startBuf+1); + // Comment out the protocol references. + InsertText(LessLoc, "/*"); + InsertText(GreaterLoc, "*/"); + } + } +} + +void RewriteObjC::RewriteObjCQualifiedInterfaceTypes(Decl *Dcl) { + SourceLocation Loc; + QualType Type; + const FunctionProtoType *proto = nullptr; + if (VarDecl *VD = dyn_cast<VarDecl>(Dcl)) { + Loc = VD->getLocation(); + Type = VD->getType(); + } + else if (FunctionDecl *FD = dyn_cast<FunctionDecl>(Dcl)) { + Loc = FD->getLocation(); + // Check for ObjC 'id' and class types that have been adorned with protocol + // information (id<p>, C<p>*). The protocol references need to be rewritten! + const FunctionType *funcType = FD->getType()->getAs<FunctionType>(); + assert(funcType && "missing function type"); + proto = dyn_cast<FunctionProtoType>(funcType); + if (!proto) + return; + Type = proto->getReturnType(); + } + else if (FieldDecl *FD = dyn_cast<FieldDecl>(Dcl)) { + Loc = FD->getLocation(); + Type = FD->getType(); + } + else + return; + + if (needToScanForQualifiers(Type)) { + // Since types are unique, we need to scan the buffer. + + const char *endBuf = SM->getCharacterData(Loc); + const char *startBuf = endBuf; + while (*startBuf != ';' && *startBuf != '<' && startBuf != MainFileStart) + startBuf--; // scan backward (from the decl location) for return type. + const char *startRef = nullptr, *endRef = nullptr; + if (scanForProtocolRefs(startBuf, endBuf, startRef, endRef)) { + // Get the locations of the startRef, endRef. + SourceLocation LessLoc = Loc.getLocWithOffset(startRef-endBuf); + SourceLocation GreaterLoc = Loc.getLocWithOffset(endRef-endBuf+1); + // Comment out the protocol references. + InsertText(LessLoc, "/*"); + InsertText(GreaterLoc, "*/"); + } + } + if (!proto) + return; // most likely, was a variable + // Now check arguments. + const char *startBuf = SM->getCharacterData(Loc); + const char *startFuncBuf = startBuf; + for (unsigned i = 0; i < proto->getNumParams(); i++) { + if (needToScanForQualifiers(proto->getParamType(i))) { + // Since types are unique, we need to scan the buffer. + + const char *endBuf = startBuf; + // scan forward (from the decl location) for argument types. + scanToNextArgument(endBuf); + const char *startRef = nullptr, *endRef = nullptr; + if (scanForProtocolRefs(startBuf, endBuf, startRef, endRef)) { + // Get the locations of the startRef, endRef. + SourceLocation LessLoc = + Loc.getLocWithOffset(startRef-startFuncBuf); + SourceLocation GreaterLoc = + Loc.getLocWithOffset(endRef-startFuncBuf+1); + // Comment out the protocol references. + InsertText(LessLoc, "/*"); + InsertText(GreaterLoc, "*/"); + } + startBuf = ++endBuf; + } + else { + // If the function name is derived from a macro expansion, then the + // argument buffer will not follow the name. Need to speak with Chris. + while (*startBuf && *startBuf != ')' && *startBuf != ',') + startBuf++; // scan forward (from the decl location) for argument types. + startBuf++; + } + } +} + +void RewriteObjC::RewriteTypeOfDecl(VarDecl *ND) { + QualType QT = ND->getType(); + const Type* TypePtr = QT->getAs<Type>(); + if (!isa<TypeOfExprType>(TypePtr)) + return; + while (isa<TypeOfExprType>(TypePtr)) { + const TypeOfExprType *TypeOfExprTypePtr = cast<TypeOfExprType>(TypePtr); + QT = TypeOfExprTypePtr->getUnderlyingExpr()->getType(); + TypePtr = QT->getAs<Type>(); + } + // FIXME. This will not work for multiple declarators; as in: + // __typeof__(a) b,c,d; + std::string TypeAsString(QT.getAsString(Context->getPrintingPolicy())); + SourceLocation DeclLoc = ND->getTypeSpecStartLoc(); + const char *startBuf = SM->getCharacterData(DeclLoc); + if (ND->getInit()) { + std::string Name(ND->getNameAsString()); + TypeAsString += " " + Name + " = "; + Expr *E = ND->getInit(); + SourceLocation startLoc; + if (const CStyleCastExpr *ECE = dyn_cast<CStyleCastExpr>(E)) + startLoc = ECE->getLParenLoc(); + else + startLoc = E->getBeginLoc(); + startLoc = SM->getExpansionLoc(startLoc); + const char *endBuf = SM->getCharacterData(startLoc); + ReplaceText(DeclLoc, endBuf-startBuf-1, TypeAsString); + } + else { + SourceLocation X = ND->getEndLoc(); + X = SM->getExpansionLoc(X); + const char *endBuf = SM->getCharacterData(X); + ReplaceText(DeclLoc, endBuf-startBuf-1, TypeAsString); + } +} + +// SynthSelGetUidFunctionDecl - SEL sel_registerName(const char *str); +void RewriteObjC::SynthSelGetUidFunctionDecl() { + IdentifierInfo *SelGetUidIdent = &Context->Idents.get("sel_registerName"); + SmallVector<QualType, 16> ArgTys; + ArgTys.push_back(Context->getPointerType(Context->CharTy.withConst())); + QualType getFuncType = + getSimpleFunctionType(Context->getObjCSelType(), ArgTys); + SelGetUidFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + SelGetUidIdent, getFuncType, + nullptr, SC_Extern); +} + +void RewriteObjC::RewriteFunctionDecl(FunctionDecl *FD) { + // declared in <objc/objc.h> + if (FD->getIdentifier() && + FD->getName() == "sel_registerName") { + SelGetUidFunctionDecl = FD; + return; + } + RewriteObjCQualifiedInterfaceTypes(FD); +} + +void RewriteObjC::RewriteBlockPointerType(std::string& Str, QualType Type) { + std::string TypeString(Type.getAsString(Context->getPrintingPolicy())); + const char *argPtr = TypeString.c_str(); + if (!strchr(argPtr, '^')) { + Str += TypeString; + return; + } + while (*argPtr) { + Str += (*argPtr == '^' ? '*' : *argPtr); + argPtr++; + } +} + +// FIXME. Consolidate this routine with RewriteBlockPointerType. +void RewriteObjC::RewriteBlockPointerTypeVariable(std::string& Str, + ValueDecl *VD) { + QualType Type = VD->getType(); + std::string TypeString(Type.getAsString(Context->getPrintingPolicy())); + const char *argPtr = TypeString.c_str(); + int paren = 0; + while (*argPtr) { + switch (*argPtr) { + case '(': + Str += *argPtr; + paren++; + break; + case ')': + Str += *argPtr; + paren--; + break; + case '^': + Str += '*'; + if (paren == 1) + Str += VD->getNameAsString(); + break; + default: + Str += *argPtr; + break; + } + argPtr++; + } +} + +void RewriteObjC::RewriteBlockLiteralFunctionDecl(FunctionDecl *FD) { + SourceLocation FunLocStart = FD->getTypeSpecStartLoc(); + const FunctionType *funcType = FD->getType()->getAs<FunctionType>(); + const FunctionProtoType *proto = dyn_cast<FunctionProtoType>(funcType); + if (!proto) + return; + QualType Type = proto->getReturnType(); + std::string FdStr = Type.getAsString(Context->getPrintingPolicy()); + FdStr += " "; + FdStr += FD->getName(); + FdStr += "("; + unsigned numArgs = proto->getNumParams(); + for (unsigned i = 0; i < numArgs; i++) { + QualType ArgType = proto->getParamType(i); + RewriteBlockPointerType(FdStr, ArgType); + if (i+1 < numArgs) + FdStr += ", "; + } + FdStr += ");\n"; + InsertText(FunLocStart, FdStr); + CurFunctionDeclToDeclareForBlock = nullptr; +} + +// SynthSuperConstructorFunctionDecl - id objc_super(id obj, id super); +void RewriteObjC::SynthSuperConstructorFunctionDecl() { + if (SuperConstructorFunctionDecl) + return; + IdentifierInfo *msgSendIdent = &Context->Idents.get("__rw_objc_super"); + SmallVector<QualType, 16> ArgTys; + QualType argT = Context->getObjCIdType(); + assert(!argT.isNull() && "Can't find 'id' type"); + ArgTys.push_back(argT); + ArgTys.push_back(argT); + QualType msgSendType = getSimpleFunctionType(Context->getObjCIdType(), + ArgTys); + SuperConstructorFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + msgSendIdent, msgSendType, + nullptr, SC_Extern); +} + +// SynthMsgSendFunctionDecl - id objc_msgSend(id self, SEL op, ...); +void RewriteObjC::SynthMsgSendFunctionDecl() { + IdentifierInfo *msgSendIdent = &Context->Idents.get("objc_msgSend"); + SmallVector<QualType, 16> ArgTys; + QualType argT = Context->getObjCIdType(); + assert(!argT.isNull() && "Can't find 'id' type"); + ArgTys.push_back(argT); + argT = Context->getObjCSelType(); + assert(!argT.isNull() && "Can't find 'SEL' type"); + ArgTys.push_back(argT); + QualType msgSendType = getSimpleFunctionType(Context->getObjCIdType(), + ArgTys, /*isVariadic=*/true); + MsgSendFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + msgSendIdent, msgSendType, + nullptr, SC_Extern); +} + +// SynthMsgSendSuperFunctionDecl - id objc_msgSendSuper(struct objc_super *, SEL op, ...); +void RewriteObjC::SynthMsgSendSuperFunctionDecl() { + IdentifierInfo *msgSendIdent = &Context->Idents.get("objc_msgSendSuper"); + SmallVector<QualType, 16> ArgTys; + RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), SourceLocation(), + &Context->Idents.get("objc_super")); + QualType argT = Context->getPointerType(Context->getTagDeclType(RD)); + assert(!argT.isNull() && "Can't build 'struct objc_super *' type"); + ArgTys.push_back(argT); + argT = Context->getObjCSelType(); + assert(!argT.isNull() && "Can't find 'SEL' type"); + ArgTys.push_back(argT); + QualType msgSendType = getSimpleFunctionType(Context->getObjCIdType(), + ArgTys, /*isVariadic=*/true); + MsgSendSuperFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + msgSendIdent, msgSendType, + nullptr, SC_Extern); +} + +// SynthMsgSendStretFunctionDecl - id objc_msgSend_stret(id self, SEL op, ...); +void RewriteObjC::SynthMsgSendStretFunctionDecl() { + IdentifierInfo *msgSendIdent = &Context->Idents.get("objc_msgSend_stret"); + SmallVector<QualType, 16> ArgTys; + QualType argT = Context->getObjCIdType(); + assert(!argT.isNull() && "Can't find 'id' type"); + ArgTys.push_back(argT); + argT = Context->getObjCSelType(); + assert(!argT.isNull() && "Can't find 'SEL' type"); + ArgTys.push_back(argT); + QualType msgSendType = getSimpleFunctionType(Context->getObjCIdType(), + ArgTys, /*isVariadic=*/true); + MsgSendStretFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + msgSendIdent, msgSendType, + nullptr, SC_Extern); +} + +// SynthMsgSendSuperStretFunctionDecl - +// id objc_msgSendSuper_stret(struct objc_super *, SEL op, ...); +void RewriteObjC::SynthMsgSendSuperStretFunctionDecl() { + IdentifierInfo *msgSendIdent = + &Context->Idents.get("objc_msgSendSuper_stret"); + SmallVector<QualType, 16> ArgTys; + RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), SourceLocation(), + &Context->Idents.get("objc_super")); + QualType argT = Context->getPointerType(Context->getTagDeclType(RD)); + assert(!argT.isNull() && "Can't build 'struct objc_super *' type"); + ArgTys.push_back(argT); + argT = Context->getObjCSelType(); + assert(!argT.isNull() && "Can't find 'SEL' type"); + ArgTys.push_back(argT); + QualType msgSendType = getSimpleFunctionType(Context->getObjCIdType(), + ArgTys, /*isVariadic=*/true); + MsgSendSuperStretFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + msgSendIdent, + msgSendType, nullptr, + SC_Extern); +} + +// SynthMsgSendFpretFunctionDecl - double objc_msgSend_fpret(id self, SEL op, ...); +void RewriteObjC::SynthMsgSendFpretFunctionDecl() { + IdentifierInfo *msgSendIdent = &Context->Idents.get("objc_msgSend_fpret"); + SmallVector<QualType, 16> ArgTys; + QualType argT = Context->getObjCIdType(); + assert(!argT.isNull() && "Can't find 'id' type"); + ArgTys.push_back(argT); + argT = Context->getObjCSelType(); + assert(!argT.isNull() && "Can't find 'SEL' type"); + ArgTys.push_back(argT); + QualType msgSendType = getSimpleFunctionType(Context->DoubleTy, + ArgTys, /*isVariadic=*/true); + MsgSendFpretFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + msgSendIdent, msgSendType, + nullptr, SC_Extern); +} + +// SynthGetClassFunctionDecl - id objc_getClass(const char *name); +void RewriteObjC::SynthGetClassFunctionDecl() { + IdentifierInfo *getClassIdent = &Context->Idents.get("objc_getClass"); + SmallVector<QualType, 16> ArgTys; + ArgTys.push_back(Context->getPointerType(Context->CharTy.withConst())); + QualType getClassType = getSimpleFunctionType(Context->getObjCIdType(), + ArgTys); + GetClassFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + getClassIdent, getClassType, + nullptr, SC_Extern); +} + +// SynthGetSuperClassFunctionDecl - Class class_getSuperclass(Class cls); +void RewriteObjC::SynthGetSuperClassFunctionDecl() { + IdentifierInfo *getSuperClassIdent = + &Context->Idents.get("class_getSuperclass"); + SmallVector<QualType, 16> ArgTys; + ArgTys.push_back(Context->getObjCClassType()); + QualType getClassType = getSimpleFunctionType(Context->getObjCClassType(), + ArgTys); + GetSuperClassFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + getSuperClassIdent, + getClassType, nullptr, + SC_Extern); +} + +// SynthGetMetaClassFunctionDecl - id objc_getMetaClass(const char *name); +void RewriteObjC::SynthGetMetaClassFunctionDecl() { + IdentifierInfo *getClassIdent = &Context->Idents.get("objc_getMetaClass"); + SmallVector<QualType, 16> ArgTys; + ArgTys.push_back(Context->getPointerType(Context->CharTy.withConst())); + QualType getClassType = getSimpleFunctionType(Context->getObjCIdType(), + ArgTys); + GetMetaClassFunctionDecl = FunctionDecl::Create(*Context, TUDecl, + SourceLocation(), + SourceLocation(), + getClassIdent, getClassType, + nullptr, SC_Extern); +} + +Stmt *RewriteObjC::RewriteObjCStringLiteral(ObjCStringLiteral *Exp) { + assert(Exp != nullptr && "Expected non-null ObjCStringLiteral"); + QualType strType = getConstantStringStructType(); + + std::string S = "__NSConstantStringImpl_"; + + std::string tmpName = InFileName; + unsigned i; + for (i=0; i < tmpName.length(); i++) { + char c = tmpName.at(i); + // replace any non-alphanumeric characters with '_'. + if (!isAlphanumeric(c)) + tmpName[i] = '_'; + } + S += tmpName; + S += "_"; + S += utostr(NumObjCStringLiterals++); + + Preamble += "static __NSConstantStringImpl " + S; + Preamble += " __attribute__ ((section (\"__DATA, __cfstring\"))) = {__CFConstantStringClassReference,"; + Preamble += "0x000007c8,"; // utf8_str + // The pretty printer for StringLiteral handles escape characters properly. + std::string prettyBufS; + llvm::raw_string_ostream prettyBuf(prettyBufS); + Exp->getString()->printPretty(prettyBuf, nullptr, PrintingPolicy(LangOpts)); + Preamble += prettyBuf.str(); + Preamble += ","; + Preamble += utostr(Exp->getString()->getByteLength()) + "};\n"; + + VarDecl *NewVD = VarDecl::Create(*Context, TUDecl, SourceLocation(), + SourceLocation(), &Context->Idents.get(S), + strType, nullptr, SC_Static); + DeclRefExpr *DRE = new (Context) + DeclRefExpr(*Context, NewVD, false, strType, VK_LValue, SourceLocation()); + Expr *Unop = new (Context) + UnaryOperator(DRE, UO_AddrOf, Context->getPointerType(DRE->getType()), + VK_RValue, OK_Ordinary, SourceLocation(), false); + // cast to NSConstantString * + CastExpr *cast = NoTypeInfoCStyleCastExpr(Context, Exp->getType(), + CK_CPointerToObjCPointerCast, Unop); + ReplaceStmt(Exp, cast); + // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. + return cast; +} + +// struct objc_super { struct objc_object *receiver; struct objc_class *super; }; +QualType RewriteObjC::getSuperStructType() { + if (!SuperStructDecl) { + SuperStructDecl = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), SourceLocation(), + &Context->Idents.get("objc_super")); + QualType FieldTypes[2]; + + // struct objc_object *receiver; + FieldTypes[0] = Context->getObjCIdType(); + // struct objc_class *super; + FieldTypes[1] = Context->getObjCClassType(); + + // Create fields + for (unsigned i = 0; i < 2; ++i) { + SuperStructDecl->addDecl(FieldDecl::Create(*Context, SuperStructDecl, + SourceLocation(), + SourceLocation(), nullptr, + FieldTypes[i], nullptr, + /*BitWidth=*/nullptr, + /*Mutable=*/false, + ICIS_NoInit)); + } + + SuperStructDecl->completeDefinition(); + } + return Context->getTagDeclType(SuperStructDecl); +} + +QualType RewriteObjC::getConstantStringStructType() { + if (!ConstantStringDecl) { + ConstantStringDecl = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), SourceLocation(), + &Context->Idents.get("__NSConstantStringImpl")); + QualType FieldTypes[4]; + + // struct objc_object *receiver; + FieldTypes[0] = Context->getObjCIdType(); + // int flags; + FieldTypes[1] = Context->IntTy; + // char *str; + FieldTypes[2] = Context->getPointerType(Context->CharTy); + // long length; + FieldTypes[3] = Context->LongTy; + + // Create fields + for (unsigned i = 0; i < 4; ++i) { + ConstantStringDecl->addDecl(FieldDecl::Create(*Context, + ConstantStringDecl, + SourceLocation(), + SourceLocation(), nullptr, + FieldTypes[i], nullptr, + /*BitWidth=*/nullptr, + /*Mutable=*/true, + ICIS_NoInit)); + } + + ConstantStringDecl->completeDefinition(); + } + return Context->getTagDeclType(ConstantStringDecl); +} + +CallExpr *RewriteObjC::SynthMsgSendStretCallExpr(FunctionDecl *MsgSendStretFlavor, + QualType msgSendType, + QualType returnType, + SmallVectorImpl<QualType> &ArgTypes, + SmallVectorImpl<Expr*> &MsgExprs, + ObjCMethodDecl *Method) { + // Create a reference to the objc_msgSend_stret() declaration. + DeclRefExpr *STDRE = + new (Context) DeclRefExpr(*Context, MsgSendStretFlavor, false, + msgSendType, VK_LValue, SourceLocation()); + // Need to cast objc_msgSend_stret to "void *" (see above comment). + CastExpr *cast = NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(Context->VoidTy), + CK_BitCast, STDRE); + // Now do the "normal" pointer to function cast. + QualType castType = getSimpleFunctionType(returnType, ArgTypes, + Method ? Method->isVariadic() + : false); + castType = Context->getPointerType(castType); + cast = NoTypeInfoCStyleCastExpr(Context, castType, CK_BitCast, + cast); + + // Don't forget the parens to enforce the proper binding. + ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), cast); + + const FunctionType *FT = msgSendType->getAs<FunctionType>(); + CallExpr *STCE = CallExpr::Create(*Context, PE, MsgExprs, FT->getReturnType(), + VK_RValue, SourceLocation()); + return STCE; +} + +Stmt *RewriteObjC::SynthMessageExpr(ObjCMessageExpr *Exp, + SourceLocation StartLoc, + SourceLocation EndLoc) { + if (!SelGetUidFunctionDecl) + SynthSelGetUidFunctionDecl(); + if (!MsgSendFunctionDecl) + SynthMsgSendFunctionDecl(); + if (!MsgSendSuperFunctionDecl) + SynthMsgSendSuperFunctionDecl(); + if (!MsgSendStretFunctionDecl) + SynthMsgSendStretFunctionDecl(); + if (!MsgSendSuperStretFunctionDecl) + SynthMsgSendSuperStretFunctionDecl(); + if (!MsgSendFpretFunctionDecl) + SynthMsgSendFpretFunctionDecl(); + if (!GetClassFunctionDecl) + SynthGetClassFunctionDecl(); + if (!GetSuperClassFunctionDecl) + SynthGetSuperClassFunctionDecl(); + if (!GetMetaClassFunctionDecl) + SynthGetMetaClassFunctionDecl(); + + // default to objc_msgSend(). + FunctionDecl *MsgSendFlavor = MsgSendFunctionDecl; + // May need to use objc_msgSend_stret() as well. + FunctionDecl *MsgSendStretFlavor = nullptr; + if (ObjCMethodDecl *mDecl = Exp->getMethodDecl()) { + QualType resultType = mDecl->getReturnType(); + if (resultType->isRecordType()) + MsgSendStretFlavor = MsgSendStretFunctionDecl; + else if (resultType->isRealFloatingType()) + MsgSendFlavor = MsgSendFpretFunctionDecl; + } + + // Synthesize a call to objc_msgSend(). + SmallVector<Expr*, 8> MsgExprs; + switch (Exp->getReceiverKind()) { + case ObjCMessageExpr::SuperClass: { + MsgSendFlavor = MsgSendSuperFunctionDecl; + if (MsgSendStretFlavor) + MsgSendStretFlavor = MsgSendSuperStretFunctionDecl; + assert(MsgSendFlavor && "MsgSendFlavor is NULL!"); + + ObjCInterfaceDecl *ClassDecl = CurMethodDef->getClassInterface(); + + SmallVector<Expr*, 4> InitExprs; + + // set the receiver to self, the first argument to all methods. + InitExprs.push_back( + NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), + CK_BitCast, + new (Context) DeclRefExpr(*Context, + CurMethodDef->getSelfDecl(), + false, + Context->getObjCIdType(), + VK_RValue, + SourceLocation())) + ); // set the 'receiver'. + + // (id)class_getSuperclass((Class)objc_getClass("CurrentClass")) + SmallVector<Expr*, 8> ClsExprs; + ClsExprs.push_back(getStringLiteral(ClassDecl->getIdentifier()->getName())); + CallExpr *Cls = SynthesizeCallToFunctionDecl(GetMetaClassFunctionDecl, + ClsExprs, StartLoc, EndLoc); + // (Class)objc_getClass("CurrentClass") + CastExpr *ArgExpr = NoTypeInfoCStyleCastExpr(Context, + Context->getObjCClassType(), + CK_BitCast, Cls); + ClsExprs.clear(); + ClsExprs.push_back(ArgExpr); + Cls = SynthesizeCallToFunctionDecl(GetSuperClassFunctionDecl, ClsExprs, + StartLoc, EndLoc); + // (id)class_getSuperclass((Class)objc_getClass("CurrentClass")) + // To turn off a warning, type-cast to 'id' + InitExprs.push_back( // set 'super class', using class_getSuperclass(). + NoTypeInfoCStyleCastExpr(Context, + Context->getObjCIdType(), + CK_BitCast, Cls)); + // struct objc_super + QualType superType = getSuperStructType(); + Expr *SuperRep; + + if (LangOpts.MicrosoftExt) { + SynthSuperConstructorFunctionDecl(); + // Simulate a constructor call... + DeclRefExpr *DRE = new (Context) + DeclRefExpr(*Context, SuperConstructorFunctionDecl, false, superType, + VK_LValue, SourceLocation()); + SuperRep = CallExpr::Create(*Context, DRE, InitExprs, superType, + VK_LValue, SourceLocation()); + // The code for super is a little tricky to prevent collision with + // the structure definition in the header. The rewriter has it's own + // internal definition (__rw_objc_super) that is uses. This is why + // we need the cast below. For example: + // (struct objc_super *)&__rw_objc_super((id)self, (id)objc_getClass("SUPER")) + // + SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf, + Context->getPointerType(SuperRep->getType()), + VK_RValue, OK_Ordinary, + SourceLocation(), false); + SuperRep = NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(superType), + CK_BitCast, SuperRep); + } else { + // (struct objc_super) { <exprs from above> } + InitListExpr *ILE = + new (Context) InitListExpr(*Context, SourceLocation(), InitExprs, + SourceLocation()); + TypeSourceInfo *superTInfo + = Context->getTrivialTypeSourceInfo(superType); + SuperRep = new (Context) CompoundLiteralExpr(SourceLocation(), superTInfo, + superType, VK_LValue, + ILE, false); + // struct objc_super * + SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf, + Context->getPointerType(SuperRep->getType()), + VK_RValue, OK_Ordinary, + SourceLocation(), false); + } + MsgExprs.push_back(SuperRep); + break; + } + + case ObjCMessageExpr::Class: { + SmallVector<Expr*, 8> ClsExprs; + ObjCInterfaceDecl *Class + = Exp->getClassReceiver()->getAs<ObjCObjectType>()->getInterface(); + IdentifierInfo *clsName = Class->getIdentifier(); + ClsExprs.push_back(getStringLiteral(clsName->getName())); + CallExpr *Cls = SynthesizeCallToFunctionDecl(GetClassFunctionDecl, ClsExprs, + StartLoc, EndLoc); + MsgExprs.push_back(Cls); + break; + } + + case ObjCMessageExpr::SuperInstance:{ + MsgSendFlavor = MsgSendSuperFunctionDecl; + if (MsgSendStretFlavor) + MsgSendStretFlavor = MsgSendSuperStretFunctionDecl; + assert(MsgSendFlavor && "MsgSendFlavor is NULL!"); + ObjCInterfaceDecl *ClassDecl = CurMethodDef->getClassInterface(); + SmallVector<Expr*, 4> InitExprs; + + InitExprs.push_back( + NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), + CK_BitCast, + new (Context) DeclRefExpr(*Context, + CurMethodDef->getSelfDecl(), + false, + Context->getObjCIdType(), + VK_RValue, SourceLocation())) + ); // set the 'receiver'. + + // (id)class_getSuperclass((Class)objc_getClass("CurrentClass")) + SmallVector<Expr*, 8> ClsExprs; + ClsExprs.push_back(getStringLiteral(ClassDecl->getIdentifier()->getName())); + CallExpr *Cls = SynthesizeCallToFunctionDecl(GetClassFunctionDecl, ClsExprs, + StartLoc, EndLoc); + // (Class)objc_getClass("CurrentClass") + CastExpr *ArgExpr = NoTypeInfoCStyleCastExpr(Context, + Context->getObjCClassType(), + CK_BitCast, Cls); + ClsExprs.clear(); + ClsExprs.push_back(ArgExpr); + Cls = SynthesizeCallToFunctionDecl(GetSuperClassFunctionDecl, ClsExprs, + StartLoc, EndLoc); + + // (id)class_getSuperclass((Class)objc_getClass("CurrentClass")) + // To turn off a warning, type-cast to 'id' + InitExprs.push_back( + // set 'super class', using class_getSuperclass(). + NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), + CK_BitCast, Cls)); + // struct objc_super + QualType superType = getSuperStructType(); + Expr *SuperRep; + + if (LangOpts.MicrosoftExt) { + SynthSuperConstructorFunctionDecl(); + // Simulate a constructor call... + DeclRefExpr *DRE = new (Context) + DeclRefExpr(*Context, SuperConstructorFunctionDecl, false, superType, + VK_LValue, SourceLocation()); + SuperRep = CallExpr::Create(*Context, DRE, InitExprs, superType, + VK_LValue, SourceLocation()); + // The code for super is a little tricky to prevent collision with + // the structure definition in the header. The rewriter has it's own + // internal definition (__rw_objc_super) that is uses. This is why + // we need the cast below. For example: + // (struct objc_super *)&__rw_objc_super((id)self, (id)objc_getClass("SUPER")) + // + SuperRep = new (Context) UnaryOperator(SuperRep, UO_AddrOf, + Context->getPointerType(SuperRep->getType()), + VK_RValue, OK_Ordinary, + SourceLocation(), false); + SuperRep = NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(superType), + CK_BitCast, SuperRep); + } else { + // (struct objc_super) { <exprs from above> } + InitListExpr *ILE = + new (Context) InitListExpr(*Context, SourceLocation(), InitExprs, + SourceLocation()); + TypeSourceInfo *superTInfo + = Context->getTrivialTypeSourceInfo(superType); + SuperRep = new (Context) CompoundLiteralExpr(SourceLocation(), superTInfo, + superType, VK_RValue, ILE, + false); + } + MsgExprs.push_back(SuperRep); + break; + } + + case ObjCMessageExpr::Instance: { + // Remove all type-casts because it may contain objc-style types; e.g. + // Foo<Proto> *. + Expr *recExpr = Exp->getInstanceReceiver(); + while (CStyleCastExpr *CE = dyn_cast<CStyleCastExpr>(recExpr)) + recExpr = CE->getSubExpr(); + CastKind CK = recExpr->getType()->isObjCObjectPointerType() + ? CK_BitCast : recExpr->getType()->isBlockPointerType() + ? CK_BlockPointerToObjCPointerCast + : CK_CPointerToObjCPointerCast; + + recExpr = NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), + CK, recExpr); + MsgExprs.push_back(recExpr); + break; + } + } + + // Create a call to sel_registerName("selName"), it will be the 2nd argument. + SmallVector<Expr*, 8> SelExprs; + SelExprs.push_back(getStringLiteral(Exp->getSelector().getAsString())); + CallExpr *SelExp = SynthesizeCallToFunctionDecl(SelGetUidFunctionDecl, + SelExprs, StartLoc, EndLoc); + MsgExprs.push_back(SelExp); + + // Now push any user supplied arguments. + for (unsigned i = 0; i < Exp->getNumArgs(); i++) { + Expr *userExpr = Exp->getArg(i); + // Make all implicit casts explicit...ICE comes in handy:-) + if (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(userExpr)) { + // Reuse the ICE type, it is exactly what the doctor ordered. + QualType type = ICE->getType(); + if (needToScanForQualifiers(type)) + type = Context->getObjCIdType(); + // Make sure we convert "type (^)(...)" to "type (*)(...)". + (void)convertBlockPointerToFunctionPointer(type); + const Expr *SubExpr = ICE->IgnoreParenImpCasts(); + CastKind CK; + if (SubExpr->getType()->isIntegralType(*Context) && + type->isBooleanType()) { + CK = CK_IntegralToBoolean; + } else if (type->isObjCObjectPointerType()) { + if (SubExpr->getType()->isBlockPointerType()) { + CK = CK_BlockPointerToObjCPointerCast; + } else if (SubExpr->getType()->isPointerType()) { + CK = CK_CPointerToObjCPointerCast; + } else { + CK = CK_BitCast; + } + } else { + CK = CK_BitCast; + } + + userExpr = NoTypeInfoCStyleCastExpr(Context, type, CK, userExpr); + } + // Make id<P...> cast into an 'id' cast. + else if (CStyleCastExpr *CE = dyn_cast<CStyleCastExpr>(userExpr)) { + if (CE->getType()->isObjCQualifiedIdType()) { + while ((CE = dyn_cast<CStyleCastExpr>(userExpr))) + userExpr = CE->getSubExpr(); + CastKind CK; + if (userExpr->getType()->isIntegralType(*Context)) { + CK = CK_IntegralToPointer; + } else if (userExpr->getType()->isBlockPointerType()) { + CK = CK_BlockPointerToObjCPointerCast; + } else if (userExpr->getType()->isPointerType()) { + CK = CK_CPointerToObjCPointerCast; + } else { + CK = CK_BitCast; + } + userExpr = NoTypeInfoCStyleCastExpr(Context, Context->getObjCIdType(), + CK, userExpr); + } + } + MsgExprs.push_back(userExpr); + // We've transferred the ownership to MsgExprs. For now, we *don't* null + // out the argument in the original expression (since we aren't deleting + // the ObjCMessageExpr). See RewritePropertyOrImplicitSetter() usage for more info. + //Exp->setArg(i, 0); + } + // Generate the funky cast. + CastExpr *cast; + SmallVector<QualType, 8> ArgTypes; + QualType returnType; + + // Push 'id' and 'SEL', the 2 implicit arguments. + if (MsgSendFlavor == MsgSendSuperFunctionDecl) + ArgTypes.push_back(Context->getPointerType(getSuperStructType())); + else + ArgTypes.push_back(Context->getObjCIdType()); + ArgTypes.push_back(Context->getObjCSelType()); + if (ObjCMethodDecl *OMD = Exp->getMethodDecl()) { + // Push any user argument types. + for (const auto *PI : OMD->parameters()) { + QualType t = PI->getType()->isObjCQualifiedIdType() + ? Context->getObjCIdType() + : PI->getType(); + // Make sure we convert "t (^)(...)" to "t (*)(...)". + (void)convertBlockPointerToFunctionPointer(t); + ArgTypes.push_back(t); + } + returnType = Exp->getType(); + convertToUnqualifiedObjCType(returnType); + (void)convertBlockPointerToFunctionPointer(returnType); + } else { + returnType = Context->getObjCIdType(); + } + // Get the type, we will need to reference it in a couple spots. + QualType msgSendType = MsgSendFlavor->getType(); + + // Create a reference to the objc_msgSend() declaration. + DeclRefExpr *DRE = new (Context) DeclRefExpr( + *Context, MsgSendFlavor, false, msgSendType, VK_LValue, SourceLocation()); + + // Need to cast objc_msgSend to "void *" (to workaround a GCC bandaid). + // If we don't do this cast, we get the following bizarre warning/note: + // xx.m:13: warning: function called through a non-compatible type + // xx.m:13: note: if this code is reached, the program will abort + cast = NoTypeInfoCStyleCastExpr(Context, + Context->getPointerType(Context->VoidTy), + CK_BitCast, DRE); + + // Now do the "normal" pointer to function cast. + // If we don't have a method decl, force a variadic cast. + const ObjCMethodDecl *MD = Exp->getMethodDecl(); + QualType castType = + getSimpleFunctionType(returnType, ArgTypes, MD ? MD->isVariadic() : true); + castType = Context->getPointerType(castType); + cast = NoTypeInfoCStyleCastExpr(Context, castType, CK_BitCast, + cast); + + // Don't forget the parens to enforce the proper binding. + ParenExpr *PE = new (Context) ParenExpr(StartLoc, EndLoc, cast); + + const FunctionType *FT = msgSendType->getAs<FunctionType>(); + CallExpr *CE = CallExpr::Create(*Context, PE, MsgExprs, FT->getReturnType(), + VK_RValue, EndLoc); + Stmt *ReplacingStmt = CE; + if (MsgSendStretFlavor) { + // We have the method which returns a struct/union. Must also generate + // call to objc_msgSend_stret and hang both varieties on a conditional + // expression which dictate which one to envoke depending on size of + // method's return type. + + CallExpr *STCE = SynthMsgSendStretCallExpr(MsgSendStretFlavor, + msgSendType, returnType, + ArgTypes, MsgExprs, + Exp->getMethodDecl()); + + // Build sizeof(returnType) + UnaryExprOrTypeTraitExpr *sizeofExpr = + new (Context) UnaryExprOrTypeTraitExpr(UETT_SizeOf, + Context->getTrivialTypeSourceInfo(returnType), + Context->getSizeType(), SourceLocation(), + SourceLocation()); + // (sizeof(returnType) <= 8 ? objc_msgSend(...) : objc_msgSend_stret(...)) + // FIXME: Value of 8 is base on ppc32/x86 ABI for the most common cases. + // For X86 it is more complicated and some kind of target specific routine + // is needed to decide what to do. + unsigned IntSize = + static_cast<unsigned>(Context->getTypeSize(Context->IntTy)); + IntegerLiteral *limit = IntegerLiteral::Create(*Context, + llvm::APInt(IntSize, 8), + Context->IntTy, + SourceLocation()); + BinaryOperator *lessThanExpr = + new (Context) BinaryOperator(sizeofExpr, limit, BO_LE, Context->IntTy, + VK_RValue, OK_Ordinary, SourceLocation(), + FPOptions()); + // (sizeof(returnType) <= 8 ? objc_msgSend(...) : objc_msgSend_stret(...)) + ConditionalOperator *CondExpr = + new (Context) ConditionalOperator(lessThanExpr, + SourceLocation(), CE, + SourceLocation(), STCE, + returnType, VK_RValue, OK_Ordinary); + ReplacingStmt = new (Context) ParenExpr(SourceLocation(), SourceLocation(), + CondExpr); + } + // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. + return ReplacingStmt; +} + +Stmt *RewriteObjC::RewriteMessageExpr(ObjCMessageExpr *Exp) { + Stmt *ReplacingStmt = + SynthMessageExpr(Exp, Exp->getBeginLoc(), Exp->getEndLoc()); + + // Now do the actual rewrite. + ReplaceStmt(Exp, ReplacingStmt); + + // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. + return ReplacingStmt; +} + +// typedef struct objc_object Protocol; +QualType RewriteObjC::getProtocolType() { + if (!ProtocolTypeDecl) { + TypeSourceInfo *TInfo + = Context->getTrivialTypeSourceInfo(Context->getObjCIdType()); + ProtocolTypeDecl = TypedefDecl::Create(*Context, TUDecl, + SourceLocation(), SourceLocation(), + &Context->Idents.get("Protocol"), + TInfo); + } + return Context->getTypeDeclType(ProtocolTypeDecl); +} + +/// RewriteObjCProtocolExpr - Rewrite a protocol expression into +/// a synthesized/forward data reference (to the protocol's metadata). +/// The forward references (and metadata) are generated in +/// RewriteObjC::HandleTranslationUnit(). +Stmt *RewriteObjC::RewriteObjCProtocolExpr(ObjCProtocolExpr *Exp) { + std::string Name = "_OBJC_PROTOCOL_" + Exp->getProtocol()->getNameAsString(); + IdentifierInfo *ID = &Context->Idents.get(Name); + VarDecl *VD = VarDecl::Create(*Context, TUDecl, SourceLocation(), + SourceLocation(), ID, getProtocolType(), + nullptr, SC_Extern); + DeclRefExpr *DRE = new (Context) DeclRefExpr( + *Context, VD, false, getProtocolType(), VK_LValue, SourceLocation()); + Expr *DerefExpr = new (Context) UnaryOperator(DRE, UO_AddrOf, + Context->getPointerType(DRE->getType()), + VK_RValue, OK_Ordinary, SourceLocation(), false); + CastExpr *castExpr = NoTypeInfoCStyleCastExpr(Context, DerefExpr->getType(), + CK_BitCast, + DerefExpr); + ReplaceStmt(Exp, castExpr); + ProtocolExprDecls.insert(Exp->getProtocol()->getCanonicalDecl()); + // delete Exp; leak for now, see RewritePropertyOrImplicitSetter() usage for more info. + return castExpr; +} + +bool RewriteObjC::BufferContainsPPDirectives(const char *startBuf, + const char *endBuf) { + while (startBuf < endBuf) { + if (*startBuf == '#') { + // Skip whitespace. + for (++startBuf; startBuf[0] == ' ' || startBuf[0] == '\t'; ++startBuf) + ; + if (!strncmp(startBuf, "if", strlen("if")) || + !strncmp(startBuf, "ifdef", strlen("ifdef")) || + !strncmp(startBuf, "ifndef", strlen("ifndef")) || + !strncmp(startBuf, "define", strlen("define")) || + !strncmp(startBuf, "undef", strlen("undef")) || + !strncmp(startBuf, "else", strlen("else")) || + !strncmp(startBuf, "elif", strlen("elif")) || + !strncmp(startBuf, "endif", strlen("endif")) || + !strncmp(startBuf, "pragma", strlen("pragma")) || + !strncmp(startBuf, "include", strlen("include")) || + !strncmp(startBuf, "import", strlen("import")) || + !strncmp(startBuf, "include_next", strlen("include_next"))) + return true; + } + startBuf++; + } + return false; +} + +/// RewriteObjCInternalStruct - Rewrite one internal struct corresponding to +/// an objective-c class with ivars. +void RewriteObjC::RewriteObjCInternalStruct(ObjCInterfaceDecl *CDecl, + std::string &Result) { + assert(CDecl && "Class missing in SynthesizeObjCInternalStruct"); + assert(CDecl->getName() != "" && + "Name missing in SynthesizeObjCInternalStruct"); + // Do not synthesize more than once. + if (ObjCSynthesizedStructs.count(CDecl)) + return; + ObjCInterfaceDecl *RCDecl = CDecl->getSuperClass(); + int NumIvars = CDecl->ivar_size(); + SourceLocation LocStart = CDecl->getBeginLoc(); + SourceLocation LocEnd = CDecl->getEndOfDefinitionLoc(); + + const char *startBuf = SM->getCharacterData(LocStart); + const char *endBuf = SM->getCharacterData(LocEnd); + + // If no ivars and no root or if its root, directly or indirectly, + // have no ivars (thus not synthesized) then no need to synthesize this class. + if ((!CDecl->isThisDeclarationADefinition() || NumIvars == 0) && + (!RCDecl || !ObjCSynthesizedStructs.count(RCDecl))) { + endBuf += Lexer::MeasureTokenLength(LocEnd, *SM, LangOpts); + ReplaceText(LocStart, endBuf-startBuf, Result); + return; + } + + // FIXME: This has potential of causing problem. If + // SynthesizeObjCInternalStruct is ever called recursively. + Result += "\nstruct "; + Result += CDecl->getNameAsString(); + if (LangOpts.MicrosoftExt) + Result += "_IMPL"; + + if (NumIvars > 0) { + const char *cursor = strchr(startBuf, '{'); + assert((cursor && endBuf) + && "SynthesizeObjCInternalStruct - malformed @interface"); + // If the buffer contains preprocessor directives, we do more fine-grained + // rewrites. This is intended to fix code that looks like (which occurs in + // NSURL.h, for example): + // + // #ifdef XYZ + // @interface Foo : NSObject + // #else + // @interface FooBar : NSObject + // #endif + // { + // int i; + // } + // @end + // + // This clause is segregated to avoid breaking the common case. + if (BufferContainsPPDirectives(startBuf, cursor)) { + SourceLocation L = RCDecl ? CDecl->getSuperClassLoc() : + CDecl->getAtStartLoc(); + const char *endHeader = SM->getCharacterData(L); + endHeader += Lexer::MeasureTokenLength(L, *SM, LangOpts); + + if (CDecl->protocol_begin() != CDecl->protocol_end()) { + // advance to the end of the referenced protocols. + while (endHeader < cursor && *endHeader != '>') endHeader++; + endHeader++; + } + // rewrite the original header + ReplaceText(LocStart, endHeader-startBuf, Result); + } else { + // rewrite the original header *without* disturbing the '{' + ReplaceText(LocStart, cursor-startBuf, Result); + } + if (RCDecl && ObjCSynthesizedStructs.count(RCDecl)) { + Result = "\n struct "; + Result += RCDecl->getNameAsString(); + Result += "_IMPL "; + Result += RCDecl->getNameAsString(); + Result += "_IVARS;\n"; + + // insert the super class structure definition. + SourceLocation OnePastCurly = + LocStart.getLocWithOffset(cursor-startBuf+1); + InsertText(OnePastCurly, Result); + } + cursor++; // past '{' + + // Now comment out any visibility specifiers. + while (cursor < endBuf) { + if (*cursor == '@') { + SourceLocation atLoc = LocStart.getLocWithOffset(cursor-startBuf); + // Skip whitespace. + for (++cursor; cursor[0] == ' ' || cursor[0] == '\t'; ++cursor) + /*scan*/; + + // FIXME: presence of @public, etc. inside comment results in + // this transformation as well, which is still correct c-code. + if (!strncmp(cursor, "public", strlen("public")) || + !strncmp(cursor, "private", strlen("private")) || + !strncmp(cursor, "package", strlen("package")) || + !strncmp(cursor, "protected", strlen("protected"))) + InsertText(atLoc, "// "); + } + // FIXME: If there are cases where '<' is used in ivar declaration part + // of user code, then scan the ivar list and use needToScanForQualifiers + // for type checking. + else if (*cursor == '<') { + SourceLocation atLoc = LocStart.getLocWithOffset(cursor-startBuf); + InsertText(atLoc, "/* "); + cursor = strchr(cursor, '>'); + cursor++; + atLoc = LocStart.getLocWithOffset(cursor-startBuf); + InsertText(atLoc, " */"); + } else if (*cursor == '^') { // rewrite block specifier. + SourceLocation caretLoc = LocStart.getLocWithOffset(cursor-startBuf); + ReplaceText(caretLoc, 1, "*"); + } + cursor++; + } + // Don't forget to add a ';'!! + InsertText(LocEnd.getLocWithOffset(1), ";"); + } else { // we don't have any instance variables - insert super struct. + endBuf += Lexer::MeasureTokenLength(LocEnd, *SM, LangOpts); + Result += " {\n struct "; + Result += RCDecl->getNameAsString(); + Result += "_IMPL "; + Result += RCDecl->getNameAsString(); + Result += "_IVARS;\n};\n"; + ReplaceText(LocStart, endBuf-startBuf, Result); + } + // Mark this struct as having been generated. + if (!ObjCSynthesizedStructs.insert(CDecl).second) + llvm_unreachable("struct already synthesize- SynthesizeObjCInternalStruct"); +} + +//===----------------------------------------------------------------------===// +// Meta Data Emission +//===----------------------------------------------------------------------===// + +/// RewriteImplementations - This routine rewrites all method implementations +/// and emits meta-data. + +void RewriteObjC::RewriteImplementations() { + int ClsDefCount = ClassImplementation.size(); + int CatDefCount = CategoryImplementation.size(); + + // Rewrite implemented methods + for (int i = 0; i < ClsDefCount; i++) + RewriteImplementationDecl(ClassImplementation[i]); + + for (int i = 0; i < CatDefCount; i++) + RewriteImplementationDecl(CategoryImplementation[i]); +} + +void RewriteObjC::RewriteByRefString(std::string &ResultStr, + const std::string &Name, + ValueDecl *VD, bool def) { + assert(BlockByRefDeclNo.count(VD) && + "RewriteByRefString: ByRef decl missing"); + if (def) + ResultStr += "struct "; + ResultStr += "__Block_byref_" + Name + + "_" + utostr(BlockByRefDeclNo[VD]) ; +} + +static bool HasLocalVariableExternalStorage(ValueDecl *VD) { + if (VarDecl *Var = dyn_cast<VarDecl>(VD)) + return (Var->isFunctionOrMethodVarDecl() && !Var->hasLocalStorage()); + return false; +} + +std::string RewriteObjC::SynthesizeBlockFunc(BlockExpr *CE, int i, + StringRef funcName, + std::string Tag) { + const FunctionType *AFT = CE->getFunctionType(); + QualType RT = AFT->getReturnType(); + std::string StructRef = "struct " + Tag; + std::string S = "static " + RT.getAsString(Context->getPrintingPolicy()) + " __" + + funcName.str() + "_" + "block_func_" + utostr(i); + + BlockDecl *BD = CE->getBlockDecl(); + + if (isa<FunctionNoProtoType>(AFT)) { + // No user-supplied arguments. Still need to pass in a pointer to the + // block (to reference imported block decl refs). + S += "(" + StructRef + " *__cself)"; + } else if (BD->param_empty()) { + S += "(" + StructRef + " *__cself)"; + } else { + const FunctionProtoType *FT = cast<FunctionProtoType>(AFT); + assert(FT && "SynthesizeBlockFunc: No function proto"); + S += '('; + // first add the implicit argument. + S += StructRef + " *__cself, "; + std::string ParamStr; + for (BlockDecl::param_iterator AI = BD->param_begin(), + E = BD->param_end(); AI != E; ++AI) { + if (AI != BD->param_begin()) S += ", "; + ParamStr = (*AI)->getNameAsString(); + QualType QT = (*AI)->getType(); + (void)convertBlockPointerToFunctionPointer(QT); + QT.getAsStringInternal(ParamStr, Context->getPrintingPolicy()); + S += ParamStr; + } + if (FT->isVariadic()) { + if (!BD->param_empty()) S += ", "; + S += "..."; + } + S += ')'; + } + S += " {\n"; + + // Create local declarations to avoid rewriting all closure decl ref exprs. + // First, emit a declaration for all "by ref" decls. + for (SmallVectorImpl<ValueDecl *>::iterator I = BlockByRefDecls.begin(), + E = BlockByRefDecls.end(); I != E; ++I) { + S += " "; + std::string Name = (*I)->getNameAsString(); + std::string TypeString; + RewriteByRefString(TypeString, Name, (*I)); + TypeString += " *"; + Name = TypeString + Name; + S += Name + " = __cself->" + (*I)->getNameAsString() + "; // bound by ref\n"; + } + // Next, emit a declaration for all "by copy" declarations. + for (SmallVectorImpl<ValueDecl *>::iterator I = BlockByCopyDecls.begin(), + E = BlockByCopyDecls.end(); I != E; ++I) { + S += " "; + // Handle nested closure invocation. For example: + // + // void (^myImportedClosure)(void); + // myImportedClosure = ^(void) { setGlobalInt(x + y); }; + // + // void (^anotherClosure)(void); + // anotherClosure = ^(void) { + // myImportedClosure(); // import and invoke the closure + // }; + // + if (isTopLevelBlockPointerType((*I)->getType())) { + RewriteBlockPointerTypeVariable(S, (*I)); + S += " = ("; + RewriteBlockPointerType(S, (*I)->getType()); + S += ")"; + S += "__cself->" + (*I)->getNameAsString() + "; // bound by copy\n"; + } + else { + std::string Name = (*I)->getNameAsString(); + QualType QT = (*I)->getType(); + if (HasLocalVariableExternalStorage(*I)) + QT = Context->getPointerType(QT); + QT.getAsStringInternal(Name, Context->getPrintingPolicy()); + S += Name + " = __cself->" + + (*I)->getNameAsString() + "; // bound by copy\n"; + } + } + std::string RewrittenStr = RewrittenBlockExprs[CE]; + const char *cstr = RewrittenStr.c_str(); + while (*cstr++ != '{') ; + S += cstr; + S += "\n"; + return S; +} + +std::string RewriteObjC::SynthesizeBlockHelperFuncs(BlockExpr *CE, int i, + StringRef funcName, + std::string Tag) { + std::string StructRef = "struct " + Tag; + std::string S = "static void __"; + + S += funcName; + S += "_block_copy_" + utostr(i); + S += "(" + StructRef; + S += "*dst, " + StructRef; + S += "*src) {"; + for (ValueDecl *VD : ImportedBlockDecls) { + S += "_Block_object_assign((void*)&dst->"; + S += VD->getNameAsString(); + S += ", (void*)src->"; + S += VD->getNameAsString(); + if (BlockByRefDeclsPtrSet.count(VD)) + S += ", " + utostr(BLOCK_FIELD_IS_BYREF) + "/*BLOCK_FIELD_IS_BYREF*/);"; + else if (VD->getType()->isBlockPointerType()) + S += ", " + utostr(BLOCK_FIELD_IS_BLOCK) + "/*BLOCK_FIELD_IS_BLOCK*/);"; + else + S += ", " + utostr(BLOCK_FIELD_IS_OBJECT) + "/*BLOCK_FIELD_IS_OBJECT*/);"; + } + S += "}\n"; + + S += "\nstatic void __"; + S += funcName; + S += "_block_dispose_" + utostr(i); + S += "(" + StructRef; + S += "*src) {"; + for (ValueDecl *VD : ImportedBlockDecls) { + S += "_Block_object_dispose((void*)src->"; + S += VD->getNameAsString(); + if (BlockByRefDeclsPtrSet.count(VD)) + S += ", " + utostr(BLOCK_FIELD_IS_BYREF) + "/*BLOCK_FIELD_IS_BYREF*/);"; + else if (VD->getType()->isBlockPointerType()) + S += ", " + utostr(BLOCK_FIELD_IS_BLOCK) + "/*BLOCK_FIELD_IS_BLOCK*/);"; + else + S += ", " + utostr(BLOCK_FIELD_IS_OBJECT) + "/*BLOCK_FIELD_IS_OBJECT*/);"; + } + S += "}\n"; + return S; +} + +std::string RewriteObjC::SynthesizeBlockImpl(BlockExpr *CE, std::string Tag, + std::string Desc) { + std::string S = "\nstruct " + Tag; + std::string Constructor = " " + Tag; + + S += " {\n struct __block_impl impl;\n"; + S += " struct " + Desc; + S += "* Desc;\n"; + + Constructor += "(void *fp, "; // Invoke function pointer. + Constructor += "struct " + Desc; // Descriptor pointer. + Constructor += " *desc"; + + if (BlockDeclRefs.size()) { + // Output all "by copy" declarations. + for (SmallVectorImpl<ValueDecl *>::iterator I = BlockByCopyDecls.begin(), + E = BlockByCopyDecls.end(); I != E; ++I) { + S += " "; + std::string FieldName = (*I)->getNameAsString(); + std::string ArgName = "_" + FieldName; + // Handle nested closure invocation. For example: + // + // void (^myImportedBlock)(void); + // myImportedBlock = ^(void) { setGlobalInt(x + y); }; + // + // void (^anotherBlock)(void); + // anotherBlock = ^(void) { + // myImportedBlock(); // import and invoke the closure + // }; + // + if (isTopLevelBlockPointerType((*I)->getType())) { + S += "struct __block_impl *"; + Constructor += ", void *" + ArgName; + } else { + QualType QT = (*I)->getType(); + if (HasLocalVariableExternalStorage(*I)) + QT = Context->getPointerType(QT); + QT.getAsStringInternal(FieldName, Context->getPrintingPolicy()); + QT.getAsStringInternal(ArgName, Context->getPrintingPolicy()); + Constructor += ", " + ArgName; + } + S += FieldName + ";\n"; + } + // Output all "by ref" declarations. + for (SmallVectorImpl<ValueDecl *>::iterator I = BlockByRefDecls.begin(), + E = BlockByRefDecls.end(); I != E; ++I) { + S += " "; + std::string FieldName = (*I)->getNameAsString(); + std::string ArgName = "_" + FieldName; + { + std::string TypeString; + RewriteByRefString(TypeString, FieldName, (*I)); + TypeString += " *"; + FieldName = TypeString + FieldName; + ArgName = TypeString + ArgName; + Constructor += ", " + ArgName; + } + S += FieldName + "; // by ref\n"; + } + // Finish writing the constructor. + Constructor += ", int flags=0)"; + // Initialize all "by copy" arguments. + bool firsTime = true; + for (SmallVectorImpl<ValueDecl *>::iterator I = BlockByCopyDecls.begin(), + E = BlockByCopyDecls.end(); I != E; ++I) { + std::string Name = (*I)->getNameAsString(); + if (firsTime) { + Constructor += " : "; + firsTime = false; + } + else + Constructor += ", "; + if (isTopLevelBlockPointerType((*I)->getType())) + Constructor += Name + "((struct __block_impl *)_" + Name + ")"; + else + Constructor += Name + "(_" + Name + ")"; + } + // Initialize all "by ref" arguments. + for (SmallVectorImpl<ValueDecl *>::iterator I = BlockByRefDecls.begin(), + E = BlockByRefDecls.end(); I != E; ++I) { + std::string Name = (*I)->getNameAsString(); + if (firsTime) { + Constructor += " : "; + firsTime = false; + } + else + Constructor += ", "; + Constructor += Name + "(_" + Name + "->__forwarding)"; + } + + Constructor += " {\n"; + if (GlobalVarDecl) + Constructor += " impl.isa = &_NSConcreteGlobalBlock;\n"; + else + Constructor += " impl.isa = &_NSConcreteStackBlock;\n"; + Constructor += " impl.Flags = flags;\n impl.FuncPtr = fp;\n"; + + Constructor += " Desc = desc;\n"; + } else { + // Finish writing the constructor. + Constructor += ", int flags=0) {\n"; + if (GlobalVarDecl) + Constructor += " impl.isa = &_NSConcreteGlobalBlock;\n"; + else + Constructor += " impl.isa = &_NSConcreteStackBlock;\n"; + Constructor += " impl.Flags = flags;\n impl.FuncPtr = fp;\n"; + Constructor += " Desc = desc;\n"; + } + Constructor += " "; + Constructor += "}\n"; + S += Constructor; + S += "};\n"; + return S; +} + +std::string RewriteObjC::SynthesizeBlockDescriptor(std::string DescTag, + std::string ImplTag, int i, + StringRef FunName, + unsigned hasCopy) { + std::string S = "\nstatic struct " + DescTag; + + S += " {\n unsigned long reserved;\n"; + S += " unsigned long Block_size;\n"; + if (hasCopy) { + S += " void (*copy)(struct "; + S += ImplTag; S += "*, struct "; + S += ImplTag; S += "*);\n"; + + S += " void (*dispose)(struct "; + S += ImplTag; S += "*);\n"; + } + S += "} "; + + S += DescTag + "_DATA = { 0, sizeof(struct "; + S += ImplTag + ")"; + if (hasCopy) { + S += ", __" + FunName.str() + "_block_copy_" + utostr(i); + S += ", __" + FunName.str() + "_block_dispose_" + utostr(i); + } + S += "};\n"; + return S; +} + +void RewriteObjC::SynthesizeBlockLiterals(SourceLocation FunLocStart, + StringRef FunName) { + // Insert declaration for the function in which block literal is used. + if (CurFunctionDeclToDeclareForBlock && !Blocks.empty()) + RewriteBlockLiteralFunctionDecl(CurFunctionDeclToDeclareForBlock); + bool RewriteSC = (GlobalVarDecl && + !Blocks.empty() && + GlobalVarDecl->getStorageClass() == SC_Static && + GlobalVarDecl->getType().getCVRQualifiers()); + if (RewriteSC) { + std::string SC(" void __"); + SC += GlobalVarDecl->getNameAsString(); + SC += "() {}"; + InsertText(FunLocStart, SC); + } + + // Insert closures that were part of the function. + for (unsigned i = 0, count=0; i < Blocks.size(); i++) { + CollectBlockDeclRefInfo(Blocks[i]); + // Need to copy-in the inner copied-in variables not actually used in this + // block. + for (int j = 0; j < InnerDeclRefsCount[i]; j++) { + DeclRefExpr *Exp = InnerDeclRefs[count++]; + ValueDecl *VD = Exp->getDecl(); + BlockDeclRefs.push_back(Exp); + if (!VD->hasAttr<BlocksAttr>() && !BlockByCopyDeclsPtrSet.count(VD)) { + BlockByCopyDeclsPtrSet.insert(VD); + BlockByCopyDecls.push_back(VD); + } + if (VD->hasAttr<BlocksAttr>() && !BlockByRefDeclsPtrSet.count(VD)) { + BlockByRefDeclsPtrSet.insert(VD); + BlockByRefDecls.push_back(VD); + } + // imported objects in the inner blocks not used in the outer + // blocks must be copied/disposed in the outer block as well. + if (VD->hasAttr<BlocksAttr>() || + VD->getType()->isObjCObjectPointerType() || + VD->getType()->isBlockPointerType()) + ImportedBlockDecls.insert(VD); + } + + std::string ImplTag = "__" + FunName.str() + "_block_impl_" + utostr(i); + std::string DescTag = "__" + FunName.str() + "_block_desc_" + utostr(i); + + std::string CI = SynthesizeBlockImpl(Blocks[i], ImplTag, DescTag); + + InsertText(FunLocStart, CI); + + std::string CF = SynthesizeBlockFunc(Blocks[i], i, FunName, ImplTag); + + InsertText(FunLocStart, CF); + + if (ImportedBlockDecls.size()) { + std::string HF = SynthesizeBlockHelperFuncs(Blocks[i], i, FunName, ImplTag); + InsertText(FunLocStart, HF); + } + std::string BD = SynthesizeBlockDescriptor(DescTag, ImplTag, i, FunName, + ImportedBlockDecls.size() > 0); + InsertText(FunLocStart, BD); + + BlockDeclRefs.clear(); + BlockByRefDecls.clear(); + BlockByRefDeclsPtrSet.clear(); + BlockByCopyDecls.clear(); + BlockByCopyDeclsPtrSet.clear(); + ImportedBlockDecls.clear(); + } + if (RewriteSC) { + // Must insert any 'const/volatile/static here. Since it has been + // removed as result of rewriting of block literals. + std::string SC; + if (GlobalVarDecl->getStorageClass() == SC_Static) + SC = "static "; + if (GlobalVarDecl->getType().isConstQualified()) + SC += "const "; + if (GlobalVarDecl->getType().isVolatileQualified()) + SC += "volatile "; + if (GlobalVarDecl->getType().isRestrictQualified()) + SC += "restrict "; + InsertText(FunLocStart, SC); + } + + Blocks.clear(); + InnerDeclRefsCount.clear(); + InnerDeclRefs.clear(); + RewrittenBlockExprs.clear(); +} + +void RewriteObjC::InsertBlockLiteralsWithinFunction(FunctionDecl *FD) { + SourceLocation FunLocStart = FD->getTypeSpecStartLoc(); + StringRef FuncName = FD->getName(); + + SynthesizeBlockLiterals(FunLocStart, FuncName); +} + +static void BuildUniqueMethodName(std::string &Name, + ObjCMethodDecl *MD) { + ObjCInterfaceDecl *IFace = MD->getClassInterface(); + Name = IFace->getName(); + Name += "__" + MD->getSelector().getAsString(); + // Convert colons to underscores. + std::string::size_type loc = 0; + while ((loc = Name.find(':', loc)) != std::string::npos) + Name.replace(loc, 1, "_"); +} + +void RewriteObjC::InsertBlockLiteralsWithinMethod(ObjCMethodDecl *MD) { + // fprintf(stderr,"In InsertBlockLiteralsWitinMethod\n"); + // SourceLocation FunLocStart = MD->getBeginLoc(); + SourceLocation FunLocStart = MD->getBeginLoc(); + std::string FuncName; + BuildUniqueMethodName(FuncName, MD); + SynthesizeBlockLiterals(FunLocStart, FuncName); +} + +void RewriteObjC::GetBlockDeclRefExprs(Stmt *S) { + for (Stmt *SubStmt : S->children()) + if (SubStmt) { + if (BlockExpr *CBE = dyn_cast<BlockExpr>(SubStmt)) + GetBlockDeclRefExprs(CBE->getBody()); + else + GetBlockDeclRefExprs(SubStmt); + } + // Handle specific things. + if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(S)) + if (DRE->refersToEnclosingVariableOrCapture() || + HasLocalVariableExternalStorage(DRE->getDecl())) + // FIXME: Handle enums. + BlockDeclRefs.push_back(DRE); +} + +void RewriteObjC::GetInnerBlockDeclRefExprs(Stmt *S, + SmallVectorImpl<DeclRefExpr *> &InnerBlockDeclRefs, + llvm::SmallPtrSetImpl<const DeclContext *> &InnerContexts) { + for (Stmt *SubStmt : S->children()) + if (SubStmt) { + if (BlockExpr *CBE = dyn_cast<BlockExpr>(SubStmt)) { + InnerContexts.insert(cast<DeclContext>(CBE->getBlockDecl())); + GetInnerBlockDeclRefExprs(CBE->getBody(), + InnerBlockDeclRefs, + InnerContexts); + } + else + GetInnerBlockDeclRefExprs(SubStmt, InnerBlockDeclRefs, InnerContexts); + } + // Handle specific things. + if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(S)) { + if (DRE->refersToEnclosingVariableOrCapture() || + HasLocalVariableExternalStorage(DRE->getDecl())) { + if (!InnerContexts.count(DRE->getDecl()->getDeclContext())) + InnerBlockDeclRefs.push_back(DRE); + if (VarDecl *Var = cast<VarDecl>(DRE->getDecl())) + if (Var->isFunctionOrMethodVarDecl()) + ImportedLocalExternalDecls.insert(Var); + } + } +} + +/// convertFunctionTypeOfBlocks - This routine converts a function type +/// whose result type may be a block pointer or whose argument type(s) +/// might be block pointers to an equivalent function type replacing +/// all block pointers to function pointers. +QualType RewriteObjC::convertFunctionTypeOfBlocks(const FunctionType *FT) { + const FunctionProtoType *FTP = dyn_cast<FunctionProtoType>(FT); + // FTP will be null for closures that don't take arguments. + // Generate a funky cast. + SmallVector<QualType, 8> ArgTypes; + QualType Res = FT->getReturnType(); + bool HasBlockType = convertBlockPointerToFunctionPointer(Res); + + if (FTP) { + for (auto &I : FTP->param_types()) { + QualType t = I; + // Make sure we convert "t (^)(...)" to "t (*)(...)". + if (convertBlockPointerToFunctionPointer(t)) + HasBlockType = true; + ArgTypes.push_back(t); + } + } + QualType FuncType; + // FIXME. Does this work if block takes no argument but has a return type + // which is of block type? + if (HasBlockType) + FuncType = getSimpleFunctionType(Res, ArgTypes); + else FuncType = QualType(FT, 0); + return FuncType; +} + +Stmt *RewriteObjC::SynthesizeBlockCall(CallExpr *Exp, const Expr *BlockExp) { + // Navigate to relevant type information. + const BlockPointerType *CPT = nullptr; + + if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(BlockExp)) { + CPT = DRE->getType()->getAs<BlockPointerType>(); + } else if (const MemberExpr *MExpr = dyn_cast<MemberExpr>(BlockExp)) { + CPT = MExpr->getType()->getAs<BlockPointerType>(); + } + else if (const ParenExpr *PRE = dyn_cast<ParenExpr>(BlockExp)) { + return SynthesizeBlockCall(Exp, PRE->getSubExpr()); + } + else if (const ImplicitCastExpr *IEXPR = dyn_cast<ImplicitCastExpr>(BlockExp)) + CPT = IEXPR->getType()->getAs<BlockPointerType>(); + else if (const ConditionalOperator *CEXPR = + dyn_cast<ConditionalOperator>(BlockExp)) { + Expr *LHSExp = CEXPR->getLHS(); + Stmt *LHSStmt = SynthesizeBlockCall(Exp, LHSExp); + Expr *RHSExp = CEXPR->getRHS(); + Stmt *RHSStmt = SynthesizeBlockCall(Exp, RHSExp); + Expr *CONDExp = CEXPR->getCond(); + ConditionalOperator *CondExpr = + new (Context) ConditionalOperator(CONDExp, + SourceLocation(), cast<Expr>(LHSStmt), + SourceLocation(), cast<Expr>(RHSStmt), + Exp->getType(), VK_RValue, OK_Ordinary); + return CondExpr; + } else if (const ObjCIvarRefExpr *IRE = dyn_cast<ObjCIvarRefExpr>(BlockExp)) { + CPT = IRE->getType()->getAs<BlockPointerType>(); + } else if (const PseudoObjectExpr *POE + = dyn_cast<PseudoObjectExpr>(BlockExp)) { + CPT = POE->getType()->castAs<BlockPointerType>(); + } else { + assert(false && "RewriteBlockClass: Bad type"); + } + assert(CPT && "RewriteBlockClass: Bad type"); + const FunctionType *FT = CPT->getPointeeType()->getAs<FunctionType>(); + assert(FT && "RewriteBlockClass: Bad type"); + const FunctionProtoType *FTP = dyn_cast<FunctionProtoType>(FT); + // FTP will be null for closures that don't take arguments. + + RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), SourceLocation(), + &Context->Idents.get("__block_impl")); + QualType PtrBlock = Context->getPointerType(Context->getTagDeclType(RD)); + + // Generate a funky cast. + SmallVector<QualType, 8> ArgTypes; + + // Push the block argument type. + ArgTypes.push_back(PtrBlock); + if (FTP) { + for (auto &I : FTP->param_types()) { + QualType t = I; + // Make sure we convert "t (^)(...)" to "t (*)(...)". + if (!convertBlockPointerToFunctionPointer(t)) + convertToUnqualifiedObjCType(t); + ArgTypes.push_back(t); + } + } + // Now do the pointer to function cast. + QualType PtrToFuncCastType = getSimpleFunctionType(Exp->getType(), ArgTypes); + + PtrToFuncCastType = Context->getPointerType(PtrToFuncCastType); + + CastExpr *BlkCast = NoTypeInfoCStyleCastExpr(Context, PtrBlock, + CK_BitCast, + const_cast<Expr*>(BlockExp)); + // Don't forget the parens to enforce the proper binding. + ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), + BlkCast); + //PE->dump(); + + FieldDecl *FD = FieldDecl::Create(*Context, nullptr, SourceLocation(), + SourceLocation(), + &Context->Idents.get("FuncPtr"), + Context->VoidPtrTy, nullptr, + /*BitWidth=*/nullptr, /*Mutable=*/true, + ICIS_NoInit); + MemberExpr *ME = + new (Context) MemberExpr(PE, true, SourceLocation(), FD, SourceLocation(), + FD->getType(), VK_LValue, OK_Ordinary); + + CastExpr *FunkCast = NoTypeInfoCStyleCastExpr(Context, PtrToFuncCastType, + CK_BitCast, ME); + PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), FunkCast); + + SmallVector<Expr*, 8> BlkExprs; + // Add the implicit argument. + BlkExprs.push_back(BlkCast); + // Add the user arguments. + for (CallExpr::arg_iterator I = Exp->arg_begin(), + E = Exp->arg_end(); I != E; ++I) { + BlkExprs.push_back(*I); + } + CallExpr *CE = CallExpr::Create(*Context, PE, BlkExprs, Exp->getType(), + VK_RValue, SourceLocation()); + return CE; +} + +// We need to return the rewritten expression to handle cases where the +// BlockDeclRefExpr is embedded in another expression being rewritten. +// For example: +// +// int main() { +// __block Foo *f; +// __block int i; +// +// void (^myblock)() = ^() { +// [f test]; // f is a BlockDeclRefExpr embedded in a message (which is being rewritten). +// i = 77; +// }; +//} +Stmt *RewriteObjC::RewriteBlockDeclRefExpr(DeclRefExpr *DeclRefExp) { + // Rewrite the byref variable into BYREFVAR->__forwarding->BYREFVAR + // for each DeclRefExp where BYREFVAR is name of the variable. + ValueDecl *VD = DeclRefExp->getDecl(); + bool isArrow = DeclRefExp->refersToEnclosingVariableOrCapture() || + HasLocalVariableExternalStorage(DeclRefExp->getDecl()); + + FieldDecl *FD = FieldDecl::Create(*Context, nullptr, SourceLocation(), + SourceLocation(), + &Context->Idents.get("__forwarding"), + Context->VoidPtrTy, nullptr, + /*BitWidth=*/nullptr, /*Mutable=*/true, + ICIS_NoInit); + MemberExpr *ME = new (Context) + MemberExpr(DeclRefExp, isArrow, SourceLocation(), FD, SourceLocation(), + FD->getType(), VK_LValue, OK_Ordinary); + + StringRef Name = VD->getName(); + FD = FieldDecl::Create(*Context, nullptr, SourceLocation(), SourceLocation(), + &Context->Idents.get(Name), + Context->VoidPtrTy, nullptr, + /*BitWidth=*/nullptr, /*Mutable=*/true, + ICIS_NoInit); + ME = + new (Context) MemberExpr(ME, true, SourceLocation(), FD, SourceLocation(), + DeclRefExp->getType(), VK_LValue, OK_Ordinary); + + // Need parens to enforce precedence. + ParenExpr *PE = new (Context) ParenExpr(DeclRefExp->getExprLoc(), + DeclRefExp->getExprLoc(), + ME); + ReplaceStmt(DeclRefExp, PE); + return PE; +} + +// Rewrites the imported local variable V with external storage +// (static, extern, etc.) as *V +// +Stmt *RewriteObjC::RewriteLocalVariableExternalStorage(DeclRefExpr *DRE) { + ValueDecl *VD = DRE->getDecl(); + if (VarDecl *Var = dyn_cast<VarDecl>(VD)) + if (!ImportedLocalExternalDecls.count(Var)) + return DRE; + Expr *Exp = new (Context) UnaryOperator(DRE, UO_Deref, DRE->getType(), + VK_LValue, OK_Ordinary, + DRE->getLocation(), false); + // Need parens to enforce precedence. + ParenExpr *PE = new (Context) ParenExpr(SourceLocation(), SourceLocation(), + Exp); + ReplaceStmt(DRE, PE); + return PE; +} + +void RewriteObjC::RewriteCastExpr(CStyleCastExpr *CE) { + SourceLocation LocStart = CE->getLParenLoc(); + SourceLocation LocEnd = CE->getRParenLoc(); + + // Need to avoid trying to rewrite synthesized casts. + if (LocStart.isInvalid()) + return; + // Need to avoid trying to rewrite casts contained in macros. + if (!Rewriter::isRewritable(LocStart) || !Rewriter::isRewritable(LocEnd)) + return; + + const char *startBuf = SM->getCharacterData(LocStart); + const char *endBuf = SM->getCharacterData(LocEnd); + QualType QT = CE->getType(); + const Type* TypePtr = QT->getAs<Type>(); + if (isa<TypeOfExprType>(TypePtr)) { + const TypeOfExprType *TypeOfExprTypePtr = cast<TypeOfExprType>(TypePtr); + QT = TypeOfExprTypePtr->getUnderlyingExpr()->getType(); + std::string TypeAsString = "("; + RewriteBlockPointerType(TypeAsString, QT); + TypeAsString += ")"; + ReplaceText(LocStart, endBuf-startBuf+1, TypeAsString); + return; + } + // advance the location to startArgList. + const char *argPtr = startBuf; + + while (*argPtr++ && (argPtr < endBuf)) { + switch (*argPtr) { + case '^': + // Replace the '^' with '*'. + LocStart = LocStart.getLocWithOffset(argPtr-startBuf); + ReplaceText(LocStart, 1, "*"); + break; + } + } +} + +void RewriteObjC::RewriteBlockPointerFunctionArgs(FunctionDecl *FD) { + SourceLocation DeclLoc = FD->getLocation(); + unsigned parenCount = 0; + + // We have 1 or more arguments that have closure pointers. + const char *startBuf = SM->getCharacterData(DeclLoc); + const char *startArgList = strchr(startBuf, '('); + + assert((*startArgList == '(') && "Rewriter fuzzy parser confused"); + + parenCount++; + // advance the location to startArgList. + DeclLoc = DeclLoc.getLocWithOffset(startArgList-startBuf); + assert((DeclLoc.isValid()) && "Invalid DeclLoc"); + + const char *argPtr = startArgList; + + while (*argPtr++ && parenCount) { + switch (*argPtr) { + case '^': + // Replace the '^' with '*'. + DeclLoc = DeclLoc.getLocWithOffset(argPtr-startArgList); + ReplaceText(DeclLoc, 1, "*"); + break; + case '(': + parenCount++; + break; + case ')': + parenCount--; + break; + } + } +} + +bool RewriteObjC::PointerTypeTakesAnyBlockArguments(QualType QT) { + const FunctionProtoType *FTP; + const PointerType *PT = QT->getAs<PointerType>(); + if (PT) { + FTP = PT->getPointeeType()->getAs<FunctionProtoType>(); + } else { + const BlockPointerType *BPT = QT->getAs<BlockPointerType>(); + assert(BPT && "BlockPointerTypeTakeAnyBlockArguments(): not a block pointer type"); + FTP = BPT->getPointeeType()->getAs<FunctionProtoType>(); + } + if (FTP) { + for (const auto &I : FTP->param_types()) + if (isTopLevelBlockPointerType(I)) + return true; + } + return false; +} + +bool RewriteObjC::PointerTypeTakesAnyObjCQualifiedType(QualType QT) { + const FunctionProtoType *FTP; + const PointerType *PT = QT->getAs<PointerType>(); + if (PT) { + FTP = PT->getPointeeType()->getAs<FunctionProtoType>(); + } else { + const BlockPointerType *BPT = QT->getAs<BlockPointerType>(); + assert(BPT && "BlockPointerTypeTakeAnyBlockArguments(): not a block pointer type"); + FTP = BPT->getPointeeType()->getAs<FunctionProtoType>(); + } + if (FTP) { + for (const auto &I : FTP->param_types()) { + if (I->isObjCQualifiedIdType()) + return true; + if (I->isObjCObjectPointerType() && + I->getPointeeType()->isObjCQualifiedInterfaceType()) + return true; + } + + } + return false; +} + +void RewriteObjC::GetExtentOfArgList(const char *Name, const char *&LParen, + const char *&RParen) { + const char *argPtr = strchr(Name, '('); + assert((*argPtr == '(') && "Rewriter fuzzy parser confused"); + + LParen = argPtr; // output the start. + argPtr++; // skip past the left paren. + unsigned parenCount = 1; + + while (*argPtr && parenCount) { + switch (*argPtr) { + case '(': parenCount++; break; + case ')': parenCount--; break; + default: break; + } + if (parenCount) argPtr++; + } + assert((*argPtr == ')') && "Rewriter fuzzy parser confused"); + RParen = argPtr; // output the end +} + +void RewriteObjC::RewriteBlockPointerDecl(NamedDecl *ND) { + if (FunctionDecl *FD = dyn_cast<FunctionDecl>(ND)) { + RewriteBlockPointerFunctionArgs(FD); + return; + } + // Handle Variables and Typedefs. + SourceLocation DeclLoc = ND->getLocation(); + QualType DeclT; + if (VarDecl *VD = dyn_cast<VarDecl>(ND)) + DeclT = VD->getType(); + else if (TypedefNameDecl *TDD = dyn_cast<TypedefNameDecl>(ND)) + DeclT = TDD->getUnderlyingType(); + else if (FieldDecl *FD = dyn_cast<FieldDecl>(ND)) + DeclT = FD->getType(); + else + llvm_unreachable("RewriteBlockPointerDecl(): Decl type not yet handled"); + + const char *startBuf = SM->getCharacterData(DeclLoc); + const char *endBuf = startBuf; + // scan backward (from the decl location) for the end of the previous decl. + while (*startBuf != '^' && *startBuf != ';' && startBuf != MainFileStart) + startBuf--; + SourceLocation Start = DeclLoc.getLocWithOffset(startBuf-endBuf); + std::string buf; + unsigned OrigLength=0; + // *startBuf != '^' if we are dealing with a pointer to function that + // may take block argument types (which will be handled below). + if (*startBuf == '^') { + // Replace the '^' with '*', computing a negative offset. + buf = '*'; + startBuf++; + OrigLength++; + } + while (*startBuf != ')') { + buf += *startBuf; + startBuf++; + OrigLength++; + } + buf += ')'; + OrigLength++; + + if (PointerTypeTakesAnyBlockArguments(DeclT) || + PointerTypeTakesAnyObjCQualifiedType(DeclT)) { + // Replace the '^' with '*' for arguments. + // Replace id<P> with id/*<>*/ + DeclLoc = ND->getLocation(); + startBuf = SM->getCharacterData(DeclLoc); + const char *argListBegin, *argListEnd; + GetExtentOfArgList(startBuf, argListBegin, argListEnd); + while (argListBegin < argListEnd) { + if (*argListBegin == '^') + buf += '*'; + else if (*argListBegin == '<') { + buf += "/*"; + buf += *argListBegin++; + OrigLength++; + while (*argListBegin != '>') { + buf += *argListBegin++; + OrigLength++; + } + buf += *argListBegin; + buf += "*/"; + } + else + buf += *argListBegin; + argListBegin++; + OrigLength++; + } + buf += ')'; + OrigLength++; + } + ReplaceText(Start, OrigLength, buf); +} + +/// SynthesizeByrefCopyDestroyHelper - This routine synthesizes: +/// void __Block_byref_id_object_copy(struct Block_byref_id_object *dst, +/// struct Block_byref_id_object *src) { +/// _Block_object_assign (&_dest->object, _src->object, +/// BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT +/// [|BLOCK_FIELD_IS_WEAK]) // object +/// _Block_object_assign(&_dest->object, _src->object, +/// BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK +/// [|BLOCK_FIELD_IS_WEAK]) // block +/// } +/// And: +/// void __Block_byref_id_object_dispose(struct Block_byref_id_object *_src) { +/// _Block_object_dispose(_src->object, +/// BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT +/// [|BLOCK_FIELD_IS_WEAK]) // object +/// _Block_object_dispose(_src->object, +/// BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK +/// [|BLOCK_FIELD_IS_WEAK]) // block +/// } + +std::string RewriteObjC::SynthesizeByrefCopyDestroyHelper(VarDecl *VD, + int flag) { + std::string S; + if (CopyDestroyCache.count(flag)) + return S; + CopyDestroyCache.insert(flag); + S = "static void __Block_byref_id_object_copy_"; + S += utostr(flag); + S += "(void *dst, void *src) {\n"; + + // offset into the object pointer is computed as: + // void * + void* + int + int + void* + void * + unsigned IntSize = + static_cast<unsigned>(Context->getTypeSize(Context->IntTy)); + unsigned VoidPtrSize = + static_cast<unsigned>(Context->getTypeSize(Context->VoidPtrTy)); + + unsigned offset = (VoidPtrSize*4 + IntSize + IntSize)/Context->getCharWidth(); + S += " _Block_object_assign((char*)dst + "; + S += utostr(offset); + S += ", *(void * *) ((char*)src + "; + S += utostr(offset); + S += "), "; + S += utostr(flag); + S += ");\n}\n"; + + S += "static void __Block_byref_id_object_dispose_"; + S += utostr(flag); + S += "(void *src) {\n"; + S += " _Block_object_dispose(*(void * *) ((char*)src + "; + S += utostr(offset); + S += "), "; + S += utostr(flag); + S += ");\n}\n"; + return S; +} + +/// RewriteByRefVar - For each __block typex ND variable this routine transforms +/// the declaration into: +/// struct __Block_byref_ND { +/// void *__isa; // NULL for everything except __weak pointers +/// struct __Block_byref_ND *__forwarding; +/// int32_t __flags; +/// int32_t __size; +/// void *__Block_byref_id_object_copy; // If variable is __block ObjC object +/// void *__Block_byref_id_object_dispose; // If variable is __block ObjC object +/// typex ND; +/// }; +/// +/// It then replaces declaration of ND variable with: +/// struct __Block_byref_ND ND = {__isa=0B, __forwarding=&ND, __flags=some_flag, +/// __size=sizeof(struct __Block_byref_ND), +/// ND=initializer-if-any}; +/// +/// +void RewriteObjC::RewriteByRefVar(VarDecl *ND) { + // Insert declaration for the function in which block literal is + // used. + if (CurFunctionDeclToDeclareForBlock) + RewriteBlockLiteralFunctionDecl(CurFunctionDeclToDeclareForBlock); + int flag = 0; + int isa = 0; + SourceLocation DeclLoc = ND->getTypeSpecStartLoc(); + if (DeclLoc.isInvalid()) + // If type location is missing, it is because of missing type (a warning). + // Use variable's location which is good for this case. + DeclLoc = ND->getLocation(); + const char *startBuf = SM->getCharacterData(DeclLoc); + SourceLocation X = ND->getEndLoc(); + X = SM->getExpansionLoc(X); + const char *endBuf = SM->getCharacterData(X); + std::string Name(ND->getNameAsString()); + std::string ByrefType; + RewriteByRefString(ByrefType, Name, ND, true); + ByrefType += " {\n"; + ByrefType += " void *__isa;\n"; + RewriteByRefString(ByrefType, Name, ND); + ByrefType += " *__forwarding;\n"; + ByrefType += " int __flags;\n"; + ByrefType += " int __size;\n"; + // Add void *__Block_byref_id_object_copy; + // void *__Block_byref_id_object_dispose; if needed. + QualType Ty = ND->getType(); + bool HasCopyAndDispose = Context->BlockRequiresCopying(Ty, ND); + if (HasCopyAndDispose) { + ByrefType += " void (*__Block_byref_id_object_copy)(void*, void*);\n"; + ByrefType += " void (*__Block_byref_id_object_dispose)(void*);\n"; + } + + QualType T = Ty; + (void)convertBlockPointerToFunctionPointer(T); + T.getAsStringInternal(Name, Context->getPrintingPolicy()); + + ByrefType += " " + Name + ";\n"; + ByrefType += "};\n"; + // Insert this type in global scope. It is needed by helper function. + SourceLocation FunLocStart; + if (CurFunctionDef) + FunLocStart = CurFunctionDef->getTypeSpecStartLoc(); + else { + assert(CurMethodDef && "RewriteByRefVar - CurMethodDef is null"); + FunLocStart = CurMethodDef->getBeginLoc(); + } + InsertText(FunLocStart, ByrefType); + if (Ty.isObjCGCWeak()) { + flag |= BLOCK_FIELD_IS_WEAK; + isa = 1; + } + + if (HasCopyAndDispose) { + flag = BLOCK_BYREF_CALLER; + QualType Ty = ND->getType(); + // FIXME. Handle __weak variable (BLOCK_FIELD_IS_WEAK) as well. + if (Ty->isBlockPointerType()) + flag |= BLOCK_FIELD_IS_BLOCK; + else + flag |= BLOCK_FIELD_IS_OBJECT; + std::string HF = SynthesizeByrefCopyDestroyHelper(ND, flag); + if (!HF.empty()) + InsertText(FunLocStart, HF); + } + + // struct __Block_byref_ND ND = + // {0, &ND, some_flag, __size=sizeof(struct __Block_byref_ND), + // initializer-if-any}; + bool hasInit = (ND->getInit() != nullptr); + unsigned flags = 0; + if (HasCopyAndDispose) + flags |= BLOCK_HAS_COPY_DISPOSE; + Name = ND->getNameAsString(); + ByrefType.clear(); + RewriteByRefString(ByrefType, Name, ND); + std::string ForwardingCastType("("); + ForwardingCastType += ByrefType + " *)"; + if (!hasInit) { + ByrefType += " " + Name + " = {(void*)"; + ByrefType += utostr(isa); + ByrefType += "," + ForwardingCastType + "&" + Name + ", "; + ByrefType += utostr(flags); + ByrefType += ", "; + ByrefType += "sizeof("; + RewriteByRefString(ByrefType, Name, ND); + ByrefType += ")"; + if (HasCopyAndDispose) { + ByrefType += ", __Block_byref_id_object_copy_"; + ByrefType += utostr(flag); + ByrefType += ", __Block_byref_id_object_dispose_"; + ByrefType += utostr(flag); + } + ByrefType += "};\n"; + unsigned nameSize = Name.size(); + // for block or function pointer declaration. Name is already + // part of the declaration. + if (Ty->isBlockPointerType() || Ty->isFunctionPointerType()) + nameSize = 1; + ReplaceText(DeclLoc, endBuf-startBuf+nameSize, ByrefType); + } + else { + SourceLocation startLoc; + Expr *E = ND->getInit(); + if (const CStyleCastExpr *ECE = dyn_cast<CStyleCastExpr>(E)) + startLoc = ECE->getLParenLoc(); + else + startLoc = E->getBeginLoc(); + startLoc = SM->getExpansionLoc(startLoc); + endBuf = SM->getCharacterData(startLoc); + ByrefType += " " + Name; + ByrefType += " = {(void*)"; + ByrefType += utostr(isa); + ByrefType += "," + ForwardingCastType + "&" + Name + ", "; + ByrefType += utostr(flags); + ByrefType += ", "; + ByrefType += "sizeof("; + RewriteByRefString(ByrefType, Name, ND); + ByrefType += "), "; + if (HasCopyAndDispose) { + ByrefType += "__Block_byref_id_object_copy_"; + ByrefType += utostr(flag); + ByrefType += ", __Block_byref_id_object_dispose_"; + ByrefType += utostr(flag); + ByrefType += ", "; + } + ReplaceText(DeclLoc, endBuf-startBuf, ByrefType); + + // Complete the newly synthesized compound expression by inserting a right + // curly brace before the end of the declaration. + // FIXME: This approach avoids rewriting the initializer expression. It + // also assumes there is only one declarator. For example, the following + // isn't currently supported by this routine (in general): + // + // double __block BYREFVAR = 1.34, BYREFVAR2 = 1.37; + // + const char *startInitializerBuf = SM->getCharacterData(startLoc); + const char *semiBuf = strchr(startInitializerBuf, ';'); + assert((*semiBuf == ';') && "RewriteByRefVar: can't find ';'"); + SourceLocation semiLoc = + startLoc.getLocWithOffset(semiBuf-startInitializerBuf); + + InsertText(semiLoc, "}"); + } +} + +void RewriteObjC::CollectBlockDeclRefInfo(BlockExpr *Exp) { + // Add initializers for any closure decl refs. + GetBlockDeclRefExprs(Exp->getBody()); + if (BlockDeclRefs.size()) { + // Unique all "by copy" declarations. + for (unsigned i = 0; i < BlockDeclRefs.size(); i++) + if (!BlockDeclRefs[i]->getDecl()->hasAttr<BlocksAttr>()) { + if (!BlockByCopyDeclsPtrSet.count(BlockDeclRefs[i]->getDecl())) { + BlockByCopyDeclsPtrSet.insert(BlockDeclRefs[i]->getDecl()); + BlockByCopyDecls.push_back(BlockDeclRefs[i]->getDecl()); + } + } + // Unique all "by ref" declarations. + for (unsigned i = 0; i < BlockDeclRefs.size(); i++) + if (BlockDeclRefs[i]->getDecl()->hasAttr<BlocksAttr>()) { + if (!BlockByRefDeclsPtrSet.count(BlockDeclRefs[i]->getDecl())) { + BlockByRefDeclsPtrSet.insert(BlockDeclRefs[i]->getDecl()); + BlockByRefDecls.push_back(BlockDeclRefs[i]->getDecl()); + } + } + // Find any imported blocks...they will need special attention. + for (unsigned i = 0; i < BlockDeclRefs.size(); i++) + if (BlockDeclRefs[i]->getDecl()->hasAttr<BlocksAttr>() || + BlockDeclRefs[i]->getType()->isObjCObjectPointerType() || + BlockDeclRefs[i]->getType()->isBlockPointerType()) + ImportedBlockDecls.insert(BlockDeclRefs[i]->getDecl()); + } +} + +FunctionDecl *RewriteObjC::SynthBlockInitFunctionDecl(StringRef name) { + IdentifierInfo *ID = &Context->Idents.get(name); + QualType FType = Context->getFunctionNoProtoType(Context->VoidPtrTy); + return FunctionDecl::Create(*Context, TUDecl, SourceLocation(), + SourceLocation(), ID, FType, nullptr, SC_Extern, + false, false); +} + +Stmt *RewriteObjC::SynthBlockInitExpr(BlockExpr *Exp, + const SmallVectorImpl<DeclRefExpr *> &InnerBlockDeclRefs) { + const BlockDecl *block = Exp->getBlockDecl(); + Blocks.push_back(Exp); + + CollectBlockDeclRefInfo(Exp); + + // Add inner imported variables now used in current block. + int countOfInnerDecls = 0; + if (!InnerBlockDeclRefs.empty()) { + for (unsigned i = 0; i < InnerBlockDeclRefs.size(); i++) { + DeclRefExpr *Exp = InnerBlockDeclRefs[i]; + ValueDecl *VD = Exp->getDecl(); + if (!VD->hasAttr<BlocksAttr>() && !BlockByCopyDeclsPtrSet.count(VD)) { + // We need to save the copied-in variables in nested + // blocks because it is needed at the end for some of the API generations. + // See SynthesizeBlockLiterals routine. + InnerDeclRefs.push_back(Exp); countOfInnerDecls++; + BlockDeclRefs.push_back(Exp); + BlockByCopyDeclsPtrSet.insert(VD); + BlockByCopyDecls.push_back(VD); + } + if (VD->hasAttr<BlocksAttr>() && !BlockByRefDeclsPtrSet.count(VD)) { + InnerDeclRefs.push_back(Exp); countOfInnerDecls++; + BlockDeclRefs.push_back(Exp); + BlockByRefDeclsPtrSet.insert(VD); + BlockByRefDecls.push_back(VD); + } + } + // Find any imported blocks...they will need special attention. + for (unsigned i = 0; i < InnerBlockDeclRefs.size(); i++) + if (InnerBlockDeclRefs[i]->getDecl()->hasAttr<BlocksAttr>() || + InnerBlockDeclRefs[i]->getType()->isObjCObjectPointerType() || + InnerBlockDeclRefs[i]->getType()->isBlockPointerType()) + ImportedBlockDecls.insert(InnerBlockDeclRefs[i]->getDecl()); + } + InnerDeclRefsCount.push_back(countOfInnerDecls); + + std::string FuncName; + + if (CurFunctionDef) + FuncName = CurFunctionDef->getNameAsString(); + else if (CurMethodDef) + BuildUniqueMethodName(FuncName, CurMethodDef); + else if (GlobalVarDecl) + FuncName = std::string(GlobalVarDecl->getNameAsString()); + + std::string BlockNumber = utostr(Blocks.size()-1); + + std::string Tag = "__" + FuncName + "_block_impl_" + BlockNumber; + std::string Func = "__" + FuncName + "_block_func_" + BlockNumber; + + // Get a pointer to the function type so we can cast appropriately. + QualType BFT = convertFunctionTypeOfBlocks(Exp->getFunctionType()); + QualType FType = Context->getPointerType(BFT); + + FunctionDecl *FD; + Expr *NewRep; + + // Simulate a constructor call... + FD = SynthBlockInitFunctionDecl(Tag); + DeclRefExpr *DRE = new (Context) + DeclRefExpr(*Context, FD, false, FType, VK_RValue, SourceLocation()); + + SmallVector<Expr*, 4> InitExprs; + + // Initialize the block function. + FD = SynthBlockInitFunctionDecl(Func); + DeclRefExpr *Arg = new (Context) DeclRefExpr( + *Context, FD, false, FD->getType(), VK_LValue, SourceLocation()); + CastExpr *castExpr = + NoTypeInfoCStyleCastExpr(Context, Context->VoidPtrTy, CK_BitCast, Arg); + InitExprs.push_back(castExpr); + + // Initialize the block descriptor. + std::string DescData = "__" + FuncName + "_block_desc_" + BlockNumber + "_DATA"; + + VarDecl *NewVD = VarDecl::Create( + *Context, TUDecl, SourceLocation(), SourceLocation(), + &Context->Idents.get(DescData), Context->VoidPtrTy, nullptr, SC_Static); + UnaryOperator *DescRefExpr = new (Context) UnaryOperator( + new (Context) DeclRefExpr(*Context, NewVD, false, Context->VoidPtrTy, + VK_LValue, SourceLocation()), + UO_AddrOf, Context->getPointerType(Context->VoidPtrTy), VK_RValue, + OK_Ordinary, SourceLocation(), false); + InitExprs.push_back(DescRefExpr); + + // Add initializers for any closure decl refs. + if (BlockDeclRefs.size()) { + Expr *Exp; + // Output all "by copy" declarations. + for (SmallVectorImpl<ValueDecl *>::iterator I = BlockByCopyDecls.begin(), + E = BlockByCopyDecls.end(); I != E; ++I) { + if (isObjCType((*I)->getType())) { + // FIXME: Conform to ABI ([[obj retain] autorelease]). + FD = SynthBlockInitFunctionDecl((*I)->getName()); + Exp = new (Context) DeclRefExpr(*Context, FD, false, FD->getType(), + VK_LValue, SourceLocation()); + if (HasLocalVariableExternalStorage(*I)) { + QualType QT = (*I)->getType(); + QT = Context->getPointerType(QT); + Exp = new (Context) UnaryOperator(Exp, UO_AddrOf, QT, VK_RValue, + OK_Ordinary, SourceLocation(), + false); + } + } else if (isTopLevelBlockPointerType((*I)->getType())) { + FD = SynthBlockInitFunctionDecl((*I)->getName()); + Arg = new (Context) DeclRefExpr(*Context, FD, false, FD->getType(), + VK_LValue, SourceLocation()); + Exp = NoTypeInfoCStyleCastExpr(Context, Context->VoidPtrTy, CK_BitCast, + Arg); + } else { + FD = SynthBlockInitFunctionDecl((*I)->getName()); + Exp = new (Context) DeclRefExpr(*Context, FD, false, FD->getType(), + VK_LValue, SourceLocation()); + if (HasLocalVariableExternalStorage(*I)) { + QualType QT = (*I)->getType(); + QT = Context->getPointerType(QT); + Exp = new (Context) UnaryOperator(Exp, UO_AddrOf, QT, VK_RValue, + OK_Ordinary, SourceLocation(), + false); + } + } + InitExprs.push_back(Exp); + } + // Output all "by ref" declarations. + for (SmallVectorImpl<ValueDecl *>::iterator I = BlockByRefDecls.begin(), + E = BlockByRefDecls.end(); I != E; ++I) { + ValueDecl *ND = (*I); + std::string Name(ND->getNameAsString()); + std::string RecName; + RewriteByRefString(RecName, Name, ND, true); + IdentifierInfo *II = &Context->Idents.get(RecName.c_str() + + sizeof("struct")); + RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), SourceLocation(), + II); + assert(RD && "SynthBlockInitExpr(): Can't find RecordDecl"); + QualType castT = Context->getPointerType(Context->getTagDeclType(RD)); + + FD = SynthBlockInitFunctionDecl((*I)->getName()); + Exp = new (Context) DeclRefExpr(*Context, FD, false, FD->getType(), + VK_LValue, SourceLocation()); + bool isNestedCapturedVar = false; + if (block) + for (const auto &CI : block->captures()) { + const VarDecl *variable = CI.getVariable(); + if (variable == ND && CI.isNested()) { + assert (CI.isByRef() && + "SynthBlockInitExpr - captured block variable is not byref"); + isNestedCapturedVar = true; + break; + } + } + // captured nested byref variable has its address passed. Do not take + // its address again. + if (!isNestedCapturedVar) + Exp = new (Context) UnaryOperator( + Exp, UO_AddrOf, Context->getPointerType(Exp->getType()), VK_RValue, + OK_Ordinary, SourceLocation(), false); + Exp = NoTypeInfoCStyleCastExpr(Context, castT, CK_BitCast, Exp); + InitExprs.push_back(Exp); + } + } + if (ImportedBlockDecls.size()) { + // generate BLOCK_HAS_COPY_DISPOSE(have helper funcs) | BLOCK_HAS_DESCRIPTOR + int flag = (BLOCK_HAS_COPY_DISPOSE | BLOCK_HAS_DESCRIPTOR); + unsigned IntSize = + static_cast<unsigned>(Context->getTypeSize(Context->IntTy)); + Expr *FlagExp = IntegerLiteral::Create(*Context, llvm::APInt(IntSize, flag), + Context->IntTy, SourceLocation()); + InitExprs.push_back(FlagExp); + } + NewRep = CallExpr::Create(*Context, DRE, InitExprs, FType, VK_LValue, + SourceLocation()); + NewRep = new (Context) UnaryOperator( + NewRep, UO_AddrOf, Context->getPointerType(NewRep->getType()), VK_RValue, + OK_Ordinary, SourceLocation(), false); + NewRep = NoTypeInfoCStyleCastExpr(Context, FType, CK_BitCast, + NewRep); + BlockDeclRefs.clear(); + BlockByRefDecls.clear(); + BlockByRefDeclsPtrSet.clear(); + BlockByCopyDecls.clear(); + BlockByCopyDeclsPtrSet.clear(); + ImportedBlockDecls.clear(); + return NewRep; +} + +bool RewriteObjC::IsDeclStmtInForeachHeader(DeclStmt *DS) { + if (const ObjCForCollectionStmt * CS = + dyn_cast<ObjCForCollectionStmt>(Stmts.back())) + return CS->getElement() == DS; + return false; +} + +//===----------------------------------------------------------------------===// +// Function Body / Expression rewriting +//===----------------------------------------------------------------------===// + +Stmt *RewriteObjC::RewriteFunctionBodyOrGlobalInitializer(Stmt *S) { + if (isa<SwitchStmt>(S) || isa<WhileStmt>(S) || + isa<DoStmt>(S) || isa<ForStmt>(S)) + Stmts.push_back(S); + else if (isa<ObjCForCollectionStmt>(S)) { + Stmts.push_back(S); + ObjCBcLabelNo.push_back(++BcLabelCount); + } + + // Pseudo-object operations and ivar references need special + // treatment because we're going to recursively rewrite them. + if (PseudoObjectExpr *PseudoOp = dyn_cast<PseudoObjectExpr>(S)) { + if (isa<BinaryOperator>(PseudoOp->getSyntacticForm())) { + return RewritePropertyOrImplicitSetter(PseudoOp); + } else { + return RewritePropertyOrImplicitGetter(PseudoOp); + } + } else if (ObjCIvarRefExpr *IvarRefExpr = dyn_cast<ObjCIvarRefExpr>(S)) { + return RewriteObjCIvarRefExpr(IvarRefExpr); + } + + SourceRange OrigStmtRange = S->getSourceRange(); + + // Perform a bottom up rewrite of all children. + for (Stmt *&childStmt : S->children()) + if (childStmt) { + Stmt *newStmt = RewriteFunctionBodyOrGlobalInitializer(childStmt); + if (newStmt) { + childStmt = newStmt; + } + } + + if (BlockExpr *BE = dyn_cast<BlockExpr>(S)) { + SmallVector<DeclRefExpr *, 8> InnerBlockDeclRefs; + llvm::SmallPtrSet<const DeclContext *, 8> InnerContexts; + InnerContexts.insert(BE->getBlockDecl()); + ImportedLocalExternalDecls.clear(); + GetInnerBlockDeclRefExprs(BE->getBody(), + InnerBlockDeclRefs, InnerContexts); + // Rewrite the block body in place. + Stmt *SaveCurrentBody = CurrentBody; + CurrentBody = BE->getBody(); + PropParentMap = nullptr; + // block literal on rhs of a property-dot-sytax assignment + // must be replaced by its synthesize ast so getRewrittenText + // works as expected. In this case, what actually ends up on RHS + // is the blockTranscribed which is the helper function for the + // block literal; as in: self.c = ^() {[ace ARR];}; + bool saveDisableReplaceStmt = DisableReplaceStmt; + DisableReplaceStmt = false; + RewriteFunctionBodyOrGlobalInitializer(BE->getBody()); + DisableReplaceStmt = saveDisableReplaceStmt; + CurrentBody = SaveCurrentBody; + PropParentMap = nullptr; + ImportedLocalExternalDecls.clear(); + // Now we snarf the rewritten text and stash it away for later use. + std::string Str = Rewrite.getRewrittenText(BE->getSourceRange()); + RewrittenBlockExprs[BE] = Str; + + Stmt *blockTranscribed = SynthBlockInitExpr(BE, InnerBlockDeclRefs); + + //blockTranscribed->dump(); + ReplaceStmt(S, blockTranscribed); + return blockTranscribed; + } + // Handle specific things. + if (ObjCEncodeExpr *AtEncode = dyn_cast<ObjCEncodeExpr>(S)) + return RewriteAtEncode(AtEncode); + + if (ObjCSelectorExpr *AtSelector = dyn_cast<ObjCSelectorExpr>(S)) + return RewriteAtSelector(AtSelector); + + if (ObjCStringLiteral *AtString = dyn_cast<ObjCStringLiteral>(S)) + return RewriteObjCStringLiteral(AtString); + + if (ObjCMessageExpr *MessExpr = dyn_cast<ObjCMessageExpr>(S)) { +#if 0 + // Before we rewrite it, put the original message expression in a comment. + SourceLocation startLoc = MessExpr->getBeginLoc(); + SourceLocation endLoc = MessExpr->getEndLoc(); + + const char *startBuf = SM->getCharacterData(startLoc); + const char *endBuf = SM->getCharacterData(endLoc); + + std::string messString; + messString += "// "; + messString.append(startBuf, endBuf-startBuf+1); + messString += "\n"; + + // FIXME: Missing definition of + // InsertText(clang::SourceLocation, char const*, unsigned int). + // InsertText(startLoc, messString); + // Tried this, but it didn't work either... + // ReplaceText(startLoc, 0, messString.c_str(), messString.size()); +#endif + return RewriteMessageExpr(MessExpr); + } + + if (ObjCAtTryStmt *StmtTry = dyn_cast<ObjCAtTryStmt>(S)) + return RewriteObjCTryStmt(StmtTry); + + if (ObjCAtSynchronizedStmt *StmtTry = dyn_cast<ObjCAtSynchronizedStmt>(S)) + return RewriteObjCSynchronizedStmt(StmtTry); + + if (ObjCAtThrowStmt *StmtThrow = dyn_cast<ObjCAtThrowStmt>(S)) + return RewriteObjCThrowStmt(StmtThrow); + + if (ObjCProtocolExpr *ProtocolExp = dyn_cast<ObjCProtocolExpr>(S)) + return RewriteObjCProtocolExpr(ProtocolExp); + + if (ObjCForCollectionStmt *StmtForCollection = + dyn_cast<ObjCForCollectionStmt>(S)) + return RewriteObjCForCollectionStmt(StmtForCollection, + OrigStmtRange.getEnd()); + if (BreakStmt *StmtBreakStmt = + dyn_cast<BreakStmt>(S)) + return RewriteBreakStmt(StmtBreakStmt); + if (ContinueStmt *StmtContinueStmt = + dyn_cast<ContinueStmt>(S)) + return RewriteContinueStmt(StmtContinueStmt); + + // Need to check for protocol refs (id <P>, Foo <P> *) in variable decls + // and cast exprs. + if (DeclStmt *DS = dyn_cast<DeclStmt>(S)) { + // FIXME: What we're doing here is modifying the type-specifier that + // precedes the first Decl. In the future the DeclGroup should have + // a separate type-specifier that we can rewrite. + // NOTE: We need to avoid rewriting the DeclStmt if it is within + // the context of an ObjCForCollectionStmt. For example: + // NSArray *someArray; + // for (id <FooProtocol> index in someArray) ; + // This is because RewriteObjCForCollectionStmt() does textual rewriting + // and it depends on the original text locations/positions. + if (Stmts.empty() || !IsDeclStmtInForeachHeader(DS)) + RewriteObjCQualifiedInterfaceTypes(*DS->decl_begin()); + + // Blocks rewrite rules. + for (auto *SD : DS->decls()) { + if (ValueDecl *ND = dyn_cast<ValueDecl>(SD)) { + if (isTopLevelBlockPointerType(ND->getType())) + RewriteBlockPointerDecl(ND); + else if (ND->getType()->isFunctionPointerType()) + CheckFunctionPointerDecl(ND->getType(), ND); + if (VarDecl *VD = dyn_cast<VarDecl>(SD)) { + if (VD->hasAttr<BlocksAttr>()) { + static unsigned uniqueByrefDeclCount = 0; + assert(!BlockByRefDeclNo.count(ND) && + "RewriteFunctionBodyOrGlobalInitializer: Duplicate byref decl"); + BlockByRefDeclNo[ND] = uniqueByrefDeclCount++; + RewriteByRefVar(VD); + } + else + RewriteTypeOfDecl(VD); + } + } + if (TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(SD)) { + if (isTopLevelBlockPointerType(TD->getUnderlyingType())) + RewriteBlockPointerDecl(TD); + else if (TD->getUnderlyingType()->isFunctionPointerType()) + CheckFunctionPointerDecl(TD->getUnderlyingType(), TD); + } + } + } + + if (CStyleCastExpr *CE = dyn_cast<CStyleCastExpr>(S)) + RewriteObjCQualifiedInterfaceTypes(CE); + + if (isa<SwitchStmt>(S) || isa<WhileStmt>(S) || + isa<DoStmt>(S) || isa<ForStmt>(S)) { + assert(!Stmts.empty() && "Statement stack is empty"); + assert ((isa<SwitchStmt>(Stmts.back()) || isa<WhileStmt>(Stmts.back()) || + isa<DoStmt>(Stmts.back()) || isa<ForStmt>(Stmts.back())) + && "Statement stack mismatch"); + Stmts.pop_back(); + } + // Handle blocks rewriting. + if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(S)) { + ValueDecl *VD = DRE->getDecl(); + if (VD->hasAttr<BlocksAttr>()) + return RewriteBlockDeclRefExpr(DRE); + if (HasLocalVariableExternalStorage(VD)) + return RewriteLocalVariableExternalStorage(DRE); + } + + if (CallExpr *CE = dyn_cast<CallExpr>(S)) { + if (CE->getCallee()->getType()->isBlockPointerType()) { + Stmt *BlockCall = SynthesizeBlockCall(CE, CE->getCallee()); + ReplaceStmt(S, BlockCall); + return BlockCall; + } + } + if (CStyleCastExpr *CE = dyn_cast<CStyleCastExpr>(S)) { + RewriteCastExpr(CE); + } +#if 0 + if (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(S)) { + CastExpr *Replacement = new (Context) CastExpr(ICE->getType(), + ICE->getSubExpr(), + SourceLocation()); + // Get the new text. + std::string SStr; + llvm::raw_string_ostream Buf(SStr); + Replacement->printPretty(Buf); + const std::string &Str = Buf.str(); + + printf("CAST = %s\n", &Str[0]); + InsertText(ICE->getSubExpr()->getBeginLoc(), Str); + delete S; + return Replacement; + } +#endif + // Return this stmt unmodified. + return S; +} + +void RewriteObjC::RewriteRecordBody(RecordDecl *RD) { + for (auto *FD : RD->fields()) { + if (isTopLevelBlockPointerType(FD->getType())) + RewriteBlockPointerDecl(FD); + if (FD->getType()->isObjCQualifiedIdType() || + FD->getType()->isObjCQualifiedInterfaceType()) + RewriteObjCQualifiedInterfaceTypes(FD); + } +} + +/// HandleDeclInMainFile - This is called for each top-level decl defined in the +/// main file of the input. +void RewriteObjC::HandleDeclInMainFile(Decl *D) { + switch (D->getKind()) { + case Decl::Function: { + FunctionDecl *FD = cast<FunctionDecl>(D); + if (FD->isOverloadedOperator()) + return; + + // Since function prototypes don't have ParmDecl's, we check the function + // prototype. This enables us to rewrite function declarations and + // definitions using the same code. + RewriteBlocksInFunctionProtoType(FD->getType(), FD); + + if (!FD->isThisDeclarationADefinition()) + break; + + // FIXME: If this should support Obj-C++, support CXXTryStmt + if (CompoundStmt *Body = dyn_cast_or_null<CompoundStmt>(FD->getBody())) { + CurFunctionDef = FD; + CurFunctionDeclToDeclareForBlock = FD; + CurrentBody = Body; + Body = + cast_or_null<CompoundStmt>(RewriteFunctionBodyOrGlobalInitializer(Body)); + FD->setBody(Body); + CurrentBody = nullptr; + if (PropParentMap) { + delete PropParentMap; + PropParentMap = nullptr; + } + // This synthesizes and inserts the block "impl" struct, invoke function, + // and any copy/dispose helper functions. + InsertBlockLiteralsWithinFunction(FD); + CurFunctionDef = nullptr; + CurFunctionDeclToDeclareForBlock = nullptr; + } + break; + } + case Decl::ObjCMethod: { + ObjCMethodDecl *MD = cast<ObjCMethodDecl>(D); + if (CompoundStmt *Body = MD->getCompoundBody()) { + CurMethodDef = MD; + CurrentBody = Body; + Body = + cast_or_null<CompoundStmt>(RewriteFunctionBodyOrGlobalInitializer(Body)); + MD->setBody(Body); + CurrentBody = nullptr; + if (PropParentMap) { + delete PropParentMap; + PropParentMap = nullptr; + } + InsertBlockLiteralsWithinMethod(MD); + CurMethodDef = nullptr; + } + break; + } + case Decl::ObjCImplementation: { + ObjCImplementationDecl *CI = cast<ObjCImplementationDecl>(D); + ClassImplementation.push_back(CI); + break; + } + case Decl::ObjCCategoryImpl: { + ObjCCategoryImplDecl *CI = cast<ObjCCategoryImplDecl>(D); + CategoryImplementation.push_back(CI); + break; + } + case Decl::Var: { + VarDecl *VD = cast<VarDecl>(D); + RewriteObjCQualifiedInterfaceTypes(VD); + if (isTopLevelBlockPointerType(VD->getType())) + RewriteBlockPointerDecl(VD); + else if (VD->getType()->isFunctionPointerType()) { + CheckFunctionPointerDecl(VD->getType(), VD); + if (VD->getInit()) { + if (CStyleCastExpr *CE = dyn_cast<CStyleCastExpr>(VD->getInit())) { + RewriteCastExpr(CE); + } + } + } else if (VD->getType()->isRecordType()) { + RecordDecl *RD = VD->getType()->getAs<RecordType>()->getDecl(); + if (RD->isCompleteDefinition()) + RewriteRecordBody(RD); + } + if (VD->getInit()) { + GlobalVarDecl = VD; + CurrentBody = VD->getInit(); + RewriteFunctionBodyOrGlobalInitializer(VD->getInit()); + CurrentBody = nullptr; + if (PropParentMap) { + delete PropParentMap; + PropParentMap = nullptr; + } + SynthesizeBlockLiterals(VD->getTypeSpecStartLoc(), VD->getName()); + GlobalVarDecl = nullptr; + + // This is needed for blocks. + if (CStyleCastExpr *CE = dyn_cast<CStyleCastExpr>(VD->getInit())) { + RewriteCastExpr(CE); + } + } + break; + } + case Decl::TypeAlias: + case Decl::Typedef: { + if (TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(D)) { + if (isTopLevelBlockPointerType(TD->getUnderlyingType())) + RewriteBlockPointerDecl(TD); + else if (TD->getUnderlyingType()->isFunctionPointerType()) + CheckFunctionPointerDecl(TD->getUnderlyingType(), TD); + } + break; + } + case Decl::CXXRecord: + case Decl::Record: { + RecordDecl *RD = cast<RecordDecl>(D); + if (RD->isCompleteDefinition()) + RewriteRecordBody(RD); + break; + } + default: + break; + } + // Nothing yet. +} + +void RewriteObjC::HandleTranslationUnit(ASTContext &C) { + if (Diags.hasErrorOccurred()) + return; + + RewriteInclude(); + + // Here's a great place to add any extra declarations that may be needed. + // Write out meta data for each @protocol(<expr>). + for (ObjCProtocolDecl *ProtDecl : ProtocolExprDecls) + RewriteObjCProtocolMetaData(ProtDecl, "", "", Preamble); + + InsertText(SM->getLocForStartOfFile(MainFileID), Preamble, false); + if (ClassImplementation.size() || CategoryImplementation.size()) + RewriteImplementations(); + + // Get the buffer corresponding to MainFileID. If we haven't changed it, then + // we are done. + if (const RewriteBuffer *RewriteBuf = + Rewrite.getRewriteBufferFor(MainFileID)) { + //printf("Changed:\n"); + *OutFile << std::string(RewriteBuf->begin(), RewriteBuf->end()); + } else { + llvm::errs() << "No changes\n"; + } + + if (ClassImplementation.size() || CategoryImplementation.size() || + ProtocolExprDecls.size()) { + // Rewrite Objective-c meta data* + std::string ResultStr; + RewriteMetaDataIntoBuffer(ResultStr); + // Emit metadata. + *OutFile << ResultStr; + } + OutFile->flush(); +} + +void RewriteObjCFragileABI::Initialize(ASTContext &context) { + InitializeCommon(context); + + // declaring objc_selector outside the parameter list removes a silly + // scope related warning... + if (IsHeader) + Preamble = "#pragma once\n"; + Preamble += "struct objc_selector; struct objc_class;\n"; + Preamble += "struct __rw_objc_super { struct objc_object *object; "; + Preamble += "struct objc_object *superClass; "; + if (LangOpts.MicrosoftExt) { + // Add a constructor for creating temporary objects. + Preamble += "__rw_objc_super(struct objc_object *o, struct objc_object *s) " + ": "; + Preamble += "object(o), superClass(s) {} "; + } + Preamble += "};\n"; + Preamble += "#ifndef _REWRITER_typedef_Protocol\n"; + Preamble += "typedef struct objc_object Protocol;\n"; + Preamble += "#define _REWRITER_typedef_Protocol\n"; + Preamble += "#endif\n"; + if (LangOpts.MicrosoftExt) { + Preamble += "#define __OBJC_RW_DLLIMPORT extern \"C\" __declspec(dllimport)\n"; + Preamble += "#define __OBJC_RW_STATICIMPORT extern \"C\"\n"; + } else + Preamble += "#define __OBJC_RW_DLLIMPORT extern\n"; + Preamble += "__OBJC_RW_DLLIMPORT struct objc_object *objc_msgSend"; + Preamble += "(struct objc_object *, struct objc_selector *, ...);\n"; + Preamble += "__OBJC_RW_DLLIMPORT struct objc_object *objc_msgSendSuper"; + Preamble += "(struct objc_super *, struct objc_selector *, ...);\n"; + Preamble += "__OBJC_RW_DLLIMPORT struct objc_object* objc_msgSend_stret"; + Preamble += "(struct objc_object *, struct objc_selector *, ...);\n"; + Preamble += "__OBJC_RW_DLLIMPORT struct objc_object* objc_msgSendSuper_stret"; + Preamble += "(struct objc_super *, struct objc_selector *, ...);\n"; + Preamble += "__OBJC_RW_DLLIMPORT double objc_msgSend_fpret"; + Preamble += "(struct objc_object *, struct objc_selector *, ...);\n"; + Preamble += "__OBJC_RW_DLLIMPORT struct objc_object *objc_getClass"; + Preamble += "(const char *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT struct objc_class *class_getSuperclass"; + Preamble += "(struct objc_class *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT struct objc_object *objc_getMetaClass"; + Preamble += "(const char *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT void objc_exception_throw(struct objc_object *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT void objc_exception_try_enter(void *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT void objc_exception_try_exit(void *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT struct objc_object *objc_exception_extract(void *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT int objc_exception_match"; + Preamble += "(struct objc_class *, struct objc_object *);\n"; + // @synchronized hooks. + Preamble += "__OBJC_RW_DLLIMPORT int objc_sync_enter(struct objc_object *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT int objc_sync_exit(struct objc_object *);\n"; + Preamble += "__OBJC_RW_DLLIMPORT Protocol *objc_getProtocol(const char *);\n"; + Preamble += "#ifndef __FASTENUMERATIONSTATE\n"; + Preamble += "struct __objcFastEnumerationState {\n\t"; + Preamble += "unsigned long state;\n\t"; + Preamble += "void **itemsPtr;\n\t"; + Preamble += "unsigned long *mutationsPtr;\n\t"; + Preamble += "unsigned long extra[5];\n};\n"; + Preamble += "__OBJC_RW_DLLIMPORT void objc_enumerationMutation(struct objc_object *);\n"; + Preamble += "#define __FASTENUMERATIONSTATE\n"; + Preamble += "#endif\n"; + Preamble += "#ifndef __NSCONSTANTSTRINGIMPL\n"; + Preamble += "struct __NSConstantStringImpl {\n"; + Preamble += " int *isa;\n"; + Preamble += " int flags;\n"; + Preamble += " char *str;\n"; + Preamble += " long length;\n"; + Preamble += "};\n"; + Preamble += "#ifdef CF_EXPORT_CONSTANT_STRING\n"; + Preamble += "extern \"C\" __declspec(dllexport) int __CFConstantStringClassReference[];\n"; + Preamble += "#else\n"; + Preamble += "__OBJC_RW_DLLIMPORT int __CFConstantStringClassReference[];\n"; + Preamble += "#endif\n"; + Preamble += "#define __NSCONSTANTSTRINGIMPL\n"; + Preamble += "#endif\n"; + // Blocks preamble. + Preamble += "#ifndef BLOCK_IMPL\n"; + Preamble += "#define BLOCK_IMPL\n"; + Preamble += "struct __block_impl {\n"; + Preamble += " void *isa;\n"; + Preamble += " int Flags;\n"; + Preamble += " int Reserved;\n"; + Preamble += " void *FuncPtr;\n"; + Preamble += "};\n"; + Preamble += "// Runtime copy/destroy helper functions (from Block_private.h)\n"; + Preamble += "#ifdef __OBJC_EXPORT_BLOCKS\n"; + Preamble += "extern \"C\" __declspec(dllexport) " + "void _Block_object_assign(void *, const void *, const int);\n"; + Preamble += "extern \"C\" __declspec(dllexport) void _Block_object_dispose(const void *, const int);\n"; + Preamble += "extern \"C\" __declspec(dllexport) void *_NSConcreteGlobalBlock[32];\n"; + Preamble += "extern \"C\" __declspec(dllexport) void *_NSConcreteStackBlock[32];\n"; + Preamble += "#else\n"; + Preamble += "__OBJC_RW_DLLIMPORT void _Block_object_assign(void *, const void *, const int);\n"; + Preamble += "__OBJC_RW_DLLIMPORT void _Block_object_dispose(const void *, const int);\n"; + Preamble += "__OBJC_RW_DLLIMPORT void *_NSConcreteGlobalBlock[32];\n"; + Preamble += "__OBJC_RW_DLLIMPORT void *_NSConcreteStackBlock[32];\n"; + Preamble += "#endif\n"; + Preamble += "#endif\n"; + if (LangOpts.MicrosoftExt) { + Preamble += "#undef __OBJC_RW_DLLIMPORT\n"; + Preamble += "#undef __OBJC_RW_STATICIMPORT\n"; + Preamble += "#ifndef KEEP_ATTRIBUTES\n"; // We use this for clang tests. + Preamble += "#define __attribute__(X)\n"; + Preamble += "#endif\n"; + Preamble += "#define __weak\n"; + } + else { + Preamble += "#define __block\n"; + Preamble += "#define __weak\n"; + } + // NOTE! Windows uses LLP64 for 64bit mode. So, cast pointer to long long + // as this avoids warning in any 64bit/32bit compilation model. + Preamble += "\n#define __OFFSETOFIVAR__(TYPE, MEMBER) ((long long) &((TYPE *)0)->MEMBER)\n"; +} + +/// RewriteIvarOffsetComputation - This routine synthesizes computation of +/// ivar offset. +void RewriteObjCFragileABI::RewriteIvarOffsetComputation(ObjCIvarDecl *ivar, + std::string &Result) { + if (ivar->isBitField()) { + // FIXME: The hack below doesn't work for bitfields. For now, we simply + // place all bitfields at offset 0. + Result += "0"; + } else { + Result += "__OFFSETOFIVAR__(struct "; + Result += ivar->getContainingInterface()->getNameAsString(); + if (LangOpts.MicrosoftExt) + Result += "_IMPL"; + Result += ", "; + Result += ivar->getNameAsString(); + Result += ")"; + } +} + +/// RewriteObjCProtocolMetaData - Rewrite protocols meta-data. +void RewriteObjCFragileABI::RewriteObjCProtocolMetaData( + ObjCProtocolDecl *PDecl, StringRef prefix, + StringRef ClassName, std::string &Result) { + static bool objc_protocol_methods = false; + + // Output struct protocol_methods holder of method selector and type. + if (!objc_protocol_methods && PDecl->hasDefinition()) { + /* struct protocol_methods { + SEL _cmd; + char *method_types; + } + */ + Result += "\nstruct _protocol_methods {\n"; + Result += "\tstruct objc_selector *_cmd;\n"; + Result += "\tchar *method_types;\n"; + Result += "};\n"; + + objc_protocol_methods = true; + } + // Do not synthesize the protocol more than once. + if (ObjCSynthesizedProtocols.count(PDecl->getCanonicalDecl())) + return; + + if (ObjCProtocolDecl *Def = PDecl->getDefinition()) + PDecl = Def; + + if (PDecl->instmeth_begin() != PDecl->instmeth_end()) { + unsigned NumMethods = std::distance(PDecl->instmeth_begin(), + PDecl->instmeth_end()); + /* struct _objc_protocol_method_list { + int protocol_method_count; + struct protocol_methods protocols[]; + } + */ + Result += "\nstatic struct {\n"; + Result += "\tint protocol_method_count;\n"; + Result += "\tstruct _protocol_methods protocol_methods["; + Result += utostr(NumMethods); + Result += "];\n} _OBJC_PROTOCOL_INSTANCE_METHODS_"; + Result += PDecl->getNameAsString(); + Result += " __attribute__ ((used, section (\"__OBJC, __cat_inst_meth\")))= " + "{\n\t" + utostr(NumMethods) + "\n"; + + // Output instance methods declared in this protocol. + for (ObjCProtocolDecl::instmeth_iterator + I = PDecl->instmeth_begin(), E = PDecl->instmeth_end(); + I != E; ++I) { + if (I == PDecl->instmeth_begin()) + Result += "\t ,{{(struct objc_selector *)\""; + else + Result += "\t ,{(struct objc_selector *)\""; + Result += (*I)->getSelector().getAsString(); + std::string MethodTypeString = Context->getObjCEncodingForMethodDecl(*I); + Result += "\", \""; + Result += MethodTypeString; + Result += "\"}\n"; + } + Result += "\t }\n};\n"; + } + + // Output class methods declared in this protocol. + unsigned NumMethods = std::distance(PDecl->classmeth_begin(), + PDecl->classmeth_end()); + if (NumMethods > 0) { + /* struct _objc_protocol_method_list { + int protocol_method_count; + struct protocol_methods protocols[]; + } + */ + Result += "\nstatic struct {\n"; + Result += "\tint protocol_method_count;\n"; + Result += "\tstruct _protocol_methods protocol_methods["; + Result += utostr(NumMethods); + Result += "];\n} _OBJC_PROTOCOL_CLASS_METHODS_"; + Result += PDecl->getNameAsString(); + Result += " __attribute__ ((used, section (\"__OBJC, __cat_cls_meth\")))= " + "{\n\t"; + Result += utostr(NumMethods); + Result += "\n"; + + // Output instance methods declared in this protocol. + for (ObjCProtocolDecl::classmeth_iterator + I = PDecl->classmeth_begin(), E = PDecl->classmeth_end(); + I != E; ++I) { + if (I == PDecl->classmeth_begin()) + Result += "\t ,{{(struct objc_selector *)\""; + else + Result += "\t ,{(struct objc_selector *)\""; + Result += (*I)->getSelector().getAsString(); + std::string MethodTypeString = Context->getObjCEncodingForMethodDecl(*I); + Result += "\", \""; + Result += MethodTypeString; + Result += "\"}\n"; + } + Result += "\t }\n};\n"; + } + + // Output: + /* struct _objc_protocol { + // Objective-C 1.0 extensions + struct _objc_protocol_extension *isa; + char *protocol_name; + struct _objc_protocol **protocol_list; + struct _objc_protocol_method_list *instance_methods; + struct _objc_protocol_method_list *class_methods; + }; + */ + static bool objc_protocol = false; + if (!objc_protocol) { + Result += "\nstruct _objc_protocol {\n"; + Result += "\tstruct _objc_protocol_extension *isa;\n"; + Result += "\tchar *protocol_name;\n"; + Result += "\tstruct _objc_protocol **protocol_list;\n"; + Result += "\tstruct _objc_protocol_method_list *instance_methods;\n"; + Result += "\tstruct _objc_protocol_method_list *class_methods;\n"; + Result += "};\n"; + + objc_protocol = true; + } + + Result += "\nstatic struct _objc_protocol _OBJC_PROTOCOL_"; + Result += PDecl->getNameAsString(); + Result += " __attribute__ ((used, section (\"__OBJC, __protocol\")))= " + "{\n\t0, \""; + Result += PDecl->getNameAsString(); + Result += "\", 0, "; + if (PDecl->instmeth_begin() != PDecl->instmeth_end()) { + Result += "(struct _objc_protocol_method_list *)&_OBJC_PROTOCOL_INSTANCE_METHODS_"; + Result += PDecl->getNameAsString(); + Result += ", "; + } + else + Result += "0, "; + if (PDecl->classmeth_begin() != PDecl->classmeth_end()) { + Result += "(struct _objc_protocol_method_list *)&_OBJC_PROTOCOL_CLASS_METHODS_"; + Result += PDecl->getNameAsString(); + Result += "\n"; + } + else + Result += "0\n"; + Result += "};\n"; + + // Mark this protocol as having been generated. + if (!ObjCSynthesizedProtocols.insert(PDecl->getCanonicalDecl()).second) + llvm_unreachable("protocol already synthesized"); +} + +void RewriteObjCFragileABI::RewriteObjCProtocolListMetaData( + const ObjCList<ObjCProtocolDecl> &Protocols, + StringRef prefix, StringRef ClassName, + std::string &Result) { + if (Protocols.empty()) return; + + for (unsigned i = 0; i != Protocols.size(); i++) + RewriteObjCProtocolMetaData(Protocols[i], prefix, ClassName, Result); + + // Output the top lovel protocol meta-data for the class. + /* struct _objc_protocol_list { + struct _objc_protocol_list *next; + int protocol_count; + struct _objc_protocol *class_protocols[]; + } + */ + Result += "\nstatic struct {\n"; + Result += "\tstruct _objc_protocol_list *next;\n"; + Result += "\tint protocol_count;\n"; + Result += "\tstruct _objc_protocol *class_protocols["; + Result += utostr(Protocols.size()); + Result += "];\n} _OBJC_"; + Result += prefix; + Result += "_PROTOCOLS_"; + Result += ClassName; + Result += " __attribute__ ((used, section (\"__OBJC, __cat_cls_meth\")))= " + "{\n\t0, "; + Result += utostr(Protocols.size()); + Result += "\n"; + + Result += "\t,{&_OBJC_PROTOCOL_"; + Result += Protocols[0]->getNameAsString(); + Result += " \n"; + + for (unsigned i = 1; i != Protocols.size(); i++) { + Result += "\t ,&_OBJC_PROTOCOL_"; + Result += Protocols[i]->getNameAsString(); + Result += "\n"; + } + Result += "\t }\n};\n"; +} + +void RewriteObjCFragileABI::RewriteObjCClassMetaData(ObjCImplementationDecl *IDecl, + std::string &Result) { + ObjCInterfaceDecl *CDecl = IDecl->getClassInterface(); + + // Explicitly declared @interface's are already synthesized. + if (CDecl->isImplicitInterfaceDecl()) { + // FIXME: Implementation of a class with no @interface (legacy) does not + // produce correct synthesis as yet. + RewriteObjCInternalStruct(CDecl, Result); + } + + // Build _objc_ivar_list metadata for classes ivars if needed + unsigned NumIvars = !IDecl->ivar_empty() + ? IDecl->ivar_size() + : (CDecl ? CDecl->ivar_size() : 0); + if (NumIvars > 0) { + static bool objc_ivar = false; + if (!objc_ivar) { + /* struct _objc_ivar { + char *ivar_name; + char *ivar_type; + int ivar_offset; + }; + */ + Result += "\nstruct _objc_ivar {\n"; + Result += "\tchar *ivar_name;\n"; + Result += "\tchar *ivar_type;\n"; + Result += "\tint ivar_offset;\n"; + Result += "};\n"; + + objc_ivar = true; + } + + /* struct { + int ivar_count; + struct _objc_ivar ivar_list[nIvars]; + }; + */ + Result += "\nstatic struct {\n"; + Result += "\tint ivar_count;\n"; + Result += "\tstruct _objc_ivar ivar_list["; + Result += utostr(NumIvars); + Result += "];\n} _OBJC_INSTANCE_VARIABLES_"; + Result += IDecl->getNameAsString(); + Result += " __attribute__ ((used, section (\"__OBJC, __instance_vars\")))= " + "{\n\t"; + Result += utostr(NumIvars); + Result += "\n"; + + ObjCInterfaceDecl::ivar_iterator IVI, IVE; + SmallVector<ObjCIvarDecl *, 8> IVars; + if (!IDecl->ivar_empty()) { + for (auto *IV : IDecl->ivars()) + IVars.push_back(IV); + IVI = IDecl->ivar_begin(); + IVE = IDecl->ivar_end(); + } else { + IVI = CDecl->ivar_begin(); + IVE = CDecl->ivar_end(); + } + Result += "\t,{{\""; + Result += IVI->getNameAsString(); + Result += "\", \""; + std::string TmpString, StrEncoding; + Context->getObjCEncodingForType(IVI->getType(), TmpString, *IVI); + QuoteDoublequotes(TmpString, StrEncoding); + Result += StrEncoding; + Result += "\", "; + RewriteIvarOffsetComputation(*IVI, Result); + Result += "}\n"; + for (++IVI; IVI != IVE; ++IVI) { + Result += "\t ,{\""; + Result += IVI->getNameAsString(); + Result += "\", \""; + std::string TmpString, StrEncoding; + Context->getObjCEncodingForType(IVI->getType(), TmpString, *IVI); + QuoteDoublequotes(TmpString, StrEncoding); + Result += StrEncoding; + Result += "\", "; + RewriteIvarOffsetComputation(*IVI, Result); + Result += "}\n"; + } + + Result += "\t }\n};\n"; + } + + // Build _objc_method_list for class's instance methods if needed + SmallVector<ObjCMethodDecl *, 32> InstanceMethods(IDecl->instance_methods()); + + // If any of our property implementations have associated getters or + // setters, produce metadata for them as well. + for (const auto *Prop : IDecl->property_impls()) { + if (Prop->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) + continue; + if (!Prop->getPropertyIvarDecl()) + continue; + ObjCPropertyDecl *PD = Prop->getPropertyDecl(); + if (!PD) + continue; + if (ObjCMethodDecl *Getter = PD->getGetterMethodDecl()) + if (!Getter->isDefined()) + InstanceMethods.push_back(Getter); + if (PD->isReadOnly()) + continue; + if (ObjCMethodDecl *Setter = PD->getSetterMethodDecl()) + if (!Setter->isDefined()) + InstanceMethods.push_back(Setter); + } + RewriteObjCMethodsMetaData(InstanceMethods.begin(), InstanceMethods.end(), + true, "", IDecl->getName(), Result); + + // Build _objc_method_list for class's class methods if needed + RewriteObjCMethodsMetaData(IDecl->classmeth_begin(), IDecl->classmeth_end(), + false, "", IDecl->getName(), Result); + + // Protocols referenced in class declaration? + RewriteObjCProtocolListMetaData(CDecl->getReferencedProtocols(), + "CLASS", CDecl->getName(), Result); + + // Declaration of class/meta-class metadata + /* struct _objc_class { + struct _objc_class *isa; // or const char *root_class_name when metadata + const char *super_class_name; + char *name; + long version; + long info; + long instance_size; + struct _objc_ivar_list *ivars; + struct _objc_method_list *methods; + struct objc_cache *cache; + struct objc_protocol_list *protocols; + const char *ivar_layout; + struct _objc_class_ext *ext; + }; + */ + static bool objc_class = false; + if (!objc_class) { + Result += "\nstruct _objc_class {\n"; + Result += "\tstruct _objc_class *isa;\n"; + Result += "\tconst char *super_class_name;\n"; + Result += "\tchar *name;\n"; + Result += "\tlong version;\n"; + Result += "\tlong info;\n"; + Result += "\tlong instance_size;\n"; + Result += "\tstruct _objc_ivar_list *ivars;\n"; + Result += "\tstruct _objc_method_list *methods;\n"; + Result += "\tstruct objc_cache *cache;\n"; + Result += "\tstruct _objc_protocol_list *protocols;\n"; + Result += "\tconst char *ivar_layout;\n"; + Result += "\tstruct _objc_class_ext *ext;\n"; + Result += "};\n"; + objc_class = true; + } + + // Meta-class metadata generation. + ObjCInterfaceDecl *RootClass = nullptr; + ObjCInterfaceDecl *SuperClass = CDecl->getSuperClass(); + while (SuperClass) { + RootClass = SuperClass; + SuperClass = SuperClass->getSuperClass(); + } + SuperClass = CDecl->getSuperClass(); + + Result += "\nstatic struct _objc_class _OBJC_METACLASS_"; + Result += CDecl->getNameAsString(); + Result += " __attribute__ ((used, section (\"__OBJC, __meta_class\")))= " + "{\n\t(struct _objc_class *)\""; + Result += (RootClass ? RootClass->getNameAsString() : CDecl->getNameAsString()); + Result += "\""; + + if (SuperClass) { + Result += ", \""; + Result += SuperClass->getNameAsString(); + Result += "\", \""; + Result += CDecl->getNameAsString(); + Result += "\""; + } + else { + Result += ", 0, \""; + Result += CDecl->getNameAsString(); + Result += "\""; + } + // Set 'ivars' field for root class to 0. ObjC1 runtime does not use it. + // 'info' field is initialized to CLS_META(2) for metaclass + Result += ", 0,2, sizeof(struct _objc_class), 0"; + if (IDecl->classmeth_begin() != IDecl->classmeth_end()) { + Result += "\n\t, (struct _objc_method_list *)&_OBJC_CLASS_METHODS_"; + Result += IDecl->getNameAsString(); + Result += "\n"; + } + else + Result += ", 0\n"; + if (CDecl->protocol_begin() != CDecl->protocol_end()) { + Result += "\t,0, (struct _objc_protocol_list *)&_OBJC_CLASS_PROTOCOLS_"; + Result += CDecl->getNameAsString(); + Result += ",0,0\n"; + } + else + Result += "\t,0,0,0,0\n"; + Result += "};\n"; + + // class metadata generation. + Result += "\nstatic struct _objc_class _OBJC_CLASS_"; + Result += CDecl->getNameAsString(); + Result += " __attribute__ ((used, section (\"__OBJC, __class\")))= " + "{\n\t&_OBJC_METACLASS_"; + Result += CDecl->getNameAsString(); + if (SuperClass) { + Result += ", \""; + Result += SuperClass->getNameAsString(); + Result += "\", \""; + Result += CDecl->getNameAsString(); + Result += "\""; + } + else { + Result += ", 0, \""; + Result += CDecl->getNameAsString(); + Result += "\""; + } + // 'info' field is initialized to CLS_CLASS(1) for class + Result += ", 0,1"; + if (!ObjCSynthesizedStructs.count(CDecl)) + Result += ",0"; + else { + // class has size. Must synthesize its size. + Result += ",sizeof(struct "; + Result += CDecl->getNameAsString(); + if (LangOpts.MicrosoftExt) + Result += "_IMPL"; + Result += ")"; + } + if (NumIvars > 0) { + Result += ", (struct _objc_ivar_list *)&_OBJC_INSTANCE_VARIABLES_"; + Result += CDecl->getNameAsString(); + Result += "\n\t"; + } + else + Result += ",0"; + if (IDecl->instmeth_begin() != IDecl->instmeth_end()) { + Result += ", (struct _objc_method_list *)&_OBJC_INSTANCE_METHODS_"; + Result += CDecl->getNameAsString(); + Result += ", 0\n\t"; + } + else + Result += ",0,0"; + if (CDecl->protocol_begin() != CDecl->protocol_end()) { + Result += ", (struct _objc_protocol_list*)&_OBJC_CLASS_PROTOCOLS_"; + Result += CDecl->getNameAsString(); + Result += ", 0,0\n"; + } + else + Result += ",0,0,0\n"; + Result += "};\n"; +} + +void RewriteObjCFragileABI::RewriteMetaDataIntoBuffer(std::string &Result) { + int ClsDefCount = ClassImplementation.size(); + int CatDefCount = CategoryImplementation.size(); + + // For each implemented class, write out all its meta data. + for (int i = 0; i < ClsDefCount; i++) + RewriteObjCClassMetaData(ClassImplementation[i], Result); + + // For each implemented category, write out all its meta data. + for (int i = 0; i < CatDefCount; i++) + RewriteObjCCategoryImplDecl(CategoryImplementation[i], Result); + + // Write objc_symtab metadata + /* + struct _objc_symtab + { + long sel_ref_cnt; + SEL *refs; + short cls_def_cnt; + short cat_def_cnt; + void *defs[cls_def_cnt + cat_def_cnt]; + }; + */ + + Result += "\nstruct _objc_symtab {\n"; + Result += "\tlong sel_ref_cnt;\n"; + Result += "\tSEL *refs;\n"; + Result += "\tshort cls_def_cnt;\n"; + Result += "\tshort cat_def_cnt;\n"; + Result += "\tvoid *defs[" + utostr(ClsDefCount + CatDefCount)+ "];\n"; + Result += "};\n\n"; + + Result += "static struct _objc_symtab " + "_OBJC_SYMBOLS __attribute__((used, section (\"__OBJC, __symbols\")))= {\n"; + Result += "\t0, 0, " + utostr(ClsDefCount) + + ", " + utostr(CatDefCount) + "\n"; + for (int i = 0; i < ClsDefCount; i++) { + Result += "\t,&_OBJC_CLASS_"; + Result += ClassImplementation[i]->getNameAsString(); + Result += "\n"; + } + + for (int i = 0; i < CatDefCount; i++) { + Result += "\t,&_OBJC_CATEGORY_"; + Result += CategoryImplementation[i]->getClassInterface()->getNameAsString(); + Result += "_"; + Result += CategoryImplementation[i]->getNameAsString(); + Result += "\n"; + } + + Result += "};\n\n"; + + // Write objc_module metadata + + /* + struct _objc_module { + long version; + long size; + const char *name; + struct _objc_symtab *symtab; + } + */ + + Result += "\nstruct _objc_module {\n"; + Result += "\tlong version;\n"; + Result += "\tlong size;\n"; + Result += "\tconst char *name;\n"; + Result += "\tstruct _objc_symtab *symtab;\n"; + Result += "};\n\n"; + Result += "static struct _objc_module " + "_OBJC_MODULES __attribute__ ((used, section (\"__OBJC, __module_info\")))= {\n"; + Result += "\t" + utostr(OBJC_ABI_VERSION) + + ", sizeof(struct _objc_module), \"\", &_OBJC_SYMBOLS\n"; + Result += "};\n\n"; + + if (LangOpts.MicrosoftExt) { + if (ProtocolExprDecls.size()) { + Result += "#pragma section(\".objc_protocol$B\",long,read,write)\n"; + Result += "#pragma data_seg(push, \".objc_protocol$B\")\n"; + for (ObjCProtocolDecl *ProtDecl : ProtocolExprDecls) { + Result += "static struct _objc_protocol *_POINTER_OBJC_PROTOCOL_"; + Result += ProtDecl->getNameAsString(); + Result += " = &_OBJC_PROTOCOL_"; + Result += ProtDecl->getNameAsString(); + Result += ";\n"; + } + Result += "#pragma data_seg(pop)\n\n"; + } + Result += "#pragma section(\".objc_module_info$B\",long,read,write)\n"; + Result += "#pragma data_seg(push, \".objc_module_info$B\")\n"; + Result += "static struct _objc_module *_POINTER_OBJC_MODULES = "; + Result += "&_OBJC_MODULES;\n"; + Result += "#pragma data_seg(pop)\n\n"; + } +} + +/// RewriteObjCCategoryImplDecl - Rewrite metadata for each category +/// implementation. +void RewriteObjCFragileABI::RewriteObjCCategoryImplDecl(ObjCCategoryImplDecl *IDecl, + std::string &Result) { + ObjCInterfaceDecl *ClassDecl = IDecl->getClassInterface(); + // Find category declaration for this implementation. + ObjCCategoryDecl *CDecl + = ClassDecl->FindCategoryDeclaration(IDecl->getIdentifier()); + + std::string FullCategoryName = ClassDecl->getNameAsString(); + FullCategoryName += '_'; + FullCategoryName += IDecl->getNameAsString(); + + // Build _objc_method_list for class's instance methods if needed + SmallVector<ObjCMethodDecl *, 32> InstanceMethods(IDecl->instance_methods()); + + // If any of our property implementations have associated getters or + // setters, produce metadata for them as well. + for (const auto *Prop : IDecl->property_impls()) { + if (Prop->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) + continue; + if (!Prop->getPropertyIvarDecl()) + continue; + ObjCPropertyDecl *PD = Prop->getPropertyDecl(); + if (!PD) + continue; + if (ObjCMethodDecl *Getter = PD->getGetterMethodDecl()) + InstanceMethods.push_back(Getter); + if (PD->isReadOnly()) + continue; + if (ObjCMethodDecl *Setter = PD->getSetterMethodDecl()) + InstanceMethods.push_back(Setter); + } + RewriteObjCMethodsMetaData(InstanceMethods.begin(), InstanceMethods.end(), + true, "CATEGORY_", FullCategoryName, Result); + + // Build _objc_method_list for class's class methods if needed + RewriteObjCMethodsMetaData(IDecl->classmeth_begin(), IDecl->classmeth_end(), + false, "CATEGORY_", FullCategoryName, Result); + + // Protocols referenced in class declaration? + // Null CDecl is case of a category implementation with no category interface + if (CDecl) + RewriteObjCProtocolListMetaData(CDecl->getReferencedProtocols(), "CATEGORY", + FullCategoryName, Result); + /* struct _objc_category { + char *category_name; + char *class_name; + struct _objc_method_list *instance_methods; + struct _objc_method_list *class_methods; + struct _objc_protocol_list *protocols; + // Objective-C 1.0 extensions + uint32_t size; // sizeof (struct _objc_category) + struct _objc_property_list *instance_properties; // category's own + // @property decl. + }; + */ + + static bool objc_category = false; + if (!objc_category) { + Result += "\nstruct _objc_category {\n"; + Result += "\tchar *category_name;\n"; + Result += "\tchar *class_name;\n"; + Result += "\tstruct _objc_method_list *instance_methods;\n"; + Result += "\tstruct _objc_method_list *class_methods;\n"; + Result += "\tstruct _objc_protocol_list *protocols;\n"; + Result += "\tunsigned int size;\n"; + Result += "\tstruct _objc_property_list *instance_properties;\n"; + Result += "};\n"; + objc_category = true; + } + Result += "\nstatic struct _objc_category _OBJC_CATEGORY_"; + Result += FullCategoryName; + Result += " __attribute__ ((used, section (\"__OBJC, __category\")))= {\n\t\""; + Result += IDecl->getNameAsString(); + Result += "\"\n\t, \""; + Result += ClassDecl->getNameAsString(); + Result += "\"\n"; + + if (IDecl->instmeth_begin() != IDecl->instmeth_end()) { + Result += "\t, (struct _objc_method_list *)" + "&_OBJC_CATEGORY_INSTANCE_METHODS_"; + Result += FullCategoryName; + Result += "\n"; + } + else + Result += "\t, 0\n"; + if (IDecl->classmeth_begin() != IDecl->classmeth_end()) { + Result += "\t, (struct _objc_method_list *)" + "&_OBJC_CATEGORY_CLASS_METHODS_"; + Result += FullCategoryName; + Result += "\n"; + } + else + Result += "\t, 0\n"; + + if (CDecl && CDecl->protocol_begin() != CDecl->protocol_end()) { + Result += "\t, (struct _objc_protocol_list *)&_OBJC_CATEGORY_PROTOCOLS_"; + Result += FullCategoryName; + Result += "\n"; + } + else + Result += "\t, 0\n"; + Result += "\t, sizeof(struct _objc_category), 0\n};\n"; +} + +// RewriteObjCMethodsMetaData - Rewrite methods metadata for instance or +/// class methods. +template<typename MethodIterator> +void RewriteObjCFragileABI::RewriteObjCMethodsMetaData(MethodIterator MethodBegin, + MethodIterator MethodEnd, + bool IsInstanceMethod, + StringRef prefix, + StringRef ClassName, + std::string &Result) { + if (MethodBegin == MethodEnd) return; + + if (!objc_impl_method) { + /* struct _objc_method { + SEL _cmd; + char *method_types; + void *_imp; + } + */ + Result += "\nstruct _objc_method {\n"; + Result += "\tSEL _cmd;\n"; + Result += "\tchar *method_types;\n"; + Result += "\tvoid *_imp;\n"; + Result += "};\n"; + + objc_impl_method = true; + } + + // Build _objc_method_list for class's methods if needed + + /* struct { + struct _objc_method_list *next_method; + int method_count; + struct _objc_method method_list[]; + } + */ + unsigned NumMethods = std::distance(MethodBegin, MethodEnd); + Result += "\nstatic struct {\n"; + Result += "\tstruct _objc_method_list *next_method;\n"; + Result += "\tint method_count;\n"; + Result += "\tstruct _objc_method method_list["; + Result += utostr(NumMethods); + Result += "];\n} _OBJC_"; + Result += prefix; + Result += IsInstanceMethod ? "INSTANCE" : "CLASS"; + Result += "_METHODS_"; + Result += ClassName; + Result += " __attribute__ ((used, section (\"__OBJC, __"; + Result += IsInstanceMethod ? "inst" : "cls"; + Result += "_meth\")))= "; + Result += "{\n\t0, " + utostr(NumMethods) + "\n"; + + Result += "\t,{{(SEL)\""; + Result += (*MethodBegin)->getSelector().getAsString(); + std::string MethodTypeString = + Context->getObjCEncodingForMethodDecl(*MethodBegin); + Result += "\", \""; + Result += MethodTypeString; + Result += "\", (void *)"; + Result += MethodInternalNames[*MethodBegin]; + Result += "}\n"; + for (++MethodBegin; MethodBegin != MethodEnd; ++MethodBegin) { + Result += "\t ,{(SEL)\""; + Result += (*MethodBegin)->getSelector().getAsString(); + std::string MethodTypeString = + Context->getObjCEncodingForMethodDecl(*MethodBegin); + Result += "\", \""; + Result += MethodTypeString; + Result += "\", (void *)"; + Result += MethodInternalNames[*MethodBegin]; + Result += "}\n"; + } + Result += "\t }\n};\n"; +} + +Stmt *RewriteObjCFragileABI::RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV) { + SourceRange OldRange = IV->getSourceRange(); + Expr *BaseExpr = IV->getBase(); + + // Rewrite the base, but without actually doing replaces. + { + DisableReplaceStmtScope S(*this); + BaseExpr = cast<Expr>(RewriteFunctionBodyOrGlobalInitializer(BaseExpr)); + IV->setBase(BaseExpr); + } + + ObjCIvarDecl *D = IV->getDecl(); + + Expr *Replacement = IV; + if (CurMethodDef) { + if (BaseExpr->getType()->isObjCObjectPointerType()) { + const ObjCInterfaceType *iFaceDecl = + dyn_cast<ObjCInterfaceType>(BaseExpr->getType()->getPointeeType()); + assert(iFaceDecl && "RewriteObjCIvarRefExpr - iFaceDecl is null"); + // lookup which class implements the instance variable. + ObjCInterfaceDecl *clsDeclared = nullptr; + iFaceDecl->getDecl()->lookupInstanceVariable(D->getIdentifier(), + clsDeclared); + assert(clsDeclared && "RewriteObjCIvarRefExpr(): Can't find class"); + + // Synthesize an explicit cast to gain access to the ivar. + std::string RecName = clsDeclared->getIdentifier()->getName(); + RecName += "_IMPL"; + IdentifierInfo *II = &Context->Idents.get(RecName); + RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), SourceLocation(), + II); + assert(RD && "RewriteObjCIvarRefExpr(): Can't find RecordDecl"); + QualType castT = Context->getPointerType(Context->getTagDeclType(RD)); + CastExpr *castExpr = NoTypeInfoCStyleCastExpr(Context, castT, + CK_BitCast, + IV->getBase()); + // Don't forget the parens to enforce the proper binding. + ParenExpr *PE = new (Context) ParenExpr(OldRange.getBegin(), + OldRange.getEnd(), + castExpr); + if (IV->isFreeIvar() && + declaresSameEntity(CurMethodDef->getClassInterface(), iFaceDecl->getDecl())) { + MemberExpr *ME = new (Context) + MemberExpr(PE, true, SourceLocation(), D, IV->getLocation(), + D->getType(), VK_LValue, OK_Ordinary); + Replacement = ME; + } else { + IV->setBase(PE); + } + } + } else { // we are outside a method. + assert(!IV->isFreeIvar() && "Cannot have a free standing ivar outside a method"); + + // Explicit ivar refs need to have a cast inserted. + // FIXME: consider sharing some of this code with the code above. + if (BaseExpr->getType()->isObjCObjectPointerType()) { + const ObjCInterfaceType *iFaceDecl = + dyn_cast<ObjCInterfaceType>(BaseExpr->getType()->getPointeeType()); + // lookup which class implements the instance variable. + ObjCInterfaceDecl *clsDeclared = nullptr; + iFaceDecl->getDecl()->lookupInstanceVariable(D->getIdentifier(), + clsDeclared); + assert(clsDeclared && "RewriteObjCIvarRefExpr(): Can't find class"); + + // Synthesize an explicit cast to gain access to the ivar. + std::string RecName = clsDeclared->getIdentifier()->getName(); + RecName += "_IMPL"; + IdentifierInfo *II = &Context->Idents.get(RecName); + RecordDecl *RD = RecordDecl::Create(*Context, TTK_Struct, TUDecl, + SourceLocation(), SourceLocation(), + II); + assert(RD && "RewriteObjCIvarRefExpr(): Can't find RecordDecl"); + QualType castT = Context->getPointerType(Context->getTagDeclType(RD)); + CastExpr *castExpr = NoTypeInfoCStyleCastExpr(Context, castT, + CK_BitCast, + IV->getBase()); + // Don't forget the parens to enforce the proper binding. + ParenExpr *PE = new (Context) ParenExpr( + IV->getBase()->getBeginLoc(), IV->getBase()->getEndLoc(), castExpr); + // Cannot delete IV->getBase(), since PE points to it. + // Replace the old base with the cast. This is important when doing + // embedded rewrites. For example, [newInv->_container addObject:0]. + IV->setBase(PE); + } + } + + ReplaceStmtWithRange(IV, Replacement, OldRange); + return Replacement; +} + +#endif // CLANG_ENABLE_OBJC_REWRITER diff --git a/contrib/llvm/tools/clang/lib/Frontend/Rewrite/RewriteTest.cpp b/contrib/llvm/tools/clang/lib/Frontend/Rewrite/RewriteTest.cpp new file mode 100644 index 000000000000..b0791f4cddd7 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/Rewrite/RewriteTest.cpp @@ -0,0 +1,39 @@ +//===--- RewriteTest.cpp - Rewriter playground ----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This is a testbed. +// +//===----------------------------------------------------------------------===// + +#include "clang/Lex/Preprocessor.h" +#include "clang/Rewrite/Core/TokenRewriter.h" +#include "clang/Rewrite/Frontend/Rewriters.h" +#include "llvm/Support/raw_ostream.h" + +void clang::DoRewriteTest(Preprocessor &PP, raw_ostream *OS) { + SourceManager &SM = PP.getSourceManager(); + const LangOptions &LangOpts = PP.getLangOpts(); + + TokenRewriter Rewriter(SM.getMainFileID(), SM, LangOpts); + + // Throw <i> </i> tags around comments. + for (TokenRewriter::token_iterator I = Rewriter.token_begin(), + E = Rewriter.token_end(); I != E; ++I) { + if (I->isNot(tok::comment)) continue; + + Rewriter.AddTokenBefore(I, "<i>"); + Rewriter.AddTokenAfter(I, "</i>"); + } + + + // Print out the output. + for (TokenRewriter::token_iterator I = Rewriter.token_begin(), + E = Rewriter.token_end(); I != E; ++I) + *OS << PP.getSpelling(*I); +} 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 000000000000..22546ce4c097 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/SerializedDiagnosticPrinter.cpp @@ -0,0 +1,858 @@ +//===--- 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/SourceManager.h" +#include "clang/Frontend/DiagnosticRenderer.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/SerializedDiagnosticReader.h" +#include "clang/Frontend/SerializedDiagnostics.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Lex/Lexer.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" +#include <utility> + +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; +typedef ArrayRef<uint64_t> RecordDataRef; + +class SDiagsWriter; + +class SDiagsRenderer : public DiagnosticNoteRenderer { + SDiagsWriter &Writer; +public: + SDiagsRenderer(SDiagsWriter &Writer, const LangOptions &LangOpts, + DiagnosticOptions *DiagOpts) + : DiagnosticNoteRenderer(LangOpts, DiagOpts), Writer(Writer) {} + + ~SDiagsRenderer() override {} + +protected: + void emitDiagnosticMessage(FullSourceLoc Loc, PresumedLoc PLoc, + DiagnosticsEngine::Level Level, StringRef Message, + ArrayRef<CharSourceRange> Ranges, + DiagOrStoredDiag D) override; + + void emitDiagnosticLoc(FullSourceLoc Loc, PresumedLoc PLoc, + DiagnosticsEngine::Level Level, + ArrayRef<CharSourceRange> Ranges) override {} + + void emitNote(FullSourceLoc Loc, StringRef Message) override; + + void emitCodeContext(FullSourceLoc Loc, DiagnosticsEngine::Level Level, + SmallVectorImpl<CharSourceRange> &Ranges, + ArrayRef<FixItHint> Hints) override; + + void beginDiagnostic(DiagOrStoredDiag D, + DiagnosticsEngine::Level Level) override; + void endDiagnostic(DiagOrStoredDiag D, + DiagnosticsEngine::Level Level) override; +}; + +typedef llvm::DenseMap<unsigned, unsigned> AbbrevLookup; + +class SDiagsMerger : SerializedDiagnosticReader { + SDiagsWriter &Writer; + AbbrevLookup FileLookup; + AbbrevLookup CategoryLookup; + AbbrevLookup DiagFlagLookup; + +public: + SDiagsMerger(SDiagsWriter &Writer) + : SerializedDiagnosticReader(), Writer(Writer) {} + + std::error_code mergeRecordsFromFile(const char *File) { + return readDiagnostics(File); + } + +protected: + std::error_code visitStartOfDiagnostic() override; + std::error_code visitEndOfDiagnostic() override; + std::error_code visitCategoryRecord(unsigned ID, StringRef Name) override; + std::error_code visitDiagFlagRecord(unsigned ID, StringRef Name) override; + std::error_code visitDiagnosticRecord( + unsigned Severity, const serialized_diags::Location &Location, + unsigned Category, unsigned Flag, StringRef Message) override; + std::error_code visitFilenameRecord(unsigned ID, unsigned Size, + unsigned Timestamp, + StringRef Name) override; + std::error_code visitFixitRecord(const serialized_diags::Location &Start, + const serialized_diags::Location &End, + StringRef CodeToInsert) override; + std::error_code + visitSourceRangeRecord(const serialized_diags::Location &Start, + const serialized_diags::Location &End) override; + +private: + std::error_code adjustSourceLocFilename(RecordData &Record, + unsigned int offset); + + void adjustAbbrevID(RecordData &Record, AbbrevLookup &Lookup, + unsigned NewAbbrev); + + void writeRecordWithAbbrev(unsigned ID, RecordData &Record); + + void writeRecordWithBlob(unsigned ID, RecordData &Record, StringRef Blob); +}; + +class SDiagsWriter : public DiagnosticConsumer { + friend class SDiagsRenderer; + friend class SDiagsMerger; + + struct SharedState; + + explicit SDiagsWriter(std::shared_ptr<SharedState> State) + : LangOpts(nullptr), OriginalInstance(false), MergeChildRecords(false), + State(std::move(State)) {} + +public: + SDiagsWriter(StringRef File, DiagnosticOptions *Diags, bool MergeChildRecords) + : LangOpts(nullptr), OriginalInstance(true), + MergeChildRecords(MergeChildRecords), + State(std::make_shared<SharedState>(File, Diags)) { + if (MergeChildRecords) + RemoveOldDiagnostics(); + EmitPreamble(); + } + + ~SDiagsWriter() override {} + + void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, + const Diagnostic &Info) override; + + void BeginSourceFile(const LangOptions &LO, const Preprocessor *PP) override { + LangOpts = &LO; + } + + void finish() override; + +private: + /// Build a DiagnosticsEngine to emit diagnostics about the diagnostics + DiagnosticsEngine *getMetaDiags(); + + /// Remove old copies of the serialized diagnostics. This is necessary + /// so that we can detect when subprocesses write diagnostics that we should + /// merge into our own. + void RemoveOldDiagnostics(); + + /// Emit the preamble for the serialized diagnostics. + void EmitPreamble(); + + /// Emit the BLOCKINFO block. + void EmitBlockInfoBlock(); + + /// Emit the META data block. + void EmitMetaBlock(); + + /// Start a DIAG block. + void EnterDiagBlock(); + + /// End a DIAG block. + void ExitDiagBlock(); + + /// Emit a DIAG record. + void EmitDiagnosticMessage(FullSourceLoc Loc, PresumedLoc PLoc, + DiagnosticsEngine::Level Level, StringRef Message, + DiagOrStoredDiag D); + + /// Emit FIXIT and SOURCE_RANGE records for a diagnostic. + void EmitCodeContext(SmallVectorImpl<CharSourceRange> &Ranges, + ArrayRef<FixItHint> Hints, + const SourceManager &SM); + + /// Emit a record for a CharSourceRange. + void EmitCharSourceRange(CharSourceRange R, const SourceManager &SM); + + /// Emit the string information for the category. + unsigned getEmitCategory(unsigned category = 0); + + /// Emit the string information for diagnostic flags. + unsigned getEmitDiagnosticFlag(DiagnosticsEngine::Level DiagLevel, + unsigned DiagID = 0); + + unsigned getEmitDiagnosticFlag(StringRef DiagName); + + /// Emit (lazily) the file string and retrieved the file identifier. + unsigned getEmitFile(const char *Filename); + + /// Add SourceLocation information the specified record. + void AddLocToRecord(FullSourceLoc Loc, PresumedLoc PLoc, + RecordDataImpl &Record, unsigned TokSize = 0); + + /// Add SourceLocation information the specified record. + void AddLocToRecord(FullSourceLoc Loc, RecordDataImpl &Record, + unsigned TokSize = 0) { + AddLocToRecord(Loc, Loc.hasManager() ? Loc.getPresumedLoc() : PresumedLoc(), + Record, TokSize); + } + + /// Add CharSourceRange information the specified record. + void AddCharSourceRangeToRecord(CharSourceRange R, RecordDataImpl &Record, + const SourceManager &SM); + + /// Language options, which can differ from one clone of this client + /// to another. + const LangOptions *LangOpts; + + /// Whether this is the original instance (rather than one of its + /// clones), responsible for writing the file at the end. + bool OriginalInstance; + + /// Whether this instance should aggregate diagnostics that are + /// generated from child processes. + bool MergeChildRecords; + + /// State that is shared among the various clones of this diagnostic + /// consumer. + struct SharedState { + SharedState(StringRef File, DiagnosticOptions *Diags) + : DiagOpts(Diags), Stream(Buffer), OutputFile(File.str()), + EmittedAnyDiagBlocks(false) {} + + /// Diagnostic options. + IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts; + + /// The byte buffer for the serialized content. + SmallString<1024> Buffer; + + /// The BitStreamWriter for the serialized diagnostics. + llvm::BitstreamWriter Stream; + + /// The name of the diagnostics file. + std::string OutputFile; + + /// The set of constructed record abbreviations. + AbbreviationMap Abbrevs; + + /// A utility buffer for constructing record content. + RecordData Record; + + /// A text buffer for rendering diagnostic text. + SmallString<256> diagBuf; + + /// The collection of diagnostic categories used. + llvm::DenseSet<unsigned> Categories; + + /// The collection of files used. + llvm::DenseMap<const char *, unsigned> Files; + + typedef llvm::DenseMap<const void *, std::pair<unsigned, StringRef> > + DiagFlagsTy; + + /// Map for uniquing strings. + DiagFlagsTy DiagFlags; + + /// 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; + + /// Engine for emitting diagnostics about the diagnostics. + std::unique_ptr<DiagnosticsEngine> MetaDiagnostics; + }; + + /// State shared among the various clones of this diagnostic consumer. + std::shared_ptr<SharedState> State; +}; +} // end anonymous namespace + +namespace clang { +namespace serialized_diags { +std::unique_ptr<DiagnosticConsumer> +create(StringRef OutputFile, DiagnosticOptions *Diags, bool MergeChildRecords) { + return llvm::make_unique<SDiagsWriter>(OutputFile, Diags, MergeChildRecords); +} + +} // end namespace serialized_diags +} // end namespace clang + +//===----------------------------------------------------------------------===// +// Serialization methods. +//===----------------------------------------------------------------------===// + +/// 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 || Name[0] == 0) + return; + + Record.clear(); + + while (*Name) + Record.push_back(*Name++); + + Stream.EmitRecord(llvm::bitc::BLOCKINFO_CODE_BLOCKNAME, Record); +} + +/// 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(FullSourceLoc Loc, 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(Loc.getFileOffset()); +} + +void SDiagsWriter::AddCharSourceRangeToRecord(CharSourceRange Range, + RecordDataImpl &Record, + const SourceManager &SM) { + AddLocToRecord(FullSourceLoc(Range.getBegin(), SM), Record); + unsigned TokSize = 0; + if (Range.isTokenRange()) + TokSize = Lexer::MeasureTokenLength(Range.getEnd(), + SM, *LangOpts); + + AddLocToRecord(FullSourceLoc(Range.getEnd(), SM), Record, 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(); + StringRef Name(FileName); + RecordData::value_type Record[] = {RECORD_FILENAME, entry, 0 /* For legacy */, + 0 /* For legacy */, 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); +} + +/// 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(); + + 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); + auto Abbrev = std::make_shared<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 = std::make_shared<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::VBR, 16)); // Text size. + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Diagnostc text. + Abbrevs.set(RECORD_DIAG, Stream.EmitBlockInfoAbbrev(BLOCK_DIAG, Abbrev)); + + // Emit abbreviation for RECORD_CATEGORY. + Abbrev = std::make_shared<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 abbreviation for RECORD_SOURCE_RANGE. + Abbrev = std::make_shared<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 = std::make_shared<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 = std::make_shared<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)); // Modification 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 = std::make_shared<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; + AbbreviationMap &Abbrevs = State->Abbrevs; + + Stream.EnterSubblock(BLOCK_META, 3); + RecordData::value_type Record[] = {RECORD_VERSION, VersionNumber}; + Stream.EmitRecordWithAbbrev(Abbrevs.get(RECORD_VERSION), Record); + Stream.ExitBlock(); +} + +unsigned SDiagsWriter::getEmitCategory(unsigned int category) { + if (!State->Categories.insert(category).second) + return 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. + StringRef catName = DiagnosticIDs::getCategoryNameFromID(category); + RecordData::value_type Record[] = {RECORD_CATEGORY, category, 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); + return getEmitDiagnosticFlag(FlagName); +} + +unsigned SDiagsWriter::getEmitDiagnosticFlag(StringRef FlagName) { + 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::value_type Record[] = {RECORD_DIAG_FLAG, entry.first, + 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(FullSourceLoc(), PresumedLoc(), DiagLevel, + State->diagBuf, &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( + FullSourceLoc(Info.getLocation(), Info.getSourceManager()), DiagLevel, + State->diagBuf, Info.getRanges(), Info.getFixItHints(), &Info); +} + +static serialized_diags::Level getStableLevel(DiagnosticsEngine::Level Level) { + switch (Level) { +#define CASE(X) case DiagnosticsEngine::X: return serialized_diags::X; + CASE(Ignored) + CASE(Note) + CASE(Remark) + CASE(Warning) + CASE(Error) + CASE(Fatal) +#undef CASE + } + + llvm_unreachable("invalid diagnostic level"); +} + +void SDiagsWriter::EmitDiagnosticMessage(FullSourceLoc Loc, PresumedLoc PLoc, + DiagnosticsEngine::Level Level, + StringRef Message, + 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(getStableLevel(Level)); + AddLocToRecord(Loc, 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( + FullSourceLoc Loc, PresumedLoc PLoc, DiagnosticsEngine::Level Level, + StringRef Message, ArrayRef<clang::CharSourceRange> Ranges, + DiagOrStoredDiag D) { + Writer.EmitDiagnosticMessage(Loc, PLoc, Level, Message, 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(FullSourceLoc Loc, + DiagnosticsEngine::Level Level, + SmallVectorImpl<CharSourceRange> &Ranges, + ArrayRef<FixItHint> Hints) { + Writer.EmitCodeContext(Ranges, Hints, Loc.getManager()); +} + +void SDiagsRenderer::emitNote(FullSourceLoc Loc, StringRef Message) { + Writer.EnterDiagBlock(); + PresumedLoc PLoc = Loc.hasManager() ? Loc.getPresumedLoc() : PresumedLoc(); + Writer.EmitDiagnosticMessage(Loc, PLoc, DiagnosticsEngine::Note, Message, + DiagOrStoredDiag()); + Writer.ExitDiagBlock(); +} + +DiagnosticsEngine *SDiagsWriter::getMetaDiags() { + // FIXME: It's slightly absurd to create a new diagnostics engine here, but + // the other options that are available today are worse: + // + // 1. Teach DiagnosticsConsumers to emit diagnostics to the engine they are a + // part of. The DiagnosticsEngine would need to know not to send + // diagnostics back to the consumer that failed. This would require us to + // rework ChainedDiagnosticsConsumer and teach the engine about multiple + // consumers, which is difficult today because most APIs interface with + // consumers rather than the engine itself. + // + // 2. Pass a DiagnosticsEngine to SDiagsWriter on creation - this would need + // to be distinct from the engine the writer was being added to and would + // normally not be used. + if (!State->MetaDiagnostics) { + IntrusiveRefCntPtr<DiagnosticIDs> IDs(new DiagnosticIDs()); + auto Client = + new TextDiagnosticPrinter(llvm::errs(), State->DiagOpts.get()); + State->MetaDiagnostics = llvm::make_unique<DiagnosticsEngine>( + IDs, State->DiagOpts.get(), Client); + } + return State->MetaDiagnostics.get(); +} + +void SDiagsWriter::RemoveOldDiagnostics() { + if (!llvm::sys::fs::remove(State->OutputFile)) + return; + + getMetaDiags()->Report(diag::warn_fe_serialized_diag_merge_failure); + // Disable merging child records, as whatever is in this file may be + // misleading. + MergeChildRecords = false; +} + +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(); + + if (MergeChildRecords) { + if (!State->EmittedAnyDiagBlocks) + // We have no diagnostics of our own, so we can just leave the child + // process' output alone + return; + + if (llvm::sys::fs::exists(State->OutputFile)) + if (SDiagsMerger(*this).mergeRecordsFromFile(State->OutputFile.c_str())) + getMetaDiags()->Report(diag::warn_fe_serialized_diag_merge_failure); + } + + std::error_code EC; + auto OS = llvm::make_unique<llvm::raw_fd_ostream>(State->OutputFile.c_str(), + EC, llvm::sys::fs::F_None); + if (EC) { + getMetaDiags()->Report(diag::warn_fe_serialized_diag_failure) + << State->OutputFile << EC.message(); + return; + } + + // Write the generated bitstream to "Out". + OS->write((char *)&State->Buffer.front(), State->Buffer.size()); + OS->flush(); +} + +std::error_code SDiagsMerger::visitStartOfDiagnostic() { + Writer.EnterDiagBlock(); + return std::error_code(); +} + +std::error_code SDiagsMerger::visitEndOfDiagnostic() { + Writer.ExitDiagBlock(); + return std::error_code(); +} + +std::error_code +SDiagsMerger::visitSourceRangeRecord(const serialized_diags::Location &Start, + const serialized_diags::Location &End) { + RecordData::value_type Record[] = { + RECORD_SOURCE_RANGE, FileLookup[Start.FileID], Start.Line, Start.Col, + Start.Offset, FileLookup[End.FileID], End.Line, End.Col, End.Offset}; + Writer.State->Stream.EmitRecordWithAbbrev( + Writer.State->Abbrevs.get(RECORD_SOURCE_RANGE), Record); + return std::error_code(); +} + +std::error_code SDiagsMerger::visitDiagnosticRecord( + unsigned Severity, const serialized_diags::Location &Location, + unsigned Category, unsigned Flag, StringRef Message) { + RecordData::value_type Record[] = { + RECORD_DIAG, Severity, FileLookup[Location.FileID], Location.Line, + Location.Col, Location.Offset, CategoryLookup[Category], + Flag ? DiagFlagLookup[Flag] : 0, Message.size()}; + + Writer.State->Stream.EmitRecordWithBlob( + Writer.State->Abbrevs.get(RECORD_DIAG), Record, Message); + return std::error_code(); +} + +std::error_code +SDiagsMerger::visitFixitRecord(const serialized_diags::Location &Start, + const serialized_diags::Location &End, + StringRef Text) { + RecordData::value_type Record[] = {RECORD_FIXIT, FileLookup[Start.FileID], + Start.Line, Start.Col, Start.Offset, + FileLookup[End.FileID], End.Line, End.Col, + End.Offset, Text.size()}; + + Writer.State->Stream.EmitRecordWithBlob( + Writer.State->Abbrevs.get(RECORD_FIXIT), Record, Text); + return std::error_code(); +} + +std::error_code SDiagsMerger::visitFilenameRecord(unsigned ID, unsigned Size, + unsigned Timestamp, + StringRef Name) { + FileLookup[ID] = Writer.getEmitFile(Name.str().c_str()); + return std::error_code(); +} + +std::error_code SDiagsMerger::visitCategoryRecord(unsigned ID, StringRef Name) { + CategoryLookup[ID] = Writer.getEmitCategory(ID); + return std::error_code(); +} + +std::error_code SDiagsMerger::visitDiagFlagRecord(unsigned ID, StringRef Name) { + DiagFlagLookup[ID] = Writer.getEmitDiagnosticFlag(Name); + return std::error_code(); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/SerializedDiagnosticReader.cpp b/contrib/llvm/tools/clang/lib/Frontend/SerializedDiagnosticReader.cpp new file mode 100644 index 000000000000..458717819c41 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/SerializedDiagnosticReader.cpp @@ -0,0 +1,311 @@ +//===- SerializedDiagnosticReader.cpp - Reads 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/SerializedDiagnosticReader.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/FileSystemOptions.h" +#include "clang/Frontend/SerializedDiagnostics.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Bitcode/BitCodes.h" +#include "llvm/Bitcode/BitstreamReader.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/ManagedStatic.h" +#include <cstdint> +#include <system_error> + +using namespace clang; +using namespace serialized_diags; + +std::error_code SerializedDiagnosticReader::readDiagnostics(StringRef File) { + // Open the diagnostics file. + FileSystemOptions FO; + FileManager FileMgr(FO); + + auto Buffer = FileMgr.getBufferForFile(File); + if (!Buffer) + return SDError::CouldNotLoad; + + llvm::BitstreamCursor Stream(**Buffer); + Optional<llvm::BitstreamBlockInfo> BlockInfo; + + if (Stream.AtEndOfStream()) + return SDError::InvalidSignature; + + // Sniff for the signature. + if (Stream.Read(8) != 'D' || + Stream.Read(8) != 'I' || + Stream.Read(8) != 'A' || + Stream.Read(8) != 'G') + return SDError::InvalidSignature; + + // Read the top level blocks. + while (!Stream.AtEndOfStream()) { + if (Stream.ReadCode() != llvm::bitc::ENTER_SUBBLOCK) + return SDError::InvalidDiagnostics; + + std::error_code EC; + switch (Stream.ReadSubBlockID()) { + case llvm::bitc::BLOCKINFO_BLOCK_ID: + BlockInfo = Stream.ReadBlockInfoBlock(); + if (!BlockInfo) + return SDError::MalformedBlockInfoBlock; + Stream.setBlockInfo(&*BlockInfo); + continue; + case BLOCK_META: + if ((EC = readMetaBlock(Stream))) + return EC; + continue; + case BLOCK_DIAG: + if ((EC = readDiagnosticBlock(Stream))) + return EC; + continue; + default: + if (!Stream.SkipBlock()) + return SDError::MalformedTopLevelBlock; + continue; + } + } + return {}; +} + +enum class SerializedDiagnosticReader::Cursor { + Record = 1, + BlockEnd, + BlockBegin +}; + +llvm::ErrorOr<SerializedDiagnosticReader::Cursor> +SerializedDiagnosticReader::skipUntilRecordOrBlock( + llvm::BitstreamCursor &Stream, unsigned &BlockOrRecordID) { + BlockOrRecordID = 0; + + while (!Stream.AtEndOfStream()) { + unsigned Code = Stream.ReadCode(); + + switch ((llvm::bitc::FixedAbbrevIDs)Code) { + case llvm::bitc::ENTER_SUBBLOCK: + BlockOrRecordID = Stream.ReadSubBlockID(); + return Cursor::BlockBegin; + + case llvm::bitc::END_BLOCK: + if (Stream.ReadBlockEnd()) + return SDError::InvalidDiagnostics; + return Cursor::BlockEnd; + + case llvm::bitc::DEFINE_ABBREV: + Stream.ReadAbbrevRecord(); + continue; + + case llvm::bitc::UNABBREV_RECORD: + return SDError::UnsupportedConstruct; + + default: + // We found a record. + BlockOrRecordID = Code; + return Cursor::Record; + } + } + + return SDError::InvalidDiagnostics; +} + +std::error_code +SerializedDiagnosticReader::readMetaBlock(llvm::BitstreamCursor &Stream) { + if (Stream.EnterSubBlock(clang::serialized_diags::BLOCK_META)) + return SDError::MalformedMetadataBlock; + + bool VersionChecked = false; + + while (true) { + unsigned BlockOrCode = 0; + llvm::ErrorOr<Cursor> Res = skipUntilRecordOrBlock(Stream, BlockOrCode); + if (!Res) + Res.getError(); + + switch (Res.get()) { + case Cursor::Record: + break; + case Cursor::BlockBegin: + if (Stream.SkipBlock()) + return SDError::MalformedMetadataBlock; + LLVM_FALLTHROUGH; + case Cursor::BlockEnd: + if (!VersionChecked) + return SDError::MissingVersion; + return {}; + } + + SmallVector<uint64_t, 1> Record; + unsigned RecordID = Stream.readRecord(BlockOrCode, Record); + + if (RecordID == RECORD_VERSION) { + if (Record.size() < 1) + return SDError::MissingVersion; + if (Record[0] > VersionNumber) + return SDError::VersionMismatch; + VersionChecked = true; + } + } +} + +std::error_code +SerializedDiagnosticReader::readDiagnosticBlock(llvm::BitstreamCursor &Stream) { + if (Stream.EnterSubBlock(clang::serialized_diags::BLOCK_DIAG)) + return SDError::MalformedDiagnosticBlock; + + std::error_code EC; + if ((EC = visitStartOfDiagnostic())) + return EC; + + SmallVector<uint64_t, 16> Record; + while (true) { + unsigned BlockOrCode = 0; + llvm::ErrorOr<Cursor> Res = skipUntilRecordOrBlock(Stream, BlockOrCode); + if (!Res) + Res.getError(); + + switch (Res.get()) { + case Cursor::BlockBegin: + // The only blocks we care about are subdiagnostics. + if (BlockOrCode == serialized_diags::BLOCK_DIAG) { + if ((EC = readDiagnosticBlock(Stream))) + return EC; + } else if (!Stream.SkipBlock()) + return SDError::MalformedSubBlock; + continue; + case Cursor::BlockEnd: + if ((EC = visitEndOfDiagnostic())) + return EC; + return {}; + case Cursor::Record: + break; + } + + // Read the record. + Record.clear(); + StringRef Blob; + unsigned RecID = Stream.readRecord(BlockOrCode, Record, &Blob); + + if (RecID < serialized_diags::RECORD_FIRST || + RecID > serialized_diags::RECORD_LAST) + continue; + + switch ((RecordIDs)RecID) { + case RECORD_CATEGORY: + // A category has ID and name size. + if (Record.size() != 2) + return SDError::MalformedDiagnosticRecord; + if ((EC = visitCategoryRecord(Record[0], Blob))) + return EC; + continue; + case RECORD_DIAG: + // A diagnostic has severity, location (4), category, flag, and message + // size. + if (Record.size() != 8) + return SDError::MalformedDiagnosticRecord; + if ((EC = visitDiagnosticRecord( + Record[0], Location(Record[1], Record[2], Record[3], Record[4]), + Record[5], Record[6], Blob))) + return EC; + continue; + case RECORD_DIAG_FLAG: + // A diagnostic flag has ID and name size. + if (Record.size() != 2) + return SDError::MalformedDiagnosticRecord; + if ((EC = visitDiagFlagRecord(Record[0], Blob))) + return EC; + continue; + case RECORD_FILENAME: + // A filename has ID, size, timestamp, and name size. The size and + // timestamp are legacy fields that are always zero these days. + if (Record.size() != 4) + return SDError::MalformedDiagnosticRecord; + if ((EC = visitFilenameRecord(Record[0], Record[1], Record[2], Blob))) + return EC; + continue; + case RECORD_FIXIT: + // A fixit has two locations (4 each) and message size. + if (Record.size() != 9) + return SDError::MalformedDiagnosticRecord; + if ((EC = visitFixitRecord( + Location(Record[0], Record[1], Record[2], Record[3]), + Location(Record[4], Record[5], Record[6], Record[7]), Blob))) + return EC; + continue; + case RECORD_SOURCE_RANGE: + // A source range is two locations (4 each). + if (Record.size() != 8) + return SDError::MalformedDiagnosticRecord; + if ((EC = visitSourceRangeRecord( + Location(Record[0], Record[1], Record[2], Record[3]), + Location(Record[4], Record[5], Record[6], Record[7])))) + return EC; + continue; + case RECORD_VERSION: + // A version is just a number. + if (Record.size() != 1) + return SDError::MalformedDiagnosticRecord; + if ((EC = visitVersionRecord(Record[0]))) + return EC; + continue; + } + } +} + +namespace { + +class SDErrorCategoryType final : public std::error_category { + const char *name() const noexcept override { + return "clang.serialized_diags"; + } + + std::string message(int IE) const override { + auto E = static_cast<SDError>(IE); + switch (E) { + case SDError::CouldNotLoad: + return "Failed to open diagnostics file"; + case SDError::InvalidSignature: + return "Invalid diagnostics signature"; + case SDError::InvalidDiagnostics: + return "Parse error reading diagnostics"; + case SDError::MalformedTopLevelBlock: + return "Malformed block at top-level of diagnostics"; + case SDError::MalformedSubBlock: + return "Malformed sub-block in a diagnostic"; + case SDError::MalformedBlockInfoBlock: + return "Malformed BlockInfo block"; + case SDError::MalformedMetadataBlock: + return "Malformed Metadata block"; + case SDError::MalformedDiagnosticBlock: + return "Malformed Diagnostic block"; + case SDError::MalformedDiagnosticRecord: + return "Malformed Diagnostic record"; + case SDError::MissingVersion: + return "No version provided in diagnostics"; + case SDError::VersionMismatch: + return "Unsupported diagnostics version"; + case SDError::UnsupportedConstruct: + return "Bitcode constructs that are not supported in diagnostics appear"; + case SDError::HandlerFailed: + return "Generic error occurred while handling a record"; + } + llvm_unreachable("Unknown error type!"); + } +}; + +} // namespace + +static llvm::ManagedStatic<SDErrorCategoryType> ErrorCategory; +const std::error_category &clang::serialized_diags::SDErrorCategory() { + return *ErrorCategory; +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/TestModuleFileExtension.cpp b/contrib/llvm/tools/clang/lib/Frontend/TestModuleFileExtension.cpp new file mode 100644 index 000000000000..087bdc543548 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/TestModuleFileExtension.cpp @@ -0,0 +1,121 @@ +//===-- TestModuleFileExtension.cpp - Module Extension Tester -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "TestModuleFileExtension.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Serialization/ASTReader.h" +#include "llvm/ADT/Hashing.h" +#include "llvm/Bitcode/BitstreamWriter.h" +#include "llvm/Support/raw_ostream.h" +#include <cstdio> +using namespace clang; +using namespace clang::serialization; + +TestModuleFileExtension::Writer::~Writer() { } + +void TestModuleFileExtension::Writer::writeExtensionContents( + Sema &SemaRef, + llvm::BitstreamWriter &Stream) { + using namespace llvm; + + // Write an abbreviation for this record. + auto Abv = std::make_shared<llvm::BitCodeAbbrev>(); + Abv->Add(BitCodeAbbrevOp(FIRST_EXTENSION_RECORD_ID)); + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // # of characters + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // message + auto Abbrev = Stream.EmitAbbrev(std::move(Abv)); + + // Write a message into the extension block. + SmallString<64> Message; + { + auto Ext = static_cast<TestModuleFileExtension *>(getExtension()); + raw_svector_ostream OS(Message); + OS << "Hello from " << Ext->BlockName << " v" << Ext->MajorVersion << "." + << Ext->MinorVersion; + } + uint64_t Record[] = {FIRST_EXTENSION_RECORD_ID, Message.size()}; + Stream.EmitRecordWithBlob(Abbrev, Record, Message); +} + +TestModuleFileExtension::Reader::Reader(ModuleFileExtension *Ext, + const llvm::BitstreamCursor &InStream) + : ModuleFileExtensionReader(Ext), Stream(InStream) +{ + // Read the extension block. + SmallVector<uint64_t, 4> Record; + while (true) { + llvm::BitstreamEntry Entry = Stream.advanceSkippingSubblocks(); + switch (Entry.Kind) { + case llvm::BitstreamEntry::SubBlock: + case llvm::BitstreamEntry::EndBlock: + case llvm::BitstreamEntry::Error: + return; + + case llvm::BitstreamEntry::Record: + break; + } + + Record.clear(); + StringRef Blob; + unsigned RecCode = Stream.readRecord(Entry.ID, Record, &Blob); + switch (RecCode) { + case FIRST_EXTENSION_RECORD_ID: { + StringRef Message = Blob.substr(0, Record[0]); + fprintf(stderr, "Read extension block message: %s\n", + Message.str().c_str()); + break; + } + } + } +} + +TestModuleFileExtension::Reader::~Reader() { } + +TestModuleFileExtension::~TestModuleFileExtension() { } + +ModuleFileExtensionMetadata +TestModuleFileExtension::getExtensionMetadata() const { + return { BlockName, MajorVersion, MinorVersion, UserInfo }; +} + +llvm::hash_code TestModuleFileExtension::hashExtension( + llvm::hash_code Code) const { + if (Hashed) { + Code = llvm::hash_combine(Code, BlockName); + Code = llvm::hash_combine(Code, MajorVersion); + Code = llvm::hash_combine(Code, MinorVersion); + Code = llvm::hash_combine(Code, UserInfo); + } + + return Code; +} + +std::unique_ptr<ModuleFileExtensionWriter> +TestModuleFileExtension::createExtensionWriter(ASTWriter &) { + return std::unique_ptr<ModuleFileExtensionWriter>(new Writer(this)); +} + +std::unique_ptr<ModuleFileExtensionReader> +TestModuleFileExtension::createExtensionReader( + const ModuleFileExtensionMetadata &Metadata, + ASTReader &Reader, serialization::ModuleFile &Mod, + const llvm::BitstreamCursor &Stream) +{ + assert(Metadata.BlockName == BlockName && "Wrong block name"); + if (std::make_pair(Metadata.MajorVersion, Metadata.MinorVersion) != + std::make_pair(MajorVersion, MinorVersion)) { + Reader.getDiags().Report(Mod.ImportLoc, + diag::err_test_module_file_extension_version) + << BlockName << Metadata.MajorVersion << Metadata.MinorVersion + << MajorVersion << MinorVersion; + return nullptr; + } + + return std::unique_ptr<ModuleFileExtensionReader>( + new TestModuleFileExtension::Reader(this, Stream)); +} diff --git a/contrib/llvm/tools/clang/lib/Frontend/TestModuleFileExtension.h b/contrib/llvm/tools/clang/lib/Frontend/TestModuleFileExtension.h new file mode 100644 index 000000000000..41f3ca9f05fc --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/TestModuleFileExtension.h @@ -0,0 +1,72 @@ +//===-- TestModuleFileExtension.h - Module Extension Tester -----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_FRONTEND_TESTMODULEFILEEXTENSION_H +#define LLVM_CLANG_FRONTEND_TESTMODULEFILEEXTENSION_H + +#include "clang/Serialization/ModuleFileExtension.h" +#include "clang/Basic/LLVM.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Bitcode/BitstreamReader.h" +#include <string> + +namespace clang { + +/// A module file extension used for testing purposes. +class TestModuleFileExtension : public ModuleFileExtension { + std::string BlockName; + unsigned MajorVersion; + unsigned MinorVersion; + bool Hashed; + std::string UserInfo; + + class Writer : public ModuleFileExtensionWriter { + public: + Writer(ModuleFileExtension *Ext) : ModuleFileExtensionWriter(Ext) { } + ~Writer() override; + + void writeExtensionContents(Sema &SemaRef, + llvm::BitstreamWriter &Stream) override; + }; + + class Reader : public ModuleFileExtensionReader { + llvm::BitstreamCursor Stream; + + public: + ~Reader() override; + + Reader(ModuleFileExtension *Ext, const llvm::BitstreamCursor &InStream); + }; + +public: + TestModuleFileExtension(StringRef BlockName, + unsigned MajorVersion, + unsigned MinorVersion, + bool Hashed, + StringRef UserInfo) + : BlockName(BlockName), + MajorVersion(MajorVersion), MinorVersion(MinorVersion), + Hashed(Hashed), UserInfo(UserInfo) { } + ~TestModuleFileExtension() override; + + ModuleFileExtensionMetadata getExtensionMetadata() const override; + + llvm::hash_code hashExtension(llvm::hash_code Code) const override; + + std::unique_ptr<ModuleFileExtensionWriter> + createExtensionWriter(ASTWriter &Writer) override; + + std::unique_ptr<ModuleFileExtensionReader> + createExtensionReader(const ModuleFileExtensionMetadata &Metadata, + ASTReader &Reader, serialization::ModuleFile &Mod, + const llvm::BitstreamCursor &Stream) override; +}; + +} // end namespace clang + +#endif // LLVM_CLANG_FRONTEND_TESTMODULEFILEEXTENSION_H 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 000000000000..35b99b10f94a --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/TextDiagnostic.cpp @@ -0,0 +1,1346 @@ +//===--- 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/Path.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 remarkColor = + raw_ostream::BLUE; +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; + +/// 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; + } +} + +/// 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; +} + +/// 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 (llvm::isLegalUTF8Sequence(begin, end)) { + llvm::UTF32 c; + llvm::UTF32 *cptr = &c; + unsigned char const *original_begin = begin; + unsigned char const *cp_end = + begin + llvm::getNumBytesForUTF8(SourceLine[*i]); + + llvm::ConversionResult res = llvm::ConvertUTF8toUTF32( + &begin, cp_end, &cptr, cptr + 1, llvm::strictConversion); + (void)res; + assert(llvm::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 multiple 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(); } + + /// 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]; + } + + /// 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]; + } + + /// 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]; + } + + /// 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_byteToColumn.size() - 1)); + while (byteToColumn(++N) == -1) {} + return N; + } + + /// 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_byteToColumn.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; +}; +} // end anonymous namespace + +/// 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 CaretColumns = CaretLine.size(); + unsigned FixItColumns = llvm::sys::locale::columnWidth(FixItInsertionLine); + unsigned MaxColumns = std::max(static_cast<unsigned>(map.columns()), + std::max(CaretColumns, FixItColumns)); + // 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 c) { return c < ' ' || '~' < c; })); + + // 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 truncation, 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); + } +} + +/// 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; +} + +/// 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; +} + +/// 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); +} + +/// 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( + FullSourceLoc Loc, PresumedLoc PLoc, DiagnosticsEngine::Level Level, + StringRef Message, ArrayRef<clang::CharSourceRange> Ranges, + DiagOrStoredDiag D) { + uint64_t StartOfLocationInfo = OS.tell(); + + // Emit the location of this particular diagnostic. + if (Loc.isValid()) + emitDiagnosticLoc(Loc, PLoc, Level, Ranges); + + if (DiagOpts->ShowColors) + OS.resetColor(); + + printDiagnosticLevel(OS, Level, DiagOpts->ShowColors, + DiagOpts->CLFallbackMode); + printDiagnosticMessage(OS, + /*IsSupplemental*/ Level == DiagnosticsEngine::Note, + 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::Remark: OS.changeColor(remarkColor, 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::Remark: OS << "remark"; 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, + bool IsSupplemental, + StringRef Message, + unsigned CurrentColumn, + unsigned Columns, bool ShowColors) { + bool Bold = false; + if (ShowColors && !IsSupplemental) { + // Print primary diagnostic messages in bold and without color, to visually + // indicate the transition from continuation notes and other output. + OS.changeColor(savedColor, true); + Bold = true; + } + + 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'; +} + +void TextDiagnostic::emitFilename(StringRef Filename, const SourceManager &SM) { + SmallVector<char, 128> AbsoluteFilename; + if (DiagOpts->AbsolutePath) { + const DirectoryEntry *Dir = SM.getFileManager().getDirectory( + llvm::sys::path::parent_path(Filename)); + if (Dir) { + StringRef DirName = SM.getFileManager().getCanonicalName(Dir); + llvm::sys::path::append(AbsoluteFilename, DirName, + llvm::sys::path::filename(Filename)); + Filename = StringRef(AbsoluteFilename.data(), AbsoluteFilename.size()); + } + } + + OS << Filename; +} + +/// 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(FullSourceLoc Loc, PresumedLoc PLoc, + DiagnosticsEngine::Level Level, + ArrayRef<CharSourceRange> Ranges) { + if (PLoc.isInvalid()) { + // At least print the file name if available: + FileID FID = Loc.getFileID(); + if (FID.isValid()) { + const FileEntry *FE = Loc.getFileEntry(); + if (FE && FE->isValid()) { + emitFilename(FE->getName(), Loc.getManager()); + if (FE->isInPCH()) + OS << " (in PCH)"; + OS << ": "; + } + } + return; + } + unsigned LineNo = PLoc.getLine(); + + if (!DiagOpts->ShowLocation) + return; + + if (DiagOpts->ShowColors) + OS.changeColor(savedColor, true); + + emitFilename(PLoc.getFilename(), Loc.getManager()); + 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 << ','; + // Visual Studio 2010 or earlier expects column number to be off by one + if (LangOpts.MSCompatibilityVersion && + !LangOpts.isCompatibleWithMSVC(LangOptions::MSVC2012)) + ColNo--; + } else + OS << ':'; + OS << ColNo; + } + switch (DiagOpts->getFormat()) { + case DiagnosticOptions::Clang: + case DiagnosticOptions::Vi: OS << ':'; break; + case DiagnosticOptions::MSVC: + // MSVC2013 and before print 'file(4) : error'. MSVC2015 gets rid of the + // space and prints 'file(4): error'. + OS << ')'; + if (LangOpts.MSCompatibilityVersion && + !LangOpts.isCompatibleWithMSVC(LangOptions::MSVC2015)) + OS << ' '; + OS << ": "; + break; + } + + if (DiagOpts->ShowSourceRanges && !Ranges.empty()) { + FileID CaretFileID = Loc.getExpansionLoc().getFileID(); + bool PrintedRange = false; + + for (ArrayRef<CharSourceRange>::const_iterator RI = Ranges.begin(), + RE = Ranges.end(); + RI != RE; ++RI) { + // Ignore invalid ranges. + if (!RI->isValid()) continue; + + auto &SM = Loc.getManager(); + SourceLocation B = SM.getExpansionLoc(RI->getBegin()); + CharSourceRange ERange = SM.getExpansionRange(RI->getEnd()); + SourceLocation E = ERange.getEnd(); + bool IsTokenRange = ERange.isTokenRange(); + + 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 (IsTokenRange) + TokSize = Lexer::MeasureTokenLength(E, SM, LangOpts); + + FullSourceLoc BF(B, SM), EF(E, SM); + OS << '{' + << BF.getLineNumber() << ':' << BF.getColumnNumber() << '-' + << EF.getLineNumber() << ':' << (EF.getColumnNumber() + TokSize) + << '}'; + PrintedRange = true; + } + + if (PrintedRange) + OS << ':'; + } + OS << ' '; +} + +void TextDiagnostic::emitIncludeLocation(FullSourceLoc Loc, PresumedLoc PLoc) { + if (DiagOpts->ShowLocation && PLoc.isValid()) + OS << "In file included from " << PLoc.getFilename() << ':' + << PLoc.getLine() << ":\n"; + else + OS << "In included file:\n"; +} + +void TextDiagnostic::emitImportLocation(FullSourceLoc Loc, PresumedLoc PLoc, + StringRef ModuleName) { + if (DiagOpts->ShowLocation && PLoc.isValid()) + OS << "In module '" << ModuleName << "' imported from " + << PLoc.getFilename() << ':' << PLoc.getLine() << ":\n"; + else + OS << "In module '" << ModuleName << "':\n"; +} + +void TextDiagnostic::emitBuildingModuleLocation(FullSourceLoc Loc, + PresumedLoc PLoc, + StringRef ModuleName) { + if (DiagOpts->ShowLocation && PLoc.isValid()) + OS << "While building module '" << ModuleName << "' imported from " + << PLoc.getFilename() << ':' << PLoc.getLine() << ":\n"; + else + OS << "While building module '" << ModuleName << "':\n"; +} + +/// Find the suitable set of lines to show to include a set of ranges. +static llvm::Optional<std::pair<unsigned, unsigned>> +findLinesForRange(const CharSourceRange &R, FileID FID, + const SourceManager &SM) { + if (!R.isValid()) return None; + + SourceLocation Begin = R.getBegin(); + SourceLocation End = R.getEnd(); + if (SM.getFileID(Begin) != FID || SM.getFileID(End) != FID) + return None; + + return std::make_pair(SM.getExpansionLineNumber(Begin), + SM.getExpansionLineNumber(End)); +} + +/// Add as much of range B into range A as possible without exceeding a maximum +/// size of MaxRange. Ranges are inclusive. +static std::pair<unsigned, unsigned> +maybeAddRange(std::pair<unsigned, unsigned> A, std::pair<unsigned, unsigned> B, + unsigned MaxRange) { + // If A is already the maximum size, we're done. + unsigned Slack = MaxRange - (A.second - A.first + 1); + if (Slack == 0) + return A; + + // Easy case: merge succeeds within MaxRange. + unsigned Min = std::min(A.first, B.first); + unsigned Max = std::max(A.second, B.second); + if (Max - Min + 1 <= MaxRange) + return {Min, Max}; + + // If we can't reach B from A within MaxRange, there's nothing to do. + // Don't add lines to the range that contain nothing interesting. + if ((B.first > A.first && B.first - A.first + 1 > MaxRange) || + (B.second < A.second && A.second - B.second + 1 > MaxRange)) + return A; + + // Otherwise, expand A towards B to produce a range of size MaxRange. We + // attempt to expand by the same amount in both directions if B strictly + // contains A. + + // Expand downwards by up to half the available amount, then upwards as + // much as possible, then downwards as much as possible. + A.second = std::min(A.second + (Slack + 1) / 2, Max); + Slack = MaxRange - (A.second - A.first + 1); + A.first = std::max(Min + Slack, A.first) - Slack; + A.second = std::min(A.first + MaxRange - 1, Max); + return A; +} + +/// 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. That most likely means we have + // a multi-line highlighting range that covers a blank line. + if (StartColNo > EndColNo) { + assert(StartLineNo != EndLineNo && "trying to highlight whitespace"); + StartColNo = EndColNo; + } + } + + 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(FileID FID, + 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 (FID == HintLocInfo.first && + 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); + } + } + } + + expandTabs(FixItInsertionLine, DiagOpts->TabStop); + + return FixItInsertionLine; +} + +/// 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( + FullSourceLoc Loc, DiagnosticsEngine::Level Level, + SmallVectorImpl<CharSourceRange> &Ranges, ArrayRef<FixItHint> Hints) { + assert(Loc.isValid() && "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 = Loc.getDecomposedLoc(); + FileID FID = LocInfo.first; + const SourceManager &SM = Loc.getManager(); + + // Get information about the buffer it points into. + bool Invalid = false; + StringRef BufData = Loc.getBufferData(&Invalid); + if (Invalid) + return; + + unsigned CaretLineNo = Loc.getLineNumber(); + unsigned CaretColNo = Loc.getColumnNumber(); + + // Arbitrarily stop showing snippets when the line is too long. + static const size_t MaxLineLengthToPrint = 4096; + if (CaretColNo > MaxLineLengthToPrint) + return; + + // Find the set of lines to include. + const unsigned MaxLines = DiagOpts->SnippetLineLimit; + std::pair<unsigned, unsigned> Lines = {CaretLineNo, CaretLineNo}; + for (SmallVectorImpl<CharSourceRange>::iterator I = Ranges.begin(), + E = Ranges.end(); + I != E; ++I) + if (auto OptionalRange = findLinesForRange(*I, FID, SM)) + Lines = maybeAddRange(Lines, *OptionalRange, MaxLines); + + for (unsigned LineNo = Lines.first; LineNo != Lines.second + 1; ++LineNo) { + const char *BufStart = BufData.data(); + const char *BufEnd = BufStart + BufData.size(); + + // Rewind from the current position to the start of the line. + const char *LineStart = + BufStart + + SM.getDecomposedLoc(SM.translateLineCol(FID, LineNo, 1)).second; + if (LineStart == BufEnd) + break; + + // Compute the line end. + const char *LineEnd = LineStart; + while (*LineEnd != '\n' && *LineEnd != '\r' && LineEnd != BufEnd) + ++LineEnd; + + // Arbitrarily stop showing snippets when the line is too long. + // FIXME: Don't print any lines in this case. + if (size_t(LineEnd - LineStart) > MaxLineLengthToPrint) + return; + + // Trim trailing null-bytes. + StringRef Line(LineStart, LineEnd - LineStart); + while (!Line.empty() && Line.back() == '\0' && + (LineNo != CaretLineNo || Line.size() > CaretColNo)) + Line = Line.drop_back(); + + // Copy the line of code into an std::string for ease of manipulation. + std::string SourceLine(Line.begin(), Line.end()); + + // Build the byte to column map. + const SourceColumnMap sourceColMap(SourceLine, DiagOpts->TabStop); + + // Create a line for the caret that is filled with spaces that is the same + // number of columns as the line of source code. + std::string CaretLine(sourceColMap.columns(), ' '); + + // 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. + if (CaretLineNo == LineNo) { + CaretColNo = sourceColMap.byteToContainingColumn(CaretColNo - 1); + if (CaretLine.size() < CaretColNo + 1) + CaretLine.resize(CaretColNo + 1, ' '); + CaretLine[CaretColNo] = '^'; + } + + std::string FixItInsertionLine = buildFixItInsertionLine( + FID, LineNo, sourceColMap, Hints, SM, DiagOpts.get()); + + // 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.empty() && CaretLine[CaretLine.size() - 1] == ' ') + CaretLine.erase(CaretLine.end() - 1); + + // Emit what we have computed. + emitSnippet(SourceLine); + + if (!CaretLine.empty()) { + 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 000000000000..44bb2bc29bc0 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/TextDiagnosticBuffer.cpp @@ -0,0 +1,75 @@ +//===- 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 "clang/Basic/Diagnostic.h" +#include "clang/Basic/LLVM.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: + All.emplace_back(Level, Notes.size()); + Notes.emplace_back(Info.getLocation(), Buf.str()); + break; + case DiagnosticsEngine::Warning: + All.emplace_back(Level, Warnings.size()); + Warnings.emplace_back(Info.getLocation(), Buf.str()); + break; + case DiagnosticsEngine::Remark: + All.emplace_back(Level, Remarks.size()); + Remarks.emplace_back(Info.getLocation(), Buf.str()); + break; + case DiagnosticsEngine::Error: + case DiagnosticsEngine::Fatal: + All.emplace_back(Level, Errors.size()); + Errors.emplace_back(Info.getLocation(), Buf.str()); + break; + } +} + +void TextDiagnosticBuffer::FlushDiagnostics(DiagnosticsEngine &Diags) const { + for (const auto &I : All) { + auto Diag = Diags.Report(Diags.getCustomDiagID(I.first, "%0")); + switch (I.first) { + default: llvm_unreachable( + "Diagnostic not handled during diagnostic flushing!"); + case DiagnosticsEngine::Note: + Diag << Notes[I.second].second; + break; + case DiagnosticsEngine::Warning: + Diag << Warnings[I.second].second; + break; + case DiagnosticsEngine::Remark: + Diag << Remarks[I.second].second; + break; + case DiagnosticsEngine::Error: + case DiagnosticsEngine::Fatal: + Diag << Errors[I.second].second; + break; + } + } +} 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 000000000000..a37382c116ae --- /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/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/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(); +} + +/// 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 ? "," : " [") + << (Level == DiagnosticsEngine::Remark ? "-R" : "-W") << Opt; + StringRef OptValue = Info.getDiags()->getFlagValue(); + if (!OptValue.empty()) + OS << "=" << OptValue; + 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( + FullSourceLoc(Info.getLocation(), Info.getSourceManager()), Level, + DiagMessageStream.str(), Info.getRanges(), Info.getFixItHints()); + + 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 000000000000..21933f474ff5 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/Frontend/VerifyDiagnosticConsumer.cpp @@ -0,0 +1,1004 @@ +//===- 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/Diagnostic.h" +#include "clang/Basic/DiagnosticOptions.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/TokenKinds.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/TextDiagnosticBuffer.h" +#include "clang/Lex/HeaderSearch.h" +#include "clang/Lex/Lexer.h" +#include "clang/Lex/PPCallbacks.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Lex/Token.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Regex.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <cassert> +#include <cstddef> +#include <cstring> +#include <iterator> +#include <memory> +#include <string> +#include <utility> +#include <vector> + +using namespace clang; + +using Directive = VerifyDiagnosticConsumer::Directive; +using DirectiveList = VerifyDiagnosticConsumer::DirectiveList; +using ExpectedData = VerifyDiagnosticConsumer::ExpectedData; + +VerifyDiagnosticConsumer::VerifyDiagnosticConsumer(DiagnosticsEngine &Diags_) + : Diags(Diags_), PrimaryClient(Diags.getClient()), + PrimaryClientOwner(Diags.takeClient()), + Buffer(new TextDiagnosticBuffer()), Status(HasNoDirectives) { + if (Diags.hasSourceManager()) + setSourceManager(Diags.getSourceManager()); +} + +VerifyDiagnosticConsumer::~VerifyDiagnosticConsumer() { + assert(!ActiveSourceFiles && "Incomplete parsing of source files!"); + assert(!CurrentPreprocessor && "CurrentPreprocessor should be invalid!"); + SrcManager = nullptr; + CheckDiagnostics(); + assert(!Diags.ownsClient() && + "The VerifyDiagnosticConsumer takes over ownership of the client!"); +} + +#ifndef NDEBUG + +namespace { + +class VerifyFileTracker : public PPCallbacks { + VerifyDiagnosticConsumer &Verify; + SourceManager &SM; + +public: + VerifyFileTracker(VerifyDiagnosticConsumer &Verify, SourceManager &SM) + : Verify(Verify), SM(SM) {} + + /// Hook into the preprocessor and update the list of parsed + /// files when the preprocessor indicates a new file is entered. + void FileChanged(SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, + FileID PrevFID) override { + Verify.UpdateParsedFileStatus(SM, SM.getFileID(Loc), + VerifyDiagnosticConsumer::IsParsed); + } +}; + +} // 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. + const_cast<Preprocessor *>(PP)->addPPCallbacks( + llvm::make_unique<VerifyFileTracker>(*this, *SrcManager)); +#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 = nullptr; + LangOpts = nullptr; + } +} + +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. +//===----------------------------------------------------------------------===// + +using DiagList = TextDiagnosticBuffer::DiagList; +using const_diag_iterator = TextDiagnosticBuffer::const_iterator; + +namespace { + +/// StandardDirective - Directive with string matching. +class StandardDirective : public Directive { +public: + StandardDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc, + bool MatchAnyLine, StringRef Text, unsigned Min, + unsigned Max) + : Directive(DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max) {} + + bool isValid(std::string &Error) override { + // all strings are considered valid; even empty ones + return true; + } + + bool match(StringRef S) override { + return S.find(Text) != StringRef::npos; + } +}; + +/// RegexDirective - Directive with regular-expression matching. +class RegexDirective : public Directive { +public: + RegexDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc, + bool MatchAnyLine, StringRef Text, unsigned Min, unsigned Max, + StringRef RegexStr) + : Directive(DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max), + Regex(RegexStr) {} + + bool isValid(std::string &Error) override { + return Regex.isValid(Error); + } + + bool match(StringRef S) override { + return Regex.match(S); + } + +private: + llvm::Regex Regex; +}; + +class ParseHelper +{ +public: + ParseHelper(StringRef S) + : Begin(S.begin()), End(S.end()), C(Begin), P(Begin) {} + + // 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()) == 0; + } + + // 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 S is matched in content. + // When true, P marks begin-position of the match, and calling Advance sets C + // to end-position of the match. + // If S is the empty string, then search for any letter instead (makes sense + // with FinishDirectiveToken=true). + // If EnsureStartOfWord, then skip matches that don't start a new word. + // If FinishDirectiveToken, then assume the match is the start of a comment + // directive for -verify, and extend the match to include the entire first + // token of that directive. + bool Search(StringRef S, bool EnsureStartOfWord = false, + bool FinishDirectiveToken = false) { + do { + if (!S.empty()) { + P = std::search(C, End, S.begin(), S.end()); + PEnd = P + S.size(); + } + else { + P = C; + while (P != End && !isLetter(*P)) + ++P; + PEnd = P + 1; + } + if (P == End) + break; + // If not start of word but required, skip and search again. + if (EnsureStartOfWord + // Check if string literal starts a new word. + && !(P == Begin || isWhitespace(P[-1]) + // Or it could be preceded by the start of a comment. + || (P > (Begin + 1) && (P[-1] == '/' || P[-1] == '*') + && P[-2] == '/'))) + continue; + if (FinishDirectiveToken) { + while (PEnd != End && (isAlphanumeric(*PEnd) + || *PEnd == '-' || *PEnd == '_')) + ++PEnd; + // Put back trailing digits and hyphens to be parsed later as a count + // or count range. Because -verify prefixes must start with letters, + // we know the actual directive we found starts with a letter, so + // we won't put back the entire directive word and thus record an empty + // string. + assert(isLetter(*P) && "-verify prefix must start with a letter"); + while (isDigit(PEnd[-1]) || PEnd[-1] == '-') + --PEnd; + } + return true; + } while (Advance()); + return false; + } + + // Return true if a CloseBrace that closes the OpenBrace at the current nest + // level is found. When true, P marks begin-position of CloseBrace. + bool SearchClosingBrace(StringRef OpenBrace, StringRef CloseBrace) { + unsigned Depth = 1; + P = C; + while (P < End) { + StringRef S(P, End - P); + if (S.startswith(OpenBrace)) { + ++Depth; + P += OpenBrace.size(); + } else if (S.startswith(CloseBrace)) { + --Depth; + if (Depth == 0) { + PEnd = P + CloseBrace.size(); + return true; + } + P += CloseBrace.size(); + } else { + ++P; + } + } + 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); + } + + // Beginning of expected content. + const char * const Begin; + + // End of expected content (1-past). + const char * const End; + + // Position of next char in content. + const char *C; + + const char *P; + +private: + // Previous next/search subject end (1-past). + const char *PEnd = nullptr; +}; + +} // 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 the initial directive token. + // If one prefix, save time by searching only for its directives. + // Otherwise, search for any potential directive token and check it later. + const auto &Prefixes = Diags.getDiagnosticOptions().VerifyPrefixes; + if (!(Prefixes.size() == 1 ? PH.Search(*Prefixes.begin(), true, true) + : PH.Search("", true, true))) + break; + PH.Advance(); + + // Default directive kind. + bool RegexKind = false; + const char* KindStr = "string"; + + // Parse the initial directive token in reverse so we can easily determine + // its exact actual prefix. If we were to parse it from the front instead, + // it would be harder to determine where the prefix ends because there + // might be multiple matching -verify prefixes because some might prefix + // others. + StringRef DToken(PH.P, PH.C - PH.P); + + // Regex in initial directive token: -re + if (DToken.endswith("-re")) { + RegexKind = true; + KindStr = "regex"; + DToken = DToken.substr(0, DToken.size()-3); + } + + // Type in initial directive token: -{error|warning|note|no-diagnostics} + DirectiveList *DL = nullptr; + bool NoDiag = false; + StringRef DType; + if (DToken.endswith(DType="-error")) + DL = ED ? &ED->Errors : nullptr; + else if (DToken.endswith(DType="-warning")) + DL = ED ? &ED->Warnings : nullptr; + else if (DToken.endswith(DType="-remark")) + DL = ED ? &ED->Remarks : nullptr; + else if (DToken.endswith(DType="-note")) + DL = ED ? &ED->Notes : nullptr; + else if (DToken.endswith(DType="-no-diagnostics")) { + NoDiag = true; + if (RegexKind) + continue; + } + else + continue; + DToken = DToken.substr(0, DToken.size()-DType.size()); + + // What's left in DToken is the actual prefix. That might not be a -verify + // prefix even if there is only one -verify prefix (for example, the full + // DToken is foo-bar-warning, but foo is the only -verify prefix). + if (!std::binary_search(Prefixes.begin(), Prefixes.end(), DToken)) + continue; + + if (NoDiag) { + if (Status == VerifyDiagnosticConsumer::HasOtherExpectedDirectives) + Diags.Report(Pos, diag::err_verify_invalid_no_diags) + << /*IsExpectedNoDiagnostics=*/true; + else + Status = VerifyDiagnosticConsumer::HasExpectedNoDiagnostics; + continue; + } + 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; + + // Next optional token: @ + SourceLocation ExpectedLoc; + bool MatchAnyLine = false; + 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, nullptr, nullptr, CurDir, + nullptr, nullptr, nullptr, nullptr); + 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); + else if (PH.Next("*")) { + MatchAnyLine = true; + ExpectedLoc = SM.translateFileLineCol(FE, 1, 1); + } + } else if (PH.Next("*")) { + MatchAnyLine = true; + ExpectedLoc = SourceLocation(); + } + + if (ExpectedLoc.isInvalid() && !MatchAnyLine) { + 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.SearchClosingBrace("{{", "}}")) { + 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); + + // Check that regex directives contain at least one regex. + if (RegexKind && Text.find("{{") == StringRef::npos) { + Diags.Report(Pos.getLocWithOffset(ContentBegin-PH.Begin), + diag::err_verify_missing_regex) << Text; + return false; + } + + // Construct new directive. + std::unique_ptr<Directive> D = Directive::create( + RegexKind, Pos, ExpectedLoc, MatchAnyLine, Text, Min, Max); + + std::string Error; + if (D->isValid(Error)) { + DL->push_back(std::move(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 +/// 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, nullptr, SM, nullptr, Tok.getLocation(), + Status)) + return true; + } + return false; +} +#endif // !NDEBUG + +/// 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); +} + +/// 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, + std::vector<Directive *> &DL, const char *Kind) { + if (DL.empty()) + return 0; + + SmallString<256> Fmt; + llvm::raw_svector_ostream OS(Fmt); + for (const auto *D : DL) { + if (D->DiagnosticLoc.isInvalid()) + OS << "\n File *"; + else + OS << "\n File " << SourceMgr.getFilename(D->DiagnosticLoc); + if (D->MatchAnyLine) + OS << " Line *"; + else + OS << " 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(); +} + +/// 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, + bool IgnoreUnexpected) { + std::vector<Directive *> LeftOnly; + DiagList Right(d2_begin, d2_end); + + for (auto &Owner : Left) { + Directive &D = *Owner; + 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) { + if (!D.MatchAnyLine) { + unsigned LineNo2 = SourceMgr.getPresumedLineNumber(II->first); + if (LineNo1 != LineNo2) + continue; + } + + if (!D.DiagnosticLoc.isInvalid() && + !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(&D); + } 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); + if (!IgnoreUnexpected) + 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; + + const DiagnosticLevelMask DiagMask = + Diags.getDiagnosticOptions().getVerifyIgnoreUnexpected(); + + // See if there are error mismatches. + NumProblems += CheckLists(Diags, SourceMgr, "error", ED.Errors, + Buffer.err_begin(), Buffer.err_end(), + bool(DiagnosticLevelMask::Error & DiagMask)); + + // See if there are warning mismatches. + NumProblems += CheckLists(Diags, SourceMgr, "warning", ED.Warnings, + Buffer.warn_begin(), Buffer.warn_end(), + bool(DiagnosticLevelMask::Warning & DiagMask)); + + // See if there are remark mismatches. + NumProblems += CheckLists(Diags, SourceMgr, "remark", ED.Remarks, + Buffer.remark_begin(), Buffer.remark_end(), + bool(DiagnosticLevelMask::Remark & DiagMask)); + + // See if there are note mismatches. + NumProblems += CheckLists(Diags, SourceMgr, "note", ED.Notes, + Buffer.note_begin(), Buffer.note_end(), + bool(DiagnosticLevelMask::Note & DiagMask)); + + 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. + DiagnosticConsumer *CurClient = Diags.getClient(); + std::unique_ptr<DiagnosticConsumer> Owner = 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.empty()) { + // Generate a cache of parsed FileEntry pointers for alias lookups. + llvm::SmallPtrSet<const FileEntry *, 8> ParsedFileCache; + for (const auto &I : ParsedFiles) + if (const FileEntry *FE = I.second) + ParsedFileCache.insert(FE); + + // Iterate through list of unparsed files. + for (const auto &I : UnparsedFiles) { + 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 { + const DiagnosticLevelMask DiagMask = + ~Diags.getDiagnosticOptions().getVerifyIgnoreUnexpected(); + if (bool(DiagnosticLevelMask::Error & DiagMask)) + NumErrors += PrintUnexpected(Diags, nullptr, Buffer->err_begin(), + Buffer->err_end(), "error"); + if (bool(DiagnosticLevelMask::Warning & DiagMask)) + NumErrors += PrintUnexpected(Diags, nullptr, Buffer->warn_begin(), + Buffer->warn_end(), "warn"); + if (bool(DiagnosticLevelMask::Remark & DiagMask)) + NumErrors += PrintUnexpected(Diags, nullptr, Buffer->remark_begin(), + Buffer->remark_end(), "remark"); + if (bool(DiagnosticLevelMask::Note & DiagMask)) + NumErrors += PrintUnexpected(Diags, nullptr, Buffer->note_begin(), + Buffer->note_end(), "note"); + } + + Diags.setClient(CurClient, Owner.release() != nullptr); + + // Reset the buffer, we have processed all the diagnostics in it. + Buffer.reset(new TextDiagnosticBuffer()); + ED.Reset(); +} + +std::unique_ptr<Directive> Directive::create(bool RegexKind, + SourceLocation DirectiveLoc, + SourceLocation DiagnosticLoc, + bool MatchAnyLine, StringRef Text, + unsigned Min, unsigned Max) { + if (!RegexKind) + return llvm::make_unique<StandardDirective>(DirectiveLoc, DiagnosticLoc, + MatchAnyLine, Text, Min, Max); + + // Parse the directive into a regular expression. + std::string RegexStr; + StringRef S = Text; + while (!S.empty()) { + if (S.startswith("{{")) { + S = S.drop_front(2); + size_t RegexMatchLength = S.find("}}"); + assert(RegexMatchLength != StringRef::npos); + // Append the regex, enclosed in parentheses. + RegexStr += "("; + RegexStr.append(S.data(), RegexMatchLength); + RegexStr += ")"; + S = S.drop_front(RegexMatchLength + 2); + } else { + size_t VerbatimMatchLength = S.find("{{"); + if (VerbatimMatchLength == StringRef::npos) + VerbatimMatchLength = S.size(); + // Escape and append the fixed string. + RegexStr += llvm::Regex::escape(S.substr(0, VerbatimMatchLength)); + S = S.drop_front(VerbatimMatchLength); + } + } + + return llvm::make_unique<RegexDirective>( + DirectiveLoc, DiagnosticLoc, MatchAnyLine, Text, Min, Max, RegexStr); +} |