aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/clang14/lib/Frontend
diff options
context:
space:
mode:
authorvitalyisaev <vitalyisaev@yandex-team.com>2023-06-29 10:00:50 +0300
committervitalyisaev <vitalyisaev@yandex-team.com>2023-06-29 10:00:50 +0300
commit6ffe9e53658409f212834330e13564e4952558f6 (patch)
tree85b1e00183517648b228aafa7c8fb07f5276f419 /contrib/libs/clang14/lib/Frontend
parent726057070f9c5a91fc10fde0d5024913d10f1ab9 (diff)
downloadydb-6ffe9e53658409f212834330e13564e4952558f6.tar.gz
YQ Connector: support managed ClickHouse
Со стороны dqrun можно обратиться к инстансу коннектора, который работает на streaming стенде, и извлечь данные из облачного CH.
Diffstat (limited to 'contrib/libs/clang14/lib/Frontend')
-rw-r--r--contrib/libs/clang14/lib/Frontend/ASTConsumers.cpp217
-rw-r--r--contrib/libs/clang14/lib/Frontend/ASTMerge.cpp114
-rw-r--r--contrib/libs/clang14/lib/Frontend/ASTUnit.cpp2692
-rw-r--r--contrib/libs/clang14/lib/Frontend/ChainedDiagnosticConsumer.cpp13
-rw-r--r--contrib/libs/clang14/lib/Frontend/ChainedIncludesSource.cpp219
-rw-r--r--contrib/libs/clang14/lib/Frontend/CompilerInstance.cpp2285
-rw-r--r--contrib/libs/clang14/lib/Frontend/CompilerInvocation.cpp4716
-rw-r--r--contrib/libs/clang14/lib/Frontend/CreateInvocationFromCommandLine.cpp108
-rw-r--r--contrib/libs/clang14/lib/Frontend/DependencyFile.cpp385
-rw-r--r--contrib/libs/clang14/lib/Frontend/DependencyGraph.cpp139
-rw-r--r--contrib/libs/clang14/lib/Frontend/DiagnosticRenderer.cpp635
-rw-r--r--contrib/libs/clang14/lib/Frontend/ExtractAPIConsumer.cpp32
-rw-r--r--contrib/libs/clang14/lib/Frontend/FrontendAction.cpp1138
-rw-r--r--contrib/libs/clang14/lib/Frontend/FrontendActions.cpp1077
-rw-r--r--contrib/libs/clang14/lib/Frontend/FrontendOptions.cpp37
-rw-r--r--contrib/libs/clang14/lib/Frontend/HeaderIncludeGen.cpp199
-rw-r--r--contrib/libs/clang14/lib/Frontend/InitPreprocessor.cpp1347
-rw-r--r--contrib/libs/clang14/lib/Frontend/InterfaceStubFunctionsConsumer.cpp340
-rw-r--r--contrib/libs/clang14/lib/Frontend/LayoutOverrideSource.cpp207
-rw-r--r--contrib/libs/clang14/lib/Frontend/LogDiagnosticPrinter.cpp165
-rw-r--r--contrib/libs/clang14/lib/Frontend/ModuleDependencyCollector.cpp200
-rw-r--r--contrib/libs/clang14/lib/Frontend/MultiplexConsumer.cpp370
-rw-r--r--contrib/libs/clang14/lib/Frontend/PrecompiledPreamble.cpp885
-rw-r--r--contrib/libs/clang14/lib/Frontend/PrintPreprocessedOutput.cpp1026
-rw-r--r--contrib/libs/clang14/lib/Frontend/Rewrite/FixItRewriter.cpp209
-rw-r--r--contrib/libs/clang14/lib/Frontend/Rewrite/FrontendActions.cpp323
-rw-r--r--contrib/libs/clang14/lib/Frontend/Rewrite/HTMLPrint.cpp91
-rw-r--r--contrib/libs/clang14/lib/Frontend/Rewrite/InclusionRewriter.cpp543
-rw-r--r--contrib/libs/clang14/lib/Frontend/Rewrite/RewriteMacros.cpp216
-rw-r--r--contrib/libs/clang14/lib/Frontend/Rewrite/RewriteModernObjC.cpp7569
-rw-r--r--contrib/libs/clang14/lib/Frontend/Rewrite/RewriteObjC.cpp5887
-rw-r--r--contrib/libs/clang14/lib/Frontend/Rewrite/RewriteTest.cpp38
-rw-r--r--contrib/libs/clang14/lib/Frontend/Rewrite/ya.make42
-rw-r--r--contrib/libs/clang14/lib/Frontend/SerializedDiagnosticPrinter.cpp884
-rw-r--r--contrib/libs/clang14/lib/Frontend/SerializedDiagnosticReader.cpp371
-rw-r--r--contrib/libs/clang14/lib/Frontend/TestModuleFileExtension.cpp137
-rw-r--r--contrib/libs/clang14/lib/Frontend/TestModuleFileExtension.h73
-rw-r--r--contrib/libs/clang14/lib/Frontend/TextDiagnostic.cpp1355
-rw-r--r--contrib/libs/clang14/lib/Frontend/TextDiagnosticBuffer.cpp74
-rw-r--r--contrib/libs/clang14/lib/Frontend/TextDiagnosticPrinter.cpp156
-rw-r--r--contrib/libs/clang14/lib/Frontend/VerifyDiagnosticConsumer.cpp1166
-rw-r--r--contrib/libs/clang14/lib/Frontend/ya.make69
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()