diff options
author | vitalyisaev <vitalyisaev@yandex-team.com> | 2023-06-29 10:00:50 +0300 |
---|---|---|
committer | vitalyisaev <vitalyisaev@yandex-team.com> | 2023-06-29 10:00:50 +0300 |
commit | 6ffe9e53658409f212834330e13564e4952558f6 (patch) | |
tree | 85b1e00183517648b228aafa7c8fb07f5276f419 /contrib/libs/clang14/lib/Frontend | |
parent | 726057070f9c5a91fc10fde0d5024913d10f1ab9 (diff) | |
download | ydb-6ffe9e53658409f212834330e13564e4952558f6.tar.gz |
YQ Connector: support managed ClickHouse
Со стороны dqrun можно обратиться к инстансу коннектора, который работает на streaming стенде, и извлечь данные из облачного CH.
Diffstat (limited to 'contrib/libs/clang14/lib/Frontend')
42 files changed, 37749 insertions, 0 deletions
diff --git a/contrib/libs/clang14/lib/Frontend/ASTConsumers.cpp b/contrib/libs/clang14/lib/Frontend/ASTConsumers.cpp new file mode 100644 index 0000000000..96f5926c0d --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/ASTConsumers.cpp @@ -0,0 +1,217 @@ +//===--- ASTConsumers.cpp - ASTConsumer implementations -------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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, + ASTDumpOutputFormat Format, StringRef FilterString, + bool DumpLookups = false, bool DumpDeclTypes = false) + : Out(Out ? *Out : llvm::outs()), OwnedOut(std::move(Out)), + OutputKind(K), OutputFormat(Format), FilterString(FilterString), + DumpLookups(DumpLookups), DumpDeclTypes(DumpDeclTypes) {} + + 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); + + if (OutputFormat == ADOF_Default) + 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, OutputFormat); + } + + if (DumpDeclTypes) { + Decl *InnerD = D; + if (auto *TD = dyn_cast<TemplateDecl>(D)) + InnerD = TD->getTemplatedDecl(); + + // FIXME: Support OutputFormat in type dumping. + // FIXME: Support combining -ast-dump-decl-types with -ast-dump-lookups. + if (auto *VD = dyn_cast<ValueDecl>(InnerD)) + VD->getType().dump(Out, VD->getASTContext()); + if (auto *TD = dyn_cast<TypeDecl>(InnerD)) + TD->getTypeForDecl()->dump(Out, TD->getASTContext()); + } + } + + raw_ostream &Out; + std::unique_ptr<raw_ostream> OwnedOut; + + /// How to output individual declarations. + Kind OutputKind; + + /// What format should the output take? + ASTDumpOutputFormat OutputFormat; + + /// 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; + + /// Whether to dump the type for each declaration dumped. + bool DumpDeclTypes; + }; + + 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 std::make_unique<ASTPrinter>(std::move(Out), ASTPrinter::Print, + ADOF_Default, FilterString); +} + +std::unique_ptr<ASTConsumer> +clang::CreateASTDumper(std::unique_ptr<raw_ostream> Out, StringRef FilterString, + bool DumpDecls, bool Deserialize, bool DumpLookups, + bool DumpDeclTypes, ASTDumpOutputFormat Format) { + assert((DumpDecls || Deserialize || DumpLookups) && "nothing to dump"); + return std::make_unique<ASTPrinter>( + std::move(Out), + Deserialize ? ASTPrinter::DumpFull + : DumpDecls ? ASTPrinter::Dump : ASTPrinter::None, + Format, FilterString, DumpLookups, DumpDeclTypes); +} + +std::unique_ptr<ASTConsumer> clang::CreateASTDeclNodeLister() { + return std::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 std::make_unique<ASTViewer>(); +} diff --git a/contrib/libs/clang14/lib/Frontend/ASTMerge.cpp b/contrib/libs/clang14/lib/Frontend/ASTMerge.cpp new file mode 100644 index 0000000000..14d781ccdf --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/ASTMerge.cpp @@ -0,0 +1,114 @@ +//===-- ASTMerge.cpp - AST Merging Frontend Action --------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#include "clang/Frontend/ASTUnit.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/ASTDiagnostic.h" +#include "clang/AST/ASTImporter.h" +#include "clang/AST/ASTImporterSharedState.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()); + auto SharedState = std::make_shared<ASTImporterSharedState>( + *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, SharedState); + + 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; + + llvm::Expected<Decl *> ToDOrError = Importer.Import(D); + + if (ToDOrError) { + DeclGroupRef DGR(*ToDOrError); + CI.getASTConsumer().HandleTopLevelDecl(DGR); + } else { + llvm::consumeError(ToDOrError.takeError()); + } + } + } + + 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/libs/clang14/lib/Frontend/ASTUnit.cpp b/contrib/libs/clang14/lib/Frontend/ASTUnit.cpp new file mode 100644 index 0000000000..5f587cc1c0 --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/ASTUnit.cpp @@ -0,0 +1,2692 @@ +//===- ASTUnit.cpp - ASTUnit utility --------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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/LangStandard.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/InMemoryModuleCache.h" +#include "clang/Serialization/ModuleFile.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/ScopeExit.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/Bitstream/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/FileUtilities.h" +#include "llvm/Support/MemoryBuffer.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 <mutex> +#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(InMemoryModuleCache &ModuleCache) + : Stream(Buffer), Writer(Stream, Buffer, ModuleCache, {}) {} +}; + +void ASTUnit::clearFileLevelDecls() { + FileDecls.clear(); +} + +/// 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_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(PP.getDiagnostics(), 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 FilterAndStoreDiagnosticConsumer : public DiagnosticConsumer { + SmallVectorImpl<StoredDiagnostic> *StoredDiags; + SmallVectorImpl<ASTUnit::StandaloneDiagnostic> *StandaloneDiags; + bool CaptureNonErrorsFromIncludes = true; + const LangOptions *LangOpts = nullptr; + SourceManager *SourceMgr = nullptr; + +public: + FilterAndStoreDiagnosticConsumer( + SmallVectorImpl<StoredDiagnostic> *StoredDiags, + SmallVectorImpl<ASTUnit::StandaloneDiagnostic> *StandaloneDiags, + bool CaptureNonErrorsFromIncludes) + : StoredDiags(StoredDiags), StandaloneDiags(StandaloneDiags), + CaptureNonErrorsFromIncludes(CaptureNonErrorsFromIncludes) { + 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 and filters diagnostics, if +/// there is no diagnostic client to capture them already. +class CaptureDroppedDiagnostics { + DiagnosticsEngine &Diags; + FilterAndStoreDiagnosticConsumer Client; + DiagnosticConsumer *PreviousClient = nullptr; + std::unique_ptr<DiagnosticConsumer> OwningPreviousClient; + +public: + CaptureDroppedDiagnostics( + CaptureDiagsKind CaptureDiagnostics, DiagnosticsEngine &Diags, + SmallVectorImpl<StoredDiagnostic> *StoredDiags, + SmallVectorImpl<ASTUnit::StandaloneDiagnostic> *StandaloneDiags) + : Diags(Diags), + Client(StoredDiags, StandaloneDiags, + CaptureDiagnostics != + CaptureDiagsKind::AllWithoutNonErrorsFromIncludes) { + if (CaptureDiagnostics != CaptureDiagsKind::None || + 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); + +static bool isInMainFile(const clang::Diagnostic &D) { + if (!D.hasSourceManager() || !D.getLocation().isValid()) + return false; + + auto &M = D.getSourceManager(); + return M.isWrittenInMainFile(M.getExpansionLoc(D.getLocation())); +} + +void FilterAndStoreDiagnosticConsumer::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) { + if (!CaptureNonErrorsFromIncludes && Level <= DiagnosticsEngine::Warning && + !isInMainFile(Info)) { + return; + } + + 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, + CaptureDiagsKind CaptureDiagnostics) { + assert(Diags.get() && "no DiagnosticsEngine was provided"); + if (CaptureDiagnostics != CaptureDiagsKind::None) + Diags->setClient(new FilterAndStoreDiagnosticConsumer( + &AST.StoredDiagnostics, nullptr, + CaptureDiagnostics != CaptureDiagsKind::AllWithoutNonErrorsFromIncludes)); +} + +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, CaptureDiagsKind CaptureDiagnostics, + bool AllowASTWithCompilerErrors, 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->ModuleCache = new InMemoryModuleCache; + AST->HSOpts = std::make_shared<HeaderSearchOptions>(); + AST->HSOpts->ModuleFormat = std::string(PCHContainerRdr.getFormat()); + AST->HeaderInfo.reset(new HeaderSearch(AST->HSOpts, + AST->getSourceManager(), + AST->getDiagnostics(), + AST->getLangOpts(), + /*Target=*/nullptr)); + AST->PPOpts = std::make_shared<PreprocessorOptions>(); + + // 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(), 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(), + AST->getTranslationUnitKind()); + + DisableValidationForModuleKind disableValid = + DisableValidationForModuleKind::None; + if (::getenv("LIBCLANG_DISABLE_PCH_VALIDATION")) + disableValid = DisableValidationForModuleKind::All; + AST->Reader = new ASTReader( + PP, *AST->ModuleCache, AST->Ctx.get(), PCHContainerRdr, {}, + /*isysroot=*/"", + /*DisableValidationKind=*/disableValid, AllowASTWithCompilerErrors); + + AST->Reader->setListener(std::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 = std::string(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( + std::make_unique<MacroDefinitionTrackerPPCallbacks>( + Unit.getCurrentTopLevelHashValue())); + return std::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 std::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. + llvm::erase_if(StoredDiags, isNonDriverDiag); +} + +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; + + if (VFS && FileMgr) + assert(VFS == &FileMgr->getVirtualFileSystem() && + "VFS passed to Parse and VFS in FileMgr are different"); + + auto CCInvocation = std::make_shared<CompilerInvocation>(*Invocation); + if (OverrideMainBuffer) { + assert(Preamble && + "No preamble was built, but OverrideMainBuffer is not null"); + Preamble->AddImplicitPreamble(*CCInvocation, VFS, OverrideMainBuffer.get()); + // VFS may have changed... + } + + // Create the compiler instance to use for building the AST. + std::unique_ptr<CompilerInstance> Clang( + new CompilerInstance(std::move(PCHContainerOps))); + + // Clean up on error, disengage it if the function returns successfully. + auto CleanOnError = llvm::make_scope_exit([&]() { + // 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; + }); + + // Ensure that Clang has a FileManager with the right VFS, which may have + // changed above in AddImplicitPreamble. If VFS is nullptr, rely on + // createFileManager to create one. + if (VFS && FileMgr && &FileMgr->getVirtualFileSystem() == VFS) + Clang->setFileManager(&*FileMgr); + else + FileMgr = Clang->createFileManager(std::move(VFS)); + + // Recover resources if we crash before exiting this method. + llvm::CrashRecoveryContextCleanupRegistrar<CompilerInstance> + CICleanup(Clang.get()); + + Clang->setInvocation(CCInvocation); + OriginalSourceFile = + std::string(Clang->getFrontendOpts().Inputs[0].getFile()); + + // Set up diagnostics, capturing any diagnostics that would + // otherwise be dropped. + Clang->setDiagnostics(&getDiagnostics()); + + // Create the target instance. + if (!Clang->createTarget()) + return true; + + 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() != + Language::LLVM_IR && + "IR inputs not support here!"); + + // Configure the various subsystems. + LangOpts = Clang->getInvocation().LangOpts; + FileSystemOpts = Clang->getFileSystemOpts(); + + ResetForParse(); + + SourceMgr = new SourceManager(getDiagnostics(), *FileMgr, + UserFilesAreVolatile); + if (!OverrideMainBuffer) { + checkAndRemoveNonDriverDiags(StoredDiagnostics); + TopLevelDeclsInPreamble.clear(); + } + + // 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])) + return true; + + if (SavedMainFileBuffer) + TranslateStoredDiagnostics(getFileManager(), getSourceManager(), + PreambleDiagnostics, StoredDiagnostics); + else + PreambleSrcLocCache.clear(); + + if (llvm::Error Err = Act->Execute()) { + consumeError(std::move(Err)); // FIXME this drops errors on the floor. + return true; + } + + transferASTDataFromCompilerInstance(*Clang); + + Act->EndSourceFile(); + + FailedParseDiagnostics.clear(); + + CleanOnError.release(); + + return false; +} + +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 = std::string(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 = std::string(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, MaxLines); + if (!Bounds.Size) + return nullptr; + + if (Preamble) { + if (Preamble->CanReuse(PreambleInvocationIn, *MainFileBuffer, Bounds, + *VFS)) { + // 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); + + PreambleRebuildCountdown = 1; + return MainFileBuffer; + } else { + Preamble.reset(); + PreambleDiagnostics.clear(); + TopLevelDeclsInPreamble.clear(); + PreambleSrcLocCache.clear(); + PreambleRebuildCountdown = 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 (PreambleRebuildCountdown > 1) { + --PreambleRebuildCountdown; + 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; + + ++PreambleCounter; + + SmallVector<StandaloneDiagnostic, 4> NewPreambleDiagsStandalone; + SmallVector<StoredDiagnostic, 4> NewPreambleDiags; + ASTUnitPreambleCallbacks Callbacks; + { + llvm::Optional<CaptureDroppedDiagnostics> Capture; + if (CaptureDiagnostics != CaptureDiagsKind::None) + Capture.emplace(CaptureDiagnostics, *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); + PreambleRebuildCountdown = 1; + } else { + switch (static_cast<BuildPreambleError>(NewPreamble.getError().value())) { + case BuildPreambleError::CouldntCreateTempFile: + // Try again next time. + PreambleRebuildCountdown = 1; + return nullptr; + case BuildPreambleError::CouldntCreateTargetInfo: + case BuildPreambleError::BeginSourceFileFailed: + case BuildPreambleError::CouldntEmitPCH: + case BuildPreambleError::BadInputs: + // These erros are more likely to repeat, retry after some period. + PreambleRebuildCountdown = 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.getASTReader(); + 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, + CaptureDiagsKind 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->ModuleCache = new InMemoryModuleCache; + + 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, CaptureDiagsKind CaptureDiagnostics, + unsigned PrecompilePreambleAfterNParses, bool CacheCodeCompletionResults, + 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 = std::string(ResourceFilesPath); + } + AST->OnlyLocalDecls = OnlyLocalDecls; + AST->CaptureDiagnostics = CaptureDiagnostics; + if (PrecompilePreambleAfterNParses > 0) + AST->PreambleRebuildCountdown = PrecompilePreambleAfterNParses; + AST->TUKind = Action ? Action->getTranslationUnitKind() : TU_Complete; + AST->ShouldCacheCodeCompletionResults = CacheCodeCompletionResults; + AST->IncludeBriefCommentsInCodeCompletion = false; + + // 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 = + std::string(Clang->getFrontendOpts().Inputs[0].getFile()); + + // Set up diagnostics, capturing any diagnostics that would + // otherwise be dropped. + Clang->setDiagnostics(&AST->getDiagnostics()); + + // Create the target instance. + if (!Clang->createTarget()) + return nullptr; + + 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() != + Language::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( + std::make_unique<MacroDefinitionTrackerPPCallbacks>( + AST->getCurrentTopLevelHashValue())); + std::vector<std::unique_ptr<ASTConsumer>> Consumers; + if (Clang->hasASTConsumer()) + Consumers.push_back(Clang->takeASTConsumer()); + Consumers.push_back(std::make_unique<TopLevelDeclTrackerConsumer>( + *AST, AST->getCurrentTopLevelHashValue())); + Clang->setASTConsumer( + std::make_unique<MultiplexConsumer>(std::move(Consumers))); + } + if (llvm::Error Err = Act->Execute()) { + consumeError(std::move(Err)); // FIXME this drops errors on the floor. + 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) { + PreambleRebuildCountdown = 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, CaptureDiagsKind 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, CaptureDiagsKind CaptureDiagnostics, + ArrayRef<RemappedFile> RemappedFiles, bool RemappedFilesKeepOriginalName, + unsigned PrecompilePreambleAfterNParses, TranslationUnitKind TUKind, + bool CacheCodeCompletionResults, bool IncludeBriefCommentsInCodeCompletion, + bool AllowPCHWithCompilerErrors, SkipFunctionBodiesScope SkipFunctionBodies, + bool SingleFileParse, bool UserFilesAreVolatile, bool ForSerialization, + bool RetainExcludedConditionalBlocks, + 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; + PPOpts.RetainExcludedConditionalBlocks = RetainExcludedConditionalBlocks; + + // Override the resources path. + CI->getHeaderSearchOpts().ResourceDir = std::string(ResourceFilesPath); + + CI->getFrontendOpts().SkipFunctionBodies = + SkipFunctionBodies == SkipFunctionBodiesScope::PreambleAndMainFile; + + if (ModuleFormat) + CI->getHeaderSearchOpts().ModuleFormat = + std::string(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->ModuleCache = new InMemoryModuleCache; + 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->ModuleCache)); + // 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 || PreambleRebuildCountdown > 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), 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, + bool Braced) override { + Next.ProcessOverloadCandidates(S, CurrentArg, Candidates, NumCandidates, + OpenParLoc, Braced); + } + + 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_Attribute: + 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 = std::string(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 = + std::string(Clang->getFrontendOpts().Inputs[0].getFile()); + + // Set up diagnostics, capturing any diagnostics produced. + Clang->setDiagnostics(&Diag); + CaptureDroppedDiagnostics Capture(CaptureDiagsKind::All, + Clang->getDiagnostics(), + &StoredDiagnostics, nullptr); + ProcessWarningOptions(Diag, Inv.getDiagnosticOpts()); + + // Create the target instance. + if (!Clang->createTarget()) { + Clang->setInvocation(nullptr); + return; + } + + 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() != + Language::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); + + auto getUniqueID = + [&FileMgr](StringRef Filename) -> Optional<llvm::sys::fs::UniqueID> { + if (auto Status = FileMgr.getVirtualFileSystem().status(Filename)) + return Status->getUniqueID(); + return None; + }; + + auto hasSameUniqueID = [getUniqueID](StringRef LHS, StringRef RHS) { + if (LHS == RHS) + return true; + if (auto LHSID = getUniqueID(LHS)) + if (auto RHSID = getUniqueID(RHS)) + return *LHSID == *RHSID; + return false; + }; + + // 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 && Line > 1 && hasSameUniqueID(File, OriginalSourceFile)) { + OverrideMainBuffer = getMainBufferWithPrecompiledPreamble( + PCHContainerOps, Inv, &FileMgr.getVirtualFileSystem(), 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"); + + IntrusiveRefCntPtr<llvm::vfs::FileSystem> 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])) { + if (llvm::Error Err = Act->Execute()) { + consumeError(std::move(Err)); // FIXME this drops errors on the floor. + } + 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 += "-%%%%%%%%"; + // FIXME: Can we somehow regenerate the stat cache here, or do we need to + // unconditionally create a stat cache when we parse the file? + + if (llvm::Error Err = llvm::writeFileAtomically( + TempPath, File, [this](llvm::raw_ostream &Out) { + return serialize(Out) ? llvm::make_error<llvm::StringError>( + "ASTUnit serialization failed", + llvm::inconvertibleErrorCode()) + : llvm::Error::success(); + })) { + consumeError(std::move(Err)); + 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); + InMemoryModuleCache ModuleCache; + ASTWriter Writer(Stream, Buffer, ModuleCache, {}); + 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; + auto 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; + + std::unique_ptr<LocDeclsTy> &Decls = FileDecls[FID]; + if (!Decls) + Decls = std::make_unique<LocDeclsTy>(); + + std::pair<unsigned, Decl *> LocDecl(Offset, D); + + if (Decls->empty() || Decls->back().first <= Offset) { + Decls->push_back(LocDecl); + return; + } + + LocDeclsTy::iterator I = + llvm::upper_bound(*Decls, 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 = + llvm::partition_point(LocDecls, [=](std::pair<unsigned, Decl *> LD) { + return LD.first < Offset; + }); + 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 = llvm::upper_bound( + LocDecls, 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(); + + Language Lang; + if (LangOpts.OpenCL) + Lang = Language::OpenCL; + else if (LangOpts.CUDA) + Lang = Language::CUDA; + else if (LangOpts.RenderScript) + Lang = Language::RenderScript; + else if (LangOpts.CPlusPlus) + Lang = LangOpts.ObjC ? Language::ObjCXX : Language::CXX; + else + Lang = LangOpts.ObjC ? Language::ObjC : Language::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 std::recursive_mutex; +} + +ASTUnit::ConcurrencyState::~ConcurrencyState() { + delete static_cast<std::recursive_mutex *>(Mutex); +} + +void ASTUnit::ConcurrencyState::start() { + bool acquired = static_cast<std::recursive_mutex *>(Mutex)->try_lock(); + assert(acquired && "Concurrent access to ASTUnit!"); +} + +void ASTUnit::ConcurrencyState::finish() { + static_cast<std::recursive_mutex *>(Mutex)->unlock(); +} + +#else // NDEBUG + +ASTUnit::ConcurrencyState::ConcurrencyState() { Mutex = nullptr; } +ASTUnit::ConcurrencyState::~ConcurrencyState() {} +void ASTUnit::ConcurrencyState::start() {} +void ASTUnit::ConcurrencyState::finish() {} + +#endif // NDEBUG diff --git a/contrib/libs/clang14/lib/Frontend/ChainedDiagnosticConsumer.cpp b/contrib/libs/clang14/lib/Frontend/ChainedDiagnosticConsumer.cpp new file mode 100644 index 0000000000..793c5ff8a2 --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/ChainedDiagnosticConsumer.cpp @@ -0,0 +1,13 @@ +//===- ChainedDiagnosticConsumer.cpp - Chain Diagnostic Clients -----------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/ChainedDiagnosticConsumer.h" + +using namespace clang; + +void ChainedDiagnosticConsumer::anchor() { } diff --git a/contrib/libs/clang14/lib/Frontend/ChainedIncludesSource.cpp b/contrib/libs/clang14/lib/Frontend/ChainedIncludesSource.cpp new file mode 100644 index 0000000000..380eba4562 --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/ChainedIncludesSource.cpp @@ -0,0 +1,219 @@ +//===- ChainedIncludesSource.cpp - Chained PCHs in Memory -------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines the ChainedIncludesSource class, which converts headers +// to chained PCHs in memory, mainly used for testing. +// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/Builtins.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/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.getModuleCache(), &CI.getASTContext(), CI.getPCHContainerReader(), + /*Extensions=*/{}, + /*isysroot=*/"", DisableValidationForModuleKind::PCH)); + 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().DisablePCHOrModuleValidation = + DisableValidationForModuleKind::PCH; + 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 = std::make_unique<PCHGenerator>( + Clang->getPreprocessor(), Clang->getModuleCache(), "-", /*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->setASTReader(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/libs/clang14/lib/Frontend/CompilerInstance.cpp b/contrib/libs/clang14/lib/Frontend/CompilerInstance.cpp new file mode 100644 index 0000000000..2465a7e245 --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/CompilerInstance.cpp @@ -0,0 +1,2285 @@ +//===--- CompilerInstance.cpp ---------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#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/LangStandard.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/FrontendPluginRegistry.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 "clang/Serialization/InMemoryModuleCache.h" +#include "llvm/ADT/ScopeExit.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/TimeProfiler.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/raw_ostream.h" +#include <time.h> +#include <utility> + +using namespace clang; + +CompilerInstance::CompilerInstance( + std::shared_ptr<PCHContainerOperations> PCHContainerOps, + InMemoryModuleCache *SharedModuleCache) + : ModuleLoader(/* BuildingModule = */ SharedModuleCache), + Invocation(new CompilerInvocation()), + ModuleCache(SharedModuleCache ? SharedModuleCache + : new InMemoryModuleCache), + ThePCHContainerOperations(std::move(PCHContainerOps)) {} + +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 || + (TheASTReader && TheASTReader->isGlobalIndexUnavailable() && + getFrontendOpts().GenerateGlobalModuleIndex)) && + !DisableGeneratingGlobalModuleIndex; +} + +void CompilerInstance::setDiagnostics(DiagnosticsEngine *Value) { + Diagnostics = Value; +} + +void CompilerInstance::setVerboseOutputStream(raw_ostream &Value) { + OwnedVerboseOutputStream.reset(); + VerboseOutputStream = &Value; +} + +void CompilerInstance::setVerboseOutputStream(std::unique_ptr<raw_ostream> Value) { + OwnedVerboseOutputStream.swap(Value); + VerboseOutputStream = OwnedVerboseOutputStream.get(); +} + +void CompilerInstance::setTarget(TargetInfo *Value) { Target = Value; } +void CompilerInstance::setAuxTarget(TargetInfo *Value) { AuxTarget = Value; } + +bool CompilerInstance::createTarget() { + // Create the target instance. + setTarget(TargetInfo::CreateTargetInfo(getDiagnostics(), + getInvocation().TargetOpts)); + if (!hasTarget()) + return false; + + // Check whether AuxTarget exists, if not, then create TargetInfo for the + // other side of CUDA/OpenMP/SYCL compilation. + if (!getAuxTarget() && + (getLangOpts().CUDA || getLangOpts().OpenMPIsDevice || + getLangOpts().SYCLIsDevice) && + !getFrontendOpts().AuxTriple.empty()) { + auto TO = std::make_shared<TargetOptions>(); + TO->Triple = llvm::Triple::normalize(getFrontendOpts().AuxTriple); + if (getFrontendOpts().AuxTargetCPU) + TO->CPU = getFrontendOpts().AuxTargetCPU.getValue(); + if (getFrontendOpts().AuxTargetFeatures) + TO->FeaturesAsWritten = getFrontendOpts().AuxTargetFeatures.getValue(); + TO->HostTriple = getTarget().getTriple().str(); + setAuxTarget(TargetInfo::CreateTargetInfo(getDiagnostics(), TO)); + } + + if (!getTarget().hasStrictFP() && !getLangOpts().ExpStrictFP) { + if (getLangOpts().getFPRoundingMode() != + llvm::RoundingMode::NearestTiesToEven) { + getDiagnostics().Report(diag::warn_fe_backend_unsupported_fp_rounding); + getLangOpts().setFPRoundingMode(llvm::RoundingMode::NearestTiesToEven); + } + if (getLangOpts().getFPExceptionMode() != LangOptions::FPE_Ignore) { + getDiagnostics().Report(diag::warn_fe_backend_unsupported_fp_exceptions); + getLangOpts().setFPExceptionMode(LangOptions::FPE_Ignore); + } + // FIXME: can we disable FEnvAccess? + } + + // We should do it here because target knows nothing about + // language options when it's being created. + if (getLangOpts().OpenCL && + !getTarget().validateOpenCLTarget(getLangOpts(), getDiagnostics())) + return false; + + // Inform the target of the language options. + // FIXME: We shouldn't need to do this, the target should be immutable once + // created. This complexity should be lifted elsewhere. + getTarget().adjust(getDiagnostics(), getLangOpts()); + + // Adjust target options based on codegen options. + getTarget().adjustTargetOptions(getCodeGenOpts(), getTargetOpts()); + + if (auto *Aux = getAuxTarget()) + getTarget().setAuxTarget(Aux); + + return true; +} + +llvm::vfs::FileSystem &CompilerInstance::getVirtualFileSystem() const { + return getFileManager().getVirtualFileSystem(); +} + +void CompilerInstance::setFileManager(FileManager *Value) { + FileMgr = Value; +} + +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::getASTReader() const { + return TheASTReader; +} +void CompilerInstance::setASTReader(IntrusiveRefCntPtr<ASTReader> Reader) { + assert(ModuleCache.get() == &Reader->getModuleManager().getModuleCache() && + "Expected ASTReader to use the same PCM cache"); + TheASTReader = 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(); + auto 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 = std::make_unique<llvm::raw_fd_ostream>( + DiagOpts->DiagnosticLogFile, EC, + llvm::sys::fs::OF_Append | llvm::sys::fs::OF_TextWithCRLF); + 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 = std::make_unique<LogDiagnosticPrinter>(*OS, DiagOpts, + std::move(StreamOwner)); + if (CodeGenOpts) + Logger->setDwarfDebugFlags(CodeGenOpts->DwarfDebugFlags); + if (Diags.ownsClient()) { + Diags.setClient( + new ChainedDiagnosticConsumer(Diags.takeClient(), std::move(Logger))); + } else { + Diags.setClient( + new ChainedDiagnosticConsumer(Diags.getClient(), 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( + IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) { + if (!VFS) + VFS = FileMgr ? &FileMgr->getVirtualFileSystem() + : createVFSFromCompilerInvocation(getInvocation(), + getDiagnostics()); + assert(VFS && "FileManager has no VFS?"); + FileMgr = new FileManager(getFileSystemOpts(), std::move(VFS)); + 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. If the caller owns the buffers, then pass a MemoryBufferRef; + // otherwise, pass as a std::unique_ptr<MemoryBuffer> to transfer ownership + // to the SourceManager. + if (InitOpts.RetainRemappedFileBuffers) + SourceMgr.overrideFileContents(FromFile, RB.second->getMemBufferRef()); + else + SourceMgr.overrideFileContents( + FromFile, std::unique_ptr<llvm::MemoryBuffer>( + const_cast<llvm::MemoryBuffer *>(RB.second))); + } + + // Remap files in the source manager (with other files). + for (const auto &RF : InitOpts.RemappedFiles) { + // Find the file that we're mapping to. + auto 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 AST reader holds a reference to the old preprocessor (if any). + TheASTReader.reset(); + + // Create the Preprocessor. + HeaderSearch *HeaderInfo = + new HeaderSearch(getHeaderSearchOptsPtr(), getSourceManager(), + getDiagnostics(), getLangOpts(), &getTarget()); + PP = std::make_shared<Preprocessor>(Invocation->getPreprocessorOptsPtr(), + getDiagnostics(), getLangOpts(), + getSourceManager(), *HeaderInfo, *this, + /*IdentifierInfoLookup=*/nullptr, + /*OwnsHeaderSearch=*/true, TUKind); + getTarget().adjust(getDiagnostics(), 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) { + std::string ModuleHash = getInvocation().getModuleHash(); + PP->getHeaderSearchInfo().setModuleHash(ModuleHash); + PP->getHeaderSearchInfo().setModuleCachePath( + getSpecificModuleCachePath(ModuleHash)); + } + + // Handle generating dependencies, if requested. + const DependencyOutputOptions &DepOpts = getDependencyOutputOpts(); + if (!DepOpts.OutputFile.empty()) + addDependencyCollector(std::make_shared<DependencyFileGenerator>(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(StringRef ModuleHash) { + // 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, ModuleHash); + return std::string(SpecificModuleCache.str()); +} + +// ASTContext + +void CompilerInstance::createASTContext() { + Preprocessor &PP = getPreprocessor(); + auto *Context = new ASTContext(getLangOpts(), PP.getSourceManager(), + PP.getIdentifierTable(), PP.getSelectorTable(), + PP.getBuiltinInfo(), PP.TUKind); + Context->InitBuiltinTypes(getTarget(), getAuxTarget()); + setASTContext(Context); +} + +// ExternalASTSource + +namespace { +// 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 { + Preprocessor &PP; + llvm::SmallVector<std::string, 8> LoadedModules; + + ReadModuleNames(Preprocessor &PP) : PP(PP) {} + + void ReadModuleName(StringRef ModuleName) override { + // Keep the module name as a string for now. It's not safe to create a new + // IdentifierInfo from an ASTReader callback. + LoadedModules.push_back(ModuleName.str()); + } + + void registerAll() { + ModuleMap &MM = PP.getHeaderSearchInfo().getModuleMap(); + for (const std::string &LoadedModule : LoadedModules) + MM.cacheModuleLoad(*PP.getIdentifierInfo(LoadedModule), + MM.findModule(LoadedModule)); + LoadedModules.clear(); + } + + void markAllUnavailable() { + for (const std::string &LoadedModule : LoadedModules) { + if (Module *M = PP.getHeaderSearchInfo().getModuleMap().findModule( + LoadedModule)) { + 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->IsUnimportable) continue; + Current->IsAvailable = true; + Stack.insert(Stack.end(), + Current->submodule_begin(), Current->submodule_end()); + } + } + } + LoadedModules.clear(); + } +}; +} // namespace + +void CompilerInstance::createPCHExternalASTSource( + StringRef Path, DisableValidationForModuleKind DisableValidation, + bool AllowPCHWithCompilerErrors, void *DeserializationListener, + bool OwnDeserializationListener) { + bool Preamble = getPreprocessorOpts().PrecompiledPreambleBytes.first != 0; + TheASTReader = createPCHExternalASTSource( + Path, getHeaderSearchOpts().Sysroot, DisableValidation, + AllowPCHWithCompilerErrors, getPreprocessor(), getModuleCache(), + getASTContext(), getPCHContainerReader(), + getFrontendOpts().ModuleFileExtensions, DependencyCollectors, + DeserializationListener, OwnDeserializationListener, Preamble, + getFrontendOpts().UseGlobalModuleIndex); +} + +IntrusiveRefCntPtr<ASTReader> CompilerInstance::createPCHExternalASTSource( + StringRef Path, StringRef Sysroot, + DisableValidationForModuleKind DisableValidation, + bool AllowPCHWithCompilerErrors, Preprocessor &PP, + InMemoryModuleCache &ModuleCache, ASTContext &Context, + const PCHContainerReader &PCHContainerRdr, + ArrayRef<std::shared_ptr<ModuleFileExtension>> Extensions, + 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, ModuleCache, &Context, PCHContainerRdr, Extensions, + Sysroot.empty() ? "" : Sysroot.data(), DisableValidation, + AllowPCHWithCompilerErrors, /*AllowConfigurationMismatch*/ false, + HSOpts.ModulesValidateSystemHeaders, HSOpts.ValidateASTInputFilesContent, + 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); + + for (auto &Listener : DependencyCollectors) + Listener->attachToASTReader(*Reader); + + auto Listener = std::make_unique<ReadModuleNames>(PP); + auto &ListenerRef = *Listener; + ASTReader::ListenerScope ReadModuleNamesListener(*Reader, + std::move(Listener)); + + 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()); + ListenerRef.registerAll(); + 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; + } + + ListenerRef.markAllUnavailable(); + 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. + auto 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; + } +} + +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::clearOutputFiles(bool EraseFiles) { + // Ignore errors that occur when trying to discard the temp file. + for (OutputFile &OF : OutputFiles) { + if (EraseFiles) { + if (OF.File) + consumeError(OF.File->discard()); + if (!OF.Filename.empty()) + llvm::sys::fs::remove(OF.Filename); + continue; + } + + if (!OF.File) + continue; + + if (OF.File->TmpName.empty()) { + consumeError(OF.File->discard()); + continue; + } + + // If '-working-directory' was passed, the output filename should be + // relative to that. + SmallString<128> NewOutFile(OF.Filename); + FileMgr->FixupRelativePath(NewOutFile); + + llvm::Error E = OF.File->keep(NewOutFile); + if (!E) + continue; + + getDiagnostics().Report(diag::err_unable_to_rename_temp) + << OF.File->TmpName << OF.Filename << std::move(E); + + llvm::sys::fs::remove(OF.File->TmpName); + } + OutputFiles.clear(); + if (DeleteBuiltModules) { + for (auto &Module : BuiltModules) + llvm::sys::fs::remove(Module.second); + BuiltModules.clear(); + } +} + +std::unique_ptr<raw_pwrite_stream> CompilerInstance::createDefaultOutputFile( + bool Binary, StringRef InFile, StringRef Extension, bool RemoveFileOnSignal, + bool CreateMissingDirectories, bool ForceUseTemporary) { + StringRef OutputPath = getFrontendOpts().OutputFile; + Optional<SmallString<128>> PathStorage; + if (OutputPath.empty()) { + if (InFile == "-" || Extension.empty()) { + OutputPath = "-"; + } else { + PathStorage.emplace(InFile); + llvm::sys::path::replace_extension(*PathStorage, Extension); + OutputPath = *PathStorage; + } + } + + return createOutputFile(OutputPath, Binary, RemoveFileOnSignal, + getFrontendOpts().UseTemporary || ForceUseTemporary, + CreateMissingDirectories); +} + +std::unique_ptr<raw_pwrite_stream> CompilerInstance::createNullOutputFile() { + return std::make_unique<llvm::raw_null_ostream>(); +} + +std::unique_ptr<raw_pwrite_stream> +CompilerInstance::createOutputFile(StringRef OutputPath, bool Binary, + bool RemoveFileOnSignal, bool UseTemporary, + bool CreateMissingDirectories) { + Expected<std::unique_ptr<raw_pwrite_stream>> OS = + createOutputFileImpl(OutputPath, Binary, RemoveFileOnSignal, UseTemporary, + CreateMissingDirectories); + if (OS) + return std::move(*OS); + getDiagnostics().Report(diag::err_fe_unable_to_open_output) + << OutputPath << errorToErrorCode(OS.takeError()).message(); + return nullptr; +} + +Expected<std::unique_ptr<llvm::raw_pwrite_stream>> +CompilerInstance::createOutputFileImpl(StringRef OutputPath, bool Binary, + bool RemoveFileOnSignal, + bool UseTemporary, + bool CreateMissingDirectories) { + assert((!CreateMissingDirectories || UseTemporary) && + "CreateMissingDirectories is only allowed when using temporary files"); + + std::unique_ptr<llvm::raw_fd_ostream> OS; + Optional<StringRef> OSFile; + + if (UseTemporary) { + if (OutputPath == "-") + UseTemporary = false; + else { + llvm::sys::fs::file_status Status; + llvm::sys::fs::status(OutputPath, Status); + if (llvm::sys::fs::exists(Status)) { + // Fail early if we can't write to the final destination. + if (!llvm::sys::fs::can_write(OutputPath)) + return llvm::errorCodeToError( + make_error_code(llvm::errc::operation_not_permitted)); + + // 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; + } + } + } + + Optional<llvm::sys::fs::TempFile> Temp; + 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(OutputPath); + SmallString<128> TempPath = + StringRef(OutputPath).drop_back(OutputExtension.size()); + TempPath += "-%%%%%%%%"; + TempPath += OutputExtension; + TempPath += ".tmp"; + Expected<llvm::sys::fs::TempFile> ExpectedFile = + llvm::sys::fs::TempFile::create( + TempPath, llvm::sys::fs::all_read | llvm::sys::fs::all_write, + Binary ? llvm::sys::fs::OF_None : llvm::sys::fs::OF_Text); + + llvm::Error E = handleErrors( + ExpectedFile.takeError(), [&](const llvm::ECError &E) -> llvm::Error { + std::error_code EC = E.convertToErrorCode(); + 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) { + ExpectedFile = llvm::sys::fs::TempFile::create(TempPath); + if (!ExpectedFile) + return llvm::errorCodeToError( + llvm::errc::no_such_file_or_directory); + } + } + return llvm::errorCodeToError(EC); + }); + + if (E) { + consumeError(std::move(E)); + } else { + Temp = std::move(ExpectedFile.get()); + OS.reset(new llvm::raw_fd_ostream(Temp->FD, /*shouldClose=*/false)); + OSFile = Temp->TmpName; + } + // 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 = OutputPath; + std::error_code EC; + OS.reset(new llvm::raw_fd_ostream( + *OSFile, EC, + (Binary ? llvm::sys::fs::OF_None : llvm::sys::fs::OF_TextWithCRLF))); + if (EC) + return llvm::errorCodeToError(EC); + } + + // Add the output file -- but don't try to remove "-", since this means we are + // using stdin. + OutputFiles.emplace_back(((OutputPath != "-") ? OutputPath : "").str(), + std::move(Temp)); + + if (!Binary || OS->supportsSeeking()) + return std::move(OS); + + return std::make_unique<llvm::buffer_unique_ostream>(std::move(OS)); +} + +// Initialization Utilities + +bool CompilerInstance::InitializeSourceManager(const FrontendInputFile &Input){ + return InitializeSourceManager(Input, getDiagnostics(), getFileManager(), + getSourceManager()); +} + +// static +bool CompilerInstance::InitializeSourceManager(const FrontendInputFile &Input, + DiagnosticsEngine &Diags, + FileManager &FileMgr, + SourceManager &SourceMgr) { + 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(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. + auto FileOrErr = InputFile == "-" + ? FileMgr.getSTDIN() + : FileMgr.getFileRef(InputFile, /*OpenFile=*/true); + if (!FileOrErr) { + // FIXME: include the error in the diagnostic even when it's not stdin. + auto EC = llvm::errorToErrorCode(FileOrErr.takeError()); + if (InputFile != "-") + Diags.Report(diag::err_fe_error_reading) << InputFile; + else + Diags.Report(diag::err_fe_error_reading_stdin) << EC.message(); + return false; + } + + SourceMgr.setMainFileID( + SourceMgr.createFileID(*FileOrErr, SourceLocation(), Kind)); + + 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'!"); + + // Mark this point as the bottom of the stack if we don't have somewhere + // better. We generally expect frontend actions to be invoked with (nearly) + // DesiredStackSpace available. + noteBottomOfStack(); + + auto FinishDiagnosticClient = llvm::make_scope_exit([&]() { + // Notify the diagnostic client that all files were processed. + getDiagnosticClient().finish(); + }); + + raw_ostream &OS = getVerboseOutputStream(); + + if (!Act.PrepareToExecute(*this)) + return false; + + if (!createTarget()) + return false; + + // 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 (getCodeGenOpts().TimePasses) + 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)) { + if (llvm::Error Err = Act.Execute()) { + consumeError(std::move(Err)); // FIXME this drops errors on the floor. + } + Act.EndSourceFile(); + } + } + + 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 = std::make_unique<llvm::raw_fd_ostream>( + StatsFile, EC, llvm::sys::fs::OF_TextWithCRLF); + if (EC) { + getDiagnostics().Report(diag::warn_fe_unable_to_open_stats_file) + << StatsFile << EC.message(); + } else { + llvm::PrintStatisticsJSON(*StatS); + } + } + + return !getDiagnostics().getClient()->getNumErrors(); +} + +void CompilerInstance::LoadRequestedPlugins() { + // Load any requested plugins. + for (const std::string &Path : getFrontendOpts().Plugins) { + std::string Error; + if (llvm::sys::DynamicLibrary::LoadLibraryPermanently(Path.c_str(), &Error)) + getDiagnostics().Report(diag::err_fe_unable_to_load_plugin) + << Path << Error; + } + + // Check if any of the loaded plugins replaces the main AST action + for (const FrontendPluginRegistry::entry &Plugin : + FrontendPluginRegistry::entries()) { + std::unique_ptr<PluginASTAction> P(Plugin.instantiate()); + if (P->getActionType() == PluginASTAction::ReplaceAction) { + getFrontendOpts().ProgramAction = clang::frontend::PluginAction; + getFrontendOpts().ActionName = Plugin.getName().str(); + break; + } + } +} + +/// Determine the appropriate source input kind based on language +/// options. +static Language getLanguageFromOptions(const LangOptions &LangOpts) { + if (LangOpts.OpenCL) + return Language::OpenCL; + if (LangOpts.CUDA) + return Language::CUDA; + if (LangOpts.ObjC) + return LangOpts.CPlusPlus ? Language::ObjCXX : Language::ObjC; + return LangOpts.CPlusPlus ? Language::CXX : Language::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 &) {}) { + llvm::TimeTraceScope TimeScope("Module Compile", ModuleName); + + // Never compile a module that's already finalized - this would cause the + // existing module to be freed, causing crashes if it is later referenced + if (ImportingInstance.getModuleCache().isPCMFinal(ModuleFileName)) { + ImportingInstance.getDiagnostics().Report( + ImportLoc, diag::err_module_rebuild_finalized) + << ModuleName; + return false; + } + + // 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(); + llvm::erase_if(PPOpts.Macros, + [&HSOpts](const std::pair<std::string, bool> &def) { + StringRef MacroDef = def.first; + return HSOpts.ModulesIgnoreMacros.contains( + llvm::CachedHashString(MacroDef.split('=').first)); + }); + + // 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 = std::string(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 = std::string(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 an in-memory module cache, + // CompilerInstance::CompilerInstance is responsible for finalizing the + // buffers to prevent use-after-frees. + CompilerInstance Instance(ImportingInstance.getPCHContainerOperations(), + &ImportingInstance.getModuleCache()); + auto &Inv = *Invocation; + Instance.setInvocation(std::move(Invocation)); + + Instance.createDiagnostics(new ForwardingDiagnosticConsumer( + ImportingInstance.getDiagnosticClient()), + /*ShouldOwnClient=*/true); + + // Note that this module is part of the module build stack, so that we + // can detect cycles in the module graph. + Instance.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 any remaining temporary files related to Instance, in case the + // module generation thread crashed. + Instance.clearOutputFiles(/*EraseFiles=*/true); + + // If \p AllowPCMWithCompilerErrors is set return 'success' even if errors + // occurred. + return !Instance.getDiagnostics().hasErrorOccurred() || + Instance.getFrontendOpts().AllowPCMWithCompilerErrors; +} + +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; + if (auto FE = FileMgr.getFile(PublicFilename)) + return *FE; + return nullptr; +} + +/// Compile a module file for the given module in a separate compiler instance, +/// using the options provided by the importing compiler instance. Returns true +/// if the module was built without errors. +static bool compileModule(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; +} + +/// Read the AST right after compiling the module. +static bool readASTAfterCompileModule(CompilerInstance &ImportingInstance, + SourceLocation ImportLoc, + SourceLocation ModuleNameLoc, + Module *Module, StringRef ModuleFileName, + bool *OutOfDate) { + DiagnosticsEngine &Diags = ImportingInstance.getDiagnostics(); + + unsigned ModuleLoadCapabilities = ASTReader::ARR_Missing; + if (OutOfDate) + ModuleLoadCapabilities |= ASTReader::ARR_OutOfDate; + + // Try to read the module file, now that we've compiled it. + ASTReader::ASTReadResult ReadResult = + ImportingInstance.getASTReader()->ReadAST( + ModuleFileName, serialization::MK_ImplicitModule, ImportLoc, + ModuleLoadCapabilities); + if (ReadResult == ASTReader::Success) + return true; + + // The caller wants to handle out-of-date failures. + if (OutOfDate && ReadResult == ASTReader::OutOfDate) { + *OutOfDate = true; + return false; + } + + // The ASTReader didn't diagnose the error, so conservatively report it. + if (ReadResult == ASTReader::Missing || !Diags.hasErrorOccurred()) + Diags.Report(ModuleNameLoc, diag::err_module_not_built) + << Module->Name << SourceRange(ImportLoc, ModuleNameLoc); + + return false; +} + +/// Compile a module in a separate compiler instance and read the AST, +/// returning true if the module compiles without errors. +static bool compileModuleAndReadASTImpl(CompilerInstance &ImportingInstance, + SourceLocation ImportLoc, + SourceLocation ModuleNameLoc, + Module *Module, + StringRef ModuleFileName) { + if (!compileModule(ImportingInstance, ModuleNameLoc, Module, + ModuleFileName)) { + ImportingInstance.getDiagnostics().Report(ModuleNameLoc, + diag::err_module_not_built) + << Module->Name << SourceRange(ImportLoc, ModuleNameLoc); + return false; + } + + return readASTAfterCompileModule(ImportingInstance, ImportLoc, ModuleNameLoc, + Module, ModuleFileName, + /*OutOfDate=*/nullptr); +} + +/// Compile a module in a separate compiler instance and read the AST, +/// returning true if the module compiles without errors, using a lock manager +/// to avoid building the same module in multiple compiler instances. +/// +/// Uses a lock file manager and exponential backoff to reduce the chances that +/// multiple instances will compete to create the same module. On timeout, +/// deletes the lock file in order to avoid deadlock from crashing processes or +/// bugs in the lock file manager. +static bool compileModuleAndReadASTBehindLock( + CompilerInstance &ImportingInstance, SourceLocation ImportLoc, + SourceLocation ModuleNameLoc, Module *Module, StringRef ModuleFileName) { + DiagnosticsEngine &Diags = ImportingInstance.getDiagnostics(); + + Diags.Report(ModuleNameLoc, diag::remark_module_lock) + << ModuleFileName << Module->Name; + + // 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 (true) { + llvm::LockFileManager Locked(ModuleFileName); + switch (Locked) { + case llvm::LockFileManager::LFS_Error: + // ModuleCache 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. + return compileModuleAndReadASTImpl(ImportingInstance, ImportLoc, + ModuleNameLoc, Module, ModuleFileName); + + case llvm::LockFileManager::LFS_Shared: + break; // The interesting case. + } + + // Someone else is responsible for building the module. Wait for them to + // finish. + switch (Locked.waitForUnlock()) { + case llvm::LockFileManager::Res_Success: + break; // The interesting case. + case llvm::LockFileManager::Res_OwnerDied: + continue; // try again to get the lock. + case llvm::LockFileManager::Res_Timeout: + // Since ModuleCache 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; + } + + // Read the module that was just written by someone else. + bool OutOfDate = false; + if (readASTAfterCompileModule(ImportingInstance, ImportLoc, ModuleNameLoc, + Module, ModuleFileName, &OutOfDate)) + return true; + if (!OutOfDate) + return false; + + // 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... + } +} + +/// Compile a module in a separate compiler instance and read the AST, +/// returning true if the module compiles without errors, potentially using a +/// lock manager to avoid building the same module in multiple compiler +/// instances. +static bool compileModuleAndReadAST(CompilerInstance &ImportingInstance, + SourceLocation ImportLoc, + SourceLocation ModuleNameLoc, + Module *Module, StringRef ModuleFileName) { + return ImportingInstance.getInvocation() + .getFrontendOpts() + .BuildingImplicitModuleUsesLock + ? compileModuleAndReadASTBehindLock(ImportingInstance, ImportLoc, + ModuleNameLoc, Module, + ModuleFileName) + : compileModuleAndReadASTImpl(ImportingInstance, ImportLoc, + ModuleNameLoc, Module, + ModuleFileName); +} + +/// 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::OF_None); +} + +/// Prune the module cache of modules that haven't been accessed in +/// a long time. +static void pruneModuleCache(const HeaderSearchOptions &HSOpts) { + llvm::sys::fs::file_status 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 (std::error_code EC = llvm::sys::fs::status(TimestampFile, StatBuf)) { + // If the timestamp file wasn't there, create one now. + if (EC == std::errc::no_such_file_or_directory) { + writeTimestampFile(TimestampFile); + } + return; + } + + // Check whether the time stamp is older than our pruning interval. + // If not, do nothing. + time_t TimeStampModTime = + llvm::sys::toTimeT(StatBuf.getLastModificationTime()); + 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 (llvm::sys::fs::status(File->path(), StatBuf)) + continue; + + // If the file has been used recently enough, leave it there. + time_t FileAccessTime = llvm::sys::toTimeT(StatBuf.getLastAccessedTime()); + 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::createASTReader() { + if (TheASTReader) + return; + + 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(); + const FrontendOptions &FEOpts = getFrontendOpts(); + std::unique_ptr<llvm::Timer> ReadTimer; + + if (FrontendTimerGroup) + ReadTimer = std::make_unique<llvm::Timer>("reading_modules", + "Reading modules", + *FrontendTimerGroup); + TheASTReader = new ASTReader( + getPreprocessor(), getModuleCache(), &getASTContext(), + getPCHContainerReader(), getFrontendOpts().ModuleFileExtensions, + Sysroot.empty() ? "" : Sysroot.c_str(), + PPOpts.DisablePCHOrModuleValidation, + /*AllowASTWithCompilerErrors=*/FEOpts.AllowPCMWithCompilerErrors, + /*AllowConfigurationMismatch=*/false, HSOpts.ModulesValidateSystemHeaders, + HSOpts.ValidateASTInputFilesContent, + getFrontendOpts().UseGlobalModuleIndex, std::move(ReadTimer)); + if (hasASTConsumer()) { + TheASTReader->setDeserializationListener( + getASTConsumer().GetASTDeserializationListener()); + getASTContext().setASTMutationListener( + getASTConsumer().GetASTMutationListener()); + } + getASTContext().setExternalSource(TheASTReader); + if (hasSema()) + TheASTReader->InitializeSema(getSema()); + if (hasASTConsumer()) + TheASTReader->StartTranslationUnit(&getASTConsumer()); + + for (auto &Listener : DependencyCollectors) + Listener->attachToASTReader(*TheASTReader); +} + +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); + + // If we don't already have an ASTReader, create one now. + if (!TheASTReader) + createASTReader(); + + // 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 = std::make_unique<ReadModuleNames>(*PP); + auto &ListenerRef = *Listener; + ASTReader::ListenerScope ReadModuleNamesListener(*TheASTReader, + std::move(Listener)); + + // Try to load the module file. + switch (TheASTReader->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; + } +} + +namespace { +enum ModuleSource { + MS_ModuleNotFound, + MS_ModuleCache, + MS_PrebuiltModulePath, + MS_ModuleBuildPragma +}; +} // end namespace + +/// Select a source for loading the named module and compute the filename to +/// load it from. +static ModuleSource selectModuleSource( + Module *M, StringRef ModuleName, std::string &ModuleFilename, + const std::map<std::string, std::string, std::less<>> &BuiltModules, + HeaderSearch &HS) { + assert(ModuleFilename.empty() && "Already has a module source?"); + + // 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; + return MS_ModuleBuildPragma; + } + + // Try to load the module from the prebuilt module path. + const HeaderSearchOptions &HSOpts = HS.getHeaderSearchOpts(); + if (!HSOpts.PrebuiltModuleFiles.empty() || + !HSOpts.PrebuiltModulePaths.empty()) { + ModuleFilename = HS.getPrebuiltModuleFileName(ModuleName); + if (HSOpts.EnablePrebuiltImplicitModules && ModuleFilename.empty()) + ModuleFilename = HS.getPrebuiltImplicitModuleFileName(M); + if (!ModuleFilename.empty()) + return MS_PrebuiltModulePath; + } + + // Try to load the module from the module cache. + if (M) { + ModuleFilename = HS.getCachedModuleFileName(M); + return MS_ModuleCache; + } + + return MS_ModuleNotFound; +} + +ModuleLoadResult CompilerInstance::findOrCompileModuleAndReadAST( + StringRef ModuleName, SourceLocation ImportLoc, + SourceLocation ModuleNameLoc, bool IsInclusionDirective) { + // Search for a module with the given name. + HeaderSearch &HS = PP->getHeaderSearchInfo(); + Module *M = + HS.lookupModule(ModuleName, ImportLoc, true, !IsInclusionDirective); + + // Select the source and filename for loading the named module. + std::string ModuleFilename; + ModuleSource Source = + selectModuleSource(M, ModuleName, ModuleFilename, BuiltModules, HS); + if (Source == MS_ModuleNotFound) { + // We can't find a module, error out here. + getDiagnostics().Report(ModuleNameLoc, diag::err_module_not_found) + << ModuleName << SourceRange(ImportLoc, ModuleNameLoc); + return nullptr; + } + if (ModuleFilename.empty()) { + if (M && M->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; + return nullptr; + } + + // Create an ASTReader on demand. + if (!getASTReader()) + createASTReader(); + + // Time how long it takes to load the module. + llvm::Timer Timer; + if (FrontendTimerGroup) + Timer.init("loading." + ModuleFilename, "Loading " + ModuleFilename, + *FrontendTimerGroup); + llvm::TimeRegion TimeLoading(FrontendTimerGroup ? &Timer : nullptr); + llvm::TimeTraceScope TimeScope("Module Load", ModuleName); + + // 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 == MS_ModuleCache + ? ASTReader::ARR_OutOfDate | ASTReader::ARR_Missing | + ASTReader::ARR_TreatModuleWithErrorsAsOutOfDate + : Source == MS_PrebuiltModulePath + ? 0 + : ASTReader::ARR_ConfigurationMismatch; + switch (getASTReader()->ReadAST(ModuleFilename, + Source == MS_PrebuiltModulePath + ? serialization::MK_PrebuiltModule + : Source == MS_ModuleBuildPragma + ? serialization::MK_ExplicitModule + : serialization::MK_ImplicitModule, + ImportLoc, ARRFlags)) { + case ASTReader::Success: { + if (M) + return M; + assert(Source != MS_ModuleCache && + "missing module, but file loaded from cache"); + + // A prebuilt module is indexed as a ModuleFile; the Module does not exist + // until the first call to ReadAST. Look it up now. + M = HS.lookupModule(ModuleName, ImportLoc, true, !IsInclusionDirective); + + // Check whether M refers to the file in the prebuilt module path. + if (M && M->getASTFile()) + if (auto ModuleFile = FileMgr->getFile(ModuleFilename)) + if (*ModuleFile == M->getASTFile()) + return M; + + getDiagnostics().Report(ModuleNameLoc, diag::err_module_prebuilt) + << ModuleName; + return ModuleLoadResult(); + } + + case ASTReader::OutOfDate: + case ASTReader::Missing: + // The most interesting case. + break; + + case ASTReader::ConfigurationMismatch: + if (Source == MS_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? + return ModuleLoadResult(); + + case ASTReader::Failure: + ModuleLoader::HadFatalFailure = true; + return ModuleLoadResult(); + } + + // ReadAST returned Missing or OutOfDate. + if (Source != MS_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. + return ModuleLoadResult(); + } + + // The module file is missing or out-of-date. Build it. + assert(M && "missing module, but trying to compile for cache"); + + // 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 nullptr; + } + + // 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); + return nullptr; + } + + // Try to compile and then read the AST. + if (!compileModuleAndReadAST(*this, ImportLoc, ModuleNameLoc, M, + ModuleFilename)) { + assert(getDiagnostics().hasErrorOccurred() && + "undiagnosed error in compileModuleAndReadAST"); + if (getPreprocessorOpts().FailedModules) + getPreprocessorOpts().FailedModules->addFailed(ModuleName); + return nullptr; + } + + // Okay, we've rebuilt and now loaded the module. + return M; +} + +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) + TheASTReader->makeModuleVisible(LastModuleImportResult, Visibility, + ImportLoc); + return LastModuleImportResult; + } + + // If we don't already have information on this module, load the module now. + Module *Module = nullptr; + ModuleMap &MM = getPreprocessor().getHeaderSearchInfo().getModuleMap(); + if (auto MaybeModule = MM.getCachedModuleLoad(*Path[0].first)) { + // Use the cached result, which may be nullptr. + Module = *MaybeModule; + } else if (ModuleName == getLangOpts().CurrentModule) { + // This is the module we're building. + Module = PP->getHeaderSearchInfo().lookupModule( + ModuleName, ImportLoc, /*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; + // DisableGeneratingGlobalModuleIndex = true; + // return ModuleLoadResult(); + //} + MM.cacheModuleLoad(*Path[0].first, Module); + } else { + ModuleLoadResult Result = findOrCompileModuleAndReadAST( + ModuleName, ImportLoc, ModuleNameLoc, IsInclusionDirective); + if (!Result.isNormal()) + return Result; + if (!Result) + DisableGeneratingGlobalModuleIndex = true; + Module = Result; + MM.cacheModuleLoad(*Path[0].first, Module); + } + + // If we never found the module, fail. Otherwise, verify the module and link + // it up. + if (!Module) + return ModuleLoadResult(); + + // Verify that the rest of the module path actually corresponds to + // a submodule. + bool MapPrivateSubModToTopLevel = false; + 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, ImportLoc, 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 (class Module *SubModule : Module->submodules()) { + unsigned ED = + Name.edit_distance(SubModule->Name, + /*AllowReplacements=*/true, BestEditDistance); + if (ED <= BestEditDistance) { + if (ED < BestEditDistance) { + Best.clear(); + BestEditDistance = ED; + } + + Best.push_back(SubModule->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(); + } + + TheASTReader->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::createModuleFromSource(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)); + + 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[std::string(ModuleName)] = std::string(ModuleFileName.str()); + llvm::sys::RemoveFileOnSignal(ModuleFileName); + } +} + +void CompilerInstance::makeModuleVisible(Module *Mod, + Module::NameVisibilityKind Visibility, + SourceLocation ImportLoc) { + if (!TheASTReader) + createASTReader(); + if (!TheASTReader) + return; + + TheASTReader->makeModuleVisible(Mod, Visibility, ImportLoc); +} + +GlobalModuleIndex *CompilerInstance::loadGlobalModuleIndex( + SourceLocation TriggerLoc) { + if (getPreprocessor().getHeaderSearchInfo().getModuleCachePath().empty()) + return nullptr; + if (!TheASTReader) + createASTReader(); + // Can't do anything if we don't have the module manager. + if (!TheASTReader) + return nullptr; + // Get an existing global index. This loads it if not already + // loaded. + TheASTReader->loadGlobalIndex(); + GlobalModuleIndex *GlobalIndex = TheASTReader->getGlobalIndex(); + // If the global index doesn't exist, create it. + if (!GlobalIndex && shouldBuildGlobalModuleIndex() && hasFileManager() && + hasPreprocessor()) { + llvm::sys::fs::create_directories( + getPreprocessor().getHeaderSearchInfo().getModuleCachePath()); + if (llvm::Error Err = GlobalModuleIndex::writeIndex( + getFileManager(), getPCHContainerReader(), + getPreprocessor().getHeaderSearchInfo().getModuleCachePath())) { + // FIXME this drops the error on the floor. This code is only used for + // typo correction and drops more than just this one source of errors + // (such as the directory creation failure above). It should handle the + // error. + consumeError(std::move(Err)); + return nullptr; + } + TheASTReader->resetForReload(); + TheASTReader->loadGlobalIndex(); + GlobalIndex = TheASTReader->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) { + if (llvm::Error Err = GlobalModuleIndex::writeIndex( + getFileManager(), getPCHContainerReader(), + getPreprocessor().getHeaderSearchInfo().getModuleCachePath())) { + // FIXME As above, this drops the error on the floor. + consumeError(std::move(Err)); + return nullptr; + } + TheASTReader->resetForReload(); + TheASTReader->loadGlobalIndex(); + GlobalIndex = TheASTReader->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/libs/clang14/lib/Frontend/CompilerInvocation.cpp b/contrib/libs/clang14/lib/Frontend/CompilerInvocation.cpp new file mode 100644 index 0000000000..e21132ed01 --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/CompilerInvocation.cpp @@ -0,0 +1,4716 @@ +//===- CompilerInvocation.cpp ---------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#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/DiagnosticDriver.h" +#include "clang/Basic/DiagnosticOptions.h" +#include "clang/Basic/FileSystemOptions.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/LangStandard.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/Driver.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/MigratorOptions.h" +#include "clang/Frontend/PreprocessorOutputOptions.h" +#include "clang/Frontend/TextDiagnosticBuffer.h" +#include "clang/Frontend/Utils.h" +#include "clang/Lex/HeaderSearchOptions.h" +#include "clang/Lex/PreprocessorOptions.h" +#include "clang/Sema/CodeCompleteOptions.h" +#include "clang/Serialization/ASTBitCodes.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/DenseSet.h" +#include "llvm/ADT/FloatingPointMode.h" +#include "llvm/ADT/Hashing.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/StringRef.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Triple.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Config/llvm-config.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/Remarks/HotnessThresholdParser.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/HashBuilder.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 <type_traits> +#include <utility> +#include <vector> + +using namespace clang; +using namespace driver; +using namespace options; +using namespace llvm::opt; + +//===----------------------------------------------------------------------===// +// Initialization. +//===----------------------------------------------------------------------===// + +CompilerInvocationRefBase::CompilerInvocationRefBase() + : LangOpts(new LangOptions()), TargetOpts(new TargetOptions()), + DiagnosticOpts(new DiagnosticOptions()), + HeaderSearchOpts(new HeaderSearchOptions()), + PreprocessorOpts(new PreprocessorOptions()), + AnalyzerOpts(new AnalyzerOptions()) {} + +CompilerInvocationRefBase::CompilerInvocationRefBase( + const CompilerInvocationRefBase &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())), + AnalyzerOpts(new AnalyzerOptions(*X.getAnalyzerOpts())) {} + +CompilerInvocationRefBase::CompilerInvocationRefBase( + CompilerInvocationRefBase &&X) = default; + +CompilerInvocationRefBase & +CompilerInvocationRefBase::operator=(CompilerInvocationRefBase X) { + LangOpts.swap(X.LangOpts); + TargetOpts.swap(X.TargetOpts); + DiagnosticOpts.swap(X.DiagnosticOpts); + HeaderSearchOpts.swap(X.HeaderSearchOpts); + PreprocessorOpts.swap(X.PreprocessorOpts); + AnalyzerOpts.swap(X.AnalyzerOpts); + return *this; +} + +CompilerInvocationRefBase & +CompilerInvocationRefBase::operator=(CompilerInvocationRefBase &&X) = default; + +CompilerInvocationRefBase::~CompilerInvocationRefBase() = default; + +//===----------------------------------------------------------------------===// +// Normalizers +//===----------------------------------------------------------------------===// + +#define SIMPLE_ENUM_VALUE_TABLE +#include "clang/Driver/Options.inc" +#undef SIMPLE_ENUM_VALUE_TABLE + +static llvm::Optional<bool> normalizeSimpleFlag(OptSpecifier Opt, + unsigned TableIndex, + const ArgList &Args, + DiagnosticsEngine &Diags) { + if (Args.hasArg(Opt)) + return true; + return None; +} + +static Optional<bool> normalizeSimpleNegativeFlag(OptSpecifier Opt, unsigned, + const ArgList &Args, + DiagnosticsEngine &) { + if (Args.hasArg(Opt)) + return false; + return None; +} + +/// The tblgen-erated code passes in a fifth parameter of an arbitrary type, but +/// denormalizeSimpleFlags never looks at it. Avoid bloating compile-time with +/// unnecessary template instantiations and just ignore it with a variadic +/// argument. +static void denormalizeSimpleFlag(SmallVectorImpl<const char *> &Args, + const char *Spelling, + CompilerInvocation::StringAllocator, + Option::OptionClass, unsigned, /*T*/...) { + Args.push_back(Spelling); +} + +template <typename T> static constexpr bool is_uint64_t_convertible() { + return !std::is_same<T, uint64_t>::value && + llvm::is_integral_or_enum<T>::value; +} + +template <typename T, + std::enable_if_t<!is_uint64_t_convertible<T>(), bool> = false> +static auto makeFlagToValueNormalizer(T Value) { + return [Value](OptSpecifier Opt, unsigned, const ArgList &Args, + DiagnosticsEngine &) -> Optional<T> { + if (Args.hasArg(Opt)) + return Value; + return None; + }; +} + +template <typename T, + std::enable_if_t<is_uint64_t_convertible<T>(), bool> = false> +static auto makeFlagToValueNormalizer(T Value) { + return makeFlagToValueNormalizer(uint64_t(Value)); +} + +static auto makeBooleanOptionNormalizer(bool Value, bool OtherValue, + OptSpecifier OtherOpt) { + return [Value, OtherValue, OtherOpt](OptSpecifier Opt, unsigned, + const ArgList &Args, + DiagnosticsEngine &) -> Optional<bool> { + if (const Arg *A = Args.getLastArg(Opt, OtherOpt)) { + return A->getOption().matches(Opt) ? Value : OtherValue; + } + return None; + }; +} + +static auto makeBooleanOptionDenormalizer(bool Value) { + return [Value](SmallVectorImpl<const char *> &Args, const char *Spelling, + CompilerInvocation::StringAllocator, Option::OptionClass, + unsigned, bool KeyPath) { + if (KeyPath == Value) + Args.push_back(Spelling); + }; +} + +static void denormalizeStringImpl(SmallVectorImpl<const char *> &Args, + const char *Spelling, + CompilerInvocation::StringAllocator SA, + Option::OptionClass OptClass, unsigned, + const Twine &Value) { + switch (OptClass) { + case Option::SeparateClass: + case Option::JoinedOrSeparateClass: + case Option::JoinedAndSeparateClass: + Args.push_back(Spelling); + Args.push_back(SA(Value)); + break; + case Option::JoinedClass: + case Option::CommaJoinedClass: + Args.push_back(SA(Twine(Spelling) + Value)); + break; + default: + llvm_unreachable("Cannot denormalize an option with option class " + "incompatible with string denormalization."); + } +} + +template <typename T> +static void +denormalizeString(SmallVectorImpl<const char *> &Args, const char *Spelling, + CompilerInvocation::StringAllocator SA, + Option::OptionClass OptClass, unsigned TableIndex, T Value) { + denormalizeStringImpl(Args, Spelling, SA, OptClass, TableIndex, Twine(Value)); +} + +static Optional<SimpleEnumValue> +findValueTableByName(const SimpleEnumValueTable &Table, StringRef Name) { + for (int I = 0, E = Table.Size; I != E; ++I) + if (Name == Table.Table[I].Name) + return Table.Table[I]; + + return None; +} + +static Optional<SimpleEnumValue> +findValueTableByValue(const SimpleEnumValueTable &Table, unsigned Value) { + for (int I = 0, E = Table.Size; I != E; ++I) + if (Value == Table.Table[I].Value) + return Table.Table[I]; + + return None; +} + +static llvm::Optional<unsigned> normalizeSimpleEnum(OptSpecifier Opt, + unsigned TableIndex, + const ArgList &Args, + DiagnosticsEngine &Diags) { + assert(TableIndex < SimpleEnumValueTablesSize); + const SimpleEnumValueTable &Table = SimpleEnumValueTables[TableIndex]; + + auto *Arg = Args.getLastArg(Opt); + if (!Arg) + return None; + + StringRef ArgValue = Arg->getValue(); + if (auto MaybeEnumVal = findValueTableByName(Table, ArgValue)) + return MaybeEnumVal->Value; + + Diags.Report(diag::err_drv_invalid_value) + << Arg->getAsString(Args) << ArgValue; + return None; +} + +static void denormalizeSimpleEnumImpl(SmallVectorImpl<const char *> &Args, + const char *Spelling, + CompilerInvocation::StringAllocator SA, + Option::OptionClass OptClass, + unsigned TableIndex, unsigned Value) { + assert(TableIndex < SimpleEnumValueTablesSize); + const SimpleEnumValueTable &Table = SimpleEnumValueTables[TableIndex]; + if (auto MaybeEnumVal = findValueTableByValue(Table, Value)) { + denormalizeString(Args, Spelling, SA, OptClass, TableIndex, + MaybeEnumVal->Name); + } else { + llvm_unreachable("The simple enum value was not correctly defined in " + "the tablegen option description"); + } +} + +template <typename T> +static void denormalizeSimpleEnum(SmallVectorImpl<const char *> &Args, + const char *Spelling, + CompilerInvocation::StringAllocator SA, + Option::OptionClass OptClass, + unsigned TableIndex, T Value) { + return denormalizeSimpleEnumImpl(Args, Spelling, SA, OptClass, TableIndex, + static_cast<unsigned>(Value)); +} + +static Optional<std::string> normalizeString(OptSpecifier Opt, int TableIndex, + const ArgList &Args, + DiagnosticsEngine &Diags) { + auto *Arg = Args.getLastArg(Opt); + if (!Arg) + return None; + return std::string(Arg->getValue()); +} + +template <typename IntTy> +static Optional<IntTy> normalizeStringIntegral(OptSpecifier Opt, int, + const ArgList &Args, + DiagnosticsEngine &Diags) { + auto *Arg = Args.getLastArg(Opt); + if (!Arg) + return None; + IntTy Res; + if (StringRef(Arg->getValue()).getAsInteger(0, Res)) { + Diags.Report(diag::err_drv_invalid_int_value) + << Arg->getAsString(Args) << Arg->getValue(); + return None; + } + return Res; +} + +static Optional<std::vector<std::string>> +normalizeStringVector(OptSpecifier Opt, int, const ArgList &Args, + DiagnosticsEngine &) { + return Args.getAllArgValues(Opt); +} + +static void denormalizeStringVector(SmallVectorImpl<const char *> &Args, + const char *Spelling, + CompilerInvocation::StringAllocator SA, + Option::OptionClass OptClass, + unsigned TableIndex, + const std::vector<std::string> &Values) { + switch (OptClass) { + case Option::CommaJoinedClass: { + std::string CommaJoinedValue; + if (!Values.empty()) { + CommaJoinedValue.append(Values.front()); + for (const std::string &Value : llvm::drop_begin(Values, 1)) { + CommaJoinedValue.append(","); + CommaJoinedValue.append(Value); + } + } + denormalizeString(Args, Spelling, SA, Option::OptionClass::JoinedClass, + TableIndex, CommaJoinedValue); + break; + } + case Option::JoinedClass: + case Option::SeparateClass: + case Option::JoinedOrSeparateClass: + for (const std::string &Value : Values) + denormalizeString(Args, Spelling, SA, OptClass, TableIndex, Value); + break; + default: + llvm_unreachable("Cannot denormalize an option with option class " + "incompatible with string vector denormalization."); + } +} + +static Optional<std::string> normalizeTriple(OptSpecifier Opt, int TableIndex, + const ArgList &Args, + DiagnosticsEngine &Diags) { + auto *Arg = Args.getLastArg(Opt); + if (!Arg) + return None; + return llvm::Triple::normalize(Arg->getValue()); +} + +template <typename T, typename U> +static T mergeForwardValue(T KeyPath, U Value) { + return static_cast<T>(Value); +} + +template <typename T, typename U> static T mergeMaskValue(T KeyPath, U Value) { + return KeyPath | Value; +} + +template <typename T> static T extractForwardValue(T KeyPath) { + return KeyPath; +} + +template <typename T, typename U, U Value> +static T extractMaskValue(T KeyPath) { + return ((KeyPath & Value) == Value) ? static_cast<T>(Value) : T(); +} + +#define PARSE_OPTION_WITH_MARSHALLING( \ + ARGS, DIAGS, ID, FLAGS, PARAM, SHOULD_PARSE, KEYPATH, DEFAULT_VALUE, \ + IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, MERGER, TABLE_INDEX) \ + if ((FLAGS)&options::CC1Option) { \ + KEYPATH = MERGER(KEYPATH, DEFAULT_VALUE); \ + if (IMPLIED_CHECK) \ + KEYPATH = MERGER(KEYPATH, IMPLIED_VALUE); \ + if (SHOULD_PARSE) \ + if (auto MaybeValue = NORMALIZER(OPT_##ID, TABLE_INDEX, ARGS, DIAGS)) \ + KEYPATH = \ + MERGER(KEYPATH, static_cast<decltype(KEYPATH)>(*MaybeValue)); \ + } + +// Capture the extracted value as a lambda argument to avoid potential issues +// with lifetime extension of the reference. +#define GENERATE_OPTION_WITH_MARSHALLING( \ + ARGS, STRING_ALLOCATOR, KIND, FLAGS, SPELLING, ALWAYS_EMIT, KEYPATH, \ + DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, DENORMALIZER, EXTRACTOR, \ + TABLE_INDEX) \ + if ((FLAGS)&options::CC1Option) { \ + [&](const auto &Extracted) { \ + if (ALWAYS_EMIT || \ + (Extracted != \ + static_cast<decltype(KEYPATH)>((IMPLIED_CHECK) ? (IMPLIED_VALUE) \ + : (DEFAULT_VALUE)))) \ + DENORMALIZER(ARGS, SPELLING, STRING_ALLOCATOR, Option::KIND##Class, \ + TABLE_INDEX, Extracted); \ + }(EXTRACTOR(KEYPATH)); \ + } + +static StringRef GetInputKindName(InputKind IK); + +static bool FixupInvocation(CompilerInvocation &Invocation, + DiagnosticsEngine &Diags, const ArgList &Args, + InputKind IK) { + unsigned NumErrorsBefore = Diags.getNumErrors(); + + LangOptions &LangOpts = *Invocation.getLangOpts(); + CodeGenOptions &CodeGenOpts = Invocation.getCodeGenOpts(); + TargetOptions &TargetOpts = Invocation.getTargetOpts(); + FrontendOptions &FrontendOpts = Invocation.getFrontendOpts(); + CodeGenOpts.XRayInstrumentFunctions = LangOpts.XRayInstrument; + CodeGenOpts.XRayAlwaysEmitCustomEvents = LangOpts.XRayAlwaysEmitCustomEvents; + CodeGenOpts.XRayAlwaysEmitTypedEvents = LangOpts.XRayAlwaysEmitTypedEvents; + CodeGenOpts.DisableFree = FrontendOpts.DisableFree; + FrontendOpts.GenerateGlobalModuleIndex = FrontendOpts.UseGlobalModuleIndex; + if (FrontendOpts.ShowStats) + CodeGenOpts.ClearASTBeforeBackend = false; + LangOpts.SanitizeCoverage = CodeGenOpts.hasSanitizeCoverage(); + LangOpts.ForceEmitVTables = CodeGenOpts.ForceEmitVTables; + LangOpts.SpeculativeLoadHardening = CodeGenOpts.SpeculativeLoadHardening; + LangOpts.CurrentModule = LangOpts.ModuleName; + + llvm::Triple T(TargetOpts.Triple); + llvm::Triple::ArchType Arch = T.getArch(); + + CodeGenOpts.CodeModel = TargetOpts.CodeModel; + + if (LangOpts.getExceptionHandling() != + LangOptions::ExceptionHandlingKind::None && + T.isWindowsMSVCEnvironment()) + Diags.Report(diag::err_fe_invalid_exception_model) + << static_cast<unsigned>(LangOpts.getExceptionHandling()) << T.str(); + + if (LangOpts.AppleKext && !LangOpts.CPlusPlus) + Diags.Report(diag::warn_c_kext); + + if (Args.hasArg(OPT_fconcepts_ts)) + Diags.Report(diag::warn_fe_concepts_ts_flag); + + if (LangOpts.NewAlignOverride && + !llvm::isPowerOf2_32(LangOpts.NewAlignOverride)) { + Arg *A = Args.getLastArg(OPT_fnew_alignment_EQ); + Diags.Report(diag::err_fe_invalid_alignment) + << A->getAsString(Args) << A->getValue(); + LangOpts.NewAlignOverride = 0; + } + + // Prevent the user from specifying both -fsycl-is-device and -fsycl-is-host. + if (LangOpts.SYCLIsDevice && LangOpts.SYCLIsHost) + Diags.Report(diag::err_drv_argument_not_allowed_with) << "-fsycl-is-device" + << "-fsycl-is-host"; + + if (Args.hasArg(OPT_fgnu89_inline) && LangOpts.CPlusPlus) + Diags.Report(diag::err_drv_argument_not_allowed_with) + << "-fgnu89-inline" << GetInputKindName(IK); + + if (Args.hasArg(OPT_fgpu_allow_device_init) && !LangOpts.HIP) + Diags.Report(diag::warn_ignored_hip_only_option) + << Args.getLastArg(OPT_fgpu_allow_device_init)->getAsString(Args); + + if (Args.hasArg(OPT_gpu_max_threads_per_block_EQ) && !LangOpts.HIP) + Diags.Report(diag::warn_ignored_hip_only_option) + << Args.getLastArg(OPT_gpu_max_threads_per_block_EQ)->getAsString(Args); + + // -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) && + (LangOpts.getOpenCLCompatibleVersion() > 100)) + Diags.Report(diag::warn_option_invalid_ocl_version) + << LangOpts.getOpenCLVersionString() + << Args.getLastArg(OPT_cl_strict_aliasing)->getAsString(Args); + + if (Arg *A = Args.getLastArg(OPT_fdefault_calling_conv_EQ)) { + auto DefaultCC = LangOpts.getDefaultCallingConv(); + + bool emitError = (DefaultCC == LangOptions::DCC_FastCall || + DefaultCC == LangOptions::DCC_StdCall) && + Arch != llvm::Triple::x86; + emitError |= (DefaultCC == LangOptions::DCC_VectorCall || + DefaultCC == LangOptions::DCC_RegCall) && + !T.isX86(); + if (emitError) + Diags.Report(diag::err_drv_argument_not_allowed_with) + << A->getSpelling() << T.getTriple(); + } + + if (!CodeGenOpts.ProfileRemappingFile.empty() && CodeGenOpts.LegacyPassManager) + Diags.Report(diag::err_drv_argument_only_allowed_with) + << Args.getLastArg(OPT_fprofile_remapping_file_EQ)->getAsString(Args) + << "-fno-legacy-pass-manager"; + + return Diags.getNumErrors() == NumErrorsBefore; +} + +//===----------------------------------------------------------------------===// +// Deserialization (from args) +//===----------------------------------------------------------------------===// + +static unsigned getOptimizationLevel(ArgList &Args, InputKind IK, + DiagnosticsEngine &Diags) { + unsigned DefaultOpt = llvm::CodeGenOpt::None; + if ((IK.getLanguage() == Language::OpenCL || + IK.getLanguage() == Language::OpenCLCXX) && + !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") + 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 GenerateArg(SmallVectorImpl<const char *> &Args, + llvm::opt::OptSpecifier OptSpecifier, + CompilerInvocation::StringAllocator SA) { + Option Opt = getDriverOptTable().getOption(OptSpecifier); + denormalizeSimpleFlag(Args, SA(Opt.getPrefix() + Opt.getName()), SA, + Option::OptionClass::FlagClass, 0); +} + +static void GenerateArg(SmallVectorImpl<const char *> &Args, + llvm::opt::OptSpecifier OptSpecifier, + const Twine &Value, + CompilerInvocation::StringAllocator SA) { + Option Opt = getDriverOptTable().getOption(OptSpecifier); + denormalizeString(Args, SA(Opt.getPrefix() + Opt.getName()), SA, + Opt.getKind(), 0, Value); +} + +// Parse command line arguments into CompilerInvocation. +using ParseFn = + llvm::function_ref<bool(CompilerInvocation &, ArrayRef<const char *>, + DiagnosticsEngine &, const char *)>; + +// Generate command line arguments from CompilerInvocation. +using GenerateFn = llvm::function_ref<void( + CompilerInvocation &, SmallVectorImpl<const char *> &, + CompilerInvocation::StringAllocator)>; + +// May perform round-trip of command line arguments. By default, the round-trip +// is enabled in assert builds. This can be overwritten at run-time via the +// "-round-trip-args" and "-no-round-trip-args" command line flags. +// During round-trip, the command line arguments are parsed into a dummy +// instance of CompilerInvocation which is used to generate the command line +// arguments again. The real CompilerInvocation instance is then created by +// parsing the generated arguments, not the original ones. +static bool RoundTrip(ParseFn Parse, GenerateFn Generate, + CompilerInvocation &RealInvocation, + CompilerInvocation &DummyInvocation, + ArrayRef<const char *> CommandLineArgs, + DiagnosticsEngine &Diags, const char *Argv0) { +#ifndef NDEBUG + bool DoRoundTripDefault = true; +#else + bool DoRoundTripDefault = false; +#endif + + bool DoRoundTrip = DoRoundTripDefault; + for (const auto *Arg : CommandLineArgs) { + if (Arg == StringRef("-round-trip-args")) + DoRoundTrip = true; + if (Arg == StringRef("-no-round-trip-args")) + DoRoundTrip = false; + } + + // If round-trip was not requested, simply run the parser with the real + // invocation diagnostics. + if (!DoRoundTrip) + return Parse(RealInvocation, CommandLineArgs, Diags, Argv0); + + // Serializes quoted (and potentially escaped) arguments. + auto SerializeArgs = [](ArrayRef<const char *> Args) { + std::string Buffer; + llvm::raw_string_ostream OS(Buffer); + for (const char *Arg : Args) { + llvm::sys::printArg(OS, Arg, /*Quote=*/true); + OS << ' '; + } + OS.flush(); + return Buffer; + }; + + // Setup a dummy DiagnosticsEngine. + DiagnosticsEngine DummyDiags(new DiagnosticIDs(), new DiagnosticOptions()); + DummyDiags.setClient(new TextDiagnosticBuffer()); + + // Run the first parse on the original arguments with the dummy invocation and + // diagnostics. + if (!Parse(DummyInvocation, CommandLineArgs, DummyDiags, Argv0) || + DummyDiags.getNumWarnings() != 0) { + // If the first parse did not succeed, it must be user mistake (invalid + // command line arguments). We won't be able to generate arguments that + // would reproduce the same result. Let's fail again with the real + // invocation and diagnostics, so all side-effects of parsing are visible. + unsigned NumWarningsBefore = Diags.getNumWarnings(); + auto Success = Parse(RealInvocation, CommandLineArgs, Diags, Argv0); + if (!Success || Diags.getNumWarnings() != NumWarningsBefore) + return Success; + + // Parse with original options and diagnostics succeeded even though it + // shouldn't have. Something is off. + Diags.Report(diag::err_cc1_round_trip_fail_then_ok); + Diags.Report(diag::note_cc1_round_trip_original) + << SerializeArgs(CommandLineArgs); + return false; + } + + // Setup string allocator. + llvm::BumpPtrAllocator Alloc; + llvm::StringSaver StringPool(Alloc); + auto SA = [&StringPool](const Twine &Arg) { + return StringPool.save(Arg).data(); + }; + + // Generate arguments from the dummy invocation. If Generate is the + // inverse of Parse, the newly generated arguments must have the same + // semantics as the original. + SmallVector<const char *> GeneratedArgs1; + Generate(DummyInvocation, GeneratedArgs1, SA); + + // Run the second parse, now on the generated arguments, and with the real + // invocation and diagnostics. The result is what we will end up using for the + // rest of compilation, so if Generate is not inverse of Parse, something down + // the line will break. + bool Success2 = Parse(RealInvocation, GeneratedArgs1, Diags, Argv0); + + // The first parse on original arguments succeeded, but second parse of + // generated arguments failed. Something must be wrong with the generator. + if (!Success2) { + Diags.Report(diag::err_cc1_round_trip_ok_then_fail); + Diags.Report(diag::note_cc1_round_trip_generated) + << 1 << SerializeArgs(GeneratedArgs1); + return false; + } + + // Generate arguments again, this time from the options we will end up using + // for the rest of the compilation. + SmallVector<const char *> GeneratedArgs2; + Generate(RealInvocation, GeneratedArgs2, SA); + + // Compares two lists of generated arguments. + auto Equal = [](const ArrayRef<const char *> A, + const ArrayRef<const char *> B) { + return std::equal(A.begin(), A.end(), B.begin(), B.end(), + [](const char *AElem, const char *BElem) { + return StringRef(AElem) == StringRef(BElem); + }); + }; + + // If we generated different arguments from what we assume are two + // semantically equivalent CompilerInvocations, the Generate function may + // be non-deterministic. + if (!Equal(GeneratedArgs1, GeneratedArgs2)) { + Diags.Report(diag::err_cc1_round_trip_mismatch); + Diags.Report(diag::note_cc1_round_trip_generated) + << 1 << SerializeArgs(GeneratedArgs1); + Diags.Report(diag::note_cc1_round_trip_generated) + << 2 << SerializeArgs(GeneratedArgs2); + return false; + } + + Diags.Report(diag::remark_cc1_round_trip_generated) + << 1 << SerializeArgs(GeneratedArgs1); + Diags.Report(diag::remark_cc1_round_trip_generated) + << 2 << SerializeArgs(GeneratedArgs2); + + return Success2; +} + +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 diagnostics. + Diagnostics.push_back( + std::string(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. Add only the group name to the diagnostics. + Diagnostics.push_back( + std::string(A->getOption().getName().drop_front(1).rtrim("=-"))); + } else { + // Otherwise, add its value (for OPT_W_Joined and similar). + Diagnostics.push_back(A->getValue()); + } + } +} + +// 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) { + std::vector<std::string> Values = Args.getAllArgValues(OPT_fno_builtin_); + auto BuiltinEnd = llvm::partition(Values, Builtin::Context::isBuiltinFunc); + Funcs.insert(Funcs.end(), Values.begin(), BuiltinEnd); +} + +static void GenerateAnalyzerArgs(AnalyzerOptions &Opts, + SmallVectorImpl<const char *> &Args, + CompilerInvocation::StringAllocator SA) { + const AnalyzerOptions *AnalyzerOpts = &Opts; + +#define ANALYZER_OPTION_WITH_MARSHALLING( \ + PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \ + DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \ + MERGER, EXTRACTOR, TABLE_INDEX) \ + GENERATE_OPTION_WITH_MARSHALLING( \ + Args, SA, KIND, FLAGS, SPELLING, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, \ + IMPLIED_CHECK, IMPLIED_VALUE, DENORMALIZER, EXTRACTOR, TABLE_INDEX) +#include "clang/Driver/Options.inc" +#undef ANALYZER_OPTION_WITH_MARSHALLING + + if (Opts.AnalysisStoreOpt != RegionStoreModel) { + switch (Opts.AnalysisStoreOpt) { +#define ANALYSIS_STORE(NAME, CMDFLAG, DESC, CREATFN) \ + case NAME##Model: \ + GenerateArg(Args, OPT_analyzer_store, CMDFLAG, SA); \ + break; +#include "clang/StaticAnalyzer/Core/Analyses.def" + default: + llvm_unreachable("Tried to generate unknown analysis store."); + } + } + + if (Opts.AnalysisConstraintsOpt != RangeConstraintsModel) { + switch (Opts.AnalysisConstraintsOpt) { +#define ANALYSIS_CONSTRAINTS(NAME, CMDFLAG, DESC, CREATFN) \ + case NAME##Model: \ + GenerateArg(Args, OPT_analyzer_constraints, CMDFLAG, SA); \ + break; +#include "clang/StaticAnalyzer/Core/Analyses.def" + default: + llvm_unreachable("Tried to generate unknown analysis constraint."); + } + } + + if (Opts.AnalysisDiagOpt != PD_HTML) { + switch (Opts.AnalysisDiagOpt) { +#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATFN) \ + case PD_##NAME: \ + GenerateArg(Args, OPT_analyzer_output, CMDFLAG, SA); \ + break; +#include "clang/StaticAnalyzer/Core/Analyses.def" + default: + llvm_unreachable("Tried to generate unknown analysis diagnostic client."); + } + } + + if (Opts.AnalysisPurgeOpt != PurgeStmt) { + switch (Opts.AnalysisPurgeOpt) { +#define ANALYSIS_PURGE(NAME, CMDFLAG, DESC) \ + case NAME: \ + GenerateArg(Args, OPT_analyzer_purge, CMDFLAG, SA); \ + break; +#include "clang/StaticAnalyzer/Core/Analyses.def" + default: + llvm_unreachable("Tried to generate unknown analysis purge mode."); + } + } + + if (Opts.InliningMode != NoRedundancy) { + switch (Opts.InliningMode) { +#define ANALYSIS_INLINING_MODE(NAME, CMDFLAG, DESC) \ + case NAME: \ + GenerateArg(Args, OPT_analyzer_inlining_mode, CMDFLAG, SA); \ + break; +#include "clang/StaticAnalyzer/Core/Analyses.def" + default: + llvm_unreachable("Tried to generate unknown analysis inlining mode."); + } + } + + for (const auto &CP : Opts.CheckersAndPackages) { + OptSpecifier Opt = + CP.second ? OPT_analyzer_checker : OPT_analyzer_disable_checker; + GenerateArg(Args, Opt, CP.first, SA); + } + + AnalyzerOptions ConfigOpts; + parseAnalyzerConfigs(ConfigOpts, nullptr); + + for (const auto &C : Opts.Config) { + // Don't generate anything that came from parseAnalyzerConfigs. It would be + // redundant and may not be valid on the command line. + auto Entry = ConfigOpts.Config.find(C.getKey()); + if (Entry != ConfigOpts.Config.end() && Entry->getValue() == C.getValue()) + continue; + + GenerateArg(Args, OPT_analyzer_config, C.getKey() + "=" + C.getValue(), SA); + } + + // Nothing to generate for FullCompilerInvocation. +} + +static bool ParseAnalyzerArgs(AnalyzerOptions &Opts, ArgList &Args, + DiagnosticsEngine &Diags) { + unsigned NumErrorsBefore = Diags.getNumErrors(); + + AnalyzerOptions *AnalyzerOpts = &Opts; + +#define ANALYZER_OPTION_WITH_MARSHALLING( \ + PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \ + DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \ + MERGER, EXTRACTOR, TABLE_INDEX) \ + PARSE_OPTION_WITH_MARSHALLING( \ + Args, Diags, ID, FLAGS, PARAM, SHOULD_PARSE, KEYPATH, DEFAULT_VALUE, \ + IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, MERGER, TABLE_INDEX) +#include "clang/Driver/Options.inc" +#undef ANALYZER_OPTION_WITH_MARSHALLING + + 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; + } 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; + } 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; + } 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; + } 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; + } else { + Opts.InliningMode = Value; + } + } + + Opts.CheckersAndPackages.clear(); + for (const Arg *A : + Args.filtered(OPT_analyzer_checker, OPT_analyzer_disable_checker)) { + A->claim(); + bool IsEnabled = A->getOption().getID() == OPT_analyzer_checker; + // We can have a list of comma separated checker names, e.g: + // '-analyzer-checker=cocoa,unix' + StringRef CheckerAndPackageList = A->getValue(); + SmallVector<StringRef, 16> CheckersAndPackages; + CheckerAndPackageList.split(CheckersAndPackages, ","); + for (const StringRef &CheckerOrPackage : CheckersAndPackages) + Opts.CheckersAndPackages.emplace_back(std::string(CheckerOrPackage), + IsEnabled); + } + + // 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; + break; + } + if (val.contains('=')) { + Diags.Report(SourceLocation(), + diag::err_analyzer_config_multiple_values) + << configVal; + 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] = std::string(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 Diags.getNumErrors() == NumErrorsBefore; +} + +static StringRef getStringOption(AnalyzerOptions::ConfigTable &Config, + StringRef OptionName, StringRef DefaultVal) { + return Config.insert({OptionName, std::string(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(0, 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. + + // FIXME: Here we try to validate the silenced checkers or packages are valid. + // The current approach only validates the registered checkers which does not + // contain the runtime enabled checkers and optimally we would validate both. + if (!AnOpts.RawSilencedCheckersAndPackages.empty()) { + std::vector<StringRef> Checkers = + AnOpts.getRegisteredCheckers(/*IncludeExperimental=*/true); + std::vector<StringRef> Packages = + AnOpts.getRegisteredPackages(/*IncludeExperimental=*/true); + + SmallVector<StringRef, 16> CheckersAndPackages; + AnOpts.RawSilencedCheckersAndPackages.split(CheckersAndPackages, ";"); + + for (const StringRef &CheckerOrPackage : CheckersAndPackages) { + if (Diags) { + bool IsChecker = CheckerOrPackage.contains('.'); + bool IsValidName = IsChecker + ? llvm::is_contained(Checkers, CheckerOrPackage) + : llvm::is_contained(Packages, CheckerOrPackage); + + if (!IsValidName) + Diags->Report(diag::err_unknown_analyzer_checker_or_package) + << CheckerOrPackage; + } + + AnOpts.SilencedCheckersAndPackages.emplace_back(CheckerOrPackage); + } + } + + if (!Diags) + return; + + if (AnOpts.ShouldTrackConditionsDebug && !AnOpts.ShouldTrackConditions) + Diags->Report(diag::err_analyzer_config_invalid_input) + << "track-conditions-debug" << "'track-conditions' to also be enabled"; + + 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"; +} + +/// Generate a remark argument. This is an inverse of `ParseOptimizationRemark`. +static void +GenerateOptimizationRemark(SmallVectorImpl<const char *> &Args, + CompilerInvocation::StringAllocator SA, + OptSpecifier OptEQ, StringRef Name, + const CodeGenOptions::OptRemark &Remark) { + if (Remark.hasValidPattern()) { + GenerateArg(Args, OptEQ, Remark.Pattern, SA); + } else if (Remark.Kind == CodeGenOptions::RK_Enabled) { + GenerateArg(Args, OPT_R_Joined, Name, SA); + } else if (Remark.Kind == CodeGenOptions::RK_Disabled) { + GenerateArg(Args, OPT_R_Joined, StringRef("no-") + Name, SA); + } +} + +/// Parse a remark command line argument. It may be missing, disabled/enabled by +/// '-R[no-]group' or specified with a regular expression by '-Rgroup=regexp'. +/// On top of that, it can be disabled/enabled globally by '-R[no-]everything'. +static CodeGenOptions::OptRemark +ParseOptimizationRemark(DiagnosticsEngine &Diags, ArgList &Args, + OptSpecifier OptEQ, StringRef Name) { + CodeGenOptions::OptRemark Result; + + auto InitializeResultPattern = [&Diags, &Args, &Result](const Arg *A, + StringRef Pattern) { + Result.Pattern = Pattern.str(); + + std::string RegexError; + Result.Regex = std::make_shared<llvm::Regex>(Result.Pattern); + if (!Result.Regex->isValid(RegexError)) { + Diags.Report(diag::err_drv_optimization_remark_pattern) + << RegexError << A->getAsString(Args); + return false; + } + + return true; + }; + + for (Arg *A : Args) { + if (A->getOption().matches(OPT_R_Joined)) { + StringRef Value = A->getValue(); + + if (Value == Name) + Result.Kind = CodeGenOptions::RK_Enabled; + else if (Value == "everything") + Result.Kind = CodeGenOptions::RK_EnabledEverything; + else if (Value.split('-') == std::make_pair(StringRef("no"), Name)) + Result.Kind = CodeGenOptions::RK_Disabled; + else if (Value == "no-everything") + Result.Kind = CodeGenOptions::RK_DisabledEverything; + else + continue; + + if (Result.Kind == CodeGenOptions::RK_Disabled || + Result.Kind == CodeGenOptions::RK_DisabledEverything) { + Result.Pattern = ""; + Result.Regex = nullptr; + } else { + InitializeResultPattern(A, ".*"); + } + } else if (A->getOption().matches(OptEQ)) { + Result.Kind = CodeGenOptions::RK_WithPattern; + if (!InitializeResultPattern(A, A->getValue())) + return CodeGenOptions::OptRemark(); + } + } + + return Result; +} + +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; + 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 == SanitizerMask()) + Diags.Report(diag::err_drv_invalid_value) << FlagName << Sanitizer; + else + S.set(K, true); + } +} + +static SmallVector<StringRef, 4> serializeSanitizerKinds(SanitizerSet S) { + SmallVector<StringRef, 4> Values; + serializeSanitizerSet(S, Values); + return Values; +} + +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); + } +} + +static std::string serializeXRayInstrumentationBundle(const XRayInstrSet &S) { + llvm::SmallVector<StringRef, 2> BundleParts; + serializeXRayInstrValue(S, BundleParts); + std::string Buffer; + llvm::raw_string_ostream OS(Buffer); + llvm::interleave(BundleParts, OS, [&OS](StringRef Part) { OS << Part; }, ","); + return Buffer; +} + +// 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()) { + if (PGOReader->hasCSIRLevelProfile()) + Opts.setProfileUse(CodeGenOptions::ProfileCSIRInstr); + else + Opts.setProfileUse(CodeGenOptions::ProfileIRInstr); + } else + Opts.setProfileUse(CodeGenOptions::ProfileClangInstr); +} + +void CompilerInvocation::GenerateCodeGenArgs( + const CodeGenOptions &Opts, SmallVectorImpl<const char *> &Args, + StringAllocator SA, const llvm::Triple &T, const std::string &OutputFile, + const LangOptions *LangOpts) { + const CodeGenOptions &CodeGenOpts = Opts; + + if (Opts.OptimizationLevel == 0) + GenerateArg(Args, OPT_O0, SA); + else + GenerateArg(Args, OPT_O, Twine(Opts.OptimizationLevel), SA); + +#define CODEGEN_OPTION_WITH_MARSHALLING( \ + PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \ + DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \ + MERGER, EXTRACTOR, TABLE_INDEX) \ + GENERATE_OPTION_WITH_MARSHALLING( \ + Args, SA, KIND, FLAGS, SPELLING, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, \ + IMPLIED_CHECK, IMPLIED_VALUE, DENORMALIZER, EXTRACTOR, TABLE_INDEX) +#include "clang/Driver/Options.inc" +#undef CODEGEN_OPTION_WITH_MARSHALLING + + if (Opts.OptimizationLevel > 0) { + if (Opts.Inlining == CodeGenOptions::NormalInlining) + GenerateArg(Args, OPT_finline_functions, SA); + else if (Opts.Inlining == CodeGenOptions::OnlyHintInlining) + GenerateArg(Args, OPT_finline_hint_functions, SA); + else if (Opts.Inlining == CodeGenOptions::OnlyAlwaysInlining) + GenerateArg(Args, OPT_fno_inline, SA); + } + + if (Opts.DirectAccessExternalData && LangOpts->PICLevel != 0) + GenerateArg(Args, OPT_fdirect_access_external_data, SA); + else if (!Opts.DirectAccessExternalData && LangOpts->PICLevel == 0) + GenerateArg(Args, OPT_fno_direct_access_external_data, SA); + + Optional<StringRef> DebugInfoVal; + switch (Opts.DebugInfo) { + case codegenoptions::DebugLineTablesOnly: + DebugInfoVal = "line-tables-only"; + break; + case codegenoptions::DebugDirectivesOnly: + DebugInfoVal = "line-directives-only"; + break; + case codegenoptions::DebugInfoConstructor: + DebugInfoVal = "constructor"; + break; + case codegenoptions::LimitedDebugInfo: + DebugInfoVal = "limited"; + break; + case codegenoptions::FullDebugInfo: + DebugInfoVal = "standalone"; + break; + case codegenoptions::UnusedTypeInfo: + DebugInfoVal = "unused-types"; + break; + case codegenoptions::NoDebugInfo: // default value + DebugInfoVal = None; + break; + case codegenoptions::LocTrackingOnly: // implied value + DebugInfoVal = None; + break; + } + if (DebugInfoVal) + GenerateArg(Args, OPT_debug_info_kind_EQ, *DebugInfoVal, SA); + + for (const auto &Prefix : Opts.DebugPrefixMap) + GenerateArg(Args, OPT_fdebug_prefix_map_EQ, + Prefix.first + "=" + Prefix.second, SA); + + for (const auto &Prefix : Opts.CoveragePrefixMap) + GenerateArg(Args, OPT_fcoverage_prefix_map_EQ, + Prefix.first + "=" + Prefix.second, SA); + + if (Opts.NewStructPathTBAA) + GenerateArg(Args, OPT_new_struct_path_tbaa, SA); + + if (Opts.OptimizeSize == 1) + GenerateArg(Args, OPT_O, "s", SA); + else if (Opts.OptimizeSize == 2) + GenerateArg(Args, OPT_O, "z", SA); + + // SimplifyLibCalls is set only in the absence of -fno-builtin and + // -ffreestanding. We'll consider that when generating them. + + // NoBuiltinFuncs are generated by LangOptions. + + if (Opts.UnrollLoops && Opts.OptimizationLevel <= 1) + GenerateArg(Args, OPT_funroll_loops, SA); + else if (!Opts.UnrollLoops && Opts.OptimizationLevel > 1) + GenerateArg(Args, OPT_fno_unroll_loops, SA); + + if (!Opts.BinutilsVersion.empty()) + GenerateArg(Args, OPT_fbinutils_version_EQ, Opts.BinutilsVersion, SA); + + if (Opts.DebugNameTable == + static_cast<unsigned>(llvm::DICompileUnit::DebugNameTableKind::GNU)) + GenerateArg(Args, OPT_ggnu_pubnames, SA); + else if (Opts.DebugNameTable == + static_cast<unsigned>( + llvm::DICompileUnit::DebugNameTableKind::Default)) + GenerateArg(Args, OPT_gpubnames, SA); + + auto TNK = Opts.getDebugSimpleTemplateNames(); + if (TNK != codegenoptions::DebugTemplateNamesKind::Full) { + if (TNK == codegenoptions::DebugTemplateNamesKind::Simple) + GenerateArg(Args, OPT_gsimple_template_names_EQ, "simple", SA); + else if (TNK == codegenoptions::DebugTemplateNamesKind::Mangled) + GenerateArg(Args, OPT_gsimple_template_names_EQ, "mangled", SA); + } + // ProfileInstrumentUsePath is marshalled automatically, no need to generate + // it or PGOUseInstrumentor. + + if (Opts.TimePasses) { + if (Opts.TimePassesPerRun) + GenerateArg(Args, OPT_ftime_report_EQ, "per-pass-run", SA); + else + GenerateArg(Args, OPT_ftime_report, SA); + } + + if (Opts.PrepareForLTO && !Opts.PrepareForThinLTO) + GenerateArg(Args, OPT_flto_EQ, "full", SA); + + if (Opts.PrepareForThinLTO) + GenerateArg(Args, OPT_flto_EQ, "thin", SA); + + if (!Opts.ThinLTOIndexFile.empty()) + GenerateArg(Args, OPT_fthinlto_index_EQ, Opts.ThinLTOIndexFile, SA); + + if (Opts.SaveTempsFilePrefix == OutputFile) + GenerateArg(Args, OPT_save_temps_EQ, "obj", SA); + + StringRef MemProfileBasename("memprof.profraw"); + if (!Opts.MemoryProfileOutput.empty()) { + if (Opts.MemoryProfileOutput == MemProfileBasename) { + GenerateArg(Args, OPT_fmemory_profile, SA); + } else { + size_t ArgLength = + Opts.MemoryProfileOutput.size() - MemProfileBasename.size(); + GenerateArg(Args, OPT_fmemory_profile_EQ, + Opts.MemoryProfileOutput.substr(0, ArgLength), SA); + } + } + + if (memcmp(Opts.CoverageVersion, "408*", 4) != 0) + GenerateArg(Args, OPT_coverage_version_EQ, + StringRef(Opts.CoverageVersion, 4), SA); + + // TODO: Check if we need to generate arguments stored in CmdArgs. (Namely + // '-fembed_bitcode', which does not map to any CompilerInvocation field and + // won't be generated.) + + if (Opts.XRayInstrumentationBundle.Mask != XRayInstrKind::All) { + std::string InstrBundle = + serializeXRayInstrumentationBundle(Opts.XRayInstrumentationBundle); + if (!InstrBundle.empty()) + GenerateArg(Args, OPT_fxray_instrumentation_bundle, InstrBundle, SA); + } + + if (Opts.CFProtectionReturn && Opts.CFProtectionBranch) + GenerateArg(Args, OPT_fcf_protection_EQ, "full", SA); + else if (Opts.CFProtectionReturn) + GenerateArg(Args, OPT_fcf_protection_EQ, "return", SA); + else if (Opts.CFProtectionBranch) + GenerateArg(Args, OPT_fcf_protection_EQ, "branch", SA); + + for (const auto &F : Opts.LinkBitcodeFiles) { + bool Builtint = F.LinkFlags == llvm::Linker::Flags::LinkOnlyNeeded && + F.PropagateAttrs && F.Internalize; + GenerateArg(Args, + Builtint ? OPT_mlink_builtin_bitcode : OPT_mlink_bitcode_file, + F.Filename, SA); + } + + // TODO: Consider removing marshalling annotations from f[no_]emulated_tls. + // That would make it easy to generate the option only **once** if it was + // explicitly set to non-default value. + if (Opts.ExplicitEmulatedTLS) { + GenerateArg( + Args, Opts.EmulatedTLS ? OPT_femulated_tls : OPT_fno_emulated_tls, SA); + } + + if (Opts.FPDenormalMode != llvm::DenormalMode::getIEEE()) + GenerateArg(Args, OPT_fdenormal_fp_math_EQ, Opts.FPDenormalMode.str(), SA); + + if (Opts.FP32DenormalMode != llvm::DenormalMode::getIEEE()) + GenerateArg(Args, OPT_fdenormal_fp_math_f32_EQ, Opts.FP32DenormalMode.str(), + SA); + + if (Opts.StructReturnConvention == CodeGenOptions::SRCK_OnStack) { + OptSpecifier Opt = + T.isPPC32() ? OPT_maix_struct_return : OPT_fpcc_struct_return; + GenerateArg(Args, Opt, SA); + } else if (Opts.StructReturnConvention == CodeGenOptions::SRCK_InRegs) { + OptSpecifier Opt = + T.isPPC32() ? OPT_msvr4_struct_return : OPT_freg_struct_return; + GenerateArg(Args, Opt, SA); + } + + if (Opts.EnableAIXExtendedAltivecABI) + GenerateArg(Args, OPT_mabi_EQ_vec_extabi, SA); + + if (!Opts.OptRecordPasses.empty()) + GenerateArg(Args, OPT_opt_record_passes, Opts.OptRecordPasses, SA); + + if (!Opts.OptRecordFormat.empty()) + GenerateArg(Args, OPT_opt_record_format, Opts.OptRecordFormat, SA); + + GenerateOptimizationRemark(Args, SA, OPT_Rpass_EQ, "pass", + Opts.OptimizationRemark); + + GenerateOptimizationRemark(Args, SA, OPT_Rpass_missed_EQ, "pass-missed", + Opts.OptimizationRemarkMissed); + + GenerateOptimizationRemark(Args, SA, OPT_Rpass_analysis_EQ, "pass-analysis", + Opts.OptimizationRemarkAnalysis); + + GenerateArg(Args, OPT_fdiagnostics_hotness_threshold_EQ, + Opts.DiagnosticsHotnessThreshold + ? Twine(*Opts.DiagnosticsHotnessThreshold) + : "auto", + SA); + + for (StringRef Sanitizer : serializeSanitizerKinds(Opts.SanitizeRecover)) + GenerateArg(Args, OPT_fsanitize_recover_EQ, Sanitizer, SA); + + for (StringRef Sanitizer : serializeSanitizerKinds(Opts.SanitizeTrap)) + GenerateArg(Args, OPT_fsanitize_trap_EQ, Sanitizer, SA); + + if (!Opts.EmitVersionIdentMetadata) + GenerateArg(Args, OPT_Qn, SA); + + switch (Opts.FiniteLoops) { + case CodeGenOptions::FiniteLoopsKind::Language: + break; + case CodeGenOptions::FiniteLoopsKind::Always: + GenerateArg(Args, OPT_ffinite_loops, SA); + break; + case CodeGenOptions::FiniteLoopsKind::Never: + GenerateArg(Args, OPT_fno_finite_loops, SA); + break; + } +} + +bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, + InputKind IK, + DiagnosticsEngine &Diags, + const llvm::Triple &T, + const std::string &OutputFile, + const LangOptions &LangOptsRef) { + unsigned NumErrorsBefore = Diags.getNumErrors(); + + 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; + + // The key paths of codegen options defined in Options.td start with + // "CodeGenOpts.". Let's provide the expected variable name and type. + CodeGenOptions &CodeGenOpts = Opts; + // Some codegen options depend on language options. Let's provide the expected + // variable name and type. + const LangOptions *LangOpts = &LangOptsRef; + +#define CODEGEN_OPTION_WITH_MARSHALLING( \ + PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \ + DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \ + MERGER, EXTRACTOR, TABLE_INDEX) \ + PARSE_OPTION_WITH_MARSHALLING( \ + Args, Diags, ID, FLAGS, PARAM, SHOULD_PARSE, KEYPATH, DEFAULT_VALUE, \ + IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, MERGER, TABLE_INDEX) +#include "clang/Driver/Options.inc" +#undef CODEGEN_OPTION_WITH_MARSHALLING + + // 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); + } + } + + // PIC defaults to -fno-direct-access-external-data while non-PIC defaults to + // -fdirect-access-external-data. + Opts.DirectAccessExternalData = + Args.hasArg(OPT_fdirect_access_external_data) || + (!Args.hasArg(OPT_fno_direct_access_external_data) && + LangOpts->PICLevel == 0); + + 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("constructor", codegenoptions::DebugInfoConstructor) + .Case("limited", codegenoptions::LimitedDebugInfo) + .Case("standalone", codegenoptions::FullDebugInfo) + .Case("unused-types", codegenoptions::UnusedTypeInfo) + .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 -fuse-ctor-homing is set and limited debug info is already on, then use + // constructor homing, and vice versa for -fno-use-ctor-homing. + if (const Arg *A = + Args.getLastArg(OPT_fuse_ctor_homing, OPT_fno_use_ctor_homing)) { + if (A->getOption().matches(OPT_fuse_ctor_homing) && + Opts.getDebugInfo() == codegenoptions::LimitedDebugInfo) + Opts.setDebugInfo(codegenoptions::DebugInfoConstructor); + if (A->getOption().matches(OPT_fno_use_ctor_homing) && + Opts.getDebugInfo() == codegenoptions::DebugInfoConstructor) + Opts.setDebugInfo(codegenoptions::LimitedDebugInfo); + } + + for (const auto &Arg : Args.getAllArgValues(OPT_fdebug_prefix_map_EQ)) { + auto Split = StringRef(Arg).split('='); + Opts.DebugPrefixMap.insert( + {std::string(Split.first), std::string(Split.second)}); + } + + for (const auto &Arg : Args.getAllArgValues(OPT_fcoverage_prefix_map_EQ)) { + auto Split = StringRef(Arg).split('='); + Opts.CoveragePrefixMap.insert( + {std::string(Split.first), std::string(Split.second)}); + } + + const llvm::Triple::ArchType DebugEntryValueArchs[] = { + llvm::Triple::x86, llvm::Triple::x86_64, llvm::Triple::aarch64, + llvm::Triple::arm, llvm::Triple::armeb, llvm::Triple::mips, + llvm::Triple::mipsel, llvm::Triple::mips64, llvm::Triple::mips64el}; + + if (Opts.OptimizationLevel > 0 && Opts.hasReducedDebugInfo() && + llvm::is_contained(DebugEntryValueArchs, T.getArch())) + Opts.EmitCallSiteInfo = true; + + if (!Opts.EnableDIPreservationVerify && Opts.DIBugsReportFilePath.size()) { + Diags.Report(diag::warn_ignoring_verify_debuginfo_preserve_export) + << Opts.DIBugsReportFilePath; + Opts.DIBugsReportFilePath = ""; + } + + Opts.NewStructPathTBAA = !Args.hasArg(OPT_no_struct_path_tbaa) && + Args.hasArg(OPT_new_struct_path_tbaa); + Opts.OptimizeSize = getOptimizationLevelSize(Args); + Opts.SimplifyLibCalls = !LangOpts->NoBuiltin; + if (Opts.SimplifyLibCalls) + Opts.NoBuiltinFuncs = LangOpts->NoBuiltinFuncs; + Opts.UnrollLoops = + Args.hasFlag(OPT_funroll_loops, OPT_fno_unroll_loops, + (Opts.OptimizationLevel > 1)); + Opts.BinutilsVersion = + std::string(Args.getLastArgValue(OPT_fbinutils_version_EQ)); + + 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); + if (const Arg *A = Args.getLastArg(OPT_gsimple_template_names_EQ)) { + StringRef Value = A->getValue(); + if (Value != "simple" && Value != "mangled") + Diags.Report(diag::err_drv_unsupported_option_argument) + << A->getOption().getName() << A->getValue(); + Opts.setDebugSimpleTemplateNames( + StringRef(A->getValue()) == "simple" + ? codegenoptions::DebugTemplateNamesKind::Simple + : codegenoptions::DebugTemplateNamesKind::Mangled); + } + + if (!Opts.ProfileInstrumentUsePath.empty()) + setPGOUseInstrumentor(Opts, Opts.ProfileInstrumentUsePath); + + if (const Arg *A = Args.getLastArg(OPT_ftime_report, OPT_ftime_report_EQ)) { + Opts.TimePasses = true; + + // -ftime-report= is only for new pass manager. + if (A->getOption().getID() == OPT_ftime_report_EQ) { + if (Opts.LegacyPassManager) + Diags.Report(diag::err_drv_argument_only_allowed_with) + << A->getAsString(Args) << "-fno-legacy-pass-manager"; + + StringRef Val = A->getValue(); + if (Val == "per-pass") + Opts.TimePassesPerRun = false; + else if (Val == "per-pass-run") + Opts.TimePassesPerRun = true; + else + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << A->getValue(); + } + } + + Opts.PrepareForLTO = false; + Opts.PrepareForThinLTO = false; + if (Arg *A = Args.getLastArg(OPT_flto_EQ)) { + Opts.PrepareForLTO = true; + 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; + } + if (Arg *A = Args.getLastArg(OPT_fthinlto_index_EQ)) { + if (IK.getLanguage() != Language::LLVM_IR) + Diags.Report(diag::err_drv_argument_only_allowed_with) + << A->getAsString(Args) << "-x ir"; + Opts.ThinLTOIndexFile = + std::string(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", OutputFile) + .Default(llvm::sys::path::filename(OutputFile).str()); + + // The memory profile runtime appends the pid to make this name more unique. + const char *MemProfileBasename = "memprof.profraw"; + if (Args.hasArg(OPT_fmemory_profile_EQ)) { + SmallString<128> Path( + std::string(Args.getLastArgValue(OPT_fmemory_profile_EQ))); + llvm::sys::path::append(Path, MemProfileBasename); + Opts.MemoryProfileOutput = std::string(Path); + } else if (Args.hasArg(OPT_fmemory_profile)) + Opts.MemoryProfileOutput = MemProfileBasename; + + memcpy(Opts.CoverageVersion, "408*", 4); + if (Opts.EmitGcovArcs || Opts.EmitGcovNotes) { + 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); + } + } + } + // 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. + 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().matches(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'); + } + } + + 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); + + 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; + } + + if (Opts.PrepareForLTO && Args.hasArg(OPT_mibt_seal)) + Opts.IBTSeal = 1; + + 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); + } + + if (Args.getLastArg(OPT_femulated_tls) || + Args.getLastArg(OPT_fno_emulated_tls)) { + Opts.ExplicitEmulatedTLS = true; + } + + if (Arg *A = Args.getLastArg(OPT_ftlsmodel_EQ)) { + if (T.isOSAIX()) { + StringRef Name = A->getValue(); + if (Name != "global-dynamic") + Diags.Report(diag::err_aix_unsupported_tls_model) << Name; + } + } + + if (Arg *A = Args.getLastArg(OPT_fdenormal_fp_math_EQ)) { + StringRef Val = A->getValue(); + Opts.FPDenormalMode = llvm::parseDenormalFPAttribute(Val); + if (!Opts.FPDenormalMode.isValid()) + Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Val; + } + + if (Arg *A = Args.getLastArg(OPT_fdenormal_fp_math_f32_EQ)) { + StringRef Val = A->getValue(); + Opts.FP32DenormalMode = llvm::parseDenormalFPAttribute(Val); + if (!Opts.FP32DenormalMode.isValid()) + Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Val; + } + + // X86_32 has -fppc-struct-return and -freg-struct-return. + // PPC32 has -maix-struct-return and -msvr4-struct-return. + if (Arg *A = + Args.getLastArg(OPT_fpcc_struct_return, OPT_freg_struct_return, + OPT_maix_struct_return, OPT_msvr4_struct_return)) { + // TODO: We might want to consider enabling these options on AIX in the + // future. + if (T.isOSAIX()) + Diags.Report(diag::err_drv_unsupported_opt_for_target) + << A->getSpelling() << T.str(); + + const Option &O = A->getOption(); + if (O.matches(OPT_fpcc_struct_return) || + O.matches(OPT_maix_struct_return)) { + Opts.setStructReturnConvention(CodeGenOptions::SRCK_OnStack); + } else { + assert(O.matches(OPT_freg_struct_return) || + O.matches(OPT_msvr4_struct_return)); + Opts.setStructReturnConvention(CodeGenOptions::SRCK_InRegs); + } + } + + if (Arg *A = + Args.getLastArg(OPT_mabi_EQ_vec_default, OPT_mabi_EQ_vec_extabi)) { + if (!T.isOSAIX()) + Diags.Report(diag::err_drv_unsupported_opt_for_target) + << A->getSpelling() << T.str(); + + const Option &O = A->getOption(); + Opts.EnableAIXExtendedAltivecABI = O.matches(OPT_mabi_EQ_vec_extabi); + } + + bool NeedLocTracking = false; + + if (!Opts.OptRecordFile.empty()) + NeedLocTracking = true; + + if (Arg *A = Args.getLastArg(OPT_opt_record_passes)) { + Opts.OptRecordPasses = A->getValue(); + NeedLocTracking = true; + } + + if (Arg *A = Args.getLastArg(OPT_opt_record_format)) { + Opts.OptRecordFormat = A->getValue(); + NeedLocTracking = true; + } + + Opts.OptimizationRemark = + ParseOptimizationRemark(Diags, Args, OPT_Rpass_EQ, "pass"); + + Opts.OptimizationRemarkMissed = + ParseOptimizationRemark(Diags, Args, OPT_Rpass_missed_EQ, "pass-missed"); + + Opts.OptimizationRemarkAnalysis = ParseOptimizationRemark( + Diags, Args, OPT_Rpass_analysis_EQ, "pass-analysis"); + + NeedLocTracking |= Opts.OptimizationRemark.hasValidPattern() || + Opts.OptimizationRemarkMissed.hasValidPattern() || + Opts.OptimizationRemarkAnalysis.hasValidPattern(); + + 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() != Language::LLVM_IR) + Diags.Report(diag::warn_drv_diagnostics_hotness_requires_pgo) + << "-fdiagnostics-show-hotness"; + + // Parse remarks hotness threshold. Valid value is either integer or 'auto'. + if (auto *arg = + Args.getLastArg(options::OPT_fdiagnostics_hotness_threshold_EQ)) { + auto ResultOrErr = + llvm::remarks::parseHotnessThresholdOption(arg->getValue()); + + if (!ResultOrErr) { + Diags.Report(diag::err_drv_invalid_diagnotics_hotness_threshold) + << "-fdiagnostics-hotness-threshold="; + } else { + Opts.DiagnosticsHotnessThreshold = *ResultOrErr; + if ((!Opts.DiagnosticsHotnessThreshold.hasValue() || + Opts.DiagnosticsHotnessThreshold.getValue() > 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 (!Opts.StackUsageOutput.empty()) + 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); + + // 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.EmitVersionIdentMetadata = Args.hasFlag(OPT_Qy, OPT_Qn, true); + + if (Args.hasArg(options::OPT_ffinite_loops)) + Opts.FiniteLoops = CodeGenOptions::FiniteLoopsKind::Always; + else if (Args.hasArg(options::OPT_fno_finite_loops)) + Opts.FiniteLoops = CodeGenOptions::FiniteLoopsKind::Never; + + Opts.EmitIEEENaNCompliantInsts = + Args.hasFlag(options::OPT_mamdgpu_ieee, options::OPT_mno_amdgpu_ieee); + if (!Opts.EmitIEEENaNCompliantInsts && !LangOptsRef.NoHonorNaNs) + Diags.Report(diag::err_drv_amdgpu_ieee_without_no_honor_nans); + + return Diags.getNumErrors() == NumErrorsBefore; +} + +static void +GenerateDependencyOutputArgs(const DependencyOutputOptions &Opts, + SmallVectorImpl<const char *> &Args, + CompilerInvocation::StringAllocator SA) { + const DependencyOutputOptions &DependencyOutputOpts = Opts; +#define DEPENDENCY_OUTPUT_OPTION_WITH_MARSHALLING( \ + PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \ + DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \ + MERGER, EXTRACTOR, TABLE_INDEX) \ + GENERATE_OPTION_WITH_MARSHALLING( \ + Args, SA, KIND, FLAGS, SPELLING, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, \ + IMPLIED_CHECK, IMPLIED_VALUE, DENORMALIZER, EXTRACTOR, TABLE_INDEX) +#include "clang/Driver/Options.inc" +#undef DEPENDENCY_OUTPUT_OPTION_WITH_MARSHALLING + + if (Opts.ShowIncludesDest != ShowIncludesDestination::None) + GenerateArg(Args, OPT_show_includes, SA); + + for (const auto &Dep : Opts.ExtraDeps) { + switch (Dep.second) { + case EDK_SanitizeIgnorelist: + // Sanitizer ignorelist arguments are generated from LanguageOptions. + continue; + case EDK_ModuleFile: + // Module file arguments are generated from FrontendOptions and + // HeaderSearchOptions. + continue; + case EDK_ProfileList: + // Profile list arguments are generated from LanguageOptions via the + // marshalling infrastructure. + continue; + case EDK_DepFileEntry: + GenerateArg(Args, OPT_fdepfile_entry, Dep.first, SA); + break; + } + } +} + +static bool ParseDependencyOutputArgs(DependencyOutputOptions &Opts, + ArgList &Args, DiagnosticsEngine &Diags, + frontend::ActionKind Action, + bool ShowLineMarkers) { + unsigned NumErrorsBefore = Diags.getNumErrors(); + + DependencyOutputOptions &DependencyOutputOpts = Opts; +#define DEPENDENCY_OUTPUT_OPTION_WITH_MARSHALLING( \ + PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \ + DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \ + MERGER, EXTRACTOR, TABLE_INDEX) \ + PARSE_OPTION_WITH_MARSHALLING( \ + Args, Diags, ID, FLAGS, PARAM, SHOULD_PARSE, KEYPATH, DEFAULT_VALUE, \ + IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, MERGER, TABLE_INDEX) +#include "clang/Driver/Options.inc" +#undef DEPENDENCY_OUTPUT_OPTION_WITH_MARSHALLING + + 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 (Action == frontend::PrintPreprocessedInput || !ShowLineMarkers) + Opts.ShowIncludesDest = ShowIncludesDestination::Stderr; + else + Opts.ShowIncludesDest = ShowIncludesDestination::Stdout; + } else { + Opts.ShowIncludesDest = ShowIncludesDestination::None; + } + + // Add sanitizer ignorelists as extra dependencies. + // They won't be discovered by the regular preprocessor, so + // we let make / ninja to know about this implicit dependency. + if (!Args.hasArg(OPT_fno_sanitize_ignorelist)) { + for (const auto *A : Args.filtered(OPT_fsanitize_ignorelist_EQ)) { + StringRef Val = A->getValue(); + if (!Val.contains('=')) + Opts.ExtraDeps.emplace_back(std::string(Val), EDK_SanitizeIgnorelist); + } + if (Opts.IncludeSystemHeaders) { + for (const auto *A : Args.filtered(OPT_fsanitize_system_ignorelist_EQ)) { + StringRef Val = A->getValue(); + if (!Val.contains('=')) + Opts.ExtraDeps.emplace_back(std::string(Val), EDK_SanitizeIgnorelist); + } + } + } + + // -fprofile-list= dependencies. + for (const auto &Filename : Args.getAllArgValues(OPT_fprofile_list_EQ)) + Opts.ExtraDeps.emplace_back(Filename, EDK_ProfileList); + + // Propagate the extra dependencies. + for (const auto *A : Args.filtered(OPT_fdepfile_entry)) + Opts.ExtraDeps.emplace_back(A->getValue(), EDK_DepFileEntry); + + // Only the -fmodule-file=<file> form. + for (const auto *A : Args.filtered(OPT_fmodule_file)) { + StringRef Val = A->getValue(); + if (!Val.contains('=')) + Opts.ExtraDeps.emplace_back(std::string(Val), EDK_ModuleFile); + } + + return Diags.getNumErrors() == NumErrorsBefore; +} + +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 = llvm::find_if(Prefix, [](char C) { + return !isAlphanumeric(C) && C != '-' && C != '_'; + }); + if (BadChar != Prefix.end() || !isLetter(Prefix[0])) { + Success = false; + Diags.Report(diag::err_drv_invalid_value) << "-verify=" << Prefix; + Diags.Report(diag::note_drv_verify_prefix_spelling); + } + } + return Success; +} + +static void GenerateFileSystemArgs(const FileSystemOptions &Opts, + SmallVectorImpl<const char *> &Args, + CompilerInvocation::StringAllocator SA) { + const FileSystemOptions &FileSystemOpts = Opts; + +#define FILE_SYSTEM_OPTION_WITH_MARSHALLING( \ + PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \ + DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \ + MERGER, EXTRACTOR, TABLE_INDEX) \ + GENERATE_OPTION_WITH_MARSHALLING( \ + Args, SA, KIND, FLAGS, SPELLING, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, \ + IMPLIED_CHECK, IMPLIED_VALUE, DENORMALIZER, EXTRACTOR, TABLE_INDEX) +#include "clang/Driver/Options.inc" +#undef FILE_SYSTEM_OPTION_WITH_MARSHALLING +} + +static bool ParseFileSystemArgs(FileSystemOptions &Opts, const ArgList &Args, + DiagnosticsEngine &Diags) { + unsigned NumErrorsBefore = Diags.getNumErrors(); + + FileSystemOptions &FileSystemOpts = Opts; + +#define FILE_SYSTEM_OPTION_WITH_MARSHALLING( \ + PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \ + DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \ + MERGER, EXTRACTOR, TABLE_INDEX) \ + PARSE_OPTION_WITH_MARSHALLING( \ + Args, Diags, ID, FLAGS, PARAM, SHOULD_PARSE, KEYPATH, DEFAULT_VALUE, \ + IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, MERGER, TABLE_INDEX) +#include "clang/Driver/Options.inc" +#undef FILE_SYSTEM_OPTION_WITH_MARSHALLING + + return Diags.getNumErrors() == NumErrorsBefore; +} + +static void GenerateMigratorArgs(const MigratorOptions &Opts, + SmallVectorImpl<const char *> &Args, + CompilerInvocation::StringAllocator SA) { + const MigratorOptions &MigratorOpts = Opts; +#define MIGRATOR_OPTION_WITH_MARSHALLING( \ + PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \ + DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \ + MERGER, EXTRACTOR, TABLE_INDEX) \ + GENERATE_OPTION_WITH_MARSHALLING( \ + Args, SA, KIND, FLAGS, SPELLING, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, \ + IMPLIED_CHECK, IMPLIED_VALUE, DENORMALIZER, EXTRACTOR, TABLE_INDEX) +#include "clang/Driver/Options.inc" +#undef MIGRATOR_OPTION_WITH_MARSHALLING +} + +static bool ParseMigratorArgs(MigratorOptions &Opts, const ArgList &Args, + DiagnosticsEngine &Diags) { + unsigned NumErrorsBefore = Diags.getNumErrors(); + + MigratorOptions &MigratorOpts = Opts; + +#define MIGRATOR_OPTION_WITH_MARSHALLING( \ + PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \ + DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \ + MERGER, EXTRACTOR, TABLE_INDEX) \ + PARSE_OPTION_WITH_MARSHALLING( \ + Args, Diags, ID, FLAGS, PARAM, SHOULD_PARSE, KEYPATH, DEFAULT_VALUE, \ + IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, MERGER, TABLE_INDEX) +#include "clang/Driver/Options.inc" +#undef MIGRATOR_OPTION_WITH_MARSHALLING + + return Diags.getNumErrors() == NumErrorsBefore; +} + +void CompilerInvocation::GenerateDiagnosticArgs( + const DiagnosticOptions &Opts, SmallVectorImpl<const char *> &Args, + StringAllocator SA, bool DefaultDiagColor) { + const DiagnosticOptions *DiagnosticOpts = &Opts; +#define DIAG_OPTION_WITH_MARSHALLING( \ + PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \ + DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \ + MERGER, EXTRACTOR, TABLE_INDEX) \ + GENERATE_OPTION_WITH_MARSHALLING( \ + Args, SA, KIND, FLAGS, SPELLING, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, \ + IMPLIED_CHECK, IMPLIED_VALUE, DENORMALIZER, EXTRACTOR, TABLE_INDEX) +#include "clang/Driver/Options.inc" +#undef DIAG_OPTION_WITH_MARSHALLING + + if (!Opts.DiagnosticSerializationFile.empty()) + GenerateArg(Args, OPT_diagnostic_serialized_file, + Opts.DiagnosticSerializationFile, SA); + + if (Opts.ShowColors) + GenerateArg(Args, OPT_fcolor_diagnostics, SA); + + if (Opts.VerifyDiagnostics && + llvm::is_contained(Opts.VerifyPrefixes, "expected")) + GenerateArg(Args, OPT_verify, SA); + + for (const auto &Prefix : Opts.VerifyPrefixes) + if (Prefix != "expected") + GenerateArg(Args, OPT_verify_EQ, Prefix, SA); + + DiagnosticLevelMask VIU = Opts.getVerifyIgnoreUnexpected(); + if (VIU == DiagnosticLevelMask::None) { + // This is the default, don't generate anything. + } else if (VIU == DiagnosticLevelMask::All) { + GenerateArg(Args, OPT_verify_ignore_unexpected, SA); + } else { + if (static_cast<unsigned>(VIU & DiagnosticLevelMask::Note) != 0) + GenerateArg(Args, OPT_verify_ignore_unexpected_EQ, "note", SA); + if (static_cast<unsigned>(VIU & DiagnosticLevelMask::Remark) != 0) + GenerateArg(Args, OPT_verify_ignore_unexpected_EQ, "remark", SA); + if (static_cast<unsigned>(VIU & DiagnosticLevelMask::Warning) != 0) + GenerateArg(Args, OPT_verify_ignore_unexpected_EQ, "warning", SA); + if (static_cast<unsigned>(VIU & DiagnosticLevelMask::Error) != 0) + GenerateArg(Args, OPT_verify_ignore_unexpected_EQ, "error", SA); + } + + for (const auto &Warning : Opts.Warnings) { + // This option is automatically generated from UndefPrefixes. + if (Warning == "undef-prefix") + continue; + Args.push_back(SA(StringRef("-W") + Warning)); + } + + for (const auto &Remark : Opts.Remarks) { + // These arguments are generated from OptimizationRemark fields of + // CodeGenOptions. + StringRef IgnoredRemarks[] = {"pass", "no-pass", + "pass-analysis", "no-pass-analysis", + "pass-missed", "no-pass-missed"}; + if (llvm::is_contained(IgnoredRemarks, Remark)) + continue; + + Args.push_back(SA(StringRef("-R") + Remark)); + } +} + +std::unique_ptr<DiagnosticOptions> +clang::CreateAndPopulateDiagOpts(ArrayRef<const char *> Argv) { + auto DiagOpts = std::make_unique<DiagnosticOptions>(); + unsigned MissingArgIndex, MissingArgCount; + InputArgList Args = getDriverOptTable().ParseArgs( + Argv.slice(1), MissingArgIndex, MissingArgCount); + // We ignore MissingArgCount and the return value of ParseDiagnosticArgs. + // Any errors that would be diagnosed here will also be diagnosed later, + // when the DiagnosticsEngine actually exists. + (void)ParseDiagnosticArgs(*DiagOpts, Args); + return DiagOpts; +} + +bool clang::ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args, + DiagnosticsEngine *Diags, + bool DefaultDiagColor) { + Optional<DiagnosticsEngine> IgnoringDiags; + if (!Diags) { + IgnoringDiags.emplace(new DiagnosticIDs(), new DiagnosticOptions(), + new IgnoringDiagConsumer()); + Diags = &*IgnoringDiags; + } + + unsigned NumErrorsBefore = Diags->getNumErrors(); + + // The key paths of diagnostic options defined in Options.td start with + // "DiagnosticOpts->". Let's provide the expected variable name and type. + DiagnosticOptions *DiagnosticOpts = &Opts; + +#define DIAG_OPTION_WITH_MARSHALLING( \ + PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \ + DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \ + MERGER, EXTRACTOR, TABLE_INDEX) \ + PARSE_OPTION_WITH_MARSHALLING( \ + Args, *Diags, ID, FLAGS, PARAM, SHOULD_PARSE, KEYPATH, DEFAULT_VALUE, \ + IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, MERGER, TABLE_INDEX) +#include "clang/Driver/Options.inc" +#undef DIAG_OPTION_WITH_MARSHALLING + + llvm::sys::Process::UseANSIEscapeCodes(Opts.UseANSIEscapeCodes); + + if (Arg *A = + Args.getLastArg(OPT_diagnostic_serialized_file, OPT__serialize_diags)) + Opts.DiagnosticSerializationFile = A->getValue(); + Opts.ShowColors = parseShowColorsArgs(Args, DefaultDiagColor); + + 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; + else + llvm::sort(Opts.VerifyPrefixes); + DiagnosticLevelMask DiagMask = DiagnosticLevelMask::None; + 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); + if (Opts.TabStop == 0 || Opts.TabStop > DiagnosticOptions::MaxTabStop) { + Opts.TabStop = DiagnosticOptions::DefaultTabStop; + Diags->Report(diag::warn_ignoring_ftabstop_value) + << Opts.TabStop << DiagnosticOptions::DefaultTabStop; + } + + addDiagnosticArgs(Args, OPT_W_Group, OPT_W_value_Group, Opts.Warnings); + addDiagnosticArgs(Args, OPT_R_Group, OPT_R_value_Group, Opts.Remarks); + + return Diags->getNumErrors() == NumErrorsBefore; +} + +/// 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 = std::string(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 = std::string(Args[4]); + return false; +} + +/// Return a table that associates command line option specifiers with the +/// frontend action. Note: The pair {frontend::PluginAction, OPT_plugin} is +/// intentionally missing, as this case is handled separately from other +/// frontend options. +static const auto &getFrontendActionTable() { + static const std::pair<frontend::ActionKind, unsigned> Table[] = { + {frontend::ASTDeclList, OPT_ast_list}, + + {frontend::ASTDump, OPT_ast_dump_all_EQ}, + {frontend::ASTDump, OPT_ast_dump_all}, + {frontend::ASTDump, OPT_ast_dump_EQ}, + {frontend::ASTDump, OPT_ast_dump}, + {frontend::ASTDump, OPT_ast_dump_lookups}, + {frontend::ASTDump, OPT_ast_dump_decl_types}, + + {frontend::ASTPrint, OPT_ast_print}, + {frontend::ASTView, OPT_ast_view}, + {frontend::DumpCompilerOptions, OPT_compiler_options_dump}, + {frontend::DumpRawTokens, OPT_dump_raw_tokens}, + {frontend::DumpTokens, OPT_dump_tokens}, + {frontend::EmitAssembly, OPT_S}, + {frontend::EmitBC, OPT_emit_llvm_bc}, + {frontend::EmitHTML, OPT_emit_html}, + {frontend::EmitLLVM, OPT_emit_llvm}, + {frontend::EmitLLVMOnly, OPT_emit_llvm_only}, + {frontend::EmitCodeGenOnly, OPT_emit_codegen_only}, + {frontend::EmitCodeGenOnly, OPT_emit_codegen_only}, + {frontend::EmitObj, OPT_emit_obj}, + {frontend::ExtractAPI, OPT_extract_api}, + + {frontend::FixIt, OPT_fixit_EQ}, + {frontend::FixIt, OPT_fixit}, + + {frontend::GenerateModule, OPT_emit_module}, + {frontend::GenerateModuleInterface, OPT_emit_module_interface}, + {frontend::GenerateHeaderModule, OPT_emit_header_module}, + {frontend::GeneratePCH, OPT_emit_pch}, + {frontend::GenerateInterfaceStubs, OPT_emit_interface_stubs}, + {frontend::InitOnly, OPT_init_only}, + {frontend::ParseSyntaxOnly, OPT_fsyntax_only}, + {frontend::ModuleFileInfo, OPT_module_file_info}, + {frontend::VerifyPCH, OPT_verify_pch}, + {frontend::PrintPreamble, OPT_print_preamble}, + {frontend::PrintPreprocessedInput, OPT_E}, + {frontend::TemplightDump, OPT_templight_dump}, + {frontend::RewriteMacros, OPT_rewrite_macros}, + {frontend::RewriteObjC, OPT_rewrite_objc}, + {frontend::RewriteTest, OPT_rewrite_test}, + {frontend::RunAnalysis, OPT_analyze}, + {frontend::MigrateSource, OPT_migrate}, + {frontend::RunPreprocessorOnly, OPT_Eonly}, + {frontend::PrintDependencyDirectivesSourceMinimizerOutput, + OPT_print_dependency_directives_minimized_source}, + }; + + return Table; +} + +/// Maps command line option to frontend action. +static Optional<frontend::ActionKind> getFrontendAction(OptSpecifier &Opt) { + for (const auto &ActionOpt : getFrontendActionTable()) + if (ActionOpt.second == Opt.getID()) + return ActionOpt.first; + + return None; +} + +/// Maps frontend action to command line option. +static Optional<OptSpecifier> +getProgramActionOpt(frontend::ActionKind ProgramAction) { + for (const auto &ActionOpt : getFrontendActionTable()) + if (ActionOpt.first == ProgramAction) + return OptSpecifier(ActionOpt.second); + + return None; +} + +static void GenerateFrontendArgs(const FrontendOptions &Opts, + SmallVectorImpl<const char *> &Args, + CompilerInvocation::StringAllocator SA, + bool IsHeader) { + const FrontendOptions &FrontendOpts = Opts; +#define FRONTEND_OPTION_WITH_MARSHALLING( \ + PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \ + DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \ + MERGER, EXTRACTOR, TABLE_INDEX) \ + GENERATE_OPTION_WITH_MARSHALLING( \ + Args, SA, KIND, FLAGS, SPELLING, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, \ + IMPLIED_CHECK, IMPLIED_VALUE, DENORMALIZER, EXTRACTOR, TABLE_INDEX) +#include "clang/Driver/Options.inc" +#undef FRONTEND_OPTION_WITH_MARSHALLING + + Optional<OptSpecifier> ProgramActionOpt = + getProgramActionOpt(Opts.ProgramAction); + + // Generating a simple flag covers most frontend actions. + std::function<void()> GenerateProgramAction = [&]() { + GenerateArg(Args, *ProgramActionOpt, SA); + }; + + if (!ProgramActionOpt) { + // PluginAction is the only program action handled separately. + assert(Opts.ProgramAction == frontend::PluginAction && + "Frontend action without option."); + GenerateProgramAction = [&]() { + GenerateArg(Args, OPT_plugin, Opts.ActionName, SA); + }; + } + + // FIXME: Simplify the complex 'AST dump' command line. + if (Opts.ProgramAction == frontend::ASTDump) { + GenerateProgramAction = [&]() { + // ASTDumpLookups, ASTDumpDeclTypes and ASTDumpFilter are generated via + // marshalling infrastructure. + + if (Opts.ASTDumpFormat != ADOF_Default) { + StringRef Format; + switch (Opts.ASTDumpFormat) { + case ADOF_Default: + llvm_unreachable("Default AST dump format."); + case ADOF_JSON: + Format = "json"; + break; + } + + if (Opts.ASTDumpAll) + GenerateArg(Args, OPT_ast_dump_all_EQ, Format, SA); + if (Opts.ASTDumpDecls) + GenerateArg(Args, OPT_ast_dump_EQ, Format, SA); + } else { + if (Opts.ASTDumpAll) + GenerateArg(Args, OPT_ast_dump_all, SA); + if (Opts.ASTDumpDecls) + GenerateArg(Args, OPT_ast_dump, SA); + } + }; + } + + if (Opts.ProgramAction == frontend::FixIt && !Opts.FixItSuffix.empty()) { + GenerateProgramAction = [&]() { + GenerateArg(Args, OPT_fixit_EQ, Opts.FixItSuffix, SA); + }; + } + + GenerateProgramAction(); + + for (const auto &PluginArgs : Opts.PluginArgs) { + Option Opt = getDriverOptTable().getOption(OPT_plugin_arg); + const char *Spelling = + SA(Opt.getPrefix() + Opt.getName() + PluginArgs.first); + for (const auto &PluginArg : PluginArgs.second) + denormalizeString(Args, Spelling, SA, Opt.getKind(), 0, PluginArg); + } + + for (const auto &Ext : Opts.ModuleFileExtensions) + if (auto *TestExt = dyn_cast_or_null<TestModuleFileExtension>(Ext.get())) + GenerateArg(Args, OPT_ftest_module_file_extension_EQ, TestExt->str(), SA); + + if (!Opts.CodeCompletionAt.FileName.empty()) + GenerateArg(Args, OPT_code_completion_at, Opts.CodeCompletionAt.ToString(), + SA); + + for (const auto &Plugin : Opts.Plugins) + GenerateArg(Args, OPT_load, Plugin, SA); + + // ASTDumpDecls and ASTDumpAll already handled with ProgramAction. + + for (const auto &ModuleFile : Opts.ModuleFiles) + GenerateArg(Args, OPT_fmodule_file, ModuleFile, SA); + + if (Opts.AuxTargetCPU.hasValue()) + GenerateArg(Args, OPT_aux_target_cpu, *Opts.AuxTargetCPU, SA); + + if (Opts.AuxTargetFeatures.hasValue()) + for (const auto &Feature : *Opts.AuxTargetFeatures) + GenerateArg(Args, OPT_aux_target_feature, Feature, SA); + + { + StringRef Preprocessed = Opts.DashX.isPreprocessed() ? "-cpp-output" : ""; + StringRef ModuleMap = + Opts.DashX.getFormat() == InputKind::ModuleMap ? "-module-map" : ""; + StringRef Header = IsHeader ? "-header" : ""; + + StringRef Lang; + switch (Opts.DashX.getLanguage()) { + case Language::C: + Lang = "c"; + break; + case Language::OpenCL: + Lang = "cl"; + break; + case Language::OpenCLCXX: + Lang = "clcpp"; + break; + case Language::CUDA: + Lang = "cuda"; + break; + case Language::HIP: + Lang = "hip"; + break; + case Language::CXX: + Lang = "c++"; + break; + case Language::ObjC: + Lang = "objective-c"; + break; + case Language::ObjCXX: + Lang = "objective-c++"; + break; + case Language::RenderScript: + Lang = "renderscript"; + break; + case Language::Asm: + Lang = "assembler-with-cpp"; + break; + case Language::Unknown: + assert(Opts.DashX.getFormat() == InputKind::Precompiled && + "Generating -x argument for unknown language (not precompiled)."); + Lang = "ast"; + break; + case Language::LLVM_IR: + Lang = "ir"; + break; + } + + GenerateArg(Args, OPT_x, Lang + Header + ModuleMap + Preprocessed, SA); + } + + // OPT_INPUT has a unique class, generate it directly. + for (const auto &Input : Opts.Inputs) + Args.push_back(SA(Input.getFile())); +} + +static bool ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, + DiagnosticsEngine &Diags, bool &IsHeaderFile) { + unsigned NumErrorsBefore = Diags.getNumErrors(); + + FrontendOptions &FrontendOpts = Opts; + +#define FRONTEND_OPTION_WITH_MARSHALLING( \ + PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \ + DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \ + MERGER, EXTRACTOR, TABLE_INDEX) \ + PARSE_OPTION_WITH_MARSHALLING( \ + Args, Diags, ID, FLAGS, PARAM, SHOULD_PARSE, KEYPATH, DEFAULT_VALUE, \ + IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, MERGER, TABLE_INDEX) +#include "clang/Driver/Options.inc" +#undef FRONTEND_OPTION_WITH_MARSHALLING + + Opts.ProgramAction = frontend::ParseSyntaxOnly; + if (const Arg *A = Args.getLastArg(OPT_Action_Group)) { + OptSpecifier Opt = OptSpecifier(A->getOption().getID()); + Optional<frontend::ActionKind> ProgramAction = getFrontendAction(Opt); + assert(ProgramAction && "Option specifier not in Action_Group."); + + if (ProgramAction == frontend::ASTDump && + (Opt == OPT_ast_dump_all_EQ || Opt == OPT_ast_dump_EQ)) { + unsigned Val = llvm::StringSwitch<unsigned>(A->getValue()) + .CaseLower("default", ADOF_Default) + .CaseLower("json", ADOF_JSON) + .Default(std::numeric_limits<unsigned>::max()); + + if (Val != std::numeric_limits<unsigned>::max()) + Opts.ASTDumpFormat = static_cast<ASTDumpOutputFormat>(Val); + else { + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << A->getValue(); + Opts.ASTDumpFormat = ADOF_Default; + } + } + + if (ProgramAction == frontend::FixIt && Opt == OPT_fixit_EQ) + Opts.FixItSuffix = A->getValue(); + + if (ProgramAction == frontend::GenerateInterfaceStubs) { + StringRef ArgStr = + Args.hasArg(OPT_interface_stub_version_EQ) + ? Args.getLastArgValue(OPT_interface_stub_version_EQ) + : "ifs-v1"; + if (ArgStr == "experimental-yaml-elf-v1" || + ArgStr == "experimental-ifs-v1" || ArgStr == "experimental-ifs-v2" || + ArgStr == "experimental-tapi-elf-v1") { + std::string ErrorMessage = + "Invalid interface stub format: " + ArgStr.str() + + " is deprecated."; + Diags.Report(diag::err_drv_invalid_value) + << "Must specify a valid interface stub format type, ie: " + "-interface-stub-version=ifs-v1" + << ErrorMessage; + ProgramAction = frontend::ParseSyntaxOnly; + } else if (!ArgStr.startswith("ifs-")) { + std::string ErrorMessage = + "Invalid interface stub format: " + ArgStr.str() + "."; + Diags.Report(diag::err_drv_invalid_value) + << "Must specify a valid interface stub format type, ie: " + "-interface-stub-version=ifs-v1" + << ErrorMessage; + ProgramAction = frontend::ParseSyntaxOnly; + } + } + + Opts.ProgramAction = *ProgramAction; + } + + if (const Arg* A = Args.getLastArg(OPT_plugin)) { + Opts.Plugins.emplace_back(A->getValue(0)); + Opts.ProgramAction = frontend::PluginAction; + Opts.ActionName = A->getValue(); + } + 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.Plugins = Args.getAllArgValues(OPT_load); + Opts.ASTDumpDecls = Args.hasArg(OPT_ast_dump, OPT_ast_dump_EQ); + Opts.ASTDumpAll = Args.hasArg(OPT_ast_dump_all, OPT_ast_dump_all_EQ); + // Only the -fmodule-file=<file> form. + for (const auto *A : Args.filtered(OPT_fmodule_file)) { + StringRef Val = A->getValue(); + if (!Val.contains('=')) + Opts.ModuleFiles.push_back(std::string(Val)); + } + + if (Opts.ProgramAction != frontend::GenerateModule && Opts.IsSystemModule) + Diags.Report(diag::err_drv_argument_only_allowed_with) << "-fsystem-module" + << "-emit-module"; + + if (Args.hasArg(OPT_aux_target_cpu)) + Opts.AuxTargetCPU = std::string(Args.getLastArgValue(OPT_aux_target_cpu)); + if (Args.hasArg(OPT_aux_target_feature)) + Opts.AuxTargetFeatures = Args.getAllArgValues(OPT_aux_target_feature); + + 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(Language::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 != "precompiled-header" && + XValue.consume_back("-header"); + + // Principal languages. + DashX = llvm::StringSwitch<InputKind>(XValue) + .Case("c", Language::C) + .Case("cl", Language::OpenCL) + .Case("clcpp", Language::OpenCLCXX) + .Case("cuda", Language::CUDA) + .Case("hip", Language::HIP) + .Case("c++", Language::CXX) + .Case("objective-c", Language::ObjC) + .Case("objective-c++", Language::ObjCXX) + .Case("renderscript", Language::RenderScript) + .Default(Language::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", Language::ObjC) + .Case("objc++", Language::ObjCXX) + .Default(Language::Unknown); + + // Some special cases cannot be combined with suffixes. + if (DashX.isUnknown() && !Preprocessed && !ModuleMap && !IsHeaderFile) + DashX = llvm::StringSwitch<InputKind>(XValue) + .Case("cpp-output", InputKind(Language::C).getPreprocessed()) + .Case("assembler-with-cpp", Language::Asm) + .Cases("ast", "pcm", "precompiled-header", + InputKind(Language::Unknown, InputKind::Precompiled)) + .Case("ir", Language::LLVM_IR) + .Default(Language::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 = Language::C; + // FIXME: Remove this hack. + if (i == 0) + DashX = IK; + } + + bool IsSystem = false; + + // The -emit-module action implicitly takes a module map. + if (Opts.ProgramAction == frontend::GenerateModule && + IK.getFormat() == InputKind::Source) { + IK = IK.withFormat(InputKind::ModuleMap); + IsSystem = Opts.IsSystemModule; + } + + Opts.Inputs.emplace_back(std::move(Inputs[i]), IK, IsSystem); + } + + Opts.DashX = DashX; + + return Diags.getNumErrors() == NumErrorsBefore; +} + +std::string CompilerInvocation::GetResourcesPath(const char *Argv0, + void *MainAddr) { + std::string ClangExecutable = + llvm::sys::fs::getMainExecutable(Argv0, MainAddr); + return Driver::GetResourcesPath(ClangExecutable, CLANG_RESOURCE_DIR); +} + +static void GenerateHeaderSearchArgs(HeaderSearchOptions &Opts, + SmallVectorImpl<const char *> &Args, + CompilerInvocation::StringAllocator SA) { + const HeaderSearchOptions *HeaderSearchOpts = &Opts; +#define HEADER_SEARCH_OPTION_WITH_MARSHALLING( \ + PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \ + DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \ + MERGER, EXTRACTOR, TABLE_INDEX) \ + GENERATE_OPTION_WITH_MARSHALLING( \ + Args, SA, KIND, FLAGS, SPELLING, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, \ + IMPLIED_CHECK, IMPLIED_VALUE, DENORMALIZER, EXTRACTOR, TABLE_INDEX) +#include "clang/Driver/Options.inc" +#undef HEADER_SEARCH_OPTION_WITH_MARSHALLING + + if (Opts.UseLibcxx) + GenerateArg(Args, OPT_stdlib_EQ, "libc++", SA); + + if (!Opts.ModuleCachePath.empty()) + GenerateArg(Args, OPT_fmodules_cache_path, Opts.ModuleCachePath, SA); + + for (const auto &File : Opts.PrebuiltModuleFiles) + GenerateArg(Args, OPT_fmodule_file, File.first + "=" + File.second, SA); + + for (const auto &Path : Opts.PrebuiltModulePaths) + GenerateArg(Args, OPT_fprebuilt_module_path, Path, SA); + + for (const auto &Macro : Opts.ModulesIgnoreMacros) + GenerateArg(Args, OPT_fmodules_ignore_macro, Macro.val(), SA); + + auto Matches = [](const HeaderSearchOptions::Entry &Entry, + llvm::ArrayRef<frontend::IncludeDirGroup> Groups, + llvm::Optional<bool> IsFramework, + llvm::Optional<bool> IgnoreSysRoot) { + return llvm::is_contained(Groups, Entry.Group) && + (!IsFramework || (Entry.IsFramework == *IsFramework)) && + (!IgnoreSysRoot || (Entry.IgnoreSysRoot == *IgnoreSysRoot)); + }; + + auto It = Opts.UserEntries.begin(); + auto End = Opts.UserEntries.end(); + + // Add -I..., -F..., and -index-header-map options in order. + for (; It < End && + Matches(*It, {frontend::IndexHeaderMap, frontend::Angled}, None, true); + ++It) { + OptSpecifier Opt = [It, Matches]() { + if (Matches(*It, frontend::IndexHeaderMap, true, true)) + return OPT_F; + if (Matches(*It, frontend::IndexHeaderMap, false, true)) + return OPT_I; + if (Matches(*It, frontend::Angled, true, true)) + return OPT_F; + if (Matches(*It, frontend::Angled, false, true)) + return OPT_I; + llvm_unreachable("Unexpected HeaderSearchOptions::Entry."); + }(); + + if (It->Group == frontend::IndexHeaderMap) + GenerateArg(Args, OPT_index_header_map, SA); + GenerateArg(Args, Opt, It->Path, SA); + }; + + // Note: some paths that came from "[-iprefix=xx] -iwithprefixbefore=yy" may + // have already been generated as "-I[xx]yy". If that's the case, their + // position on command line was such that this has no semantic impact on + // include paths. + for (; It < End && + Matches(*It, {frontend::After, frontend::Angled}, false, true); + ++It) { + OptSpecifier Opt = + It->Group == frontend::After ? OPT_iwithprefix : OPT_iwithprefixbefore; + GenerateArg(Args, Opt, It->Path, SA); + } + + // Note: Some paths that came from "-idirafter=xxyy" may have already been + // generated as "-iwithprefix=xxyy". If that's the case, their position on + // command line was such that this has no semantic impact on include paths. + for (; It < End && Matches(*It, {frontend::After}, false, true); ++It) + GenerateArg(Args, OPT_idirafter, It->Path, SA); + for (; It < End && Matches(*It, {frontend::Quoted}, false, true); ++It) + GenerateArg(Args, OPT_iquote, It->Path, SA); + for (; It < End && Matches(*It, {frontend::System}, false, None); ++It) + GenerateArg(Args, It->IgnoreSysRoot ? OPT_isystem : OPT_iwithsysroot, + It->Path, SA); + for (; It < End && Matches(*It, {frontend::System}, true, true); ++It) + GenerateArg(Args, OPT_iframework, It->Path, SA); + for (; It < End && Matches(*It, {frontend::System}, true, false); ++It) + GenerateArg(Args, OPT_iframeworkwithsysroot, It->Path, SA); + + // Add the paths for the various language specific isystem flags. + for (; It < End && Matches(*It, {frontend::CSystem}, false, true); ++It) + GenerateArg(Args, OPT_c_isystem, It->Path, SA); + for (; It < End && Matches(*It, {frontend::CXXSystem}, false, true); ++It) + GenerateArg(Args, OPT_cxx_isystem, It->Path, SA); + for (; It < End && Matches(*It, {frontend::ObjCSystem}, false, true); ++It) + GenerateArg(Args, OPT_objc_isystem, It->Path, SA); + for (; It < End && Matches(*It, {frontend::ObjCXXSystem}, false, true); ++It) + GenerateArg(Args, OPT_objcxx_isystem, It->Path, SA); + + // Add the internal paths from a driver that detects standard include paths. + // Note: Some paths that came from "-internal-isystem" arguments may have + // already been generated as "-isystem". If that's the case, their position on + // command line was such that this has no semantic impact on include paths. + for (; It < End && + Matches(*It, {frontend::System, frontend::ExternCSystem}, false, true); + ++It) { + OptSpecifier Opt = It->Group == frontend::System + ? OPT_internal_isystem + : OPT_internal_externc_isystem; + GenerateArg(Args, Opt, It->Path, SA); + } + + assert(It == End && "Unhandled HeaderSearchOption::Entry."); + + // Add the path prefixes which are implicitly treated as being system headers. + for (const auto &P : Opts.SystemHeaderPrefixes) { + OptSpecifier Opt = P.IsSystemHeader ? OPT_system_header_prefix + : OPT_no_system_header_prefix; + GenerateArg(Args, Opt, P.Prefix, SA); + } + + for (const std::string &F : Opts.VFSOverlayFiles) + GenerateArg(Args, OPT_ivfsoverlay, F, SA); +} + +static bool ParseHeaderSearchArgs(HeaderSearchOptions &Opts, ArgList &Args, + DiagnosticsEngine &Diags, + const std::string &WorkingDir) { + unsigned NumErrorsBefore = Diags.getNumErrors(); + + HeaderSearchOptions *HeaderSearchOpts = &Opts; + +#define HEADER_SEARCH_OPTION_WITH_MARSHALLING( \ + PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \ + DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \ + MERGER, EXTRACTOR, TABLE_INDEX) \ + PARSE_OPTION_WITH_MARSHALLING( \ + Args, Diags, ID, FLAGS, PARAM, SHOULD_PARSE, KEYPATH, DEFAULT_VALUE, \ + IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, MERGER, TABLE_INDEX) +#include "clang/Driver/Options.inc" +#undef HEADER_SEARCH_OPTION_WITH_MARSHALLING + + if (const Arg *A = Args.getLastArg(OPT_stdlib_EQ)) + Opts.UseLibcxx = (strcmp(A->getValue(), "libc++") == 0); + + // 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 = std::string(P.str()); + + // Only the -fmodule-file=<name>=<file> form. + for (const auto *A : Args.filtered(OPT_fmodule_file)) { + StringRef Val = A->getValue(); + if (Val.contains('=')) { + auto Split = Val.split('='); + Opts.PrebuiltModuleFiles.insert( + {std::string(Split.first), std::string(Split.second)}); + } + } + for (const auto *A : Args.filtered(OPT_fprebuilt_module_path)) + Opts.AddPrebuiltModulePath(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 = std::string(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()); + + Opts.CaseInsensitive = Args.hasArg(OPT_fcase_insensitive_paths); + + return Diags.getNumErrors() == NumErrorsBefore; +} + +void CompilerInvocation::setLangDefaults(LangOptions &Opts, InputKind IK, + const llvm::Triple &T, + std::vector<std::string> &Includes, + 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() == Language::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 Language::Unknown: + case Language::LLVM_IR: + llvm_unreachable("Invalid input kind!"); + case Language::OpenCL: + LangStd = LangStandard::lang_opencl12; + break; + case Language::OpenCLCXX: + LangStd = LangStandard::lang_openclcpp10; + break; + case Language::CUDA: + LangStd = LangStandard::lang_cuda; + break; + case Language::Asm: + case Language::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_gnu17; +#endif + break; + case Language::ObjC: +#if defined(CLANG_DEFAULT_STD_C) + LangStd = CLANG_DEFAULT_STD_C; +#else + LangStd = LangStandard::lang_gnu11; +#endif + break; + case Language::CXX: + case Language::ObjCXX: +#if defined(CLANG_DEFAULT_STD_CXX) + LangStd = CLANG_DEFAULT_STD_CXX; +#else + LangStd = LangStandard::lang_gnucxx14; +#endif + break; + case Language::RenderScript: + LangStd = LangStandard::lang_c99; + break; + case Language::HIP: + LangStd = LangStandard::lang_hip; + break; + } + } + + const LangStandard &Std = LangStandard::getLangStandardForKind(LangStd); + Opts.LangStd = LangStd; + Opts.LineComment = Std.hasLineComments(); + Opts.C99 = Std.isC99(); + Opts.C11 = Std.isC11(); + Opts.C17 = Std.isC17(); + Opts.C2x = Std.isC2x(); + Opts.CPlusPlus = Std.isCPlusPlus(); + Opts.CPlusPlus11 = Std.isCPlusPlus11(); + Opts.CPlusPlus14 = Std.isCPlusPlus14(); + Opts.CPlusPlus17 = Std.isCPlusPlus17(); + Opts.CPlusPlus20 = Std.isCPlusPlus20(); + Opts.CPlusPlus2b = Std.isCPlusPlus2b(); + Opts.GNUMode = Std.isGNUMode(); + Opts.GNUCVersion = 0; + 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_opencl30) + Opts.OpenCLVersion = 300; + else if (LangStd == LangStandard::lang_openclcpp10) + Opts.OpenCLCPlusPlusVersion = 100; + else if (LangStd == LangStandard::lang_openclcpp2021) + Opts.OpenCLCPlusPlusVersion = 202100; + + // OpenCL has some additional defaults. + if (Opts.OpenCL) { + Opts.AltiVec = 0; + Opts.ZVector = 0; + Opts.setDefaultFPContractMode(LangOptions::FPM_On); + Opts.OpenCLCPlusPlus = Opts.CPlusPlus; + Opts.OpenCLPipes = Opts.getOpenCLCompatibleVersion() == 200; + Opts.OpenCLGenericAddressSpace = Opts.getOpenCLCompatibleVersion() == 200; + + // Include default header file for OpenCL. + if (Opts.IncludeDefaultHeader) { + if (Opts.DeclareOpenCLBuiltins) { + // Only include base header file for builtin types and constants. + Includes.push_back("opencl-c-base.h"); + } else { + Includes.push_back("opencl-c.h"); + } + } + } + + Opts.HIP = IK.getLanguage() == Language::HIP; + Opts.CUDA = IK.getLanguage() == Language::CUDA || Opts.HIP; + if (Opts.HIP) { + // HIP toolchain does not support 'Fast' FPOpFusion in backends since it + // fuses multiplication/addition instructions without contract flag from + // device library functions in LLVM bitcode, which causes accuracy loss in + // certain math functions, e.g. tan(-1e20) becomes -0.933 instead of 0.8446. + // For device library functions in bitcode to work, 'Strict' or 'Standard' + // FPOpFusion options in backends is needed. Therefore 'fast-honor-pragmas' + // FP contract option is used to allow fuse across statements in frontend + // whereas respecting contract flag in backend. + Opts.setDefaultFPContractMode(LangOptions::FPM_FastHonorPragmas); + } else if (Opts.CUDA) { + // Allow fuse across statements disregarding pragmas. + Opts.setDefaultFPContractMode(LangOptions::FPM_Fast); + } + + Opts.RenderScript = IK.getLanguage() == Language::RenderScript; + + // OpenCL and C++ both have bool, true, false keywords. + Opts.Bool = Opts.OpenCL || Opts.CPlusPlus; + + // OpenCL has half keyword + Opts.Half = Opts.OpenCL; +} + +/// Check if input file kind and language standard are compatible. +static bool IsInputCompatibleWithStandard(InputKind IK, + const LangStandard &S) { + switch (IK.getLanguage()) { + case Language::Unknown: + case Language::LLVM_IR: + llvm_unreachable("should not parse language flags for this input"); + + case Language::C: + case Language::ObjC: + case Language::RenderScript: + return S.getLanguage() == Language::C; + + case Language::OpenCL: + return S.getLanguage() == Language::OpenCL || + S.getLanguage() == Language::OpenCLCXX; + + case Language::OpenCLCXX: + return S.getLanguage() == Language::OpenCLCXX; + + case Language::CXX: + case Language::ObjCXX: + return S.getLanguage() == Language::CXX; + + case Language::CUDA: + // FIXME: What -std= values should be permitted for CUDA compilations? + return S.getLanguage() == Language::CUDA || + S.getLanguage() == Language::CXX; + + case Language::HIP: + return S.getLanguage() == Language::CXX || S.getLanguage() == Language::HIP; + + case Language::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 StringRef GetInputKindName(InputKind IK) { + switch (IK.getLanguage()) { + case Language::C: + return "C"; + case Language::ObjC: + return "Objective-C"; + case Language::CXX: + return "C++"; + case Language::ObjCXX: + return "Objective-C++"; + case Language::OpenCL: + return "OpenCL"; + case Language::OpenCLCXX: + return "C++ for OpenCL"; + case Language::CUDA: + return "CUDA"; + case Language::RenderScript: + return "RenderScript"; + case Language::HIP: + return "HIP"; + + case Language::Asm: + return "Asm"; + case Language::LLVM_IR: + return "LLVM IR"; + + case Language::Unknown: + break; + } + llvm_unreachable("unknown input language"); +} + +void CompilerInvocation::GenerateLangArgs(const LangOptions &Opts, + SmallVectorImpl<const char *> &Args, + StringAllocator SA, + const llvm::Triple &T, InputKind IK) { + if (IK.getFormat() == InputKind::Precompiled || + IK.getLanguage() == Language::LLVM_IR) { + if (Opts.ObjCAutoRefCount) + GenerateArg(Args, OPT_fobjc_arc, SA); + if (Opts.PICLevel != 0) + GenerateArg(Args, OPT_pic_level, Twine(Opts.PICLevel), SA); + if (Opts.PIE) + GenerateArg(Args, OPT_pic_is_pie, SA); + for (StringRef Sanitizer : serializeSanitizerKinds(Opts.Sanitize)) + GenerateArg(Args, OPT_fsanitize_EQ, Sanitizer, SA); + + return; + } + + OptSpecifier StdOpt; + switch (Opts.LangStd) { + case LangStandard::lang_opencl10: + case LangStandard::lang_opencl11: + case LangStandard::lang_opencl12: + case LangStandard::lang_opencl20: + case LangStandard::lang_opencl30: + case LangStandard::lang_openclcpp10: + case LangStandard::lang_openclcpp2021: + StdOpt = OPT_cl_std_EQ; + break; + default: + StdOpt = OPT_std_EQ; + break; + } + + auto LangStandard = LangStandard::getLangStandardForKind(Opts.LangStd); + GenerateArg(Args, StdOpt, LangStandard.getName(), SA); + + if (Opts.IncludeDefaultHeader) + GenerateArg(Args, OPT_finclude_default_header, SA); + if (Opts.DeclareOpenCLBuiltins) + GenerateArg(Args, OPT_fdeclare_opencl_builtins, SA); + + const LangOptions *LangOpts = &Opts; + +#define LANG_OPTION_WITH_MARSHALLING( \ + PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \ + DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \ + MERGER, EXTRACTOR, TABLE_INDEX) \ + GENERATE_OPTION_WITH_MARSHALLING( \ + Args, SA, KIND, FLAGS, SPELLING, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, \ + IMPLIED_CHECK, IMPLIED_VALUE, DENORMALIZER, EXTRACTOR, TABLE_INDEX) +#include "clang/Driver/Options.inc" +#undef LANG_OPTION_WITH_MARSHALLING + + // The '-fcf-protection=' option is generated by CodeGenOpts generator. + + if (Opts.ObjC) { + GenerateArg(Args, OPT_fobjc_runtime_EQ, Opts.ObjCRuntime.getAsString(), SA); + + if (Opts.GC == LangOptions::GCOnly) + GenerateArg(Args, OPT_fobjc_gc_only, SA); + else if (Opts.GC == LangOptions::HybridGC) + GenerateArg(Args, OPT_fobjc_gc, SA); + else if (Opts.ObjCAutoRefCount == 1) + GenerateArg(Args, OPT_fobjc_arc, SA); + + if (Opts.ObjCWeakRuntime) + GenerateArg(Args, OPT_fobjc_runtime_has_weak, SA); + + if (Opts.ObjCWeak) + GenerateArg(Args, OPT_fobjc_weak, SA); + + if (Opts.ObjCSubscriptingLegacyRuntime) + GenerateArg(Args, OPT_fobjc_subscripting_legacy_runtime, SA); + } + + if (Opts.GNUCVersion != 0) { + unsigned Major = Opts.GNUCVersion / 100 / 100; + unsigned Minor = (Opts.GNUCVersion / 100) % 100; + unsigned Patch = Opts.GNUCVersion % 100; + GenerateArg(Args, OPT_fgnuc_version_EQ, + Twine(Major) + "." + Twine(Minor) + "." + Twine(Patch), SA); + } + + if (Opts.IgnoreXCOFFVisibility) + GenerateArg(Args, OPT_mignore_xcoff_visibility, SA); + + if (Opts.SignedOverflowBehavior == LangOptions::SOB_Trapping) { + GenerateArg(Args, OPT_ftrapv, SA); + GenerateArg(Args, OPT_ftrapv_handler, Opts.OverflowHandler, SA); + } else if (Opts.SignedOverflowBehavior == LangOptions::SOB_Defined) { + GenerateArg(Args, OPT_fwrapv, SA); + } + + if (Opts.MSCompatibilityVersion != 0) { + unsigned Major = Opts.MSCompatibilityVersion / 10000000; + unsigned Minor = (Opts.MSCompatibilityVersion / 100000) % 100; + unsigned Subminor = Opts.MSCompatibilityVersion % 100000; + GenerateArg(Args, OPT_fms_compatibility_version, + Twine(Major) + "." + Twine(Minor) + "." + Twine(Subminor), SA); + } + + if ((!Opts.GNUMode && !Opts.MSVCCompat && !Opts.CPlusPlus17) || T.isOSzOS()) { + if (!Opts.Trigraphs) + GenerateArg(Args, OPT_fno_trigraphs, SA); + } else { + if (Opts.Trigraphs) + GenerateArg(Args, OPT_ftrigraphs, SA); + } + + if (Opts.Blocks && !(Opts.OpenCL && Opts.OpenCLVersion == 200)) + GenerateArg(Args, OPT_fblocks, SA); + + if (Opts.ConvergentFunctions && + !(Opts.OpenCL || (Opts.CUDA && Opts.CUDAIsDevice) || Opts.SYCLIsDevice)) + GenerateArg(Args, OPT_fconvergent_functions, SA); + + if (Opts.NoBuiltin && !Opts.Freestanding) + GenerateArg(Args, OPT_fno_builtin, SA); + + if (!Opts.NoBuiltin) + for (const auto &Func : Opts.NoBuiltinFuncs) + GenerateArg(Args, OPT_fno_builtin_, Func, SA); + + if (Opts.LongDoubleSize == 128) + GenerateArg(Args, OPT_mlong_double_128, SA); + else if (Opts.LongDoubleSize == 64) + GenerateArg(Args, OPT_mlong_double_64, SA); + + // Not generating '-mrtd', it's just an alias for '-fdefault-calling-conv='. + + // OpenMP was requested via '-fopenmp', not implied by '-fopenmp-simd' or + // '-fopenmp-targets='. + if (Opts.OpenMP && !Opts.OpenMPSimd) { + GenerateArg(Args, OPT_fopenmp, SA); + + if (Opts.OpenMP != 50) + GenerateArg(Args, OPT_fopenmp_version_EQ, Twine(Opts.OpenMP), SA); + + if (!Opts.OpenMPUseTLS) + GenerateArg(Args, OPT_fnoopenmp_use_tls, SA); + + if (Opts.OpenMPIsDevice) + GenerateArg(Args, OPT_fopenmp_is_device, SA); + + if (Opts.OpenMPIRBuilder) + GenerateArg(Args, OPT_fopenmp_enable_irbuilder, SA); + } + + if (Opts.OpenMPSimd) { + GenerateArg(Args, OPT_fopenmp_simd, SA); + + if (Opts.OpenMP != 50) + GenerateArg(Args, OPT_fopenmp_version_EQ, Twine(Opts.OpenMP), SA); + } + + if (Opts.OpenMPTargetNewRuntime) + GenerateArg(Args, OPT_fopenmp_target_new_runtime, SA); + + if (Opts.OpenMPThreadSubscription) + GenerateArg(Args, OPT_fopenmp_assume_threads_oversubscription, SA); + + if (Opts.OpenMPTeamSubscription) + GenerateArg(Args, OPT_fopenmp_assume_teams_oversubscription, SA); + + if (Opts.OpenMPTargetDebug != 0) + GenerateArg(Args, OPT_fopenmp_target_debug_EQ, + Twine(Opts.OpenMPTargetDebug), SA); + + if (Opts.OpenMPCUDANumSMs != 0) + GenerateArg(Args, OPT_fopenmp_cuda_number_of_sm_EQ, + Twine(Opts.OpenMPCUDANumSMs), SA); + + if (Opts.OpenMPCUDABlocksPerSM != 0) + GenerateArg(Args, OPT_fopenmp_cuda_blocks_per_sm_EQ, + Twine(Opts.OpenMPCUDABlocksPerSM), SA); + + if (Opts.OpenMPCUDAReductionBufNum != 1024) + GenerateArg(Args, OPT_fopenmp_cuda_teams_reduction_recs_num_EQ, + Twine(Opts.OpenMPCUDAReductionBufNum), SA); + + if (!Opts.OMPTargetTriples.empty()) { + std::string Targets; + llvm::raw_string_ostream OS(Targets); + llvm::interleave( + Opts.OMPTargetTriples, OS, + [&OS](const llvm::Triple &T) { OS << T.str(); }, ","); + GenerateArg(Args, OPT_fopenmp_targets_EQ, OS.str(), SA); + } + + if (!Opts.OMPHostIRFile.empty()) + GenerateArg(Args, OPT_fopenmp_host_ir_file_path, Opts.OMPHostIRFile, SA); + + if (Opts.OpenMPCUDAMode) + GenerateArg(Args, OPT_fopenmp_cuda_mode, SA); + + if (Opts.OpenMPCUDAForceFullRuntime) + GenerateArg(Args, OPT_fopenmp_cuda_force_full_runtime, SA); + + // The arguments used to set Optimize, OptimizeSize and NoInlineDefine are + // generated from CodeGenOptions. + + if (Opts.DefaultFPContractMode == LangOptions::FPM_Fast) + GenerateArg(Args, OPT_ffp_contract, "fast", SA); + else if (Opts.DefaultFPContractMode == LangOptions::FPM_On) + GenerateArg(Args, OPT_ffp_contract, "on", SA); + else if (Opts.DefaultFPContractMode == LangOptions::FPM_Off) + GenerateArg(Args, OPT_ffp_contract, "off", SA); + else if (Opts.DefaultFPContractMode == LangOptions::FPM_FastHonorPragmas) + GenerateArg(Args, OPT_ffp_contract, "fast-honor-pragmas", SA); + + for (StringRef Sanitizer : serializeSanitizerKinds(Opts.Sanitize)) + GenerateArg(Args, OPT_fsanitize_EQ, Sanitizer, SA); + + // Conflating '-fsanitize-system-ignorelist' and '-fsanitize-ignorelist'. + for (const std::string &F : Opts.NoSanitizeFiles) + GenerateArg(Args, OPT_fsanitize_ignorelist_EQ, F, SA); + + if (Opts.getClangABICompat() == LangOptions::ClangABI::Ver3_8) + GenerateArg(Args, OPT_fclang_abi_compat_EQ, "3.8", SA); + else if (Opts.getClangABICompat() == LangOptions::ClangABI::Ver4) + GenerateArg(Args, OPT_fclang_abi_compat_EQ, "4.0", SA); + else if (Opts.getClangABICompat() == LangOptions::ClangABI::Ver6) + GenerateArg(Args, OPT_fclang_abi_compat_EQ, "6.0", SA); + else if (Opts.getClangABICompat() == LangOptions::ClangABI::Ver7) + GenerateArg(Args, OPT_fclang_abi_compat_EQ, "7.0", SA); + else if (Opts.getClangABICompat() == LangOptions::ClangABI::Ver9) + GenerateArg(Args, OPT_fclang_abi_compat_EQ, "9.0", SA); + else if (Opts.getClangABICompat() == LangOptions::ClangABI::Ver11) + GenerateArg(Args, OPT_fclang_abi_compat_EQ, "11.0", SA); + else if (Opts.getClangABICompat() == LangOptions::ClangABI::Ver12) + GenerateArg(Args, OPT_fclang_abi_compat_EQ, "12.0", SA); + + if (Opts.getSignReturnAddressScope() == + LangOptions::SignReturnAddressScopeKind::All) + GenerateArg(Args, OPT_msign_return_address_EQ, "all", SA); + else if (Opts.getSignReturnAddressScope() == + LangOptions::SignReturnAddressScopeKind::NonLeaf) + GenerateArg(Args, OPT_msign_return_address_EQ, "non-leaf", SA); + + if (Opts.getSignReturnAddressKey() == + LangOptions::SignReturnAddressKeyKind::BKey) + GenerateArg(Args, OPT_msign_return_address_key_EQ, "b_key", SA); + + if (Opts.CXXABI) + GenerateArg(Args, OPT_fcxx_abi_EQ, TargetCXXABI::getSpelling(*Opts.CXXABI), + SA); + + if (Opts.RelativeCXXABIVTables) + GenerateArg(Args, OPT_fexperimental_relative_cxx_abi_vtables, SA); + else + GenerateArg(Args, OPT_fno_experimental_relative_cxx_abi_vtables, SA); + + for (const auto &MP : Opts.MacroPrefixMap) + GenerateArg(Args, OPT_fmacro_prefix_map_EQ, MP.first + "=" + MP.second, SA); +} + +bool CompilerInvocation::ParseLangArgs(LangOptions &Opts, ArgList &Args, + InputKind IK, const llvm::Triple &T, + std::vector<std::string> &Includes, + DiagnosticsEngine &Diags) { + unsigned NumErrorsBefore = Diags.getNumErrors(); + + if (IK.getFormat() == InputKind::Precompiled || + IK.getLanguage() == Language::LLVM_IR) { + // ObjCAAutoRefCount and Sanitize LangOpts are used to setup the + // PassManager in BackendUtil.cpp. They need to be initialized no matter + // what the input type is. + if (Args.hasArg(OPT_fobjc_arc)) + Opts.ObjCAutoRefCount = 1; + // PICLevel and PIELevel are needed during code generation and this should + // be set regardless of the input type. + Opts.PICLevel = getLastArgIntValue(Args, OPT_pic_level, 0, Diags); + Opts.PIE = Args.hasArg(OPT_pic_is_pie); + parseSanitizerKinds("-fsanitize=", Args.getAllArgValues(OPT_fsanitize_EQ), + Diags, Opts.Sanitize); + + return Diags.getNumErrors() == NumErrorsBefore; + } + + // Other LangOpts are only initialized when the input is not AST or LLVM IR. + // FIXME: Should we really be parsing this for an Language::Asm input? + + // FIXME: Cleanup per-file based stuff. + LangStandard::Kind LangStd = LangStandard::lang_unspecified; + if (const Arg *A = Args.getLastArg(OPT_std_EQ)) { + LangStd = LangStandard::getLangKind(A->getValue()); + 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/Basic/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/Basic/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); + } + } + } + + // -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.0", "CL1.0", 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) + .Cases("cl3.0", "CL3.0", LangStandard::lang_opencl30) + .Cases("clc++", "CLC++", LangStandard::lang_openclcpp10) + .Cases("clc++1.0", "CLC++1.0", LangStandard::lang_openclcpp10) + .Cases("clc++2021", "CLC++2021", LangStandard::lang_openclcpp2021) + .Default(LangStandard::lang_unspecified); + + if (OpenCLLangStd == LangStandard::lang_unspecified) { + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << A->getValue(); + } + else + LangStd = OpenCLLangStd; + } + + // These need to be parsed now. They are used to set OpenCL defaults. + Opts.IncludeDefaultHeader = Args.hasArg(OPT_finclude_default_header); + Opts.DeclareOpenCLBuiltins = Args.hasArg(OPT_fdeclare_opencl_builtins); + + CompilerInvocation::setLangDefaults(Opts, IK, T, Includes, LangStd); + + // The key paths of codegen options defined in Options.td start with + // "LangOpts->". Let's provide the expected variable name and type. + LangOptions *LangOpts = &Opts; + +#define LANG_OPTION_WITH_MARSHALLING( \ + PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \ + DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \ + MERGER, EXTRACTOR, TABLE_INDEX) \ + PARSE_OPTION_WITH_MARSHALLING( \ + Args, Diags, ID, FLAGS, PARAM, SHOULD_PARSE, KEYPATH, DEFAULT_VALUE, \ + IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, MERGER, TABLE_INDEX) +#include "clang/Driver/Options.inc" +#undef LANG_OPTION_WITH_MARSHALLING + + if (const Arg *A = Args.getLastArg(OPT_fcf_protection_EQ)) { + StringRef Name = A->getValue(); + if (Name == "full" || Name == "branch") { + Opts.CFProtectionBranch = 1; + } + } + + if ((Args.hasArg(OPT_fsycl_is_device) || Args.hasArg(OPT_fsycl_is_host)) && + !Args.hasArg(OPT_sycl_std_EQ)) { + // If the user supplied -fsycl-is-device or -fsycl-is-host, but failed to + // provide -sycl-std=, we want to default it to whatever the default SYCL + // version is. I could not find a way to express this with the options + // tablegen because we still want this value to be SYCL_None when the user + // is not in device or host mode. + Opts.setSYCLVersion(LangOptions::SYCL_Default); + } + + 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_fobjc_subscripting_legacy_runtime)) + Opts.ObjCSubscriptingLegacyRuntime = + (Opts.ObjCRuntime.getKind() == ObjCRuntime::FragileMacOSX); + } + + if (Arg *A = Args.getLastArg(options::OPT_fgnuc_version_EQ)) { + // Check that the version has 1 to 3 components and the minor and patch + // versions fit in two decimal digits. + VersionTuple GNUCVer; + bool Invalid = GNUCVer.tryParse(A->getValue()); + unsigned Major = GNUCVer.getMajor(); + unsigned Minor = GNUCVer.getMinor().getValueOr(0); + unsigned Patch = GNUCVer.getSubminor().getValueOr(0); + if (Invalid || GNUCVer.getBuild() || Minor >= 100 || Patch >= 100) { + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << A->getValue(); + } + Opts.GNUCVersion = Major * 100 * 100 + Minor * 100 + Patch; + } + + // In AIX OS, the -mignore-xcoff-visibility is enable by default if there is + // no -fvisibility=* option. + // This is the reason why '-fvisibility' needs to be always generated: + // its absence implies '-mignore-xcoff-visibility'. + // + // Suppose the original cc1 command line does contain '-fvisibility default': + // '-mignore-xcoff-visibility' should not be implied. + // * If '-fvisibility' is not generated (as most options with default values + // don't), its absence would imply '-mignore-xcoff-visibility'. This changes + // the command line semantics. + // * If '-fvisibility' is generated regardless of its presence and value, + // '-mignore-xcoff-visibility' won't be implied and the command line + // semantics are kept intact. + // + // When the original cc1 command line does **not** contain '-fvisibility', + // '-mignore-xcoff-visibility' is implied. The generated command line will + // contain both '-fvisibility default' and '-mignore-xcoff-visibility' and + // subsequent calls to `CreateFromArgs`/`generateCC1CommandLine` will always + // produce the same arguments. + + if (T.isOSAIX() && (Args.hasArg(OPT_mignore_xcoff_visibility) || + !Args.hasArg(OPT_fvisibility))) + Opts.IgnoreXCOFFVisibility = 1; + + if (Args.hasArg(OPT_ftrapv)) { + Opts.setSignedOverflowBehavior(LangOptions::SOB_Trapping); + // Set the handler, if one is specified. + Opts.OverflowHandler = + std::string(Args.getLastArgValue(OPT_ftrapv_handler)); + } + else if (Args.hasArg(OPT_fwrapv)) + Opts.setSignedOverflowBehavior(LangOptions::SOB_Defined); + + Opts.CaseInsensitive = Args.hasArg(OPT_fcase_insensitive_paths); + + 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. + // For z/OS, trigraphs are enabled by default (without regard to the above). + Opts.Trigraphs = + (!Opts.GNUMode && !Opts.MSVCCompat && !Opts.CPlusPlus17) || T.isOSzOS(); + Opts.Trigraphs = + Args.hasFlag(OPT_ftrigraphs, OPT_fno_trigraphs, Opts.Trigraphs); + + Opts.Blocks = Args.hasArg(OPT_fblocks) || (Opts.OpenCL + && Opts.OpenCLVersion == 200); + + Opts.ConvergentFunctions = Opts.OpenCL || (Opts.CUDA && Opts.CUDAIsDevice) || + Opts.SYCLIsDevice || + Args.hasArg(OPT_fconvergent_functions); + + Opts.NoBuiltin = Args.hasArg(OPT_fno_builtin) || Opts.Freestanding; + if (!Opts.NoBuiltin) + getAllNoBuiltinFuncValues(Args, Opts.NoBuiltinFuncs); + Opts.LongDoubleSize = Args.hasArg(OPT_mlong_double_128) + ? 128 + : Args.hasArg(OPT_mlong_double_64) ? 64 : 0; + if (Opts.FastRelaxedMath) + Opts.setDefaultFPContractMode(LangOptions::FPM_Fast); + llvm::sort(Opts.ModuleFeatures); + + // -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 { + 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 and set default version to 5.0. + Opts.OpenMP = Args.hasArg(OPT_fopenmp) ? 50 : 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); + Opts.OpenMPIRBuilder = + Opts.OpenMP && Args.hasArg(options::OPT_fopenmp_enable_irbuilder); + bool IsTargetSpecified = + Opts.OpenMPIsDevice || Args.hasArg(options::OPT_fopenmp_targets_EQ); + Opts.OpenMPTargetNewRuntime = + Opts.OpenMPIsDevice && + Args.hasArg(options::OPT_fopenmp_target_new_runtime); + + Opts.ConvergentFunctions = Opts.ConvergentFunctions || Opts.OpenMPIsDevice; + + if (Opts.OpenMP || Opts.OpenMPSimd) { + if (int Version = getLastArgIntValue( + Args, OPT_fopenmp_version_EQ, + (IsSimdSpecified || IsTargetSpecified) ? 50 : Opts.OpenMP, Diags)) + Opts.OpenMP = Version; + // 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) << T.str(); + break; + } + } + } + + // Set the flag to prevent the implementation from emitting device exception + // handling code for those requiring so. + if ((Opts.OpenMPIsDevice && (T.isNVPTX() || T.isAMDGCN())) || + 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); + Opts.OpenMPCUDAReductionBufNum = getLastArgIntValue( + Args, options::OPT_fopenmp_cuda_teams_reduction_recs_num_EQ, + Opts.OpenMPCUDAReductionBufNum, Diags); + } + + // Set the value of the debugging flag used in the new offloading device RTL. + // Set either by a specific value or to a default if not specified. + if (Opts.OpenMPIsDevice && (Args.hasArg(OPT_fopenmp_target_debug) || + Args.hasArg(OPT_fopenmp_target_debug_EQ))) { + if (Opts.OpenMPTargetNewRuntime) { + Opts.OpenMPTargetDebug = getLastArgIntValue( + Args, OPT_fopenmp_target_debug_EQ, Opts.OpenMPTargetDebug, Diags); + if (!Opts.OpenMPTargetDebug && Args.hasArg(OPT_fopenmp_target_debug)) + Opts.OpenMPTargetDebug = 1; + } else { + Diags.Report(diag::err_drv_debug_no_new_runtime); + } + } + + if (Opts.OpenMPIsDevice && Opts.OpenMPTargetNewRuntime) { + if (Args.hasArg(OPT_fopenmp_assume_teams_oversubscription)) + Opts.OpenMPTeamSubscription = true; + if (Args.hasArg(OPT_fopenmp_assume_threads_oversubscription)) + Opts.OpenMPThreadSubscription = true; + } + + // Get the OpenMP target triples if any. + if (Arg *A = Args.getLastArg(options::OPT_fopenmp_targets_EQ)) { + enum ArchPtrSize { Arch16Bit, Arch32Bit, Arch64Bit }; + auto getArchPtrSize = [](const llvm::Triple &T) { + if (T.isArch16Bit()) + return Arch16Bit; + if (T.isArch32Bit()) + return Arch32Bit; + assert(T.isArch64Bit() && "Expected 64-bit architecture"); + return Arch64Bit; + }; + + for (unsigned i = 0; i < A->getNumValues(); ++i) { + llvm::Triple TT(A->getValue(i)); + + if (TT.getArch() == llvm::Triple::UnknownArch || + !(TT.getArch() == llvm::Triple::aarch64 || TT.isPPC() || + TT.getArch() == llvm::Triple::nvptx || + TT.getArch() == llvm::Triple::nvptx64 || + TT.getArch() == llvm::Triple::amdgcn || + TT.getArch() == llvm::Triple::x86 || + TT.getArch() == llvm::Triple::x86_64)) + Diags.Report(diag::err_drv_invalid_omp_target) << A->getValue(i); + else if (getArchPtrSize(T) != getArchPtrSize(TT)) + Diags.Report(diag::err_drv_incompatible_omp_arch) + << A->getValue(i) << T.str(); + 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/AMDGCN if specified in options + Opts.OpenMPCUDAMode = Opts.OpenMPIsDevice && (T.isNVPTX() || T.isAMDGCN()) && + Args.hasArg(options::OPT_fopenmp_cuda_mode); + + // Set CUDA mode for OpenMP target NVPTX/AMDGCN if specified in options + Opts.OpenMPCUDAForceFullRuntime = + Opts.OpenMPIsDevice && (T.isNVPTX() || T.isAMDGCN()) && + Args.hasArg(options::OPT_fopenmp_cuda_force_full_runtime); + + // 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; + + if (Arg *A = Args.getLastArg(OPT_ffp_contract)) { + StringRef Val = A->getValue(); + if (Val == "fast") + Opts.setDefaultFPContractMode(LangOptions::FPM_Fast); + else if (Val == "on") + Opts.setDefaultFPContractMode(LangOptions::FPM_On); + else if (Val == "off") + Opts.setDefaultFPContractMode(LangOptions::FPM_Off); + else if (Val == "fast-honor-pragmas") + Opts.setDefaultFPContractMode(LangOptions::FPM_FastHonorPragmas); + 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); + Opts.NoSanitizeFiles = Args.getAllArgValues(OPT_fsanitize_ignorelist_EQ); + std::vector<std::string> systemIgnorelists = + Args.getAllArgValues(OPT_fsanitize_system_ignorelist_EQ); + Opts.NoSanitizeFiles.insert(Opts.NoSanitizeFiles.end(), + systemIgnorelists.begin(), + systemIgnorelists.end()); + + 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 (Major <= 9) + Opts.setClangABICompat(LangOptions::ClangABI::Ver9); + else if (Major <= 11) + Opts.setClangABICompat(LangOptions::ClangABI::Ver11); + else if (Major <= 12) + Opts.setClangABICompat(LangOptions::ClangABI::Ver12); + } else if (Ver != "latest") { + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << A->getValue(); + } + } + + if (Arg *A = Args.getLastArg(OPT_msign_return_address_EQ)) { + StringRef SignScope = A->getValue(); + + if (SignScope.equals_insensitive("none")) + Opts.setSignReturnAddressScope( + LangOptions::SignReturnAddressScopeKind::None); + else if (SignScope.equals_insensitive("all")) + Opts.setSignReturnAddressScope( + LangOptions::SignReturnAddressScopeKind::All); + else if (SignScope.equals_insensitive("non-leaf")) + Opts.setSignReturnAddressScope( + LangOptions::SignReturnAddressScopeKind::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_insensitive("a_key")) + Opts.setSignReturnAddressKey( + LangOptions::SignReturnAddressKeyKind::AKey); + else if (SignKey.equals_insensitive("b_key")) + Opts.setSignReturnAddressKey( + LangOptions::SignReturnAddressKeyKind::BKey); + else + Diags.Report(diag::err_drv_invalid_value) + << A->getAsString(Args) << SignKey; + } + } + } + + // The value can be empty, which indicates the system default should be used. + StringRef CXXABI = Args.getLastArgValue(OPT_fcxx_abi_EQ); + if (!CXXABI.empty()) { + if (!TargetCXXABI::isABI(CXXABI)) { + Diags.Report(diag::err_invalid_cxx_abi) << CXXABI; + } else { + auto Kind = TargetCXXABI::getKind(CXXABI); + if (!TargetCXXABI::isSupportedCXXABI(T, Kind)) + Diags.Report(diag::err_unsupported_cxx_abi) << CXXABI << T.str(); + else + Opts.CXXABI = Kind; + } + } + + Opts.RelativeCXXABIVTables = + Args.hasFlag(options::OPT_fexperimental_relative_cxx_abi_vtables, + options::OPT_fno_experimental_relative_cxx_abi_vtables, + TargetCXXABI::usesRelativeVTables(T)); + + for (const auto &A : Args.getAllArgValues(OPT_fmacro_prefix_map_EQ)) { + auto Split = StringRef(A).split('='); + Opts.MacroPrefixMap.insert( + {std::string(Split.first), std::string(Split.second)}); + } + + // Error if -mvscale-min is unbounded. + if (Arg *A = Args.getLastArg(options::OPT_mvscale_min_EQ)) { + unsigned VScaleMin; + if (StringRef(A->getValue()).getAsInteger(10, VScaleMin) || VScaleMin == 0) + Diags.Report(diag::err_cc1_unbounded_vscale_min); + } + + return Diags.getNumErrors() == NumErrorsBefore; +} + +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::ExtractAPI: + case frontend::FixIt: + case frontend::GenerateModule: + case frontend::GenerateModuleInterface: + case frontend::GenerateHeaderModule: + case frontend::GeneratePCH: + case frontend::GenerateInterfaceStubs: + 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: + case frontend::PrintDependencyDirectivesSourceMinimizerOutput: + return true; + } + llvm_unreachable("invalid frontend action"); +} + +static void GeneratePreprocessorArgs(PreprocessorOptions &Opts, + SmallVectorImpl<const char *> &Args, + CompilerInvocation::StringAllocator SA, + const LangOptions &LangOpts, + const FrontendOptions &FrontendOpts, + const CodeGenOptions &CodeGenOpts) { + PreprocessorOptions *PreprocessorOpts = &Opts; + +#define PREPROCESSOR_OPTION_WITH_MARSHALLING( \ + PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \ + DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \ + MERGER, EXTRACTOR, TABLE_INDEX) \ + GENERATE_OPTION_WITH_MARSHALLING( \ + Args, SA, KIND, FLAGS, SPELLING, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, \ + IMPLIED_CHECK, IMPLIED_VALUE, DENORMALIZER, EXTRACTOR, TABLE_INDEX) +#include "clang/Driver/Options.inc" +#undef PREPROCESSOR_OPTION_WITH_MARSHALLING + + if (Opts.PCHWithHdrStop && !Opts.PCHWithHdrStopCreate) + GenerateArg(Args, OPT_pch_through_hdrstop_use, SA); + + for (const auto &D : Opts.DeserializedPCHDeclsToErrorOn) + GenerateArg(Args, OPT_error_on_deserialized_pch_decl, D, SA); + + if (Opts.PrecompiledPreambleBytes != std::make_pair(0u, false)) + GenerateArg(Args, OPT_preamble_bytes_EQ, + Twine(Opts.PrecompiledPreambleBytes.first) + "," + + (Opts.PrecompiledPreambleBytes.second ? "1" : "0"), + SA); + + for (const auto &M : Opts.Macros) { + // Don't generate __CET__ macro definitions. They are implied by the + // -fcf-protection option that is generated elsewhere. + if (M.first == "__CET__=1" && !M.second && + !CodeGenOpts.CFProtectionReturn && CodeGenOpts.CFProtectionBranch) + continue; + if (M.first == "__CET__=2" && !M.second && CodeGenOpts.CFProtectionReturn && + !CodeGenOpts.CFProtectionBranch) + continue; + if (M.first == "__CET__=3" && !M.second && CodeGenOpts.CFProtectionReturn && + CodeGenOpts.CFProtectionBranch) + continue; + + GenerateArg(Args, M.second ? OPT_U : OPT_D, M.first, SA); + } + + for (const auto &I : Opts.Includes) { + // Don't generate OpenCL includes. They are implied by other flags that are + // generated elsewhere. + if (LangOpts.OpenCL && LangOpts.IncludeDefaultHeader && + ((LangOpts.DeclareOpenCLBuiltins && I == "opencl-c-base.h") || + I == "opencl-c.h")) + continue; + + GenerateArg(Args, OPT_include, I, SA); + } + + for (const auto &CI : Opts.ChainedIncludes) + GenerateArg(Args, OPT_chain_include, CI, SA); + + for (const auto &RF : Opts.RemappedFiles) + GenerateArg(Args, OPT_remap_file, RF.first + ";" + RF.second, SA); + + // Don't handle LexEditorPlaceholders. It is implied by the action that is + // generated elsewhere. +} + +static bool ParsePreprocessorArgs(PreprocessorOptions &Opts, ArgList &Args, + DiagnosticsEngine &Diags, + frontend::ActionKind Action, + const FrontendOptions &FrontendOpts) { + unsigned NumErrorsBefore = Diags.getNumErrors(); + + PreprocessorOptions *PreprocessorOpts = &Opts; + +#define PREPROCESSOR_OPTION_WITH_MARSHALLING( \ + PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \ + DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \ + MERGER, EXTRACTOR, TABLE_INDEX) \ + PARSE_OPTION_WITH_MARSHALLING( \ + Args, Diags, ID, FLAGS, PARAM, SHOULD_PARSE, KEYPATH, DEFAULT_VALUE, \ + IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, MERGER, TABLE_INDEX) +#include "clang/Driver/Options.inc" +#undef PREPROCESSOR_OPTION_WITH_MARSHALLING + + Opts.PCHWithHdrStop = Args.hasArg(OPT_pch_through_hdrstop_create) || + Args.hasArg(OPT_pch_through_hdrstop_use); + + 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()); + } + + // 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); + } + + // 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; + + return Diags.getNumErrors() == NumErrorsBefore; +} + +static void GeneratePreprocessorOutputArgs( + const PreprocessorOutputOptions &Opts, SmallVectorImpl<const char *> &Args, + CompilerInvocation::StringAllocator SA, frontend::ActionKind Action) { + const PreprocessorOutputOptions &PreprocessorOutputOpts = Opts; + +#define PREPROCESSOR_OUTPUT_OPTION_WITH_MARSHALLING( \ + PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \ + DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \ + MERGER, EXTRACTOR, TABLE_INDEX) \ + GENERATE_OPTION_WITH_MARSHALLING( \ + Args, SA, KIND, FLAGS, SPELLING, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, \ + IMPLIED_CHECK, IMPLIED_VALUE, DENORMALIZER, EXTRACTOR, TABLE_INDEX) +#include "clang/Driver/Options.inc" +#undef PREPROCESSOR_OUTPUT_OPTION_WITH_MARSHALLING + + bool Generate_dM = isStrictlyPreprocessorAction(Action) && !Opts.ShowCPP; + if (Generate_dM) + GenerateArg(Args, OPT_dM, SA); + if (!Generate_dM && Opts.ShowMacros) + GenerateArg(Args, OPT_dD, SA); +} + +static bool ParsePreprocessorOutputArgs(PreprocessorOutputOptions &Opts, + ArgList &Args, DiagnosticsEngine &Diags, + frontend::ActionKind Action) { + unsigned NumErrorsBefore = Diags.getNumErrors(); + + PreprocessorOutputOptions &PreprocessorOutputOpts = Opts; + +#define PREPROCESSOR_OUTPUT_OPTION_WITH_MARSHALLING( \ + PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \ + DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \ + MERGER, EXTRACTOR, TABLE_INDEX) \ + PARSE_OPTION_WITH_MARSHALLING( \ + Args, Diags, ID, FLAGS, PARAM, SHOULD_PARSE, KEYPATH, DEFAULT_VALUE, \ + IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, MERGER, TABLE_INDEX) +#include "clang/Driver/Options.inc" +#undef PREPROCESSOR_OUTPUT_OPTION_WITH_MARSHALLING + + Opts.ShowCPP = isStrictlyPreprocessorAction(Action) && !Args.hasArg(OPT_dM); + Opts.ShowMacros = Args.hasArg(OPT_dM) || Args.hasArg(OPT_dD); + + return Diags.getNumErrors() == NumErrorsBefore; +} + +static void GenerateTargetArgs(const TargetOptions &Opts, + SmallVectorImpl<const char *> &Args, + CompilerInvocation::StringAllocator SA) { + const TargetOptions *TargetOpts = &Opts; +#define TARGET_OPTION_WITH_MARSHALLING( \ + PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \ + DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \ + MERGER, EXTRACTOR, TABLE_INDEX) \ + GENERATE_OPTION_WITH_MARSHALLING( \ + Args, SA, KIND, FLAGS, SPELLING, ALWAYS_EMIT, KEYPATH, DEFAULT_VALUE, \ + IMPLIED_CHECK, IMPLIED_VALUE, DENORMALIZER, EXTRACTOR, TABLE_INDEX) +#include "clang/Driver/Options.inc" +#undef TARGET_OPTION_WITH_MARSHALLING + + if (!Opts.SDKVersion.empty()) + GenerateArg(Args, OPT_target_sdk_version_EQ, Opts.SDKVersion.getAsString(), + SA); +} + +static bool ParseTargetArgs(TargetOptions &Opts, ArgList &Args, + DiagnosticsEngine &Diags) { + unsigned NumErrorsBefore = Diags.getNumErrors(); + + TargetOptions *TargetOpts = &Opts; + +#define TARGET_OPTION_WITH_MARSHALLING( \ + PREFIX_TYPE, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES, SPELLING, SHOULD_PARSE, ALWAYS_EMIT, KEYPATH, \ + DEFAULT_VALUE, IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, DENORMALIZER, \ + MERGER, EXTRACTOR, TABLE_INDEX) \ + PARSE_OPTION_WITH_MARSHALLING( \ + Args, Diags, ID, FLAGS, PARAM, SHOULD_PARSE, KEYPATH, DEFAULT_VALUE, \ + IMPLIED_CHECK, IMPLIED_VALUE, NORMALIZER, MERGER, TABLE_INDEX) +#include "clang/Driver/Options.inc" +#undef TARGET_OPTION_WITH_MARSHALLING + + 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; + } + + return Diags.getNumErrors() == NumErrorsBefore; +} + +bool CompilerInvocation::CreateFromArgsImpl( + CompilerInvocation &Res, ArrayRef<const char *> CommandLineArgs, + DiagnosticsEngine &Diags, const char *Argv0) { + unsigned NumErrorsBefore = Diags.getNumErrors(); + + // Parse the arguments. + const OptTable &Opts = getDriverOptTable(); + const unsigned IncludedFlagsBitmask = options::CC1Option; + unsigned MissingArgIndex, MissingArgCount; + InputArgList Args = Opts.ParseArgs(CommandLineArgs, 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; + + // 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; + } + + ParseFileSystemArgs(Res.getFileSystemOpts(), Args, Diags); + ParseMigratorArgs(Res.getMigratorOpts(), Args, Diags); + ParseAnalyzerArgs(*Res.getAnalyzerOpts(), Args, Diags); + ParseDiagnosticArgs(Res.getDiagnosticOpts(), Args, &Diags, + /*DefaultDiagColor=*/false); + ParseFrontendArgs(Res.getFrontendOpts(), Args, Diags, LangOpts.IsHeaderFile); + // FIXME: We shouldn't have to pass the DashX option around here + InputKind DashX = Res.getFrontendOpts().DashX; + ParseTargetArgs(Res.getTargetOpts(), Args, Diags); + llvm::Triple T(Res.getTargetOpts().Triple); + ParseHeaderSearchArgs(Res.getHeaderSearchOpts(), Args, Diags, + Res.getFileSystemOpts().WorkingDir); + + ParseLangArgs(LangOpts, Args, DashX, T, Res.getPreprocessorOpts().Includes, + Diags); + if (Res.getFrontendOpts().ProgramAction == frontend::RewriteObjC) + LangOpts.ObjCExceptions = 1; + + 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; + + ParseCodeGenArgs(Res.getCodeGenOpts(), Args, DashX, Diags, T, + Res.getFrontendOpts().OutputFile, LangOpts); + + // 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, + Res.getFrontendOpts()); + ParsePreprocessorOutputArgs(Res.getPreprocessorOutputOpts(), Args, Diags, + Res.getFrontendOpts().ProgramAction); + + ParseDependencyOutputArgs(Res.getDependencyOutputOpts(), Args, Diags, + Res.getFrontendOpts().ProgramAction, + Res.getPreprocessorOutputOpts().ShowLineMarkers); + if (!Res.getDependencyOutputOpts().OutputFile.empty() && + Res.getDependencyOutputOpts().Targets.empty()) + Diags.Report(diag::err_fe_dependency_file_requires_MT); + + // 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); + } + + // Store the command-line for using in the CodeView backend. + Res.getCodeGenOpts().Argv0 = Argv0; + append_range(Res.getCodeGenOpts().CommandLineArgs, CommandLineArgs); + + FixupInvocation(Res, Diags, Args, DashX); + + return Diags.getNumErrors() == NumErrorsBefore; +} + +bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Invocation, + ArrayRef<const char *> CommandLineArgs, + DiagnosticsEngine &Diags, + const char *Argv0) { + CompilerInvocation DummyInvocation; + + return RoundTrip( + [](CompilerInvocation &Invocation, ArrayRef<const char *> CommandLineArgs, + DiagnosticsEngine &Diags, const char *Argv0) { + return CreateFromArgsImpl(Invocation, CommandLineArgs, Diags, Argv0); + }, + [](CompilerInvocation &Invocation, SmallVectorImpl<const char *> &Args, + StringAllocator SA) { Invocation.generateCC1CommandLine(Args, SA); }, + Invocation, DummyInvocation, CommandLineArgs, Diags, Argv0); +} + +std::string CompilerInvocation::getModuleHash() const { + // FIXME: Consider using SHA1 instead of MD5. + llvm::HashBuilder<llvm::MD5, llvm::support::endianness::native> HBuilder; + + // Note: For QoI reasons, the things we use as a hash here should all be + // dumped via the -module-info flag. + + // Start the signature with the compiler version. + HBuilder.add(getClangFullRepositoryVersion()); + + // Also include the serialization version, in case LLVM_APPEND_VC_REV is off + // and getClangFullRepositoryVersion() doesn't include git revision. + HBuilder.add(serialization::VERSION_MAJOR, serialization::VERSION_MINOR); + + // Extend the signature with the language options +#define LANGOPT(Name, Bits, Default, Description) HBuilder.add(LangOpts->Name); +#define ENUM_LANGOPT(Name, Type, Bits, Default, Description) \ + HBuilder.add(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" + + HBuilder.addRange(LangOpts->ModuleFeatures); + + HBuilder.add(LangOpts->ObjCRuntime); + HBuilder.addRange(LangOpts->CommentOpts.BlockCommandNames); + + // Extend the signature with the target options. + HBuilder.add(TargetOpts->Triple, TargetOpts->CPU, TargetOpts->TuneCPU, + TargetOpts->ABI); + HBuilder.addRange(TargetOpts->FeaturesAsWritten); + + // Extend the signature with preprocessor options. + const PreprocessorOptions &ppOpts = getPreprocessorOpts(); + HBuilder.add(ppOpts.UsePredefines, ppOpts.DetailedRecord); + + const HeaderSearchOptions &hsOpts = getHeaderSearchOpts(); + for (const auto &Macro : 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 = Macro.first; + if (hsOpts.ModulesIgnoreMacros.count( + llvm::CachedHashString(MacroDef.split('=').first))) + continue; + } + + HBuilder.add(Macro); + } + + // Extend the signature with the sysroot and other header search options. + HBuilder.add(hsOpts.Sysroot, hsOpts.ModuleFormat, hsOpts.UseDebugInfo, + hsOpts.UseBuiltinIncludes, hsOpts.UseStandardSystemIncludes, + hsOpts.UseStandardCXXIncludes, hsOpts.UseLibcxx, + hsOpts.ModulesValidateDiagnosticOptions); + HBuilder.add(hsOpts.ResourceDir); + + if (hsOpts.ModulesStrictContextHash) { + HBuilder.addRange(hsOpts.SystemHeaderPrefixes); + HBuilder.addRange(hsOpts.UserEntries); + + const DiagnosticOptions &diagOpts = getDiagnosticOpts(); +#define DIAGOPT(Name, Bits, Default) HBuilder.add(diagOpts.Name); +#define ENUM_DIAGOPT(Name, Type, Bits, Default) \ + HBuilder.add(diagOpts.get##Name()); +#include "clang/Basic/DiagnosticOptions.def" +#undef DIAGOPT +#undef ENUM_DIAGOPT + } + + // Extend the signature with the user build path. + HBuilder.add(hsOpts.ModuleUserBuildPath); + + // Extend the signature with the module file extensions. + for (const auto &ext : getFrontendOpts().ModuleFileExtensions) + ext->hashExtension(HBuilder); + + // When compiling with -gmodules, also hash -fdebug-prefix-map as it + // affects the debug info in the PCM. + if (getCodeGenOpts().DebugTypeExtRefs) + HBuilder.addRange(getCodeGenOpts().DebugPrefixMap); + + // 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()) + HBuilder.add(SanHash.Mask); + + llvm::MD5::MD5Result Result; + HBuilder.getHasher().final(Result); + uint64_t Hash = Result.high() ^ Result.low(); + return toString(llvm::APInt(64, Hash), 36, /*Signed=*/false); +} + +void CompilerInvocation::generateCC1CommandLine( + SmallVectorImpl<const char *> &Args, StringAllocator SA) const { + llvm::Triple T(TargetOpts->Triple); + + GenerateFileSystemArgs(FileSystemOpts, Args, SA); + GenerateMigratorArgs(MigratorOpts, Args, SA); + GenerateAnalyzerArgs(*AnalyzerOpts, Args, SA); + GenerateDiagnosticArgs(*DiagnosticOpts, Args, SA, false); + GenerateFrontendArgs(FrontendOpts, Args, SA, LangOpts->IsHeaderFile); + GenerateTargetArgs(*TargetOpts, Args, SA); + GenerateHeaderSearchArgs(*HeaderSearchOpts, Args, SA); + GenerateLangArgs(*LangOpts, Args, SA, T, FrontendOpts.DashX); + GenerateCodeGenArgs(CodeGenOpts, Args, SA, T, FrontendOpts.OutputFile, + &*LangOpts); + GeneratePreprocessorArgs(*PreprocessorOpts, Args, SA, *LangOpts, FrontendOpts, + CodeGenOpts); + GeneratePreprocessorOutputArgs(PreprocessorOutputOpts, Args, SA, + FrontendOpts.ProgramAction); + GenerateDependencyOutputArgs(DependencyOutputOpts, Args, SA); +} + +IntrusiveRefCntPtr<llvm::vfs::FileSystem> +clang::createVFSFromCompilerInvocationOrig(const CompilerInvocation &CI, + DiagnosticsEngine &Diags) { + return createVFSFromCompilerInvocation(CI, Diags, + llvm::vfs::getRealFileSystem()); +} + +IntrusiveRefCntPtr<llvm::vfs::FileSystem> +clang::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; +} + +IntrusiveRefCntPtr<llvm::vfs::FileSystem> +clang::createVFSFromCompilerInvocation(const CompilerInvocation &CI, + DiagnosticsEngine &Diags) { + IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS = clang::createVFSFromCompilerInvocationOrig(CI, Diags); + if (!CI.getHeaderSearchOpts().CaseInsensitive) { + return FS; + } else { + return new llvm::vfs::CaseInsensitiveFileSystem(FS); + } +} diff --git a/contrib/libs/clang14/lib/Frontend/CreateInvocationFromCommandLine.cpp b/contrib/libs/clang14/lib/Frontend/CreateInvocationFromCommandLine.cpp new file mode 100644 index 0000000000..c5627d13a7 --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/CreateInvocationFromCommandLine.cpp @@ -0,0 +1,108 @@ +//===--- CreateInvocationFromCommandLine.cpp - CompilerInvocation from Args ==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Construct a compiler invocation object for command line driver arguments +// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/DiagnosticOptions.h" +#include "clang/Driver/Action.h" +#include "clang/Driver/Compilation.h" +#include "clang/Driver/Driver.h" +#include "clang/Driver/Options.h" +#include "clang/Driver/Tool.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Frontend/Utils.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Support/Host.h" +using namespace clang; +using namespace llvm::opt; + +std::unique_ptr<CompilerInvocation> clang::createInvocationFromCommandLine( + ArrayRef<const char *> ArgList, IntrusiveRefCntPtr<DiagnosticsEngine> Diags, + IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS, bool ShouldRecoverOnErorrs, + std::vector<std::string> *CC1Args) { + assert(!ArgList.empty()); + 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.insert( + llvm::find_if( + Args, [](const char *Elem) { return llvm::StringRef(Elem) == "--"; }), + "-fsyntax-only"); + + // FIXME: We shouldn't have to pass in the path info. + driver::Driver TheDriver(Args[0], llvm::sys::getDefaultTargetTriple(), *Diags, + "clang LLVM compiler", 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; + } + } + } + + bool PickFirstOfMany = OffloadCompilation || ShouldRecoverOnErorrs; + if (Jobs.size() == 0 || (Jobs.size() > 1 && !PickFirstOfMany)) { + 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; + } + auto Cmd = llvm::find_if(Jobs, [](const driver::Command &Cmd) { + return StringRef(Cmd.getCreator().getName()) == "clang"; + }); + if (Cmd == Jobs.end()) { + Diags->Report(diag::err_fe_expected_clang_command); + return nullptr; + } + + const ArgStringList &CCArgs = Cmd->getArguments(); + if (CC1Args) + *CC1Args = {CCArgs.begin(), CCArgs.end()}; + auto CI = std::make_unique<CompilerInvocation>(); + if (!CompilerInvocation::CreateFromArgs(*CI, CCArgs, *Diags, Args[0]) && + !ShouldRecoverOnErorrs) + return nullptr; + return CI; +} diff --git a/contrib/libs/clang14/lib/Frontend/DependencyFile.cpp b/contrib/libs/clang14/lib/Frontend/DependencyFile.cpp new file mode 100644 index 0000000000..2888273741 --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/DependencyFile.cpp @@ -0,0 +1,385 @@ +//===--- DependencyFile.cpp - Generate dependency file --------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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; + DiagnosticsEngine &Diags; + DepCollectorPPCallbacks(DependencyCollector &L, SourceManager &SM, + DiagnosticsEngine &Diags) + : DepCollector(L), SM(SM), Diags(Diags) {} + + 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! + if (Optional<StringRef> Filename = SM.getNonBuiltinFilenameForID( + SM.getFileID(SM.getExpansionLoc(Loc)))) + DepCollector.maybeAddDependency( + llvm::sys::path::remove_leading_dotslash(*Filename), + /*FromModule*/ false, isSystem(FileType), /*IsModuleFile*/ false, + /*IsMissing*/ false); + } + + void FileSkipped(const FileEntryRef &SkippedFile, const Token &FilenameTok, + SrcMgr::CharacteristicKind FileType) override { + StringRef Filename = + llvm::sys::path::remove_leading_dotslash(SkippedFile.getName()); + DepCollector.maybeAddDependency(Filename, /*FromModule=*/false, + /*IsSystem=*/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 HasInclude(SourceLocation Loc, StringRef SpelledFilename, bool IsAngled, + Optional<FileEntryRef> File, + SrcMgr::CharacteristicKind FileType) override { + if (!File) + return; + StringRef Filename = + llvm::sys::path::remove_leading_dotslash(File->getName()); + DepCollector.maybeAddDependency(Filename, /*FromModule=*/false, + /*IsSystem=*/isSystem(FileType), + /*IsModuleFile=*/false, + /*IsMissing=*/false); + } + + void EndOfMainFile() override { DepCollector.finishedMainFile(Diags); } +}; + +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 (sawDependency(Filename, FromModule, IsSystem, IsModuleFile, IsMissing)) + addDependency(Filename); +} + +bool DependencyCollector::addDependency(StringRef Filename) { + StringRef SearchPath; +#ifdef _WIN32 + // Make the search insensitive to case and separators. + llvm::SmallString<256> TmpPath = Filename; + llvm::sys::path::native(TmpPath); + std::transform(TmpPath.begin(), TmpPath.end(), TmpPath.begin(), ::tolower); + SearchPath = TmpPath.str(); +#else + SearchPath = Filename; +#endif + + if (Seen.insert(SearchPath).second) { + Dependencies.push_back(std::string(Filename)); + return true; + } + return false; +} + +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(std::make_unique<DepCollectorPPCallbacks>( + *this, PP.getSourceManager(), PP.getDiagnostics())); + PP.getHeaderSearchInfo().getModuleMap().addModuleMapCallbacks( + std::make_unique<DepCollectorMMCallbacks>(*this)); +} +void DependencyCollector::attachToASTReader(ASTReader &R) { + R.addListener(std::make_unique<DepCollectorASTListener>(*this)); +} + +DependencyFileGenerator::DependencyFileGenerator( + const DependencyOutputOptions &Opts) + : 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 (addDependency(ExtraDep.first)) + ++InputFileIndex; + } +} + +void DependencyFileGenerator::attachToPreprocessor(Preprocessor &PP) { + // Disable the "file not found" diagnostic if the -MG option was given. + if (AddMissingHeaderDeps) + PP.SetSuppressIncludeNotFoundError(true); + + DependencyCollector::attachToPreprocessor(PP); +} + +bool DependencyFileGenerator::sawDependency(StringRef Filename, bool FromModule, + bool IsSystem, bool IsModuleFile, + bool IsMissing) { + if (IsMissing) { + // Handle the case of missing file from an inclusion directive. + if (AddMissingHeaderDeps) + return true; + SeenMissingHeader = true; + return false; + } + if (IsModuleFile && !IncludeModuleFiles) + return false; + + if (isSpecialFilename(Filename)) + return false; + + if (IncludeSystemHeaders) + return true; + + return !IsSystem; +} + +void DependencyFileGenerator::finishedMainFile(DiagnosticsEngine &Diags) { + outputDependencyFile(Diags); +} + +/// 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 DependencyFileGenerator::outputDependencyFile(DiagnosticsEngine &Diags) { + if (SeenMissingHeader) { + llvm::sys::fs::remove(OutputFile); + return; + } + + std::error_code EC; + llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::OF_TextWithCRLF); + if (EC) { + Diags.Report(diag::err_fe_error_opening) << OutputFile << EC.message(); + return; + } + + outputDependencyFile(OS); +} + +void DependencyFileGenerator::outputDependencyFile(llvm::raw_ostream &OS) { + // 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. + ArrayRef<std::string> Files = getDependencies(); + 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"; + } + } +} diff --git a/contrib/libs/clang14/lib/Frontend/DependencyGraph.cpp b/contrib/libs/clang14/lib/Frontend/DependencyGraph.cpp new file mode 100644 index 0000000000..4bed4e2d44 --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/DependencyGraph.cpp @@ -0,0 +1,139 @@ +//===--- DependencyGraph.cpp - Generate dependency file -------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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(std::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::OF_TextWithCRLF); + 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(std::string(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/libs/clang14/lib/Frontend/DiagnosticRenderer.cpp b/contrib/libs/clang14/lib/Frontend/DiagnosticRenderer.cpp new file mode 100644 index 0000000000..0afc8f3b1d --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/DiagnosticRenderer.cpp @@ -0,0 +1,635 @@ +//===- DiagnosticRenderer.cpp - Diagnostic Pretty-Printing ----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#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; + } + } + + // There is a chance that begin or end is invalid here, for example if + // specific compile error is reported. + // It is possible that the FileID's do not match, if one comes from an + // included file. In this case we can not produce a meaningful source range. + if (Begin.isInvalid() || End.isInvalid() || BeginFileID != EndFileID) + continue; + + // 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/libs/clang14/lib/Frontend/ExtractAPIConsumer.cpp b/contrib/libs/clang14/lib/Frontend/ExtractAPIConsumer.cpp new file mode 100644 index 0000000000..cdf67f3c32 --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/ExtractAPIConsumer.cpp @@ -0,0 +1,32 @@ +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/ASTConsumers.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" + +using namespace clang; + +namespace { +class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> { +public: + bool VisitNamedDecl(NamedDecl *Decl) { + llvm::outs() << Decl->getName() << "\n"; + return true; + } +}; + +class ExtractAPIConsumer : public ASTConsumer { +public: + void HandleTranslationUnit(ASTContext &Context) override { + Visitor.TraverseDecl(Context.getTranslationUnitDecl()); + } + +private: + ExtractAPIVisitor Visitor; +}; +} // namespace + +std::unique_ptr<ASTConsumer> +ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + return std::make_unique<ExtractAPIConsumer>(); +} diff --git a/contrib/libs/clang14/lib/Frontend/FrontendAction.cpp b/contrib/libs/clang14/lib/Frontend/FrontendAction.cpp new file mode 100644 index 0000000000..089f40b360 --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/FrontendAction.cpp @@ -0,0 +1,1138 @@ +//===--- FrontendAction.cpp -----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/FrontendAction.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclGroup.h" +#include "clang/Basic/Builtins.h" +#include "clang/Basic/LangStandard.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/ADT/ScopeExit.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; + } + + 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, SourceLocation(), /*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 (const FrontendPluginRegistry::entry &Plugin : + FrontendPluginRegistry::entries()) { + if (Plugin.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 (const FrontendPluginRegistry::entry &Plugin : + FrontendPluginRegistry::entries()) { + std::unique_ptr<PluginASTAction> P = Plugin.instantiate(); + PluginASTAction::ActionType ActionType = P->getActionType(); + if (ActionType == PluginASTAction::CmdlineAfterMainAction || + ActionType == PluginASTAction::CmdlineBeforeMainAction) { + // This is O(|plugins| * |add_plugins|), but since both numbers are + // way below 50 in practice, that's ok. + if (llvm::any_of(CI.getFrontendOpts().AddPluginActions, + [&](const std::string &PluginAction) { + return PluginAction == Plugin.getName(); + })) { + if (ActionType == PluginASTAction::CmdlineBeforeMainAction) + ActionType = PluginASTAction::AddBeforeMainAction; + else + ActionType = PluginASTAction::AddAfterMainAction; + } + } + if ((ActionType == PluginASTAction::AddBeforeMainAction || + ActionType == PluginASTAction::AddAfterMainAction) && + P->ParseArgs( + CI, + CI.getFrontendOpts().PluginArgs[std::string(Plugin.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)); + if (!AfterConsumers.empty()) { + // If we have plugins after the main consumer, which may be the codegen + // action, they likely will need the ASTContext, so don't clear it in the + // codegen action. + CI.getCodeGenOpts().ClearASTBeforeBackend = false; + for (auto &C : AfterConsumers) + Consumers.push_back(std::move(C)); + } + + return std::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(); + + auto MainFileBuf = SourceMgr.getBufferOrNone(MainFileID); + if (!MainFileBuf) + 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.PathRelativeToRootModuleDirectory, 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.PathRelativeToRootModuleDirectory, + 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(); + SmallVector<std::pair<std::string, const FileEntry *>, 8> Headers; + 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; + + auto 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.PathRelativeToRootModuleDirectory); + for (auto It = Components.rbegin(), End = Components.rend(); It != End; + ++It) + llvm::sys::path::append(RelativeHeader, *It); + + std::string RelName = RelativeHeader.c_str(); + Headers.push_back(std::make_pair(RelName, *Header)); + } + + if (EC) + return EC; + + // Sort header paths and make the header inclusion order deterministic + // across different OSs and filesystems. + llvm::sort(Headers.begin(), Headers.end(), []( + const std::pair<std::string, const FileEntry *> &LHS, + const std::pair<std::string, const FileEntry *> &RHS) { + return LHS.first < RHS.first; + }); + for (auto &H : Headers) { + // Include this header as part of the umbrella directory. + Module->addTopHeader(H.second); + addHeaderInclude(H.first, Includes, LangOpts, Module->IsExternC); + } + } + + // 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.getBufferOrFake(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, SourceLocation(), + /*AllowSearch=*/true); + 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.PathRelativeToRootModuleDirectory, + 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 we fail, reset state since the client will not end up calling the + // matching EndSourceFile(). All paths that return true should release this. + auto FailureCleanup = llvm::make_scope_exit([&]() { + if (HasBegunSourceFile) + CI.getDiagnosticClient().EndSourceFile(); + CI.clearOutputFiles(/*EraseFiles=*/true); + CI.getLangOpts().setCompilingModule(LangOptions::CMK_None); + setCurrentInput(FrontendInputFile()); + setCompilerInstance(nullptr); + }); + + if (!BeginInvocation(CI)) + return false; + + // 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( + std::string(InputFile), CI.getPCHContainerReader(), + ASTUnit::LoadPreprocessorOnly, ASTDiags, CI.getFileSystemOpts(), + CI.getCodeGenOpts().DebugTypeExtRefs); + if (!AST) + return false; + + // 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( + std::string(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, SourceLocation(), + /*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.getBufferOrFake(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( + std::string(InputFile), CI.getPCHContainerReader(), + ASTUnit::LoadEverything, Diags, CI.getFileSystemOpts(), + CI.getCodeGenOpts().DebugTypeExtRefs); + + if (!AST) + return false; + + // 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)) + return false; + + // Create the AST consumer. + CI.setASTConsumer(CreateWrappedASTConsumer(CI, InputFile)); + if (!CI.hasASTConsumer()) + return false; + + FailureCleanup.release(); + return true; + } + + // Set up the file and source managers, if needed. + if (!CI.hasFileManager()) { + if (!CI.createFileManager()) { + return false; + } + } + 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 (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() == Language::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)) + return false; + + // Initialize the main file entry. + if (!CI.InitializeSourceManager(CurrentInput)) + return false; + + FailureCleanup.release(); + 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 (auto 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 = std::string(Dir->path()); + Found = true; + break; + } + } + + if (!Found) { + CI.getDiagnostics().Report(diag::err_fe_no_pch_in_dir) << PCHInclude; + return false; + } + } + } + + // 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)) + return false; + + // 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)) + return false; + + auto *CurrentModule = prepareToBuildModule(CI, Input.getFile()); + if (!CurrentModule) + return false; + + 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) + return false; + + // 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 create module buffer ID"); + SourceMgr.setMainFileID(BufferID); + } + } + + // Initialize the action. + if (!BeginSourceFileAction(CI)) + return false; + + // 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 = std::string(getCurrentFileOrBufferName()); + if (Input.isPreprocessed()) + ReadOriginalFileName(CI, PresumedInputFile); + + std::unique_ptr<ASTConsumer> Consumer = + CreateWrappedASTConsumer(CI, PresumedInputFile); + if (!Consumer) + return false; + + // 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) + return false; + CI.setASTReader(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().DisablePCHOrModuleValidation, + CI.getPreprocessorOpts().AllowPCHWithCompilerErrors, + DeserialListener, DeleteDeserialListener); + if (!CI.getASTContext().getExternalSource()) + return false; + } + // If modules are enabled, create the AST reader 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.createASTReader(); + CI.getASTReader()->setDeserializationListener(DeserialListener, + DeleteDeserialListener); + } + } + + CI.setASTConsumer(std::move(Consumer)); + if (!CI.hasASTConsumer()) + return false; + } + + // 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.getASTReader()) && + "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)) + return false; + + // 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); + } + + FailureCleanup.release(); + return true; +} + +llvm::Error 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()) { + if (llvm::Error Err = GlobalModuleIndex::writeIndex( + CI.getFileManager(), CI.getPCHContainerReader(), Cache)) { + // FIXME this drops the error on the floor, but + // Index/pch-from-libclang.c seems to rely on dropping at least some of + // the error conditions! + consumeError(std::move(Err)); + } + } + } + + return llvm::Error::success(); +} + +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!"); +} + +bool WrapperFrontendAction::PrepareToExecuteAction(CompilerInstance &CI) { + return WrappedAction->PrepareToExecuteAction(CI); +} +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::EndSourceFile() { WrappedAction->EndSourceFile(); } +void WrapperFrontendAction::EndSourceFileAction() { + WrappedAction->EndSourceFileAction(); +} +bool WrapperFrontendAction::shouldEraseOutputFiles() { + return WrappedAction->shouldEraseOutputFiles(); +} + +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/libs/clang14/lib/Frontend/FrontendActions.cpp b/contrib/libs/clang14/lib/Frontend/FrontendActions.cpp new file mode 100644 index 0000000000..ad2e603947 --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/FrontendActions.cpp @@ -0,0 +1,1077 @@ +//===--- FrontendActions.cpp ----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/FrontendActions.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/Decl.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/LangStandard.h" +#include "clang/Basic/TargetInfo.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/DependencyDirectivesSourceMinimizer.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/ErrorHandling.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/YAMLTraits.h" +#include "llvm/Support/raw_ostream.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 std::make_unique<ASTConsumer>(); +} + +void InitOnlyAction::ExecuteAction() { +} + +// Basically PreprocessOnlyAction::ExecuteAction. +void ReadPCHAndPreprocessAction::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)); +} + +std::unique_ptr<ASTConsumer> +ReadPCHAndPreprocessAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + return std::make_unique<ASTConsumer>(); +} + +//===----------------------------------------------------------------------===// +// 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) { + const FrontendOptions &Opts = CI.getFrontendOpts(); + return CreateASTDumper(nullptr /*Dump to stdout.*/, Opts.ASTDumpFilter, + Opts.ASTDumpDecls, Opts.ASTDumpAll, + Opts.ASTDumpLookups, Opts.ASTDumpDeclTypes, + Opts.ASTDumpFormat); +} + +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(std::make_unique<PCHGenerator>( + CI.getPreprocessor(), CI.getModuleCache(), OutputFile, Sysroot, Buffer, + FrontendOpts.ModuleFileExtensions, + CI.getPreprocessorOpts().AllowPCHWithCompilerErrors, + FrontendOpts.IncludeTimestamps, +CI.getLangOpts().CacheGeneratedPCH)); + Consumers.push_back(CI.getPCHContainerWriter().CreatePCHContainerGenerator( + CI, std::string(InFile), OutputFile, std::move(OS), Buffer)); + + return std::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) { + // Because this is exposed via libclang we must disable RemoveFileOnSignal. + std::unique_ptr<raw_pwrite_stream> OS = CI.createDefaultOutputFile( + /*Binary=*/true, InFile, /*Extension=*/"", /*RemoveFileOnSignal=*/false); + 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(std::make_unique<PCHGenerator>( + CI.getPreprocessor(), CI.getModuleCache(), OutputFile, Sysroot, Buffer, + CI.getFrontendOpts().ModuleFileExtensions, + /*AllowASTWithErrors=*/ + +CI.getFrontendOpts().AllowPCMWithCompilerErrors, + /*IncludeTimestamps=*/ + +CI.getFrontendOpts().BuildingImplicitModule, + /*ShouldCacheASTInMemory=*/ + +CI.getFrontendOpts().BuildingImplicitModule)); + Consumers.push_back(CI.getPCHContainerWriter().CreatePCHContainerGenerator( + CI, std::string(InFile), OutputFile, std::move(OS), Buffer)); + return std::make_unique<MultiplexConsumer>(std::move(Consumers)); +} + +bool GenerateModuleAction::shouldEraseOutputFiles() { + return !getCompilerInstance().getFrontendOpts().AllowPCMWithCompilerErrors && + ASTFrontendAction::shouldEraseOutputFiles(); +} + +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); + } + + // Because this is exposed via libclang we must disable RemoveFileOnSignal. + return CI.createDefaultOutputFile(/*Binary=*/true, InFile, /*Extension=*/"", + /*RemoveFileOnSignal=*/false, + /*CreateMissingDirectories=*/true, + /*ForceUseTemporary=*/true); +} + +bool GenerateModuleInterfaceAction::BeginSourceFileAction( + CompilerInstance &CI) { + if (!CI.getLangOpts().ModulesTS && !CI.getLangOpts().CPlusPlusModules) { + CI.getDiagnostics().Report(diag::err_module_interface_requires_cpp_modules); + 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.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(std::string(FIF.getFile())); + } + Buffer = llvm::MemoryBuffer::getMemBufferCopy( + HeaderContents, Module::getModuleInputBufferName()); + + // Set that buffer up as our "real" input. + Inputs.clear(); + Inputs.push_back( + FrontendInputFile(Buffer->getMemBufferRef(), 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) { + Optional<FileEntryRef> FE = HS.LookupFile( + Name, SourceLocation(), /*Angled*/ false, nullptr, nullptr, None, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); + if (!FE) { + CI.getDiagnostics().Report(diag::err_module_header_file_not_found) + << Name; + continue; + } + Headers.push_back( + {std::string(Name), std::string(Name), &FE->getFileEntry()}); + } + 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 std::make_unique<ASTConsumer>(); +} + +std::unique_ptr<ASTConsumer> +DumpModuleInfoAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + return std::make_unique<ASTConsumer>(); +} + +std::unique_ptr<ASTConsumer> +VerifyPCHAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + return std::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.getModuleCache(), &CI.getASTContext(), + CI.getPCHContainerReader(), CI.getFrontendOpts().ModuleFileExtensions, + Sysroot.empty() ? "" : Sysroot.c_str(), + DisableValidationForModuleKind::None, + /*AllowASTWithCompilerErrors*/ 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::DeclaringImplicitEqualityComparison: + return "DeclaringImplicitEqualityComparison"; + case CodeSynthesisContext::DefiningSynthesizedFunction: + return "DefiningSynthesizedFunction"; + case CodeSynthesisContext::RewritingOperatorAsSpaceship: + return "RewritingOperatorAsSpaceship"; + case CodeSynthesisContext::Memoization: + return "Memoization"; + case CodeSynthesisContext::ConstraintsCheck: + return "ConstraintsCheck"; + case CodeSynthesisContext::ConstraintSubstitution: + return "ConstraintSubstitution"; + case CodeSynthesisContext::ConstraintNormalization: + return "ConstraintNormalization"; + case CodeSynthesisContext::ParameterMappingSubstitution: + return "ParameterMappingSubstitution"; + case CodeSynthesisContext::RequirementInstantiation: + return "RequirementInstantiation"; + case CodeSynthesisContext::NestedRequirementConstraintsCheck: + return "NestedRequirementConstraintsCheck"; + case CodeSynthesisContext::InitializingStructuredBinding: + return "InitializingStructuredBinding"; + case CodeSynthesisContext::MarkingClassDllexported: + return "MarkingClassDllexported"; + } + 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"; + } + + static void printEntryName(const Sema &TheSema, const Decl *Entity, + llvm::raw_string_ostream &OS) { + auto *NamedTemplate = cast<NamedDecl>(Entity); + + PrintingPolicy Policy = TheSema.Context.getPrintingPolicy(); + // FIXME: Also ask for FullyQualifiedNames? + Policy.SuppressDefaultTemplateArgs = false; + NamedTemplate->getNameForDiagnostic(OS, Policy, true); + + if (!OS.str().empty()) + return; + + Decl *Ctx = Decl::castFromDeclContext(NamedTemplate->getDeclContext()); + NamedDecl *NamedCtx = dyn_cast_or_null<NamedDecl>(Ctx); + + if (const auto *Decl = dyn_cast<TagDecl>(NamedTemplate)) { + if (const auto *R = dyn_cast<RecordDecl>(Decl)) { + if (R->isLambda()) { + OS << "lambda at "; + Decl->getLocation().print(OS, TheSema.getSourceManager()); + return; + } + } + OS << "unnamed " << Decl->getKindName(); + return; + } + + if (const auto *Decl = dyn_cast<ParmVarDecl>(NamedTemplate)) { + OS << "unnamed function parameter " << Decl->getFunctionScopeIndex() + << " "; + if (Decl->getFunctionScopeDepth() > 0) + OS << "(at depth " << Decl->getFunctionScopeDepth() << ") "; + OS << "of "; + NamedCtx->getNameForDiagnostic(OS, TheSema.getLangOpts(), true); + return; + } + + if (const auto *Decl = dyn_cast<TemplateTypeParmDecl>(NamedTemplate)) { + if (const Type *Ty = Decl->getTypeForDecl()) { + if (const auto *TTPT = dyn_cast_or_null<TemplateTypeParmType>(Ty)) { + OS << "unnamed template type parameter " << TTPT->getIndex() << " "; + if (TTPT->getDepth() > 0) + OS << "(at depth " << TTPT->getDepth() << ") "; + OS << "of "; + NamedCtx->getNameForDiagnostic(OS, TheSema.getLangOpts(), true); + return; + } + } + } + + if (const auto *Decl = dyn_cast<NonTypeTemplateParmDecl>(NamedTemplate)) { + OS << "unnamed template non-type parameter " << Decl->getIndex() << " "; + if (Decl->getDepth() > 0) + OS << "(at depth " << Decl->getDepth() << ") "; + OS << "of "; + NamedCtx->getNameForDiagnostic(OS, TheSema.getLangOpts(), true); + return; + } + + if (const auto *Decl = dyn_cast<TemplateTemplateParmDecl>(NamedTemplate)) { + OS << "unnamed template template parameter " << Decl->getIndex() << " "; + if (Decl->getDepth() > 0) + OS << "(at depth " << Decl->getDepth() << ") "; + OS << "of "; + NamedCtx->getNameForDiagnostic(OS, TheSema.getLangOpts(), true); + return; + } + + llvm_unreachable("Failed to retrieve a name for this entry!"); + OS << "unnamed identifier"; + } + + template <bool BeginInstantiation> + static TemplightEntry getTemplightEntry(const Sema &TheSema, + const CodeSynthesisContext &Inst) { + TemplightEntry Entry; + Entry.Kind = toString(Inst.Kind); + Entry.Event = BeginInstantiation ? "Begin" : "End"; + llvm::raw_string_ostream OS(Entry.Name); + printEntryName(TheSema, Inst.Entity, OS); + 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 std::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( + std::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) << " TuneCPU: " << TargetOpts.TuneCPU << "\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::OF_TextWithCRLF)); + } + 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. + llvm::MemoryBufferRef FromFile = SM.getBufferOrFake(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>) on Windows. + // + // 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 = false; + if (llvm::Triple(LLVM_HOST_TRIPLE).isOSWindows()) { + BinaryMode = true; + const SourceManager &SM = CI.getSourceManager(); + if (llvm::Optional<llvm::MemoryBufferRef> Buffer = + SM.getBufferOrNone(SM.getMainFileID())) { + 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 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 Language::C: + case Language::CXX: + case Language::ObjC: + case Language::ObjCXX: + case Language::OpenCL: + case Language::OpenCLCXX: + case Language::CUDA: + case Language::HIP: + break; + + case Language::Unknown: + case Language::Asm: + case Language::LLVM_IR: + case Language::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 << "}"; +} + +void PrintDependencyDirectivesSourceMinimizerAction::ExecuteAction() { + CompilerInstance &CI = getCompilerInstance(); + SourceManager &SM = CI.getPreprocessor().getSourceManager(); + llvm::MemoryBufferRef FromFile = SM.getBufferOrFake(SM.getMainFileID()); + + llvm::SmallString<1024> Output; + llvm::SmallVector<minimize_source_to_dependency_directives::Token, 32> Toks; + if (minimizeSourceToDependencyDirectives( + FromFile.getBuffer(), Output, Toks, &CI.getDiagnostics(), + SM.getLocForStartOfFile(SM.getMainFileID()))) { + assert(CI.getDiagnostics().hasErrorOccurred() && + "no errors reported for failure"); + + // Preprocess the source when verifying the diagnostics to capture the + // 'expected' comments. + if (CI.getDiagnosticOpts().VerifyDiagnostics) { + // Make sure we don't emit new diagnostics! + CI.getDiagnostics().setSuppressAllDiagnostics(true); + Preprocessor &PP = getCompilerInstance().getPreprocessor(); + PP.EnterMainSourceFile(); + Token Tok; + do { + PP.Lex(Tok); + } while (Tok.isNot(tok::eof)); + } + return; + } + llvm::outs() << Output; +} + +void GetDependenciesByModuleNameAction::ExecuteAction() { + CompilerInstance &CI = getCompilerInstance(); + Preprocessor &PP = CI.getPreprocessor(); + SourceManager &SM = PP.getSourceManager(); + FileID MainFileID = SM.getMainFileID(); + SourceLocation FileStart = SM.getLocForStartOfFile(MainFileID); + SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Path; + IdentifierInfo *ModuleID = PP.getIdentifierInfo(ModuleName); + Path.push_back(std::make_pair(ModuleID, FileStart)); + auto ModResult = CI.loadModule(FileStart, Path, Module::Hidden, false); + PPCallbacks *CB = PP.getPPCallbacks(); + CB->moduleImport(SourceLocation(), Path, ModResult); +} diff --git a/contrib/libs/clang14/lib/Frontend/FrontendOptions.cpp b/contrib/libs/clang14/lib/Frontend/FrontendOptions.cpp new file mode 100644 index 0000000000..37ac428a80 --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/FrontendOptions.cpp @@ -0,0 +1,37 @@ +//===- FrontendOptions.cpp ------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/FrontendOptions.h" +#include "clang/Basic/LangStandard.h" +#include "llvm/ADT/StringSwitch.h" + +using namespace clang; + +InputKind FrontendOptions::getInputKindForExtension(StringRef Extension) { + return llvm::StringSwitch<InputKind>(Extension) + .Cases("ast", "pcm", InputKind(Language::Unknown, InputKind::Precompiled)) + .Case("c", Language::C) + .Cases("S", "s", Language::Asm) + .Case("i", InputKind(Language::C).getPreprocessed()) + .Case("ii", InputKind(Language::CXX).getPreprocessed()) + .Case("cui", InputKind(Language::CUDA).getPreprocessed()) + .Case("m", Language::ObjC) + .Case("mi", InputKind(Language::ObjC).getPreprocessed()) + .Cases("mm", "M", Language::ObjCXX) + .Case("mii", InputKind(Language::ObjCXX).getPreprocessed()) + .Cases("C", "cc", "cp", Language::CXX) + .Cases("cpp", "CPP", "c++", "cxx", "hpp", "hxx", Language::CXX) + .Case("cppm", Language::CXX) + .Case("iim", InputKind(Language::CXX).getPreprocessed()) + .Case("cl", Language::OpenCL) + .Case("clcpp", Language::OpenCLCXX) + .Cases("cu", "cuh", Language::CUDA) + .Case("hip", Language::HIP) + .Cases("ll", "bc", Language::LLVM_IR) + .Default(Language::Unknown); +} diff --git a/contrib/libs/clang14/lib/Frontend/HeaderIncludeGen.cpp b/contrib/libs/clang14/lib/Frontend/HeaderIncludeGen.cpp new file mode 100644 index 0000000000..5db8792bf4 --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/HeaderIncludeGen.cpp @@ -0,0 +1,199 @@ +//===-- HeaderIncludeGen.cpp - Generate Header Includes -------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#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; + + void FileSkipped(const FileEntryRef &SkippedFile, const Token &FilenameTok, + SrcMgr::CharacteristicKind FileType) 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::OF_Append | llvm::sys::fs::OF_TextWithCRLF); + 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 ignorelists. 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.first, ShowDepth, 2, MSStyle); + PP.addPPCallbacks(std::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. + + if (!DepOpts.IncludeSystemHeaders && isSystem(NewFileType)) + ShowHeader = false; + + // 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); + } +} + +void HeaderIncludesCallback::FileSkipped(const FileEntryRef &SkippedFile, const + Token &FilenameTok, + SrcMgr::CharacteristicKind FileType) { + if (!DepOpts.ShowSkippedHeaderIncludes) + return; + + if (!DepOpts.IncludeSystemHeaders && isSystem(FileType)) + return; + + PrintHeaderInfo(OutputFile, SkippedFile.getName(), ShowDepth, + CurrentIncludeDepth + 1, MSStyle); +} diff --git a/contrib/libs/clang14/lib/Frontend/InitPreprocessor.cpp b/contrib/libs/clang14/lib/Frontend/InitPreprocessor.cpp new file mode 100644 index 0000000000..e259ab47c5 --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/InitPreprocessor.cpp @@ -0,0 +1,1347 @@ +//===--- InitPreprocessor.cpp - PP initialization code. ---------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/DerivedTypes.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( + std::string(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, -4, -37, -307, -4931, -291, -4931); + int Max10Exp = PickFP(Sem, 4, 38, 308, 4932, 308, 4932); + int MinExp = PickFP(Sem, -13, -125, -1021, -16381, -968, -16381); + int MaxExp = PickFP(Sem, 16, 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, toString(MaxVal, 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(const Twine &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())); +} + +// This will generate a macro based on the prefix with `_MAX__` as the suffix +// for the max value representable for the type, and a macro with a `_WIDTH__` +// suffix for the width of the type. +static void DefineTypeSizeAndWidth(const Twine &Prefix, TargetInfo::IntType Ty, + const TargetInfo &TI, + MacroBuilder &Builder) { + DefineTypeSize(Prefix + "_MAX__", Ty, TI, Builder); + DefineTypeWidth(Prefix + "_WIDTH__", Ty, TI, Builder); +} + +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(); + + // Use the target specified int16 type when appropriate. Some MCU targets + // (such as AVR) have definition of [u]int16_t to [un]signed int. + if (TypeWidth == 16) + Ty = IsSigned ? TI.getInt16Type() : TI.getUInt16Type(); + + 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(); + + // We don't need to define a _WIDTH macro for the exact-width types because + // we already know the width. + 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); + // We only want the *_WIDTH macro for the signed types to avoid too many + // predefined macros (the unsigned width and the signed width are identical.) + if (IsSigned) + DefineTypeSizeAndWidth(Prefix + Twine(TypeWidth), Ty, TI, Builder); + else + 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); + // We only want the *_WIDTH macro for the signed types to avoid too many + // predefined macros (the unsigned width and the signed width are identical.) + if (IsSigned) + DefineTypeSizeAndWidth(Prefix + Twine(TypeWidth), Ty, TI, Builder); + else + 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) { + // C++ [cpp.predefined]p1: + // The following macro names shall be defined by the implementation: + + // -- __STDC__ + // [C++] Whether __STDC__ is predefined and if so, what its value is, + // are implementation-defined. + // (Removed in C++20.) + if (!LangOpts.MSVCCompat && !LangOpts.TraditionalCPP) + Builder.defineMacro("__STDC__"); + // -- __STDC_HOSTED__ + // The integer literal 1 if the implementation is a hosted + // implementation or the integer literal 0 if it is not. + if (LangOpts.Freestanding) + Builder.defineMacro("__STDC_HOSTED__", "0"); + else + Builder.defineMacro("__STDC_HOSTED__"); + + // -- __STDC_VERSION__ + // [C++] Whether __STDC_VERSION__ is predefined and if so, what its + // value is, are implementation-defined. + // (Removed in C++20.) + if (!LangOpts.CPlusPlus) { + // FIXME: Use correct value for C23. + if (LangOpts.C2x) + Builder.defineMacro("__STDC_VERSION__", "202000L"); + else 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 { + // -- __cplusplus + // FIXME: Use correct value for C++23. + if (LangOpts.CPlusPlus2b) + Builder.defineMacro("__cplusplus", "202101L"); + // [C++20] The integer literal 202002L. + else if (LangOpts.CPlusPlus20) + Builder.defineMacro("__cplusplus", "202002L"); + // [C++17] The integer literal 201703L. + else if (LangOpts.CPlusPlus17) + Builder.defineMacro("__cplusplus", "201703L"); + // [C++14] 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] 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] The name __cplusplus is defined to the value 199711L when + // compiling a C++ translation unit. + else + Builder.defineMacro("__cplusplus", "199711L"); + + // -- __STDCPP_DEFAULT_NEW_ALIGNMENT__ + // [C++17] 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())); + + // -- __STDCPP_THREADS__ + // Defined, and has the value integer literal 1, if and only if a + // program can have more than one thread of execution. + if (LangOpts.getThreadModel() == LangOptions::ThreadModelKind::POSIX) + Builder.defineMacro("__STDCPP_THREADS__", "1"); + } + + // 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) { + switch (LangOpts.OpenCLCPlusPlusVersion) { + case 100: + Builder.defineMacro("__OPENCL_CPP_VERSION__", "100"); + break; + case 202100: + Builder.defineMacro("__OPENCL_CPP_VERSION__", "202100"); + break; + default: + llvm_unreachable("Unsupported C++ version for OpenCL"); + } + Builder.defineMacro("__CL_CPP_VERSION_1_0__", "100"); + Builder.defineMacro("__CL_CPP_VERSION_2021__", "202100"); + } 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; + case 300: + Builder.defineMacro("__OPENCL_C_VERSION__", "300"); + 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"); + Builder.defineMacro("CL_VERSION_3_0", "300"); + + if (TI.isLittleEndian()) + Builder.defineMacro("__ENDIAN_LITTLE__"); + + if (LangOpts.FastRelaxedMath) + Builder.defineMacro("__FAST_RELAXED_MATH__"); + } + + if (LangOpts.SYCLIsDevice || LangOpts.SYCLIsHost) { + // SYCL Version is set to a value when building SYCL applications + if (LangOpts.getSYCLVersion() == LangOptions::SYCL_2017) + Builder.defineMacro("CL_SYCL_LANGUAGE_VERSION", "121"); + else if (LangOpts.getSYCLVersion() == LangOptions::SYCL_2020) + Builder.defineMacro("SYCL_LANGUAGE_VERSION", "202001"); + } + + // Not "standard" per se, but available even with the -undef flag. + if (LangOpts.AsmPreprocessor) + Builder.defineMacro("__ASSEMBLER__"); + if (LangOpts.CUDA) { + if (LangOpts.GPURelocatableDeviceCode) + Builder.defineMacro("__CLANG_RDC__"); + if (!LangOpts.HIP) + Builder.defineMacro("__CUDA__"); + } + if (LangOpts.HIP) { + Builder.defineMacro("__HIP__"); + Builder.defineMacro("__HIPCC__"); + Builder.defineMacro("__HIP_MEMORY_SCOPE_SINGLETHREAD", "1"); + Builder.defineMacro("__HIP_MEMORY_SCOPE_WAVEFRONT", "2"); + Builder.defineMacro("__HIP_MEMORY_SCOPE_WORKGROUP", "3"); + Builder.defineMacro("__HIP_MEMORY_SCOPE_AGENT", "4"); + Builder.defineMacro("__HIP_MEMORY_SCOPE_SYSTEM", "5"); + 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.CPlusPlus20 ? "201907L" : + LangOpts.CPlusPlus17 ? "201603L" : + LangOpts.CPlusPlus14 ? "201304L" : "200704"); + Builder.defineMacro("__cpp_constexpr_in_decltype", "201711L"); + 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", + LangOpts.CPlusPlus20 ? "201803L" : "201304L"); + Builder.defineMacro("__cpp_generic_lambdas", + LangOpts.CPlusPlus20 ? "201707L" : "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"); // (not latest) + 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"); // (not latest) + 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.CPlusPlus20) { + //Builder.defineMacro("__cpp_aggregate_paren_init", "201902L"); + Builder.defineMacro("__cpp_concepts", "201907L"); + Builder.defineMacro("__cpp_conditional_explicit", "201806L"); + //Builder.defineMacro("__cpp_consteval", "201811L"); + Builder.defineMacro("__cpp_constexpr_dynamic_alloc", "201907L"); + Builder.defineMacro("__cpp_constinit", "201907L"); + Builder.defineMacro("__cpp_impl_coroutine", "201902L"); + Builder.defineMacro("__cpp_designated_initializers", "201707L"); + Builder.defineMacro("__cpp_impl_three_way_comparison", "201907L"); + //Builder.defineMacro("__cpp_modules", "201907L"); + Builder.defineMacro("__cpp_using_enum", "201907L"); + } + // C++2b features. + if (LangOpts.CPlusPlus2b) { + Builder.defineMacro("__cpp_implicit_move", "202011L"); + Builder.defineMacro("__cpp_size_t_suffix", "202011L"); + Builder.defineMacro("__cpp_if_consteval", "202106L"); + } + if (LangOpts.Char8) + Builder.defineMacro("__cpp_char8_t", "201811L"); + Builder.defineMacro("__cpp_impl_destroying_delete", "201806L"); + + // TS features. + if (LangOpts.Coroutines) + Builder.defineMacro("__cpp_coroutines", "201703L"); +} + +/// InitializeOpenCLFeatureTestMacros - Define OpenCL macros based on target +/// settings and language version +void InitializeOpenCLFeatureTestMacros(const TargetInfo &TI, + const LangOptions &Opts, + MacroBuilder &Builder) { + const llvm::StringMap<bool> &OpenCLFeaturesMap = TI.getSupportedOpenCLOpts(); + // FIXME: OpenCL options which affect language semantics/syntax + // should be moved into LangOptions. + auto defineOpenCLExtMacro = [&](llvm::StringRef Name, auto... OptArgs) { + // Check if extension is supported by target and is available in this + // OpenCL version + if (TI.hasFeatureEnabled(OpenCLFeaturesMap, Name) && + OpenCLOptions::isOpenCLOptionAvailableIn(Opts, OptArgs...)) + Builder.defineMacro(Name); + }; +#define OPENCL_GENERIC_EXTENSION(Ext, ...) \ + defineOpenCLExtMacro(#Ext, __VA_ARGS__); +#include "clang/Basic/OpenCLExtensions.def" + + // Assume compiling for FULL profile + Builder.defineMacro("__opencl_c_int64"); +} + +static void InitializePredefinedMacros(const TargetInfo &TI, + const LangOptions &LangOpts, + const FrontendOptions &FEOpts, + const PreprocessorOptions &PPOpts, + 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.GNUCVersion != 0) { + // Major, minor, patch, are given two decimal places each, so 4.2.1 becomes + // 40201. + unsigned GNUCMajor = LangOpts.GNUCVersion / 100 / 100; + unsigned GNUCMinor = LangOpts.GNUCVersion / 100 % 100; + unsigned GNUCPatch = LangOpts.GNUCVersion % 100; + Builder.defineMacro("__GNUC__", Twine(GNUCMajor)); + Builder.defineMacro("__GNUC_MINOR__", Twine(GNUCMinor)); + Builder.defineMacro("__GNUC_PATCHLEVEL__", Twine(GNUCPatch)); + Builder.defineMacro("__GXX_ABI_VERSION", "1002"); + + if (LangOpts.CPlusPlus) { + Builder.defineMacro("__GNUG__", Twine(GNUCMajor)); + Builder.defineMacro("__GXX_WEAK__"); + } + } + + // 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"); + + // Previously this macro was set to a string aiming to achieve compatibility + // with GCC 4.2.1. Now, just return the full Clang version + Builder.defineMacro("__VERSION__", "\"" + + Twine(getClangFullCPPVersion()) + "\""); + + // Initialize language-specific preprocessor defines. + + // Standard conforming mode? + if (!LangOpts.GNUMode && !LangOpts.MSVCCompat) + Builder.defineMacro("__STRICT_ANSI__"); + + if (LangOpts.GNUCVersion && 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.GNUCVersion && LangOpts.RTTI) + Builder.defineMacro("__GXX_RTTI"); + + if (LangOpts.hasSjLjExceptions()) + Builder.defineMacro("__USING_SJLJ_EXCEPTIONS__"); + else if (LangOpts.hasSEHExceptions()) + Builder.defineMacro("__SEH__"); + else if (LangOpts.hasDWARFExceptions() && + (TI.getTriple().isThumb() || TI.getTriple().isARM())) + Builder.defineMacro("__ARM_DWARF_EH__"); + + if (LangOpts.Deprecated) + Builder.defineMacro("__DEPRECATED"); + + if (!LangOpts.MSVCCompat && LangOpts.CPlusPlus) + 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"); + } + } + + // Macros to help identify the narrow and wide character sets + // FIXME: clang currently ignores -fexec-charset=. If this changes, + // then this may need to be updated. + Builder.defineMacro("__clang_literal_encoding__", "\"UTF-8\""); + if (TI.getTypeWidth(TI.getWCharType()) >= 32) { + // FIXME: 32-bit wchar_t signals UTF-32. This may change + // if -fwide-exec-charset= is ever supported. + Builder.defineMacro("__clang_wide_literal_encoding__", "\"UTF-32\""); + } else { + // FIXME: Less-than 32-bit wchar_t generally means UTF-16 + // (e.g., Windows, 32-bit IBM). This may need to be + // updated if -fwide-exec-charset= is ever supported. + Builder.defineMacro("__clang_wide_literal_encoding__", "\"UTF-16\""); + } + + 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())); + + Builder.defineMacro("__BOOL_WIDTH__", Twine(TI.getBoolWidth())); + Builder.defineMacro("__SHRT_WIDTH__", Twine(TI.getShortWidth())); + Builder.defineMacro("__INT_WIDTH__", Twine(TI.getIntWidth())); + Builder.defineMacro("__LONG_WIDTH__", Twine(TI.getLongWidth())); + Builder.defineMacro("__LLONG_WIDTH__", Twine(TI.getLongLongWidth())); + + size_t BitIntMaxWidth = TI.getMaxBitIntWidth(); + assert(BitIntMaxWidth <= llvm::IntegerType::MAX_INT_BITS && + "Target defined a max bit width larger than LLVM can support!"); + assert(BitIntMaxWidth >= TI.getLongLongWidth() && + "Target defined a max bit width smaller than the C standard allows!"); + Builder.defineMacro("__BITINT_MAXWIDTH__", Twine(BitIntMaxWidth)); + + 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); + DefineTypeSizeAndWidth("__WCHAR", TI.getWCharType(), TI, Builder); + DefineTypeSizeAndWidth("__WINT", TI.getWIntType(), TI, Builder); + DefineTypeSizeAndWidth("__INTMAX", TI.getIntMaxType(), TI, Builder); + DefineTypeSizeAndWidth("__SIZE", TI.getSizeType(), TI, Builder); + + DefineTypeSizeAndWidth("__UINTMAX", TI.getUIntMaxType(), TI, Builder); + DefineTypeSizeAndWidth("__PTRDIFF", TI.getPtrDiffType(0), TI, Builder); + DefineTypeSizeAndWidth("__INTPTR", TI.getIntPtrType(), TI, Builder); + DefineTypeSizeAndWidth("__UINTPTR", 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())); + DefineType("__PTRDIFF_TYPE__", TI.getPtrDiffType(0), Builder); + DefineFmt("__PTRDIFF", TI.getPtrDiffType(0), TI, Builder); + DefineType("__INTPTR_TYPE__", TI.getIntPtrType(), Builder); + DefineFmt("__INTPTR", TI.getIntPtrType(), TI, Builder); + DefineType("__SIZE_TYPE__", TI.getSizeType(), Builder); + DefineFmt("__SIZE", TI.getSizeType(), TI, Builder); + DefineType("__WCHAR_TYPE__", TI.getWCharType(), Builder); + DefineType("__WINT_TYPE__", TI.getWIntType(), Builder); + DefineTypeSizeAndWidth("__SIG_ATOMIC", TI.getSigAtomicType(), TI, Builder); + DefineType("__CHAR16_TYPE__", TI.getChar16Type(), Builder); + DefineType("__CHAR32_TYPE__", TI.getChar32Type(), Builder); + + DefineType("__UINTPTR_TYPE__", TI.getUIntPtrType(), Builder); + DefineFmt("__UINTPTR", TI.getUIntPtrType(), TI, Builder); + + // The C standard requires the width of uintptr_t and intptr_t to be the same, + // per 7.20.2.4p1. Same for intmax_t and uintmax_t, per 7.20.2.5p1. + assert(TI.getTypeWidth(TI.getUIntPtrType()) == + TI.getTypeWidth(TI.getIntPtrType()) && + "uintptr_t and intptr_t have different widths?"); + assert(TI.getTypeWidth(TI.getUIntMaxType()) == + TI.getTypeWidth(TI.getIntMaxType()) && + "uintmax_t and intmax_t have different widths?"); + + if (TI.hasFloat16Type()) + 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); + + Builder.defineMacro("__USER_LABEL_PREFIX__", TI.getUserLabelPrefix()); + + if (!LangOpts.MathErrno) + Builder.defineMacro("__NO_MATH_ERRNO__"); + + if (LangOpts.FastMath || LangOpts.FiniteMathOnly) + Builder.defineMacro("__FINITE_MATH_ONLY__", "1"); + else + Builder.defineMacro("__FINITE_MATH_ONLY__", "0"); + + if (LangOpts.GNUCVersion) { + 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.GNUCVersion) + 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"); + + if (PPOpts.SetUpStaticAnalyzer) + 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 31: + Builder.defineMacro("_OPENMP", "201107"); + break; + case 40: + Builder.defineMacro("_OPENMP", "201307"); + break; + case 45: + Builder.defineMacro("_OPENMP", "201511"); + break; + case 51: + Builder.defineMacro("_OPENMP", "202011"); + break; + case 52: + Builder.defineMacro("_OPENMP", "202111"); + break; + default: + // Default version is OpenMP 5.0 + Builder.defineMacro("_OPENMP", "201811"); + 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__"); + } + + // Define a macro indicating that the source file is being compiled with a + // SYCL device compiler which doesn't produce host binary. + if (LangOpts.SYCLIsDevice) { + Builder.defineMacro("__SYCL_DEVICE_ONLY__", "1"); + } + + // OpenCL definitions. + if (LangOpts.OpenCL) { + InitializeOpenCLFeatureTestMacros(TI, LangOpts, Builder); + + if (TI.getTriple().isSPIR() || TI.getTriple().isSPIRV()) + 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 || LangOpts.SYCLIsDevice) && + PP.getAuxTargetInfo()) + InitializePredefinedMacros(*PP.getAuxTargetInfo(), LangOpts, FEOpts, + PP.getPreprocessorOpts(), Builder); + + InitializePredefinedMacros(PP.getTargetInfo(), LangOpts, FEOpts, + PP.getPreprocessorOpts(), 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/libs/clang14/lib/Frontend/InterfaceStubFunctionsConsumer.cpp b/contrib/libs/clang14/lib/Frontend/InterfaceStubFunctionsConsumer.cpp new file mode 100644 index 0000000000..d58f5bb091 --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/InterfaceStubFunctionsConsumer.cpp @@ -0,0 +1,340 @@ +//===--- InterfaceStubFunctionsConsumer.cpp -------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/Mangle.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Sema/TemplateInstCallback.h" +#include "llvm/BinaryFormat/ELF.h" + +using namespace clang; + +namespace { +class InterfaceStubFunctionsConsumer : public ASTConsumer { + CompilerInstance &Instance; + StringRef InFile; + StringRef Format; + std::set<std::string> ParsedTemplates; + + enum RootDeclOrigin { TopLevel = 0, FromTU = 1, IsLate = 2 }; + struct MangledSymbol { + std::string ParentName; + uint8_t Type; + uint8_t Binding; + std::vector<std::string> Names; + MangledSymbol() = delete; + + MangledSymbol(const std::string &ParentName, uint8_t Type, uint8_t Binding, + std::vector<std::string> Names) + : ParentName(ParentName), Type(Type), Binding(Binding), Names(Names) {} + }; + using MangledSymbols = std::map<const NamedDecl *, MangledSymbol>; + + bool WriteNamedDecl(const NamedDecl *ND, MangledSymbols &Symbols, int RDO) { + // Here we filter out anything that's not set to DefaultVisibility. + // DefaultVisibility is set on a decl when -fvisibility is not specified on + // the command line (or specified as default) and the decl does not have + // __attribute__((visibility("hidden"))) set or when the command line + // argument is set to hidden but the decl explicitly has + // __attribute__((visibility ("default"))) set. We do this so that the user + // can have fine grain control of what they want to expose in the stub. + auto isVisible = [](const NamedDecl *ND) -> bool { + return ND->getVisibility() == DefaultVisibility; + }; + + auto ignoreDecl = [this, isVisible](const NamedDecl *ND) -> bool { + if (!isVisible(ND)) + return true; + + if (const VarDecl *VD = dyn_cast<VarDecl>(ND)) { + if (const auto *Parent = VD->getParentFunctionOrMethod()) + if (isa<BlockDecl>(Parent) || isa<CXXMethodDecl>(Parent)) + return true; + + if ((VD->getStorageClass() == StorageClass::SC_Extern) || + (VD->getStorageClass() == StorageClass::SC_Static && + VD->getParentFunctionOrMethod() == nullptr)) + return true; + } + + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(ND)) { + if (FD->isInlined() && !isa<CXXMethodDecl>(FD) && + !Instance.getLangOpts().GNUInline) + return true; + if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD)) { + if (const auto *RC = dyn_cast<CXXRecordDecl>(MD->getParent())) + if (isa<ClassTemplateDecl>(RC->getParent()) || !isVisible(RC)) + return true; + if (MD->isDependentContext() || !MD->hasBody()) + return true; + } + if (FD->getStorageClass() == StorageClass::SC_Static) + return true; + } + return false; + }; + + auto getParentFunctionDecl = [](const NamedDecl *ND) -> const NamedDecl * { + if (const VarDecl *VD = dyn_cast<VarDecl>(ND)) + if (const auto *FD = + dyn_cast_or_null<FunctionDecl>(VD->getParentFunctionOrMethod())) + return FD; + return nullptr; + }; + + auto getMangledNames = [](const NamedDecl *ND) -> std::vector<std::string> { + if (!ND) + return {""}; + ASTNameGenerator NameGen(ND->getASTContext()); + std::vector<std::string> MangledNames = NameGen.getAllManglings(ND); + if (isa<CXXConstructorDecl>(ND) || isa<CXXDestructorDecl>(ND)) + return MangledNames; +#ifdef EXPENSIVE_CHECKS + assert(MangledNames.size() <= 1 && "Expected only one name mangling."); +#endif + return {NameGen.getName(ND)}; + }; + + if (!(RDO & FromTU)) + return true; + if (Symbols.find(ND) != Symbols.end()) + return true; + // - Currently have not figured out how to produce the names for FieldDecls. + // - Do not want to produce symbols for function paremeters. + if (isa<FieldDecl>(ND) || isa<ParmVarDecl>(ND)) + return true; + + const NamedDecl *ParentDecl = getParentFunctionDecl(ND); + if ((ParentDecl && ignoreDecl(ParentDecl)) || ignoreDecl(ND)) + return true; + + if (RDO & IsLate) { + Instance.getDiagnostics().Report(diag::err_asm_invalid_type_in_input) + << "Generating Interface Stubs is not supported with " + "delayed template parsing."; + } else { + if (const auto *FD = dyn_cast<FunctionDecl>(ND)) + if (FD->isDependentContext()) + return true; + + const bool IsWeak = (ND->hasAttr<WeakAttr>() || + ND->hasAttr<WeakRefAttr>() || ND->isWeakImported()); + + Symbols.insert(std::make_pair( + ND, + MangledSymbol(getMangledNames(ParentDecl).front(), + // Type: + isa<VarDecl>(ND) ? llvm::ELF::STT_OBJECT + : llvm::ELF::STT_FUNC, + // Binding: + IsWeak ? llvm::ELF::STB_WEAK : llvm::ELF::STB_GLOBAL, + getMangledNames(ND)))); + } + return true; + } + + void + HandleDecls(const llvm::iterator_range<DeclContext::decl_iterator> &Decls, + MangledSymbols &Symbols, int RDO) { + for (const auto *D : Decls) + HandleNamedDecl(dyn_cast<NamedDecl>(D), Symbols, RDO); + } + + void HandleTemplateSpecializations(const FunctionTemplateDecl &FTD, + MangledSymbols &Symbols, int RDO) { + for (const auto *D : FTD.specializations()) + HandleNamedDecl(dyn_cast<NamedDecl>(D), Symbols, RDO); + } + + void HandleTemplateSpecializations(const ClassTemplateDecl &CTD, + MangledSymbols &Symbols, int RDO) { + for (const auto *D : CTD.specializations()) + HandleNamedDecl(dyn_cast<NamedDecl>(D), Symbols, RDO); + } + + bool HandleNamedDecl(const NamedDecl *ND, MangledSymbols &Symbols, int RDO) { + if (!ND) + return false; + + switch (ND->getKind()) { + default: + break; + case Decl::Kind::Namespace: + HandleDecls(cast<NamespaceDecl>(ND)->decls(), Symbols, RDO); + return true; + case Decl::Kind::CXXRecord: + HandleDecls(cast<CXXRecordDecl>(ND)->decls(), Symbols, RDO); + return true; + case Decl::Kind::ClassTemplateSpecialization: + HandleDecls(cast<ClassTemplateSpecializationDecl>(ND)->decls(), Symbols, + RDO); + return true; + case Decl::Kind::ClassTemplate: + HandleTemplateSpecializations(*cast<ClassTemplateDecl>(ND), Symbols, RDO); + return true; + case Decl::Kind::FunctionTemplate: + HandleTemplateSpecializations(*cast<FunctionTemplateDecl>(ND), Symbols, + RDO); + return true; + case Decl::Kind::Record: + case Decl::Kind::Typedef: + case Decl::Kind::Enum: + case Decl::Kind::EnumConstant: + case Decl::Kind::TemplateTypeParm: + case Decl::Kind::NonTypeTemplateParm: + case Decl::Kind::CXXConversion: + case Decl::Kind::UnresolvedUsingValue: + case Decl::Kind::Using: + case Decl::Kind::UsingShadow: + case Decl::Kind::TypeAliasTemplate: + case Decl::Kind::TypeAlias: + case Decl::Kind::VarTemplate: + case Decl::Kind::VarTemplateSpecialization: + case Decl::Kind::UsingDirective: + case Decl::Kind::TemplateTemplateParm: + case Decl::Kind::ClassTemplatePartialSpecialization: + case Decl::Kind::IndirectField: + case Decl::Kind::ConstructorUsingShadow: + case Decl::Kind::CXXDeductionGuide: + case Decl::Kind::NamespaceAlias: + case Decl::Kind::UnresolvedUsingTypename: + return true; + case Decl::Kind::Var: { + // Bail on any VarDecl that either has no named symbol. + if (!ND->getIdentifier()) + return true; + const auto *VD = cast<VarDecl>(ND); + // Bail on any VarDecl that is a dependent or templated type. + if (VD->isTemplated() || VD->getType()->isDependentType()) + return true; + if (WriteNamedDecl(ND, Symbols, RDO)) + return true; + break; + } + case Decl::Kind::ParmVar: + case Decl::Kind::CXXMethod: + case Decl::Kind::CXXConstructor: + case Decl::Kind::CXXDestructor: + case Decl::Kind::Function: + case Decl::Kind::Field: + if (WriteNamedDecl(ND, Symbols, RDO)) + return true; + } + + // While interface stubs are in the development stage, it's probably best to + // catch anything that's not a VarDecl or Template/FunctionDecl. + Instance.getDiagnostics().Report(diag::err_asm_invalid_type_in_input) + << "Expected a function or function template decl."; + return false; + } + +public: + InterfaceStubFunctionsConsumer(CompilerInstance &Instance, StringRef InFile, + StringRef Format) + : Instance(Instance), InFile(InFile), Format(Format) {} + + void HandleTranslationUnit(ASTContext &context) override { + struct Visitor : public RecursiveASTVisitor<Visitor> { + bool VisitNamedDecl(NamedDecl *ND) { + if (const auto *FD = dyn_cast<FunctionDecl>(ND)) + if (FD->isLateTemplateParsed()) { + LateParsedDecls.insert(FD); + return true; + } + + if (const auto *VD = dyn_cast<ValueDecl>(ND)) { + ValueDecls.insert(VD); + return true; + } + + NamedDecls.insert(ND); + return true; + } + + std::set<const NamedDecl *> LateParsedDecls; + std::set<NamedDecl *> NamedDecls; + std::set<const ValueDecl *> ValueDecls; + } v; + + v.TraverseDecl(context.getTranslationUnitDecl()); + + MangledSymbols Symbols; + auto OS = Instance.createDefaultOutputFile(/*Binary=*/false, InFile, "ifs"); + if (!OS) + return; + + if (Instance.getLangOpts().DelayedTemplateParsing) { + clang::Sema &S = Instance.getSema(); + for (const auto *FD : v.LateParsedDecls) { + clang::LateParsedTemplate &LPT = + *S.LateParsedTemplateMap.find(cast<FunctionDecl>(FD))->second; + S.LateTemplateParser(S.OpaqueParser, LPT); + HandleNamedDecl(FD, Symbols, (FromTU | IsLate)); + } + } + + for (const NamedDecl *ND : v.ValueDecls) + HandleNamedDecl(ND, Symbols, FromTU); + for (const NamedDecl *ND : v.NamedDecls) + HandleNamedDecl(ND, Symbols, FromTU); + + auto writeIfsV1 = [this](const llvm::Triple &T, + const MangledSymbols &Symbols, + const ASTContext &context, StringRef Format, + raw_ostream &OS) -> void { + OS << "--- !" << Format << "\n"; + OS << "IfsVersion: 3.0\n"; + OS << "Target: " << T.str() << "\n"; + OS << "Symbols:\n"; + for (const auto &E : Symbols) { + const MangledSymbol &Symbol = E.second; + for (auto Name : Symbol.Names) { + OS << " - { Name: \"" + << (Symbol.ParentName.empty() || Instance.getLangOpts().CPlusPlus + ? "" + : (Symbol.ParentName + ".")) + << Name << "\", Type: "; + switch (Symbol.Type) { + default: + llvm_unreachable( + "clang -emit-interface-stubs: Unexpected symbol type."); + case llvm::ELF::STT_NOTYPE: + OS << "NoType"; + break; + case llvm::ELF::STT_OBJECT: { + auto VD = cast<ValueDecl>(E.first)->getType(); + OS << "Object, Size: " + << context.getTypeSizeInChars(VD).getQuantity(); + break; + } + case llvm::ELF::STT_FUNC: + OS << "Func"; + break; + } + if (Symbol.Binding == llvm::ELF::STB_WEAK) + OS << ", Weak: true"; + OS << " }\n"; + } + } + OS << "...\n"; + OS.flush(); + }; + + assert(Format == "ifs-v1" && "Unexpected IFS Format."); + writeIfsV1(Instance.getTarget().getTriple(), Symbols, context, Format, *OS); + } +}; +} // namespace + +std::unique_ptr<ASTConsumer> +GenerateInterfaceStubsAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + return std::make_unique<InterfaceStubFunctionsConsumer>(CI, InFile, "ifs-v1"); +} diff --git a/contrib/libs/clang14/lib/Frontend/LayoutOverrideSource.cpp b/contrib/libs/clang14/lib/Frontend/LayoutOverrideSource.cpp new file mode 100644 index 0000000000..0d288db063 --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/LayoutOverrideSource.cpp @@ -0,0 +1,207 @@ +//===--- LayoutOverrideSource.cpp --Override Record Layouts ---------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#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() || !isAsciiIdentifierStart(S[0])) + return ""; + + unsigned Offset = 1; + while (Offset < S.size() && isAsciiIdentifierContinue(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.contains("*** Dumping AST Record Layout")) { + // 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/libs/clang14/lib/Frontend/LogDiagnosticPrinter.cpp b/contrib/libs/clang14/lib/Frontend/LogDiagnosticPrinter.cpp new file mode 100644 index 0000000000..df8b23691a --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/LogDiagnosticPrinter.cpp @@ -0,0 +1,165 @@ +//===--- LogDiagnosticPrinter.cpp - Log Diagnostic Printer ----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#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 = std::string(FE->getName()); + } + } + + // Create the diag entry. + DiagEntry DE; + DE.DiagnosticID = Info.getID(); + DE.DiagnosticLevel = Level; + + DE.WarningOption = + std::string(DiagnosticIDs::getWarningOptionForDiag(DE.DiagnosticID)); + + // Format the message. + SmallString<100> MessageStr; + Info.FormatDiagnostic(MessageStr); + DE.Message = std::string(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 = std::string(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/libs/clang14/lib/Frontend/ModuleDependencyCollector.cpp b/contrib/libs/clang14/lib/Frontend/ModuleDependencyCollector.cpp new file mode 100644 index 0000000000..4301e49f1d --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/ModuleDependencyCollector.cpp @@ -0,0 +1,200 @@ +//===--- ModuleDependencyCollector.cpp - Collect module dependencies ------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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); + } + } +}; + +} + +void ModuleDependencyCollector::attachToASTReader(ASTReader &R) { + R.addListener(std::make_unique<ModuleDependencyListener>(*this)); +} + +void ModuleDependencyCollector::attachToPreprocessor(Preprocessor &PP) { + PP.addPPCallbacks(std::make_unique<ModuleDependencyPPCallbacks>( + *this, PP.getSourceManager())); + PP.getHeaderSearchInfo().getModuleMap().addModuleMapCallbacks( + std::make_unique<ModuleDependencyMMCallbacks>(*this)); +} + +static bool isCaseSensitivePath(StringRef Path) { + SmallString<256> TmpDest = Path, UpperDest, RealDest; + // Remove component traversals, links, etc. + if (llvm::sys::fs::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 (!llvm::sys::fs::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::OF_TextWithCRLF); + if (EC) { + HasErrors = true; + return; + } + VFSWriter.write(OS); +} + +std::error_code ModuleDependencyCollector::copyToRoot(StringRef Src, + StringRef Dst) { + using namespace llvm::sys; + llvm::FileCollector::PathCanonicalizer::PathStorage Paths = + Canonicalizer.canonicalize(Src); + + 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(Paths.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); + Paths.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(Paths.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(Paths.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/libs/clang14/lib/Frontend/MultiplexConsumer.cpp b/contrib/libs/clang14/lib/Frontend/MultiplexConsumer.cpp new file mode 100644 index 0000000000..34bbc365e6 --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/MultiplexConsumer.cpp @@ -0,0 +1,370 @@ +//===- MultiplexConsumer.cpp - AST Consumer for PCH Generation --*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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 DeclarationMarkedOpenMPAllocate(const Decl *D, const Attr *A) 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::DeclarationMarkedOpenMPAllocate( + const Decl *D, const Attr *A) { + for (ASTMutationListener *L : Listeners) + L->DeclarationMarkedOpenMPAllocate(D, A); +} +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)) { + // 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 = + std::make_unique<MultiplexASTMutationListener>(mutationListeners); + } + if (!serializationListeners.empty()) { + DeserializationListener = + std::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::CompleteExternalDeclaration(VarDecl *D) { + for (auto &Consumer : Consumers) + Consumer->CompleteExternalDeclaration(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/libs/clang14/lib/Frontend/PrecompiledPreamble.cpp b/contrib/libs/clang14/lib/Frontend/PrecompiledPreamble.cpp new file mode 100644 index 0000000000..8aa80a4c96 --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/PrecompiledPreamble.cpp @@ -0,0 +1,885 @@ +//===--- PrecompiledPreamble.cpp - Build precompiled preambles --*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Helper class to build precompiled preamble. +// +//===----------------------------------------------------------------------===// + +#include "clang/Frontend/PrecompiledPreamble.h" +#include "clang/AST/DeclObjC.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/LangStandard.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/HeaderSearch.h" +#include "clang/Lex/Lexer.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Lex/PreprocessorOptions.h" +#include "clang/Serialization/ASTWriter.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/Config/llvm-config.h" +#include "llvm/Support/CrashRecoveryContext.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/VirtualFileSystem.h" +#include <limits> +#include <mutex> +#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; } +}; + +// Collects files whose existence would invalidate the preamble. +// Collecting *all* of these would make validating it too slow though, so we +// just find all the candidates for 'file not found' diagnostics. +// +// A caveat that may be significant for generated files: we'll omit files under +// search path entries whose roots don't exist when the preamble is built. +// These are pruned by InitHeaderSearch and so we don't see the search path. +// It would be nice to include them but we don't want to duplicate all the rest +// of the InitHeaderSearch logic to reconstruct them. +class MissingFileCollector : public PPCallbacks { + llvm::StringSet<> &Out; + const HeaderSearch &Search; + const SourceManager &SM; + +public: + MissingFileCollector(llvm::StringSet<> &Out, const HeaderSearch &Search, + const SourceManager &SM) + : Out(Out), Search(Search), 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 { + // File is null if it wasn't found. + // (We have some false negatives if PP recovered e.g. <foo> -> "foo") + if (File != nullptr) + return; + + // If it's a rare absolute include, we know the full path already. + if (llvm::sys::path::is_absolute(FileName)) { + Out.insert(FileName); + return; + } + + // Reconstruct the filenames that would satisfy this directive... + llvm::SmallString<256> Buf; + auto NotFoundRelativeTo = [&](const DirectoryEntry *DE) { + Buf = DE->getName(); + llvm::sys::path::append(Buf, FileName); + llvm::sys::path::remove_dots(Buf, /*remove_dot_dot=*/true); + Out.insert(Buf); + }; + // ...relative to the including file. + if (!IsAngled) { + if (const FileEntry *IncludingFile = + SM.getFileEntryForID(SM.getFileID(IncludeTok.getLocation()))) + if (IncludingFile->getDir()) + NotFoundRelativeTo(IncludingFile->getDir()); + } + // ...relative to the search paths. + for (const auto &Dir : llvm::make_range( + IsAngled ? Search.angled_dir_begin() : Search.search_dir_begin(), + Search.search_dir_end())) { + // No support for frameworks or header maps yet. + if (Dir.isNormalDir()) + NotFoundRelativeTo(Dir.getDir()); + } + } +}; + +/// 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: + std::mutex Mutex; + llvm::StringSet<> Files; +}; + +TemporaryFiles &TemporaryFiles::getInstance() { + static TemporaryFiles Instance; + return Instance; +} + +TemporaryFiles::~TemporaryFiles() { + std::lock_guard<std::mutex> Guard(Mutex); + for (const auto &File : Files) + llvm::sys::fs::remove(File.getKey()); +} + +void TemporaryFiles::addFile(StringRef File) { + std::lock_guard<std::mutex> Guard(Mutex); + auto IsInserted = Files.insert(File).second; + (void)IsInserted; + assert(IsInserted && "File has already been added"); +} + +void TemporaryFiles::removeFile(StringRef File) { + std::lock_guard<std::mutex> 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 BeginSourceFileAction(CompilerInstance &CI) override { + assert(CI.getLangOpts().CompilingPCH); + return ASTFrontendAction::BeginSourceFileAction(CI); + } + + 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, + InMemoryModuleCache &ModuleCache, + StringRef isysroot, + std::unique_ptr<raw_ostream> Out) + : PCHGenerator(PP, ModuleCache, "", 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()); + } + + bool shouldSkipFunctionBody(Decl *D) override { + return Action.Callbacks.shouldSkipFunctionBody(D); + } + +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 = std::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 std::make_unique<PrecompilePreambleConsumer>( + *this, CI.getPreprocessor(), CI.getModuleCache(), 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, + const llvm::MemoryBufferRef &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 = + std::string(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. + if (!Clang->createTarget()) + return BuildPreambleError::CouldntCreateTargetInfo; + + if (Clang->getFrontendOpts().Inputs.size() != 1 || + Clang->getFrontendOpts().Inputs[0].getKind().getFormat() != + InputKind::Source || + Clang->getFrontendOpts().Inputs[0].getKind().getLanguage() == + Language::LLVM_IR) { + return BuildPreambleError::BadInputs; + } + + // 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); + + Clang->getLangOpts().CompilingPCH = true; + + // 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)); + if (!Act->BeginSourceFile(*Clang.get(), Clang->getFrontendOpts().Inputs[0])) + return BuildPreambleError::BeginSourceFileFailed; + + // Performed after BeginSourceFile to ensure Clang->Preprocessor can be + // referenced in the callback. + Callbacks.BeforeExecute(*Clang); + + std::unique_ptr<PPCallbacks> DelegatedPPCallbacks = + Callbacks.createPPCallbacks(); + if (DelegatedPPCallbacks) + Clang->getPreprocessor().addPPCallbacks(std::move(DelegatedPPCallbacks)); + if (auto CommentHandler = Callbacks.getCommentHandler()) + Clang->getPreprocessor().addCommentHandler(CommentHandler); + llvm::StringSet<> MissingFiles; + Clang->getPreprocessor().addPPCallbacks( + std::make_unique<MissingFileCollector>( + MissingFiles, Clang->getPreprocessor().getHeaderSearchInfo(), + Clang->getSourceManager())); + + if (llvm::Error Err = Act->Execute()) + return errorToErrorCode(std::move(Err)); + + // 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()) { + auto FileOrErr = Clang->getFileManager().getFile(Filename); + if (!FileOrErr || + *FileOrErr == SourceMgr.getFileEntryForID(SourceMgr.getMainFileID())) + continue; + auto File = *FileOrErr; + if (time_t ModTime = File->getModificationTime()) { + FilesInPreamble[File->getName()] = + PrecompiledPreamble::PreambleFileHash::createForFile(File->getSize(), + ModTime); + } else { + llvm::MemoryBufferRef Buffer = + SourceMgr.getMemoryBufferForFileOrFake(File); + FilesInPreamble[File->getName()] = + PrecompiledPreamble::PreambleFileHash::createForMemoryBuffer(Buffer); + } + } + + return PrecompiledPreamble( + std::move(Storage), std::move(PreambleBytes), PreambleEndsAtStartOfLine, + std::move(FilesInPreamble), std::move(MissingFiles)); +} + +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::MemoryBufferRef &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; + llvm::StringSet<> OverriddenAbsPaths; // Either by buffers or files. + 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; + } + // If a mapped file was previously missing, then it has changed. + llvm::SmallString<128> MappedPath(R.first); + if (!VFS.makeAbsolute(MappedPath)) + OverriddenAbsPaths.insert(MappedPath); + + OverriddenFiles[Status.getUniqueID()] = PreambleFileHash::createForFile( + Status.getSize(), llvm::sys::toTimeT(Status.getLastModificationTime())); + } + + // OverridenFileBuffers tracks only the files not found in VFS. + llvm::StringMap<PreambleFileHash> OverridenFileBuffers; + for (const auto &RB : PreprocessorOpts.RemappedFileBuffers) { + const PrecompiledPreamble::PreambleFileHash PreambleHash = + PreambleFileHash::createForMemoryBuffer(RB.second->getMemBufferRef()); + llvm::vfs::Status Status; + if (moveOnNoError(VFS.status(RB.first), Status)) + OverriddenFiles[Status.getUniqueID()] = PreambleHash; + else + OverridenFileBuffers[RB.first] = PreambleHash; + + llvm::SmallString<128> MappedPath(RB.first); + if (!VFS.makeAbsolute(MappedPath)) + OverriddenAbsPaths.insert(MappedPath); + } + + // Check whether anything has changed. + for (const auto &F : FilesInPreamble) { + auto OverridenFileBuffer = OverridenFileBuffers.find(F.first()); + if (OverridenFileBuffer != OverridenFileBuffers.end()) { + // The file's buffer was remapped and the file was not found in VFS. + // Check whether it matches up with the previous mapping. + if (OverridenFileBuffer->second != F.second) + return false; + continue; + } + + llvm::vfs::Status Status; + if (!moveOnNoError(VFS.status(F.first()), Status)) { + // If the file's buffer is not remapped and we can't stat it, + // 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; + } + + // Neither the file's buffer nor the file itself was 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; + } + for (const auto &F : MissingFiles) { + // A missing file may be "provided" by an override buffer or file. + if (OverriddenAbsPaths.count(F.getKey())) + return false; + // If a file previously recorded as missing exists as a regular file, then + // consider the preamble out-of-date. + if (auto Status = VFS.status(F.getKey())) { + if (Status->isRegularFile()) + 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, + llvm::StringSet<> MissingFiles) + : Storage(std::move(Storage)), FilesInPreamble(std::move(FilesInPreamble)), + MissingFiles(std::move(MissingFiles)), + 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. + if (const char *TmpFile = ::getenv("CINDEXTEST_PREAMBLE_FILE")) + return TempPCHFile(TmpFile); + + 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("preamble", "pch", 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::string(std::move(File).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); +} + +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); +} + +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::MemoryBufferRef &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.DisablePCHOrModuleValidation = + DisableValidationForModuleKind::PCH; + + 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 = std::string(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 = std::string(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; +} +CommentHandler *PreambleCallbacks::getCommentHandler() { 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"; + case BuildPreambleError::BadInputs: + return "Command line arguments must contain exactly one source file"; + } + llvm_unreachable("unexpected BuildPreambleError"); +} diff --git a/contrib/libs/clang14/lib/Frontend/PrintPreprocessedOutput.cpp b/contrib/libs/clang14/lib/Frontend/PrintPreprocessedOutput.cpp new file mode 100644 index 0000000000..1d0022bda4 --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/PrintPreprocessedOutput.cpp @@ -0,0 +1,1026 @@ +//===--- PrintPreprocessedOutput.cpp - Implement the -E mode --------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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; + bool MinimizeWhitespace; + + Token PrevTok; + Token PrevPrevTok; + +public: + PrintPPOutputPPCallbacks(Preprocessor &pp, raw_ostream &os, bool lineMarkers, + bool defines, bool DumpIncludeDirectives, + bool UseLineDirectives, bool MinimizeWhitespace) + : PP(pp), SM(PP.getSourceManager()), ConcatInfo(PP), OS(os), + DisableLineMarkers(lineMarkers), DumpDefines(defines), + DumpIncludeDirectives(DumpIncludeDirectives), + UseLineDirectives(UseLineDirectives), + MinimizeWhitespace(MinimizeWhitespace) { + CurLine = 0; + CurFilename += "<uninit>"; + EmittedTokensOnThisLine = false; + EmittedDirectiveOnThisLine = false; + FileType = SrcMgr::C_User; + Initialized = false; + IsFirstFileEntered = false; + + PrevTok.startToken(); + PrevPrevTok.startToken(); + } + + bool isMinimizeWhitespace() const { return MinimizeWhitespace; } + + void setEmittedTokensOnThisLine() { EmittedTokensOnThisLine = true; } + bool hasEmittedTokensOnThisLine() const { return EmittedTokensOnThisLine; } + + void setEmittedDirectiveOnThisLine() { EmittedDirectiveOnThisLine = true; } + bool hasEmittedDirectiveOnThisLine() const { + return EmittedDirectiveOnThisLine; + } + + /// Ensure that the output stream position is at the beginning of a new line + /// and inserts one if it does not. It is intended to ensure that directives + /// inserted by the directives not from the input source (such as #line) are + /// in the first column. To insert newlines that represent the input, use + /// MoveToLine(/*...*/, /*RequireStartOfLine=*/true). + void startNewLineIfNeeded(); + + 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, PragmaWarningSpecifier WarningSpec, + ArrayRef<int> Ids) override; + void PragmaWarningPush(SourceLocation Loc, int Level) override; + void PragmaWarningPop(SourceLocation Loc) override; + void PragmaExecCharsetPush(SourceLocation Loc, StringRef Str) override; + void PragmaExecCharsetPop(SourceLocation Loc) override; + void PragmaAssumeNonNullBegin(SourceLocation Loc) override; + void PragmaAssumeNonNullEnd(SourceLocation Loc) override; + + /// Insert whitespace before emitting the next token. + /// + /// @param Tok Next token to be emitted. + /// @param RequireSpace Ensure at least one whitespace is emitted. Useful + /// if non-tokens have been emitted to the stream. + /// @param RequireSameLine Never emit newlines. Useful when semantics depend + /// on being on the same line, such as directives. + void HandleWhitespaceBeforeTok(const Token &Tok, bool RequireSpace, + bool RequireSameLine); + + /// Move to the line of the provided source location. This will + /// return true if a newline was inserted or if + /// the requested location is the first token on the first line. + /// In these cases the next output will be the first column on the line and + /// make it possible to insert indention. The newline was inserted + /// implicitly when at the beginning of the file. + /// + /// @param Tok Token where to move to. + /// @param RequireStartOfLine Whether the next line depends on being in the + /// first column, such as a directive. + /// + /// @return Whether column adjustments are necessary. + bool MoveToLine(const Token &Tok, bool RequireStartOfLine) { + PresumedLoc PLoc = SM.getPresumedLoc(Tok.getLocation()); + unsigned TargetLine = PLoc.isValid() ? PLoc.getLine() : CurLine; + bool IsFirstInFile = Tok.isAtStartOfLine() && PLoc.getLine() == 1; + return MoveToLine(TargetLine, RequireStartOfLine) || IsFirstInFile; + } + + /// Move to the line of the provided source location. Returns true if a new + /// line was inserted. + bool MoveToLine(SourceLocation Loc, bool RequireStartOfLine) { + PresumedLoc PLoc = SM.getPresumedLoc(Loc); + unsigned TargetLine = PLoc.isValid() ? PLoc.getLine() : CurLine; + return MoveToLine(TargetLine, RequireStartOfLine); + } + bool MoveToLine(unsigned LineNo, bool RequireStartOfLine); + + 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(); + + // 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, + bool RequireStartOfLine) { + // If it is required to start a new line or finish the current, insert + // vertical whitespace now and take it into account when moving to the + // expected line. + bool StartedNewLine = false; + if ((RequireStartOfLine && EmittedTokensOnThisLine) || + EmittedDirectiveOnThisLine) { + OS << '\n'; + StartedNewLine = true; + CurLine += 1; + EmittedTokensOnThisLine = false; + EmittedDirectiveOnThisLine = false; + } + + // If this line is "close enough" to the original line, just print newlines, + // otherwise print a #line directive. + if (CurLine == LineNo) { + // Nothing to do if we are already on the correct line. + } else if (MinimizeWhitespace && DisableLineMarkers) { + // With -E -P -fminimize-whitespace, don't emit anything if not necessary. + } else if (!StartedNewLine && LineNo - CurLine == 1) { + // Printing a single line has priority over printing a #line directive, even + // when minimizing whitespace which otherwise would print #line directives + // for every single line. + OS << '\n'; + StartedNewLine = true; + } else if (!DisableLineMarkers) { + if (LineNo - CurLine <= 8) { + const char *NewLines = "\n\n\n\n\n\n\n\n"; + OS.write(NewLines, LineNo - CurLine); + } else { + // Emit a #line or line marker. + WriteLineInfo(LineNo, nullptr, 0); + } + StartedNewLine = true; + } else if (EmittedTokensOnThisLine) { + // If we are not on the correct line and don't need to be line-correct, + // at least ensure we start on a new line. + OS << '\n'; + StartedNewLine = true; + } + + if (StartedNewLine) { + EmittedTokensOnThisLine = false; + EmittedDirectiveOnThisLine = false; + } + + CurLine = LineNo; + return StartedNewLine; +} + +void PrintPPOutputPPCallbacks::startNewLineIfNeeded() { + if (EmittedTokensOnThisLine || EmittedDirectiveOnThisLine) { + OS << '\n'; + EmittedTokensOnThisLine = false; + EmittedDirectiveOnThisLine = 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, /*RequireStartOfLine=*/false); + } 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) { + if (!MinimizeWhitespace) + startNewLineIfNeeded(); + 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) { + MoveToLine(HashLoc, /*RequireStartOfLine=*/true); + const std::string TokenText = PP.getSpelling(IncludeTok); + assert(!TokenText.empty()); + OS << "#" << TokenText << " " + << (IsAngled ? '<' : '"') << FileName << (IsAngled ? '>' : '"') + << " /* clang -E -dI */"; + setEmittedDirectiveOnThisLine(); + } + + // 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: + MoveToLine(HashLoc, /*RequireStartOfLine=*/true); + OS << "#pragma clang module import " << Imported->getFullModuleName(true) + << " /* clang -E: implicit import for " + << "#" << PP.getSpelling(IncludeTok) << " " + << (IsAngled ? '<' : '"') << FileName << (IsAngled ? '>' : '"') + << " */"; + setEmittedDirectiveOnThisLine(); + 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, /*RequireStartOfLine=*/true); + + OS.write("#ident ", strlen("#ident ")); + OS.write(S.begin(), S.size()); + setEmittedTokensOnThisLine(); +} + +/// 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(), /*RequireStartOfLine=*/true); + 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(), /*RequireStartOfLine=*/true); + 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) { + MoveToLine(Loc, /*RequireStartOfLine=*/true); + 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) { + MoveToLine(Loc, /*RequireStartOfLine=*/true); + + OS << "#pragma clang __debug "; + OS << DebugType; + + setEmittedDirectiveOnThisLine(); +} + +void PrintPPOutputPPCallbacks:: +PragmaDiagnosticPush(SourceLocation Loc, StringRef Namespace) { + MoveToLine(Loc, /*RequireStartOfLine=*/true); + OS << "#pragma " << Namespace << " diagnostic push"; + setEmittedDirectiveOnThisLine(); +} + +void PrintPPOutputPPCallbacks:: +PragmaDiagnosticPop(SourceLocation Loc, StringRef Namespace) { + MoveToLine(Loc, /*RequireStartOfLine=*/true); + OS << "#pragma " << Namespace << " diagnostic pop"; + setEmittedDirectiveOnThisLine(); +} + +void PrintPPOutputPPCallbacks::PragmaDiagnostic(SourceLocation Loc, + StringRef Namespace, + diag::Severity Map, + StringRef Str) { + MoveToLine(Loc, /*RequireStartOfLine=*/true); + 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, + PragmaWarningSpecifier WarningSpec, + ArrayRef<int> Ids) { + MoveToLine(Loc, /*RequireStartOfLine=*/true); + + OS << "#pragma warning("; + switch(WarningSpec) { + case PWS_Default: OS << "default"; break; + case PWS_Disable: OS << "disable"; break; + case PWS_Error: OS << "error"; break; + case PWS_Once: OS << "once"; break; + case PWS_Suppress: OS << "suppress"; break; + case PWS_Level1: OS << '1'; break; + case PWS_Level2: OS << '2'; break; + case PWS_Level3: OS << '3'; break; + case PWS_Level4: OS << '4'; break; + } + OS << ':'; + + for (ArrayRef<int>::iterator I = Ids.begin(), E = Ids.end(); I != E; ++I) + OS << ' ' << *I; + OS << ')'; + setEmittedDirectiveOnThisLine(); +} + +void PrintPPOutputPPCallbacks::PragmaWarningPush(SourceLocation Loc, + int Level) { + MoveToLine(Loc, /*RequireStartOfLine=*/true); + OS << "#pragma warning(push"; + if (Level >= 0) + OS << ", " << Level; + OS << ')'; + setEmittedDirectiveOnThisLine(); +} + +void PrintPPOutputPPCallbacks::PragmaWarningPop(SourceLocation Loc) { + MoveToLine(Loc, /*RequireStartOfLine=*/true); + OS << "#pragma warning(pop)"; + setEmittedDirectiveOnThisLine(); +} + +void PrintPPOutputPPCallbacks::PragmaExecCharsetPush(SourceLocation Loc, + StringRef Str) { + MoveToLine(Loc, /*RequireStartOfLine=*/true); + OS << "#pragma character_execution_set(push"; + if (!Str.empty()) + OS << ", " << Str; + OS << ')'; + setEmittedDirectiveOnThisLine(); +} + +void PrintPPOutputPPCallbacks::PragmaExecCharsetPop(SourceLocation Loc) { + MoveToLine(Loc, /*RequireStartOfLine=*/true); + OS << "#pragma character_execution_set(pop)"; + setEmittedDirectiveOnThisLine(); +} + +void PrintPPOutputPPCallbacks:: +PragmaAssumeNonNullBegin(SourceLocation Loc) { + MoveToLine(Loc, /*RequireStartOfLine=*/true); + OS << "#pragma clang assume_nonnull begin"; + setEmittedDirectiveOnThisLine(); +} + +void PrintPPOutputPPCallbacks:: +PragmaAssumeNonNullEnd(SourceLocation Loc) { + MoveToLine(Loc, /*RequireStartOfLine=*/true); + OS << "#pragma clang assume_nonnull end"; + setEmittedDirectiveOnThisLine(); +} + +void PrintPPOutputPPCallbacks::HandleWhitespaceBeforeTok(const Token &Tok, + bool RequireSpace, + bool RequireSameLine) { + // These tokens are not expanded to anything and don't need whitespace before + // them. + if (Tok.is(tok::eof) || + (Tok.isAnnotation() && !Tok.is(tok::annot_header_unit) && + !Tok.is(tok::annot_module_begin) && !Tok.is(tok::annot_module_end))) + return; + + // EmittedDirectiveOnThisLine takes priority over RequireSameLine. + if ((!RequireSameLine || EmittedDirectiveOnThisLine) && + MoveToLine(Tok, /*RequireStartOfLine=*/EmittedDirectiveOnThisLine)) { + if (MinimizeWhitespace) { + // Avoid interpreting hash as a directive under -fpreprocessed. + if (Tok.is(tok::hash)) + OS << ' '; + } else { + // 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 << ' '; + } + } else { + // Insert whitespace between the previous and next token if either + // - The caller requires it + // - The input had whitespace between them and we are not in + // whitespace-minimization mode + // - The whitespace is necessary to keep the tokens apart and there is not + // already a newline between them + if (RequireSpace || (!MinimizeWhitespace && Tok.hasLeadingSpace()) || + ((EmittedTokensOnThisLine || EmittedDirectiveOnThisLine) && + AvoidConcat(PrevPrevTok, PrevTok, Tok))) + OS << ' '; + } + + PrevPrevTok = PrevTok; + PrevTok = Tok; +} + +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, PragmaIntroducer Introducer, + Token &PragmaTok) override { + // Figure out what line we went to and insert the appropriate number of + // newline characters. + Callbacks->MoveToLine(PragmaTok.getLocation(), /*RequireStartOfLine=*/true); + Callbacks->OS.write(Prefix, strlen(Prefix)); + Callbacks->setEmittedTokensOnThisLine(); + + if (ShouldExpandTokens) { + // The first token does not have expanded macros. Expand them, if + // required. + auto Toks = std::make_unique<Token[]>(1); + Toks[0] = PragmaTok; + PP.EnterTokenStream(std::move(Toks), /*NumToks=*/1, + /*DisableMacroExpansion=*/false, + /*IsReinject=*/false); + PP.Lex(PragmaTok); + } + + // Read and print all of the pragma tokens. + bool IsFirst = true; + while (PragmaTok.isNot(tok::eod)) { + Callbacks->HandleWhitespaceBeforeTok(PragmaTok, /*RequireSpace=*/IsFirst, + /*RequireSameLine=*/true); + IsFirst = false; + std::string TokSpell = PP.getSpelling(PragmaTok); + Callbacks->OS.write(&TokSpell[0], TokSpell.size()); + Callbacks->setEmittedTokensOnThisLine(); + + 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(); + + bool IsStartOfLine = false; + char Buffer[256]; + while (true) { + // Two lines joined with line continuation ('\' as last character on the + // line) must be emitted as one line even though Tok.getLine() returns two + // different values. In this situation Tok.isAtStartOfLine() is false even + // though it may be the first token on the lexical line. When + // dropping/skipping a token that is at the start of a line, propagate the + // start-of-line-ness to the next token to not append it to the previous + // line. + IsStartOfLine = IsStartOfLine || Tok.isAtStartOfLine(); + + Callbacks->HandleWhitespaceBeforeTok(Tok, /*RequireSpace=*/false, + /*RequireSameLine=*/!IsStartOfLine); + + 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. + PP.Lex(Tok); + continue; + } 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); + // FIXME: The token on the next line after #include should have + // Tok.isAtStartOfLine() set. + IsStartOfLine = true; + continue; + } else if (Tok.is(tok::annot_module_include)) { + // PrintPPOutputPPCallbacks::InclusionDirective handles producing + // appropriate output here. Ignore this token entirely. + PP.Lex(Tok); + IsStartOfLine = true; + 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); + IsStartOfLine = true; + continue; + } else if (Tok.is(tok::annot_module_end)) { + Callbacks->EndModule( + reinterpret_cast<Module *>(Tok.getAnnotationValue())); + PP.Lex(Tok); + IsStartOfLine = true; + continue; + } else if (Tok.is(tok::annot_header_unit)) { + // This is a header-name that has been (effectively) converted into a + // module-name. + // FIXME: The module name could contain non-identifier module name + // components. We don't have a good way to round-trip those. + Module *M = reinterpret_cast<Module *>(Tok.getAnnotationValue()); + std::string Name = M->getFullModuleName(); + OS.write(Name.data(), Name.size()); + Callbacks->HandleNewlinesInToken(Name.data(), Name.size()); + } 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. + // FIXME: The token may end with a newline in which case + // setEmittedDirectiveOnThisLine/setEmittedTokensOnThisLine afterwards is + // wrong. + if (Tok.getKind() == tok::comment || Tok.getKind() == tok::unknown) + Callbacks->HandleNewlinesInToken(TokPtr, Len); + if (Tok.is(tok::comment) && Len >= 2 && TokPtr[0] == '/' && + TokPtr[1] == '/') { + // It's a line comment; + // Ensure that we don't concatenate anything behind it. + Callbacks->setEmittedDirectiveOnThisLine(); + } + } else { + std::string S = PP.getSpelling(Tok); + OS.write(S.data(), 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.data(), S.size()); + if (Tok.is(tok::comment) && S.size() >= 2 && S[0] == '/' && S[1] == '/') { + // It's a line comment; + // Ensure that we don't concatenate anything behind it. + Callbacks->setEmittedDirectiveOnThisLine(); + } + } + Callbacks->setEmittedTokensOnThisLine(); + IsStartOfLine = false; + + if (Tok.is(tok::eof)) break; + + 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, + Opts.MinimizeWhitespace); + + // 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/libs/clang14/lib/Frontend/Rewrite/FixItRewriter.cpp b/contrib/libs/clang14/lib/Frontend/Rewrite/FixItRewriter.cpp new file mode 100644 index 0000000000..4fe64b96cb --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/Rewrite/FixItRewriter.cpp @@ -0,0 +1,209 @@ +//===- FixItRewriter.cpp - Fix-It Rewriter Diagnostic Client --------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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(std::string(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::OF_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(std::string(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/libs/clang14/lib/Frontend/Rewrite/FrontendActions.cpp b/contrib/libs/clang14/lib/Frontend/Rewrite/FrontendActions.cpp new file mode 100644 index 0000000000..6685109f8d --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/Rewrite/FrontendActions.cpp @@ -0,0 +1,323 @@ +//===--- FrontendActions.cpp ----------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/Rewrite/Frontend/FrontendActions.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/Basic/CharInfo.h" +#include "clang/Basic/LangStandard.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/ModuleFile.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 std::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 std::string(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 std::string(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()); + if (llvm::Error Err = FixAction->Execute()) { + // FIXME this drops the error on the floor. + consumeError(std::move(Err)); + return false; + } + + 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( + std::string(InFile), std::move(OS), CI.getDiagnostics(), + CI.getLangOpts(), CI.getDiagnosticOpts().NoRewriteMacros, + (CI.getCodeGenOpts().getDebugInfo() != codegenoptions::NoDebugInfo)); + return CreateObjCRewriter(std::string(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(/*Binary=*/true, getCurrentFileOrBufferName()); + if (!OS) return; + + RewriteMacrosInInput(CI.getPreprocessor(), OS.get()); +} + +void RewriteTestAction::ExecuteAction() { + CompilerInstance &CI = getCompilerInstance(); + std::unique_ptr<raw_ostream> OS = + CI.createDefaultOutputFile(/*Binary=*/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.getASTReader()->getModuleManager().lookup(*File); + assert(MF && "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 (isValidAsciiIdentifier(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.getModuleCache()); + 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(Language::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(/*Binary=*/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.createASTReader(); + CI.getASTReader()->addListener( + std::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/libs/clang14/lib/Frontend/Rewrite/HTMLPrint.cpp b/contrib/libs/clang14/lib/Frontend/Rewrite/HTMLPrint.cpp new file mode 100644 index 0000000000..1388c2e1fa --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/Rewrite/HTMLPrint.cpp @@ -0,0 +1,91 @@ +//===--- HTMLPrint.cpp - Source code -> HTML pretty-printing --------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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 std::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().getBufferOrFake(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/libs/clang14/lib/Frontend/Rewrite/InclusionRewriter.cpp b/contrib/libs/clang14/lib/Frontend/Rewrite/InclusionRewriter.cpp new file mode 100644 index 0000000000..3e8d582f90 --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/Rewrite/InclusionRewriter.cpp @@ -0,0 +1,543 @@ +//===--- InclusionRewriter.cpp - Rewrite includes into their expansions ---===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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/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; + IncludedFile(FileID Id, SrcMgr::CharacteristicKind FileType) + : Id(Id), FileType(FileType) {} + }; + 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. + llvm::MemoryBufferRef 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<SourceLocation, IncludedFile> FileIncludes; + /// Tracks where inclusions that import modules are found. + std::map<SourceLocation, const Module *> ModuleIncludes; + /// Tracks where inclusions that enter modules (in a module build) are found. + std::map<SourceLocation, const Module *> ModuleEntryIncludes; + /// Tracks where #if and #elif directives get evaluated and whether to true. + std::map<SourceLocation, bool> IfConditions; + /// 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); + void setPredefinesBuffer(const llvm::MemoryBufferRef &Buf) { + PredefinesBuffer = Buf; + } + void detectMainFileEOL(); + void handleModuleBegin(Token &Tok) { + assert(Tok.getKind() == tok::annot_module_begin); + ModuleEntryIncludes.insert( + {Tok.getLocation(), (Module *)Tok.getAnnotationValue()}); + } +private: + void FileChanged(SourceLocation Loc, FileChangeReason Reason, + SrcMgr::CharacteristicKind FileType, + FileID PrevFID) override; + void FileSkipped(const FileEntryRef &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 If(SourceLocation Loc, SourceRange ConditionRange, + ConditionValueKind ConditionValue) override; + void Elif(SourceLocation Loc, SourceRange ConditionRange, + ConditionValueKind ConditionValue, SourceLocation IfLoc) override; + void WriteLineInfo(StringRef Filename, int Line, + SrcMgr::CharacteristicKind FileType, + StringRef Extra = StringRef()); + void WriteImplicitModuleImport(const Module *Mod); + void OutputContentUpTo(const MemoryBufferRef &FromFile, unsigned &WriteFrom, + unsigned WriteTo, StringRef EOL, int &lines, + bool EnsureNewline); + void CommentOutDirective(Lexer &DirectivesLex, const Token &StartToken, + const MemoryBufferRef &FromFile, StringRef EOL, + unsigned &NextToWrite, int &Lines); + const IncludedFile *FindIncludeAtLocation(SourceLocation Loc) const; + const Module *FindModuleAtLocation(SourceLocation Loc) const; + const Module *FindEnteredModule(SourceLocation Loc) const; + bool IsIfAtLocationTrue(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"), + 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, IncludedFile(Id, NewFileType))); + (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 FileEntryRef & /*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, Imported)); + (void)P; + assert(P.second && "Unexpected revisitation of the same include directive"); + } else + LastInclusionLocation = HashLoc; +} + +void InclusionRewriter::If(SourceLocation Loc, SourceRange ConditionRange, + ConditionValueKind ConditionValue) { + auto P = IfConditions.insert(std::make_pair(Loc, ConditionValue == CVK_True)); + (void)P; + assert(P.second && "Unexpected revisitation of the same if directive"); +} + +void InclusionRewriter::Elif(SourceLocation Loc, SourceRange ConditionRange, + ConditionValueKind ConditionValue, + SourceLocation IfLoc) { + auto P = IfConditions.insert(std::make_pair(Loc, ConditionValue == CVK_True)); + (void)P; + assert(P.second && "Unexpected revisitation of the same elif directive"); +} + +/// 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); + 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); + 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); + if (I != ModuleEntryIncludes.end()) + return I->second; + return nullptr; +} + +bool InclusionRewriter::IsIfAtLocationTrue(SourceLocation Loc) const { + const auto I = IfConditions.find(Loc); + if (I != IfConditions.end()) + return I->second; + return false; +} + +void InclusionRewriter::detectMainFileEOL() { + Optional<MemoryBufferRef> FromFile = *SM.getBufferOrNone(SM.getMainFileID()); + assert(FromFile); + if (!FromFile) + return; // Should never happen, but whatever. + MainEOL = FromFile->getBuffer().detectEOL(); +} + +/// Writes out bytes from \p FromFile, starting at \p NextToWrite and ending at +/// \p WriteTo - 1. +void InclusionRewriter::OutputContentUpTo(const MemoryBufferRef &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 MemoryBufferRef &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(); +} + +/// 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) { + MemoryBufferRef FromFile; + { + auto B = SM.getBufferOrNone(FileId); + assert(B && "Attempting to process invalid inclusion"); + if (B) + FromFile = *B; + } + StringRef FileName = FromFile.getBufferIdentifier(); + Lexer RawLex(FileId, FromFile, PP.getSourceManager(), PP.getLangOpts()); + RawLex.SetCommentRetentionState(false); + + StringRef LocalEOL = FromFile.getBuffer().detectEOL(); + + // 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); + + 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); + bool isTrue = IsIfAtLocationTrue(RawToken.getLocation()); + OutputContentUpTo(FromFile, NextToWrite, + SM.getFileOffset(HashToken.getLocation()), + LocalEOL, Line, /*EnsureNewline=*/true); + do { + RawLex.LexFromRawLexer(RawToken); + } while (!RawToken.is(tok::eod) && RawToken.isNot(tok::eof)); + // We need to disable the old condition, but that is tricky. + // Trying to comment it out can easily lead to comment nesting. + // So instead make the condition harmless by making it enclose + // and empty block. Moreover, put it itself inside an #if 0 block + // to disable it from getting evaluated (e.g. __has_include_next + // warns if used from the primary source file). + OS << "#if 0 /* disabled by -frewrite-includes */" << MainEOL; + if (elif) { + OS << "#if 0" << MainEOL; + } + OutputContentUpTo(FromFile, NextToWrite, + SM.getFileOffset(RawToken.getLocation()) + + RawToken.getLength(), + LocalEOL, Line, /*EnsureNewline=*/true); + // Close the empty block and the disabling block. + OS << "#endif" << MainEOL; + OS << "#endif /* disabled by -frewrite-includes */" << MainEOL; + OS << (elif ? "#elif " : "#if ") << (isTrue ? "1" : "0") + << " /* evaluated by -frewrite-includes */" << MainEOL; + 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.getBufferOrFake(PP.getPredefinesFileID())); + Rewrite->Process(PP.getPredefinesFileID(), SrcMgr::C_User); + Rewrite->Process(SM.getMainFileID(), SrcMgr::C_User); + OS->flush(); +} diff --git a/contrib/libs/clang14/lib/Frontend/Rewrite/RewriteMacros.cpp b/contrib/libs/clang14/lib/Frontend/Rewrite/RewriteMacros.cpp new file mode 100644 index 0000000000..5701b271af --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/Rewrite/RewriteMacros.cpp @@ -0,0 +1,216 @@ +//===--- RewriteMacros.cpp - Rewrite macros into their expansions ---------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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. + llvm::MemoryBufferRef FromFile = SM.getBufferOrFake(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/libs/clang14/lib/Frontend/Rewrite/RewriteModernObjC.cpp b/contrib/libs/clang14/lib/Frontend/Rewrite/RewriteModernObjC.cpp new file mode 100644 index 0000000000..b4487f0047 --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/Rewrite/RewriteModernObjC.cpp @@ -0,0 +1,7569 @@ +//===-- RewriteModernObjC.cpp - Playground for the code rewriter ----------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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 auto *BPT = T->castAs<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_PRValue, Kind, E, nullptr, + FPOptionsOverride(), 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), nullptr, + 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 = Filename.substr(DotPos + 1); + // 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 std::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(); + llvm::MemoryBufferRef MainBuf = SM->getBufferOrFake(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->castAs<RecordType>()->getDecl(); + RD = RD->getDefinition(); + if (RD && !RD->getDeclName().getAsIdentifierInfo()) { + // decltype(((Foo_IMPL*)0)->bar) * + auto *CDecl = cast<ObjCContainerDecl>(D->getDeclContext()); + // ivar in class extensions requires special treatment. + if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl)) + CDecl = CatDecl->getClassInterface(); + std::string RecName = std::string(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 = MemberExpr::CreateImplicit( + *Context, PE, true, FD, 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) { + auto *OMD = IMP->getInstanceMethod(getter ? PD->getGetterName() + : PD->getSetterName()); + return !OMD || OMD->isSynthesizedAccessorStub(); +} + +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 & ObjCPropertyAttribute::kind_nonatomic) && + (Attributes & (ObjCPropertyAttribute::kind_retain | + ObjCPropertyAttribute::kind_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(), + PID->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(PID->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 & (ObjCPropertyAttribute::kind_retain | + ObjCPropertyAttribute::kind_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(), + PID->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 & ObjCPropertyAttribute::kind_nonatomic) + Setr += "0, "; + else + Setr += "1, "; + if (Attributes & ObjCPropertyAttribute::kind_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); + assert((IMD || CID) && "Unknown implementation type"); + + 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()) { + if (!OMD->getBody()) + continue; + 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()) { + if (!OMD->getBody()) + continue; + 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 (ObjCAtCatchStmt *Catch : S->catch_stmts()) { + 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_PRValue, FPOptionsOverride()); + + const auto *FT = msgSendType->castAs<FunctionType>(); + CallExpr *Exp = + CallExpr::Create(*Context, ICE, Args, FT->getCallResultType(*Context), + VK_PRValue, EndLoc, FPOptionsOverride()); + 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, /*variadic=*/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, /*variadic=*/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, /*variadic=*/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, /*variadic=*/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, /*variadic=*/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 = UnaryOperator::Create( + const_cast<ASTContext &>(*Context), DRE, UO_AddrOf, + Context->getPointerType(DRE->getType()), VK_PRValue, OK_Ordinary, + SourceLocation(), false, FPOptionsOverride()); + // 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); + + auto *FT = msgSendType->castAs<FunctionType>(); + CallExpr *CE = CallExpr::Create(*Context, PE, MsgExprs, FT->getReturnType(), + VK_PRValue, EndLoc, FPOptionsOverride()); + 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_PRValue, 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(), FPOptionsOverride()); + + 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 = + MemberExpr::CreateImplicit(*Context, NSArrayCallExpr, false, ARRFD, + 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()->castAs<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->castAs<FunctionType>(); + CallExpr *CE = CallExpr::Create(*Context, PE, MsgExprs, FT->getReturnType(), + VK_PRValue, EndLoc, FPOptionsOverride()); + 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_PRValue, 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(), FPOptionsOverride()); + + 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 = + MemberExpr::CreateImplicit(*Context, NSValueCallExpr, false, ARRFD, + 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(), FPOptionsOverride()); + + MemberExpr *DictLiteralKeyME = + MemberExpr::CreateImplicit(*Context, NSKeyCallExpr, false, ARRFD, + 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()->castAs<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->castAs<FunctionType>(); + CallExpr *CE = CallExpr::Create(*Context, PE, MsgExprs, FT->getReturnType(), + VK_PRValue, EndLoc, FPOptionsOverride()); + 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_PRValue, SourceLocation()); + CallExpr *STCE = + CallExpr::Create(*Context, DRE, MsgExprs, castType, VK_LValue, + SourceLocation(), FPOptionsOverride()); + + FieldDecl *FieldD = FieldDecl::Create(*Context, nullptr, SourceLocation(), + SourceLocation(), + &Context->Idents.get("s"), + returnType, nullptr, + /*BitWidth=*/nullptr, + /*Mutable=*/true, ICIS_NoInit); + MemberExpr *ME = MemberExpr::CreateImplicit( + *Context, STCE, false, FieldD, 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_PRValue, + 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(), FPOptionsOverride()); + // 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 = UnaryOperator::Create( + const_cast<ASTContext &>(*Context), SuperRep, UO_AddrOf, + Context->getPointerType(SuperRep->getType()), VK_PRValue, OK_Ordinary, + SourceLocation(), false, FPOptionsOverride()); + 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 = UnaryOperator::Create( + const_cast<ASTContext &>(*Context), SuperRep, UO_AddrOf, + Context->getPointerType(SuperRep->getType()), VK_PRValue, OK_Ordinary, + SourceLocation(), false, FPOptionsOverride()); + } + MsgExprs.push_back(SuperRep); + break; + } + + case ObjCMessageExpr::Class: { + SmallVector<Expr*, 8> ClsExprs; + ObjCInterfaceDecl *Class + = Exp->getClassReceiver()->castAs<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_PRValue, + 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(), FPOptionsOverride()); + // 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 = UnaryOperator::Create( + const_cast<ASTContext &>(*Context), SuperRep, UO_AddrOf, + Context->getPointerType(SuperRep->getType()), VK_PRValue, OK_Ordinary, + SourceLocation(), false, FPOptionsOverride()); + 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_PRValue, 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->castAs<FunctionType>(); + CallExpr *CE = CallExpr::Create(*Context, PE, MsgExprs, FT->getReturnType(), + VK_PRValue, EndLoc, FPOptionsOverride()); + 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->castAs<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->castAs<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 += " = "; + Result += toString(EC->getInitVal(), 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); + + auto *IDecl = dyn_cast<ObjCContainerDecl>(fieldDecl->getDeclContext()); + + TagDecl *TD = nullptr; + if (Type->isRecordType()) { + TD = Type->castAs<RecordType>()->getDecl(); + } + else if (Type->isEnumeralType()) { + TD = Type->castAs<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 = std::string(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_PRValue, 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 = MemberExpr::CreateImplicit( + *Context, PE, true, FD, 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_PRValue, + SourceLocation(), FPOptionsOverride()); + 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 = MemberExpr::CreateImplicit( + *Context, DeclRefExp, isArrow, FD, 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 = MemberExpr::CreateImplicit(*Context, ME, true, FD, 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 = UnaryOperator::Create( + const_cast<ASTContext &>(*Context), DRE, UO_Deref, DRE->getType(), + VK_LValue, OK_Ordinary, DRE->getLocation(), false, FPOptionsOverride()); + // 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_PRValue, 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 = UnaryOperator::Create( + const_cast<ASTContext &>(*Context), + new (Context) DeclRefExpr(*Context, NewVD, false, Context->VoidPtrTy, + VK_LValue, SourceLocation()), + UO_AddrOf, Context->getPointerType(Context->VoidPtrTy), VK_PRValue, + OK_Ordinary, SourceLocation(), false, FPOptionsOverride()); + 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 = UnaryOperator::Create(const_cast<ASTContext &>(*Context), Exp, + UO_AddrOf, QT, VK_PRValue, OK_Ordinary, + SourceLocation(), false, + FPOptionsOverride()); + } + } 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 = UnaryOperator::Create(const_cast<ASTContext &>(*Context), Exp, + UO_AddrOf, QT, VK_PRValue, OK_Ordinary, + SourceLocation(), false, + FPOptionsOverride()); + } + + } + 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 = UnaryOperator::Create( + const_cast<ASTContext &>(*Context), Exp, UO_AddrOf, + Context->getPointerType(Exp->getType()), VK_PRValue, OK_Ordinary, + SourceLocation(), false, FPOptionsOverride()); + 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(), FPOptionsOverride()); + + if (GlobalBlockExpr) { + assert (!GlobalConstructionExp && + "SynthBlockInitExpr - GlobalConstructionExp must be null"); + GlobalConstructionExp = NewRep; + NewRep = DRE; + } + + NewRep = UnaryOperator::Create( + const_cast<ASTContext &>(*Context), NewRep, UO_AddrOf, + Context->getPointerType(NewRep->getType()), VK_PRValue, OK_Ordinary, + SourceLocation(), false, FPOptionsOverride()); + 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()->castAs<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 = Prop->getGetterMethodDecl()) + if (mustSynthesizeSetterGetterMethod(IDecl, PD, true /*getter*/)) + InstanceMethods.push_back(Getter); + if (PD->isReadOnly()) + continue; + if (ObjCMethodDecl *Setter = Prop->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 = Prop->getGetterMethodDecl()) + InstanceMethods.push_back(Getter); + if (PD->isReadOnly()) + continue; + if (ObjCMethodDecl *Setter = Prop->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 = BinaryOperator::Create( + *Context, castExpr, DRE, BO_Add, + Context->getPointerType(Context->CharTy), VK_PRValue, OK_Ordinary, + SourceLocation(), FPOptionsOverride()); + // 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->castAs<RecordType>()->getDecl(); + RD = RD->getDefinition(); + if (RD && !RD->getDeclName().getAsIdentifierInfo()) { + // decltype(((Foo_IMPL*)0)->bar) * + auto *CDecl = cast<ObjCContainerDecl>(D->getDeclContext()); + // ivar in class extensions requires special treatment. + if (ObjCCategoryDecl *CatDecl = dyn_cast<ObjCCategoryDecl>(CDecl)) + CDecl = CatDecl->getClassInterface(); + std::string RecName = std::string(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 = MemberExpr::CreateImplicit( + *Context, PE, true, FD, 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 = UnaryOperator::Create( + const_cast<ASTContext &>(*Context), castExpr, UO_Deref, IvarT, + VK_LValue, OK_Ordinary, SourceLocation(), false, FPOptionsOverride()); + 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 = + MemberExpr::CreateImplicit(*Context, PE, /*isArrow*/ false, FD, + 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/libs/clang14/lib/Frontend/Rewrite/RewriteObjC.cpp b/contrib/libs/clang14/lib/Frontend/Rewrite/RewriteObjC.cpp new file mode 100644 index 0000000000..b2ecb42c43 --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/Rewrite/RewriteObjC.cpp @@ -0,0 +1,5887 @@ +//===--- RewriteObjC.cpp - Playground for the code rewriter ---------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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 auto *BPT = T->castAs<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_PRValue, Kind, E, nullptr, + FPOptionsOverride(), TInfo, + SourceLocation(), SourceLocation()); + } + + StringLiteral *getStringLiteral(StringRef Str) { + QualType StrType = Context->getConstantArrayType( + Context->CharTy, llvm::APInt(32, Str.size() + 1), nullptr, + 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 = Filename.substr(DotPos + 1); + // 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 std::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(); + llvm::MemoryBufferRef MainBuf = SM->getBufferOrFake(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 (PID->getGetterMethodDecl() && !PID->getGetterMethodDecl()->isDefined()) { + bool GenGetProperty = + !(Attributes & ObjCPropertyAttribute::kind_nonatomic) && + (Attributes & (ObjCPropertyAttribute::kind_retain | + ObjCPropertyAttribute::kind_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(), + PID->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(PID->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() || !PID->getSetterMethodDecl() || + PID->getSetterMethodDecl()->isDefined()) + return; + + // Generate the 'setter' function. + std::string Setr; + bool GenSetProperty = Attributes & (ObjCPropertyAttribute::kind_retain | + ObjCPropertyAttribute::kind_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(), + PID->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 & ObjCPropertyAttribute::kind_nonatomic) + Setr += "0, "; + else + Setr += "1, "; + if (Attributes & ObjCPropertyAttribute::kind_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); + assert((IMD || CID) && "Unknown ImplementationDecl"); + + InsertText(IMD ? IMD->getBeginLoc() : CID->getBeginLoc(), "// "); + + for (auto *OMD : IMD ? IMD->instance_methods() : CID->instance_methods()) { + if (!OMD->getBody()) + continue; + 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()) { + if (!OMD->getBody()) + continue; + 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_PRValue, FPOptionsOverride()); + + const auto *FT = msgSendType->castAs<FunctionType>(); + + CallExpr *Exp = + CallExpr::Create(*Context, ICE, Args, FT->getCallResultType(*Context), + VK_PRValue, EndLoc, FPOptionsOverride()); + 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_or_null<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, /*variadic=*/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, /*variadic=*/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, /*variadic=*/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, /*variadic=*/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, /*variadic=*/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 = UnaryOperator::Create( + const_cast<ASTContext &>(*Context), DRE, UO_AddrOf, + Context->getPointerType(DRE->getType()), VK_PRValue, OK_Ordinary, + SourceLocation(), false, FPOptionsOverride()); + // 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 auto *FT = msgSendType->castAs<FunctionType>(); + CallExpr *STCE = + CallExpr::Create(*Context, PE, MsgExprs, FT->getReturnType(), VK_PRValue, + SourceLocation(), FPOptionsOverride()); + 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_PRValue, + 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(), FPOptionsOverride()); + // 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 = UnaryOperator::Create( + const_cast<ASTContext &>(*Context), SuperRep, UO_AddrOf, + Context->getPointerType(SuperRep->getType()), VK_PRValue, OK_Ordinary, + SourceLocation(), false, FPOptionsOverride()); + 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 = UnaryOperator::Create( + const_cast<ASTContext &>(*Context), SuperRep, UO_AddrOf, + Context->getPointerType(SuperRep->getType()), VK_PRValue, OK_Ordinary, + SourceLocation(), false, FPOptionsOverride()); + } + MsgExprs.push_back(SuperRep); + break; + } + + case ObjCMessageExpr::Class: { + SmallVector<Expr*, 8> ClsExprs; + auto *Class = + Exp->getClassReceiver()->castAs<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_PRValue, + 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(), FPOptionsOverride()); + // 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 = UnaryOperator::Create( + const_cast<ASTContext &>(*Context), SuperRep, UO_AddrOf, + Context->getPointerType(SuperRep->getType()), VK_PRValue, OK_Ordinary, + SourceLocation(), false, FPOptionsOverride()); + 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_PRValue, 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 auto *FT = msgSendType->castAs<FunctionType>(); + CallExpr *CE = CallExpr::Create(*Context, PE, MsgExprs, FT->getReturnType(), + VK_PRValue, EndLoc, FPOptionsOverride()); + 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 = BinaryOperator::Create( + *Context, sizeofExpr, limit, BO_LE, Context->IntTy, VK_PRValue, + OK_Ordinary, SourceLocation(), FPOptionsOverride()); + // (sizeof(returnType) <= 8 ? objc_msgSend(...) : objc_msgSend_stret(...)) + ConditionalOperator *CondExpr = new (Context) ConditionalOperator( + lessThanExpr, SourceLocation(), CE, SourceLocation(), STCE, returnType, + VK_PRValue, 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 = UnaryOperator::Create( + const_cast<ASTContext &>(*Context), DRE, UO_AddrOf, + Context->getPointerType(DRE->getType()), VK_PRValue, OK_Ordinary, + SourceLocation(), false, FPOptionsOverride()); + 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 = std::string(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_PRValue, 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 = MemberExpr::CreateImplicit( + *Context, PE, true, FD, 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_PRValue, + SourceLocation(), FPOptionsOverride()); + 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 = + MemberExpr::CreateImplicit(*Context, DeclRefExp, isArrow, FD, + 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 = MemberExpr::CreateImplicit(*Context, ME, true, FD, 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 = UnaryOperator::Create( + const_cast<ASTContext &>(*Context), DRE, UO_Deref, DRE->getType(), + VK_LValue, OK_Ordinary, DRE->getLocation(), false, FPOptionsOverride()); + // 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_PRValue, 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 = UnaryOperator::Create( + const_cast<ASTContext &>(*Context), + new (Context) DeclRefExpr(*Context, NewVD, false, Context->VoidPtrTy, + VK_LValue, SourceLocation()), + UO_AddrOf, Context->getPointerType(Context->VoidPtrTy), VK_PRValue, + OK_Ordinary, SourceLocation(), false, FPOptionsOverride()); + 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 = UnaryOperator::Create(const_cast<ASTContext &>(*Context), Exp, + UO_AddrOf, QT, VK_PRValue, OK_Ordinary, + SourceLocation(), false, + FPOptionsOverride()); + } + } 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 = UnaryOperator::Create(const_cast<ASTContext &>(*Context), Exp, + UO_AddrOf, QT, VK_PRValue, OK_Ordinary, + SourceLocation(), false, + FPOptionsOverride()); + } + } + 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 = UnaryOperator::Create( + const_cast<ASTContext &>(*Context), Exp, UO_AddrOf, + Context->getPointerType(Exp->getType()), VK_PRValue, OK_Ordinary, + SourceLocation(), false, FPOptionsOverride()); + 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(), FPOptionsOverride()); + NewRep = UnaryOperator::Create( + const_cast<ASTContext &>(*Context), NewRep, UO_AddrOf, + Context->getPointerType(NewRep->getType()), VK_PRValue, OK_Ordinary, + SourceLocation(), false, FPOptionsOverride()); + 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()->castAs<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->ivar_size(); + 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 = Prop->getGetterMethodDecl()) + if (!Getter->isDefined()) + InstanceMethods.push_back(Getter); + if (PD->isReadOnly()) + continue; + if (ObjCMethodDecl *Setter = Prop->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 = Prop->getGetterMethodDecl()) + InstanceMethods.push_back(Getter); + if (PD->isReadOnly()) + continue; + if (ObjCMethodDecl *Setter = Prop->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 = + std::string(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 = MemberExpr::CreateImplicit( + *Context, PE, true, D, 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 = + std::string(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/libs/clang14/lib/Frontend/Rewrite/RewriteTest.cpp b/contrib/libs/clang14/lib/Frontend/Rewrite/RewriteTest.cpp new file mode 100644 index 0000000000..fa8232c8c3 --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/Rewrite/RewriteTest.cpp @@ -0,0 +1,38 @@ +//===--- RewriteTest.cpp - Rewriter playground ----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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/libs/clang14/lib/Frontend/Rewrite/ya.make b/contrib/libs/clang14/lib/Frontend/Rewrite/ya.make new file mode 100644 index 0000000000..4b70265e96 --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/Rewrite/ya.make @@ -0,0 +1,42 @@ +# Generated by devtools/yamaker. + +LIBRARY() + +LICENSE(Apache-2.0 WITH LLVM-exception) + +LICENSE_TEXTS(.yandex_meta/licenses.list.txt) + +PEERDIR( + contrib/libs/clang14 + contrib/libs/clang14/include + contrib/libs/clang14/lib/AST + contrib/libs/clang14/lib/Basic + contrib/libs/clang14/lib/Edit + contrib/libs/clang14/lib/Frontend + contrib/libs/clang14/lib/Lex + contrib/libs/clang14/lib/Rewrite + contrib/libs/clang14/lib/Serialization + contrib/libs/llvm14 + contrib/libs/llvm14/lib/Support +) + +ADDINCL( + contrib/libs/clang14/lib/Frontend/Rewrite +) + +NO_COMPILER_WARNINGS() + +NO_UTIL() + +SRCS( + FixItRewriter.cpp + FrontendActions.cpp + HTMLPrint.cpp + InclusionRewriter.cpp + RewriteMacros.cpp + RewriteModernObjC.cpp + RewriteObjC.cpp + RewriteTest.cpp +) + +END() diff --git a/contrib/libs/clang14/lib/Frontend/SerializedDiagnosticPrinter.cpp b/contrib/libs/clang14/lib/Frontend/SerializedDiagnosticPrinter.cpp new file mode 100644 index 0000000000..fc8fce4b42 --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/SerializedDiagnosticPrinter.cpp @@ -0,0 +1,884 @@ +//===--- SerializedDiagnosticPrinter.cpp - Serializer for diagnostics -----===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#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/Bitstream/BitCodes.h" +#include "llvm/Bitstream/BitstreamReader.h" +#include "llvm/Support/FileSystem.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) : 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; + + /// Whether we've started finishing and tearing down this instance. + bool IsFinishing = false; + + /// 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 std::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) { + assert(!IsFinishing && + "Received a diagnostic after we've already started teardown."); + if (IsFinishing) { + SmallString<256> diagnostic; + Info.FormatDiagnostic(diagnostic); + getMetaDiags()->Report( + diag::warn_fe_serialized_diag_failure_during_finalisation) + << diagnostic; + return; + } + + // 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 = std::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() { + assert(!IsFinishing); + IsFinishing = true; + + // 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 = std::make_unique<llvm::raw_fd_ostream>(State->OutputFile.c_str(), + EC, llvm::sys::fs::OF_None); + if (EC) { + getMetaDiags()->Report(diag::warn_fe_serialized_diag_failure) + << State->OutputFile << EC.message(); + OS->clear_error(); + return; + } + + // Write the generated bitstream to "Out". + OS->write((char *)&State->Buffer.front(), State->Buffer.size()); + OS->flush(); + + assert(!OS->has_error()); + if (OS->has_error()) { + getMetaDiags()->Report(diag::warn_fe_serialized_diag_failure) + << State->OutputFile << OS->error().message(); + OS->clear_error(); + } +} + +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/libs/clang14/lib/Frontend/SerializedDiagnosticReader.cpp b/contrib/libs/clang14/lib/Frontend/SerializedDiagnosticReader.cpp new file mode 100644 index 0000000000..eca6f5ee18 --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/SerializedDiagnosticReader.cpp @@ -0,0 +1,371 @@ +//===- SerializedDiagnosticReader.cpp - Reads diagnostics -----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#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/Bitstream/BitCodes.h" +#include "llvm/Bitstream/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. + for (unsigned char C : {'D', 'I', 'A', 'G'}) { + if (Expected<llvm::SimpleBitstreamCursor::word_t> Res = Stream.Read(8)) { + if (Res.get() == C) + continue; + } else { + // FIXME this drops the error on the floor. + consumeError(Res.takeError()); + } + return SDError::InvalidSignature; + } + + // Read the top level blocks. + while (!Stream.AtEndOfStream()) { + if (Expected<unsigned> Res = Stream.ReadCode()) { + if (Res.get() != llvm::bitc::ENTER_SUBBLOCK) + return SDError::InvalidDiagnostics; + } else { + // FIXME this drops the error on the floor. + consumeError(Res.takeError()); + return SDError::InvalidDiagnostics; + } + + std::error_code EC; + Expected<unsigned> MaybeSubBlockID = Stream.ReadSubBlockID(); + if (!MaybeSubBlockID) { + // FIXME this drops the error on the floor. + consumeError(MaybeSubBlockID.takeError()); + return SDError::InvalidDiagnostics; + } + + switch (MaybeSubBlockID.get()) { + case llvm::bitc::BLOCKINFO_BLOCK_ID: { + Expected<Optional<llvm::BitstreamBlockInfo>> MaybeBlockInfo = + Stream.ReadBlockInfoBlock(); + if (!MaybeBlockInfo) { + // FIXME this drops the error on the floor. + consumeError(MaybeBlockInfo.takeError()); + return SDError::InvalidDiagnostics; + } + BlockInfo = std::move(MaybeBlockInfo.get()); + } + 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 (llvm::Error Err = Stream.SkipBlock()) { + // FIXME this drops the error on the floor. + consumeError(std::move(Err)); + 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; + if (Expected<unsigned> Res = Stream.ReadCode()) + Code = Res.get(); + else + return llvm::errorToErrorCode(Res.takeError()); + + if (Code >= static_cast<unsigned>(llvm::bitc::FIRST_APPLICATION_ABBREV)) { + // We found a record. + BlockOrRecordID = Code; + return Cursor::Record; + } + switch (static_cast<llvm::bitc::FixedAbbrevIDs>(Code)) { + case llvm::bitc::ENTER_SUBBLOCK: + if (Expected<unsigned> Res = Stream.ReadSubBlockID()) + BlockOrRecordID = Res.get(); + else + return llvm::errorToErrorCode(Res.takeError()); + return Cursor::BlockBegin; + + case llvm::bitc::END_BLOCK: + if (Stream.ReadBlockEnd()) + return SDError::InvalidDiagnostics; + return Cursor::BlockEnd; + + case llvm::bitc::DEFINE_ABBREV: + if (llvm::Error Err = Stream.ReadAbbrevRecord()) + return llvm::errorToErrorCode(std::move(Err)); + continue; + + case llvm::bitc::UNABBREV_RECORD: + return SDError::UnsupportedConstruct; + + case llvm::bitc::FIRST_APPLICATION_ABBREV: + llvm_unreachable("Unexpected abbrev id."); + } + } + + return SDError::InvalidDiagnostics; +} + +std::error_code +SerializedDiagnosticReader::readMetaBlock(llvm::BitstreamCursor &Stream) { + if (llvm::Error Err = + Stream.EnterSubBlock(clang::serialized_diags::BLOCK_META)) { + // FIXME this drops the error on the floor. + consumeError(std::move(Err)); + 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 (llvm::Error Err = Stream.SkipBlock()) { + // FIXME this drops the error on the floor. + consumeError(std::move(Err)); + return SDError::MalformedMetadataBlock; + } + LLVM_FALLTHROUGH; + case Cursor::BlockEnd: + if (!VersionChecked) + return SDError::MissingVersion; + return {}; + } + + SmallVector<uint64_t, 1> Record; + Expected<unsigned> MaybeRecordID = Stream.readRecord(BlockOrCode, Record); + if (!MaybeRecordID) + return errorToErrorCode(MaybeRecordID.takeError()); + unsigned RecordID = MaybeRecordID.get(); + + 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 (llvm::Error Err = + Stream.EnterSubBlock(clang::serialized_diags::BLOCK_DIAG)) { + // FIXME this drops the error on the floor. + consumeError(std::move(Err)); + 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 (llvm::Error Err = Stream.SkipBlock()) { + // FIXME this drops the error on the floor. + consumeError(std::move(Err)); + return SDError::MalformedSubBlock; + } + continue; + case Cursor::BlockEnd: + if ((EC = visitEndOfDiagnostic())) + return EC; + return {}; + case Cursor::Record: + break; + } + + // Read the record. + Record.clear(); + StringRef Blob; + Expected<unsigned> MaybeRecID = + Stream.readRecord(BlockOrCode, Record, &Blob); + if (!MaybeRecID) + return errorToErrorCode(MaybeRecID.takeError()); + unsigned RecID = MaybeRecID.get(); + + 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/libs/clang14/lib/Frontend/TestModuleFileExtension.cpp b/contrib/libs/clang14/lib/Frontend/TestModuleFileExtension.cpp new file mode 100644 index 0000000000..2d5145d0c5 --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/TestModuleFileExtension.cpp @@ -0,0 +1,137 @@ +//===-- TestModuleFileExtension.cpp - Module Extension Tester -------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#include "TestModuleFileExtension.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/Serialization/ASTReader.h" +#include "llvm/ADT/Hashing.h" +#include "llvm/Bitstream/BitstreamWriter.h" +#include "llvm/Support/raw_ostream.h" +#include <cstdio> +using namespace clang; +using namespace clang::serialization; + +char TestModuleFileExtension::ID = 0; + +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::Expected<llvm::BitstreamEntry> MaybeEntry = + Stream.advanceSkippingSubblocks(); + if (!MaybeEntry) + (void)MaybeEntry.takeError(); + llvm::BitstreamEntry Entry = MaybeEntry.get(); + + 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; + Expected<unsigned> MaybeRecCode = + Stream.readRecord(Entry.ID, Record, &Blob); + if (!MaybeRecCode) + fprintf(stderr, "Failed reading rec code: %s\n", + toString(MaybeRecCode.takeError()).c_str()); + switch (MaybeRecCode.get()) { + 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 }; +} + +void TestModuleFileExtension::hashExtension( + ExtensionHashBuilder &HBuilder) const { + if (Hashed) { + HBuilder.add(BlockName); + HBuilder.add(MajorVersion); + HBuilder.add(MinorVersion); + HBuilder.add(UserInfo); + } +} + +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)); +} + +std::string TestModuleFileExtension::str() const { + std::string Buffer; + llvm::raw_string_ostream OS(Buffer); + OS << BlockName << ":" << MajorVersion << ":" << MinorVersion << ":" << Hashed + << ":" << UserInfo; + return Buffer; +} diff --git a/contrib/libs/clang14/lib/Frontend/TestModuleFileExtension.h b/contrib/libs/clang14/lib/Frontend/TestModuleFileExtension.h new file mode 100644 index 0000000000..e22c87ed2d --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/TestModuleFileExtension.h @@ -0,0 +1,73 @@ +//===-- TestModuleFileExtension.h - Module Extension Tester -----*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +#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/Bitstream/BitstreamReader.h" +#include <string> + +namespace clang { + +/// A module file extension used for testing purposes. +class TestModuleFileExtension + : public llvm::RTTIExtends<TestModuleFileExtension, 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: + static char ID; + + 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; + + void hashExtension(ExtensionHashBuilder &HBuilder) 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; + + std::string str() const; +}; + +} // end namespace clang + +#endif // LLVM_CLANG_FRONTEND_TESTMODULEFILEEXTENSION_H diff --git a/contrib/libs/clang14/lib/Frontend/TextDiagnostic.cpp b/contrib/libs/clang14/lib/Frontend/TextDiagnostic.cpp new file mode 100644 index 0000000000..1c4a76e689 --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/TextDiagnostic.cpp @@ -0,0 +1,1355 @@ +//===--- TextDiagnostic.cpp - Text Diagnostic Pretty-Printing -------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#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 (true) { + 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() == + llvm::find_if(CaretLine, [](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(); + + if (DiagOpts->ShowLevel) + printDiagnosticLevel(OS, Level, DiagOpts->ShowColors); + 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) { + 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; + } + + 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) { +#ifdef _WIN32 + SmallString<4096> TmpFilename; +#endif + if (DiagOpts->AbsolutePath) { + auto File = SM.getFileManager().getFile(Filename); + if (File) { + // We want to print a simplified absolute path, i. e. without "dots". + // + // The hardest part here are the paths like "<part1>/<link>/../<part2>". + // On Unix-like systems, we cannot just collapse "<link>/..", because + // paths are resolved sequentially, and, thereby, the path + // "<part1>/<part2>" may point to a different location. That is why + // we use FileManager::getCanonicalName(), which expands all indirections + // with llvm::sys::fs::real_path() and caches the result. + // + // On the other hand, it would be better to preserve as much of the + // original path as possible, because that helps a user to recognize it. + // real_path() expands all links, which sometimes too much. Luckily, + // on Windows we can just use llvm::sys::path::remove_dots(), because, + // on that system, both aforementioned paths point to the same place. +#ifdef _WIN32 + TmpFilename = (*File)->getName(); + llvm::sys::fs::make_absolute(TmpFilename); + llvm::sys::path::native(TmpFilename); + llvm::sys::path::remove_dots(TmpFilename, /* remove_dot_dot */ true); + Filename = StringRef(TmpFilename.data(), TmpFilename.size()); +#else + Filename = SM.getFileManager().getCanonicalName(*File); +#endif + } + } + + 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()); + 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: + if (DiagOpts->ShowLine) + 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/libs/clang14/lib/Frontend/TextDiagnosticBuffer.cpp b/contrib/libs/clang14/lib/Frontend/TextDiagnosticBuffer.cpp new file mode 100644 index 0000000000..90f273e65f --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/TextDiagnosticBuffer.cpp @@ -0,0 +1,74 @@ +//===- TextDiagnosticBuffer.cpp - Buffer Text Diagnostics -----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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(), std::string(Buf.str())); + break; + case DiagnosticsEngine::Warning: + All.emplace_back(Level, Warnings.size()); + Warnings.emplace_back(Info.getLocation(), std::string(Buf.str())); + break; + case DiagnosticsEngine::Remark: + All.emplace_back(Level, Remarks.size()); + Remarks.emplace_back(Info.getLocation(), std::string(Buf.str())); + break; + case DiagnosticsEngine::Error: + case DiagnosticsEngine::Fatal: + All.emplace_back(Level, Errors.size()); + Errors.emplace_back(Info.getLocation(), std::string(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/libs/clang14/lib/Frontend/TextDiagnosticPrinter.cpp b/contrib/libs/clang14/lib/Frontend/TextDiagnosticPrinter.cpp new file mode 100644 index 0000000000..0ff5376098 --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/TextDiagnosticPrinter.cpp @@ -0,0 +1,156 @@ +//===--- TextDiagnosticPrinter.cpp - Diagnostic Printer -------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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); + TextDiagnostic::printDiagnosticMessage( + OS, /*IsSupplemental=*/Level == DiagnosticsEngine::Note, + 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/libs/clang14/lib/Frontend/VerifyDiagnosticConsumer.cpp b/contrib/libs/clang14/lib/Frontend/VerifyDiagnosticConsumer.cpp new file mode 100644 index 0000000000..f67dceea91 --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/VerifyDiagnosticConsumer.cpp @@ -0,0 +1,1166 @@ +//===- VerifyDiagnosticConsumer.cpp - Verifying Diagnostic Client ---------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// 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; + +#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 + +//===----------------------------------------------------------------------===// +// 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 MatchAnyFileAndLine, bool MatchAnyLine, StringRef Text, + unsigned Min, unsigned Max) + : Directive(DirectiveLoc, DiagnosticLoc, MatchAnyFileAndLine, + 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.contains(Text); } +}; + +/// RegexDirective - Directive with regular-expression matching. +class RegexDirective : public Directive { +public: + RegexDirective(SourceLocation DirectiveLoc, SourceLocation DiagnosticLoc, + bool MatchAnyFileAndLine, bool MatchAnyLine, StringRef Text, + unsigned Min, unsigned Max, StringRef RegexStr) + : Directive(DirectiveLoc, DiagnosticLoc, MatchAnyFileAndLine, + 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; + PEnd = P; + for (; PEnd < End && *PEnd >= '0' && *PEnd <= '9'; ++PEnd) { + TMP *= 10; + TMP += *PEnd - '0'; + } + if (PEnd == C) + return false; + N = TMP; + return true; + } + + // Return true if a marker is next. + // A marker is the longest match for /#[A-Za-z0-9_-]+/. + bool NextMarker() { + P = C; + if (P == End || *P != '#') + return false; + PEnd = P; + ++PEnd; + while ((isAlphanumeric(*PEnd) || *PEnd == '-' || *PEnd == '_') && + PEnd < End) + ++PEnd; + return PEnd > P + 1; + } + + // 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; + } + + // Return the text matched by the previous next/search. + // Behavior is undefined if previous next/search failed. + StringRef Match() { return StringRef(P, PEnd - P); } + + // 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; + + // Previous next/search subject start. + const char *P; + +private: + // Previous next/search subject end (1-past). + const char *PEnd = nullptr; +}; + +// The information necessary to create a directive. +struct UnattachedDirective { + DirectiveList *DL = nullptr; + bool RegexKind = false; + SourceLocation DirectivePos, ContentBegin; + std::string Text; + unsigned Min = 1, Max = 1; +}; + +// Attach the specified directive to the line of code indicated by +// \p ExpectedLoc. +void attachDirective(DiagnosticsEngine &Diags, const UnattachedDirective &UD, + SourceLocation ExpectedLoc, + bool MatchAnyFileAndLine = false, + bool MatchAnyLine = false) { + // Construct new directive. + std::unique_ptr<Directive> D = Directive::create( + UD.RegexKind, UD.DirectivePos, ExpectedLoc, MatchAnyFileAndLine, + MatchAnyLine, UD.Text, UD.Min, UD.Max); + + std::string Error; + if (!D->isValid(Error)) { + Diags.Report(UD.ContentBegin, diag::err_verify_invalid_content) + << (UD.RegexKind ? "regex" : "string") << Error; + } + + UD.DL->push_back(std::move(D)); +} + +} // anonymous + +// Tracker for markers in the input files. A marker is a comment of the form +// +// n = 123; // #123 +// +// ... that can be referred to by a later expected-* directive: +// +// // expected-error@#123 {{undeclared identifier 'n'}} +// +// Marker declarations must be at the start of a comment or preceded by +// whitespace to distinguish them from uses of markers in directives. +class VerifyDiagnosticConsumer::MarkerTracker { + DiagnosticsEngine &Diags; + + struct Marker { + SourceLocation DefLoc; + SourceLocation RedefLoc; + SourceLocation UseLoc; + }; + llvm::StringMap<Marker> Markers; + + // Directives that couldn't be created yet because they name an unknown + // marker. + llvm::StringMap<llvm::SmallVector<UnattachedDirective, 2>> DeferredDirectives; + +public: + MarkerTracker(DiagnosticsEngine &Diags) : Diags(Diags) {} + + // Register a marker. + void addMarker(StringRef MarkerName, SourceLocation Pos) { + auto InsertResult = Markers.insert( + {MarkerName, Marker{Pos, SourceLocation(), SourceLocation()}}); + + Marker &M = InsertResult.first->second; + if (!InsertResult.second) { + // Marker was redefined. + M.RedefLoc = Pos; + } else { + // First definition: build any deferred directives. + auto Deferred = DeferredDirectives.find(MarkerName); + if (Deferred != DeferredDirectives.end()) { + for (auto &UD : Deferred->second) { + if (M.UseLoc.isInvalid()) + M.UseLoc = UD.DirectivePos; + attachDirective(Diags, UD, Pos); + } + DeferredDirectives.erase(Deferred); + } + } + } + + // Register a directive at the specified marker. + void addDirective(StringRef MarkerName, const UnattachedDirective &UD) { + auto MarkerIt = Markers.find(MarkerName); + if (MarkerIt != Markers.end()) { + Marker &M = MarkerIt->second; + if (M.UseLoc.isInvalid()) + M.UseLoc = UD.DirectivePos; + return attachDirective(Diags, UD, M.DefLoc); + } + DeferredDirectives[MarkerName].push_back(UD); + } + + // Ensure we have no remaining deferred directives, and no + // multiply-defined-and-used markers. + void finalize() { + for (auto &MarkerInfo : Markers) { + StringRef Name = MarkerInfo.first(); + Marker &M = MarkerInfo.second; + if (M.RedefLoc.isValid() && M.UseLoc.isValid()) { + Diags.Report(M.UseLoc, diag::err_verify_ambiguous_marker) << Name; + Diags.Report(M.DefLoc, diag::note_verify_ambiguous_marker) << Name; + Diags.Report(M.RedefLoc, diag::note_verify_ambiguous_marker) << Name; + } + } + + for (auto &DeferredPair : DeferredDirectives) { + Diags.Report(DeferredPair.second.front().DirectivePos, + diag::err_verify_no_such_marker) + << DeferredPair.first(); + } + } +}; + +/// 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, + VerifyDiagnosticConsumer::MarkerTracker &Markers) { + DiagnosticsEngine &Diags = PP ? PP->getDiagnostics() : SM.getDiagnostics(); + + // First, scan the comment looking for markers. + for (ParseHelper PH(S); !PH.Done();) { + if (!PH.Search("#", true)) + break; + PH.C = PH.P; + if (!PH.NextMarker()) { + PH.Next("#"); + PH.Advance(); + continue; + } + PH.Advance(); + Markers.addMarker(PH.Match(), Pos); + } + + // 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; + + StringRef DToken = PH.Match(); + PH.Advance(); + + // Default directive kind. + UnattachedDirective D; + 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. + + // Regex in initial directive token: -re + if (DToken.endswith("-re")) { + D.RegexKind = true; + KindStr = "regex"; + DToken = DToken.substr(0, DToken.size()-3); + } + + // Type in initial directive token: -{error|warning|note|no-diagnostics} + bool NoDiag = false; + StringRef DType; + if (DToken.endswith(DType="-error")) + D.DL = ED ? &ED->Errors : nullptr; + else if (DToken.endswith(DType="-warning")) + D.DL = ED ? &ED->Warnings : nullptr; + else if (DToken.endswith(DType="-remark")) + D.DL = ED ? &ED->Remarks : nullptr; + else if (DToken.endswith(DType="-note")) + D.DL = ED ? &ED->Notes : nullptr; + else if (DToken.endswith(DType="-no-diagnostics")) { + NoDiag = true; + if (D.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 (!D.DL) + return true; + + // Next optional token: @ + SourceLocation ExpectedLoc; + StringRef Marker; + bool MatchAnyFileAndLine = false; + 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 (PH.NextMarker()) { + Marker = PH.Match(); + } else if (PP && PH.Search(":")) { + // Specific source file. + StringRef Filename(PH.C, PH.P-PH.C); + PH.Advance(); + + if (Filename == "*") { + MatchAnyFileAndLine = true; + if (!PH.Next("*")) { + Diags.Report(Pos.getLocWithOffset(PH.C - PH.Begin), + diag::err_verify_missing_line) + << "'*'"; + continue; + } + MatchAnyLine = true; + ExpectedLoc = SourceLocation(); + } else { + // Lookup file via Preprocessor, like a #include. + Optional<FileEntryRef> File = + PP->LookupFile(Pos, Filename, false, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr); + if (!File) { + Diags.Report(Pos.getLocWithOffset(PH.C - PH.Begin), + diag::err_verify_missing_file) + << Filename << KindStr; + continue; + } + + FileID FID = SM.translateFile(*File); + if (FID.isInvalid()) + FID = SM.createFileID(*File, Pos, SrcMgr::C_User); + + if (PH.Next(Line) && Line > 0) + ExpectedLoc = SM.translateLineCol(FID, Line, 1); + else if (PH.Next("*")) { + MatchAnyLine = true; + ExpectedLoc = SM.translateLineCol(FID, 1, 1); + } + } + } else if (PH.Next("*")) { + MatchAnyLine = true; + ExpectedLoc = SourceLocation(); + } + + if (ExpectedLoc.isInvalid() && !MatchAnyLine && Marker.empty()) { + 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 '+'. + if (PH.Next(D.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("+")) { + D.Max = Directive::MaxCount; + PH.Advance(); + } else if (PH.Next("-")) { + PH.Advance(); + if (!PH.Next(D.Max) || D.Max < D.Min) { + Diags.Report(Pos.getLocWithOffset(PH.C-PH.Begin), + diag::err_verify_invalid_range) << KindStr; + continue; + } + PH.Advance(); + } else { + D.Max = D.Min; + } + } else if (PH.Next("+")) { + // '+' on its own means "1 or more". + D.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(); + + D.DirectivePos = Pos; + D.ContentBegin = Pos.getLocWithOffset(ContentBegin - PH.Begin); + + // Build directive text; convert \n to newlines. + StringRef NewlineStr = "\\n"; + StringRef Content(ContentBegin, ContentEnd-ContentBegin); + size_t CPos = 0; + size_t FPos; + while ((FPos = Content.find(NewlineStr, CPos)) != StringRef::npos) { + D.Text += Content.substr(CPos, FPos-CPos); + D.Text += '\n'; + CPos = FPos + NewlineStr.size(); + } + if (D.Text.empty()) + D.Text.assign(ContentBegin, ContentEnd); + + // Check that regex directives contain at least one regex. + if (D.RegexKind && D.Text.find("{{") == StringRef::npos) { + Diags.Report(D.ContentBegin, diag::err_verify_missing_regex) << D.Text; + return false; + } + + if (Marker.empty()) + attachDirective(Diags, D, ExpectedLoc, MatchAnyFileAndLine, MatchAnyLine); + else + Markers.addDirective(Marker, D); + FoundDirective = true; + } + + return FoundDirective; +} + +VerifyDiagnosticConsumer::VerifyDiagnosticConsumer(DiagnosticsEngine &Diags_) + : Diags(Diags_), PrimaryClient(Diags.getClient()), + PrimaryClientOwner(Diags.takeClient()), + Buffer(new TextDiagnosticBuffer()), Markers(new MarkerTracker(Diags)), + 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!"); +} + +// 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( + std::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); + + // Diagnose any used-but-not-defined markers. + Markers->finalize(); + + // 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); +} + +/// 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, *Markers); + 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, *Markers); + 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. + llvm::MemoryBufferRef FromFile = SM.getBufferOrFake(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; + + // We don't care about tracking markers for this phase. + VerifyDiagnosticConsumer::MarkerTracker Markers(SM.getDiagnostics()); + + // Find first directive. + if (ParseDirective(Comment, nullptr, SM, nullptr, Tok.getLocation(), + Status, Markers)) + 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() || D->MatchAnyFileAndLine) + 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() && !D.MatchAnyFileAndLine && + !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 MatchAnyFileAndLine, + bool MatchAnyLine, StringRef Text, + unsigned Min, unsigned Max) { + if (!RegexKind) + return std::make_unique<StandardDirective>(DirectiveLoc, DiagnosticLoc, + MatchAnyFileAndLine, + 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 std::make_unique<RegexDirective>(DirectiveLoc, DiagnosticLoc, + MatchAnyFileAndLine, MatchAnyLine, + Text, Min, Max, RegexStr); +} diff --git a/contrib/libs/clang14/lib/Frontend/ya.make b/contrib/libs/clang14/lib/Frontend/ya.make new file mode 100644 index 0000000000..34b9d30175 --- /dev/null +++ b/contrib/libs/clang14/lib/Frontend/ya.make @@ -0,0 +1,69 @@ +# Generated by devtools/yamaker. + +LIBRARY() + +LICENSE(Apache-2.0 WITH LLVM-exception) + +LICENSE_TEXTS(.yandex_meta/licenses.list.txt) + +PEERDIR( + contrib/libs/clang14 + contrib/libs/clang14/include + contrib/libs/clang14/lib/AST + contrib/libs/clang14/lib/Basic + contrib/libs/clang14/lib/Driver + contrib/libs/clang14/lib/Edit + contrib/libs/clang14/lib/Lex + contrib/libs/clang14/lib/Parse + contrib/libs/clang14/lib/Sema + contrib/libs/clang14/lib/Serialization + contrib/libs/llvm14 + contrib/libs/llvm14/lib/Bitstream/Reader + contrib/libs/llvm14/lib/Option + contrib/libs/llvm14/lib/ProfileData + contrib/libs/llvm14/lib/Support +) + +ADDINCL( + contrib/libs/clang14/lib/Frontend +) + +NO_COMPILER_WARNINGS() + +NO_UTIL() + +SRCS( + ASTConsumers.cpp + ASTMerge.cpp + ASTUnit.cpp + ChainedDiagnosticConsumer.cpp + ChainedIncludesSource.cpp + CompilerInstance.cpp + CompilerInvocation.cpp + CreateInvocationFromCommandLine.cpp + DependencyFile.cpp + DependencyGraph.cpp + DiagnosticRenderer.cpp + ExtractAPIConsumer.cpp + FrontendAction.cpp + FrontendActions.cpp + FrontendOptions.cpp + HeaderIncludeGen.cpp + InitPreprocessor.cpp + InterfaceStubFunctionsConsumer.cpp + LayoutOverrideSource.cpp + LogDiagnosticPrinter.cpp + ModuleDependencyCollector.cpp + MultiplexConsumer.cpp + PrecompiledPreamble.cpp + PrintPreprocessedOutput.cpp + SerializedDiagnosticPrinter.cpp + SerializedDiagnosticReader.cpp + TestModuleFileExtension.cpp + TextDiagnostic.cpp + TextDiagnosticBuffer.cpp + TextDiagnosticPrinter.cpp + VerifyDiagnosticConsumer.cpp +) + +END() |