1//===--- FrontendActions.cpp ----------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "clang/Frontend/FrontendActions.h"
10#include "clang/AST/ASTConsumer.h"
11#include "clang/AST/Decl.h"
12#include "clang/Basic/DiagnosticFrontend.h"
13#include "clang/Basic/FileManager.h"
14#include "clang/Basic/LangStandard.h"
15#include "clang/Basic/Module.h"
16#include "clang/Basic/TargetInfo.h"
17#include "clang/Frontend/ASTConsumers.h"
18#include "clang/Frontend/CompilerInstance.h"
19#include "clang/Frontend/MultiplexConsumer.h"
20#include "clang/Frontend/Utils.h"
21#include "clang/Lex/DependencyDirectivesScanner.h"
22#include "clang/Lex/HeaderSearch.h"
23#include "clang/Lex/Preprocessor.h"
24#include "clang/Lex/PreprocessorOptions.h"
25#include "clang/Parse/ParseHLSLRootSignature.h"
26#include "clang/Serialization/ASTReader.h"
27#include "clang/Serialization/ASTWriter.h"
28#include "clang/Serialization/ModuleFile.h"
29#include "llvm/Config/llvm-config.h" // for LLVM_HOST_TRIPLE
30#include "llvm/Support/ErrorHandling.h"
31#include "llvm/Support/FileSystem.h"
32#include "llvm/Support/MemoryBuffer.h"
33#include "llvm/Support/YAMLTraits.h"
34#include "llvm/Support/raw_ostream.h"
35#include <memory>
36#include <optional>
37#include <system_error>
38
39using namespace clang;
40
41//===----------------------------------------------------------------------===//
42// Custom Actions
43//===----------------------------------------------------------------------===//
44
45std::unique_ptr<ASTConsumer>
46InitOnlyAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
47 return std::make_unique<ASTConsumer>();
48}
49
50void InitOnlyAction::ExecuteAction() {
51}
52
53// Basically PreprocessOnlyAction::ExecuteAction.
54void ReadPCHAndPreprocessAction::ExecuteAction() {
55 Preprocessor &PP = getCompilerInstance().getPreprocessor();
56
57 // Ignore unknown pragmas.
58 PP.IgnorePragmas();
59
60 Token Tok;
61 // Start parsing the specified input file.
62 PP.EnterMainSourceFile();
63 do {
64 PP.Lex(Result&: Tok);
65 } while (Tok.isNot(K: tok::eof));
66}
67
68std::unique_ptr<ASTConsumer>
69ReadPCHAndPreprocessAction::CreateASTConsumer(CompilerInstance &CI,
70 StringRef InFile) {
71 return std::make_unique<ASTConsumer>();
72}
73
74//===----------------------------------------------------------------------===//
75// AST Consumer Actions
76//===----------------------------------------------------------------------===//
77
78std::unique_ptr<ASTConsumer>
79ASTPrintAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
80 if (std::unique_ptr<raw_ostream> OS =
81 CI.createDefaultOutputFile(Binary: false, BaseInput: InFile))
82 return CreateASTPrinter(OS: std::move(OS), FilterString: CI.getFrontendOpts().ASTDumpFilter);
83 return nullptr;
84}
85
86std::unique_ptr<ASTConsumer>
87ASTDumpAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
88 const FrontendOptions &Opts = CI.getFrontendOpts();
89 return CreateASTDumper(OS: nullptr /*Dump to stdout.*/, FilterString: Opts.ASTDumpFilter,
90 DumpDecls: Opts.ASTDumpDecls, Deserialize: Opts.ASTDumpAll,
91 DumpLookups: Opts.ASTDumpLookups, DumpDeclTypes: Opts.ASTDumpDeclTypes,
92 Format: Opts.ASTDumpFormat);
93}
94
95std::unique_ptr<ASTConsumer>
96ASTDeclListAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
97 return CreateASTDeclNodeLister();
98}
99
100std::unique_ptr<ASTConsumer>
101ASTViewAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
102 return CreateASTViewer();
103}
104
105std::unique_ptr<ASTConsumer>
106GeneratePCHAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
107 std::string Sysroot;
108 if (!ComputeASTConsumerArguments(CI, /*ref*/ Sysroot))
109 return nullptr;
110
111 std::string OutputFile;
112 std::unique_ptr<raw_pwrite_stream> OS =
113 CreateOutputFile(CI, InFile, /*ref*/ OutputFile, SetOnlyIfDifferent);
114 if (!OS)
115 return nullptr;
116
117 if (!CI.getFrontendOpts().RelocatablePCH)
118 Sysroot.clear();
119
120 const auto &FrontendOpts = CI.getFrontendOpts();
121 auto Buffer = std::make_shared<PCHBuffer>();
122 std::vector<std::unique_ptr<ASTConsumer>> Consumers;
123 Consumers.push_back(x: std::make_unique<PCHGenerator>(
124 args&: CI.getPreprocessor(), args&: CI.getModuleCache(), args&: OutputFile, args&: Sysroot, args&: Buffer,
125 args&: CI.getCodeGenOpts(), args: FrontendOpts.ModuleFileExtensions,
126 args&: CI.getPreprocessorOpts().AllowPCHWithCompilerErrors,
127 args: FrontendOpts.IncludeTimestamps, args: FrontendOpts.BuildingImplicitModule));
128 Consumers.push_back(x: CI.getPCHContainerWriter().CreatePCHContainerGenerator(
129 CI, MainFileName: std::string(InFile), OutputFileName: OutputFile, OS: std::move(OS), Buffer));
130
131 return std::make_unique<MultiplexConsumer>(args: std::move(Consumers));
132}
133
134bool GeneratePCHAction::ComputeASTConsumerArguments(CompilerInstance &CI,
135 std::string &Sysroot) {
136 Sysroot = CI.getHeaderSearchOpts().Sysroot;
137 if (CI.getFrontendOpts().RelocatablePCH && Sysroot.empty()) {
138 CI.getDiagnostics().Report(DiagID: diag::err_relocatable_without_isysroot);
139 return false;
140 }
141
142 return true;
143}
144
145std::unique_ptr<llvm::raw_pwrite_stream>
146GeneratePCHAction::CreateOutputFile(CompilerInstance &CI, StringRef InFile,
147 std::string &OutputFile,
148 bool SetOnlyIfDifferent) {
149 // Because this is exposed via libclang we must disable RemoveFileOnSignal.
150 std::unique_ptr<raw_pwrite_stream> OS = CI.createDefaultOutputFile(
151 /*Binary=*/true, BaseInput: InFile, /*Extension=*/"", /*RemoveFileOnSignal=*/false,
152 /*CreateMissingDirectories=*/false, /*ForceUseTemporary=*/false,
153 SetOnlyIfDifferent);
154 if (!OS)
155 return nullptr;
156
157 OutputFile = CI.getFrontendOpts().OutputFile;
158 return OS;
159}
160
161bool GeneratePCHAction::shouldEraseOutputFiles() {
162 if (getCompilerInstance().getPreprocessorOpts().AllowPCHWithCompilerErrors)
163 return false;
164 return ASTFrontendAction::shouldEraseOutputFiles();
165}
166
167bool GeneratePCHAction::BeginSourceFileAction(CompilerInstance &CI) {
168 CI.getLangOpts().CompilingPCH = true;
169 return ASTFrontendAction::BeginSourceFileAction(CI);
170}
171
172std::vector<std::unique_ptr<ASTConsumer>>
173GenerateModuleAction::CreateMultiplexConsumer(CompilerInstance &CI,
174 StringRef InFile) {
175 if (!OS)
176 OS = CreateOutputFile(CI, InFile);
177 if (!OS)
178 return {};
179
180 std::string OutputFile = CI.getFrontendOpts().OutputFile;
181 std::string Sysroot;
182
183 auto Buffer = std::make_shared<PCHBuffer>();
184 std::vector<std::unique_ptr<ASTConsumer>> Consumers;
185
186 Consumers.push_back(x: std::make_unique<PCHGenerator>(
187 args&: CI.getPreprocessor(), args&: CI.getModuleCache(), args&: OutputFile, args&: Sysroot, args&: Buffer,
188 args&: CI.getCodeGenOpts(), args&: CI.getFrontendOpts().ModuleFileExtensions,
189 /*AllowASTWithErrors=*/
190 args: +CI.getFrontendOpts().AllowPCMWithCompilerErrors,
191 /*IncludeTimestamps=*/
192 args: +CI.getFrontendOpts().BuildingImplicitModule &&
193 +CI.getFrontendOpts().IncludeTimestamps,
194 /*BuildingImplicitModule=*/args: +CI.getFrontendOpts().BuildingImplicitModule));
195 Consumers.push_back(x: CI.getPCHContainerWriter().CreatePCHContainerGenerator(
196 CI, MainFileName: std::string(InFile), OutputFileName: OutputFile, OS: std::move(OS), Buffer));
197 return Consumers;
198}
199
200std::unique_ptr<ASTConsumer>
201GenerateModuleAction::CreateASTConsumer(CompilerInstance &CI,
202 StringRef InFile) {
203 std::vector<std::unique_ptr<ASTConsumer>> Consumers =
204 CreateMultiplexConsumer(CI, InFile);
205 if (Consumers.empty())
206 return nullptr;
207
208 return std::make_unique<MultiplexConsumer>(args: std::move(Consumers));
209}
210
211bool GenerateModuleAction::shouldEraseOutputFiles() {
212 return !getCompilerInstance().getFrontendOpts().AllowPCMWithCompilerErrors &&
213 ASTFrontendAction::shouldEraseOutputFiles();
214}
215
216bool GenerateModuleFromModuleMapAction::BeginSourceFileAction(
217 CompilerInstance &CI) {
218 if (!CI.getLangOpts().Modules) {
219 CI.getDiagnostics().Report(DiagID: diag::err_module_build_requires_fmodules);
220 return false;
221 }
222
223 return GenerateModuleAction::BeginSourceFileAction(CI);
224}
225
226std::unique_ptr<raw_pwrite_stream>
227GenerateModuleFromModuleMapAction::CreateOutputFile(CompilerInstance &CI,
228 StringRef InFile) {
229 // If no output file was provided, figure out where this module would go
230 // in the module cache.
231 if (CI.getFrontendOpts().OutputFile.empty()) {
232 StringRef ModuleMapFile = CI.getFrontendOpts().OriginalModuleMap;
233 if (ModuleMapFile.empty())
234 ModuleMapFile = InFile;
235
236 HeaderSearch &HS = CI.getPreprocessor().getHeaderSearchInfo();
237 ModuleFileName FileName = HS.getCachedModuleFileName(
238 ModuleName: CI.getLangOpts().CurrentModule, ModuleMapPath: ModuleMapFile);
239 CI.getFrontendOpts().OutputFile = FileName.str();
240 }
241
242 // Because this is exposed via libclang we must disable RemoveFileOnSignal.
243 return CI.createDefaultOutputFile(/*Binary=*/true, BaseInput: InFile, /*Extension=*/"",
244 /*RemoveFileOnSignal=*/false,
245 /*CreateMissingDirectories=*/true,
246 /*ForceUseTemporary=*/true,
247 /*SetOnlyIfDifferent=*/SetOnlyIfDifferent);
248}
249
250bool GenerateModuleInterfaceAction::PrepareToExecuteAction(
251 CompilerInstance &CI) {
252 for (const auto &FIF : CI.getFrontendOpts().Inputs) {
253 if (const auto InputFormat = FIF.getKind().getFormat();
254 InputFormat != InputKind::Format::Source) {
255 CI.getDiagnostics().Report(
256 DiagID: diag::err_frontend_action_unsupported_input_format)
257 << "module interface compilation" << FIF.getFile() << InputFormat;
258 return false;
259 }
260 }
261 return GenerateModuleAction::PrepareToExecuteAction(CI);
262}
263
264bool GenerateModuleInterfaceAction::BeginSourceFileAction(
265 CompilerInstance &CI) {
266 CI.getLangOpts().setCompilingModule(LangOptions::CMK_ModuleInterface);
267
268 return GenerateModuleAction::BeginSourceFileAction(CI);
269}
270
271std::unique_ptr<ASTConsumer>
272GenerateModuleInterfaceAction::CreateASTConsumer(CompilerInstance &CI,
273 StringRef InFile) {
274 std::vector<std::unique_ptr<ASTConsumer>> Consumers;
275
276 if (CI.getFrontendOpts().GenReducedBMI &&
277 !CI.getFrontendOpts().ModuleOutputPath.empty()) {
278 Consumers.push_back(x: std::make_unique<ReducedBMIGenerator>(
279 args&: CI.getPreprocessor(), args&: CI.getModuleCache(),
280 args&: CI.getFrontendOpts().ModuleOutputPath, args&: CI.getCodeGenOpts(),
281 args: +CI.getFrontendOpts().AllowPCMWithCompilerErrors));
282 }
283
284 Consumers.push_back(x: std::make_unique<CXX20ModulesGenerator>(
285 args&: CI.getPreprocessor(), args&: CI.getModuleCache(),
286 args&: CI.getFrontendOpts().OutputFile, args&: CI.getCodeGenOpts(),
287 args: +CI.getFrontendOpts().AllowPCMWithCompilerErrors));
288
289 return std::make_unique<MultiplexConsumer>(args: std::move(Consumers));
290}
291
292std::unique_ptr<raw_pwrite_stream>
293GenerateModuleInterfaceAction::CreateOutputFile(CompilerInstance &CI,
294 StringRef InFile) {
295 return CI.createDefaultOutputFile(/*Binary=*/true, BaseInput: InFile, Extension: "pcm");
296}
297
298std::unique_ptr<ASTConsumer>
299GenerateReducedModuleInterfaceAction::CreateASTConsumer(CompilerInstance &CI,
300 StringRef InFile) {
301 return std::make_unique<ReducedBMIGenerator>(
302 args&: CI.getPreprocessor(), args&: CI.getModuleCache(),
303 args&: CI.getFrontendOpts().OutputFile, args&: CI.getCodeGenOpts());
304}
305
306bool GenerateHeaderUnitAction::BeginSourceFileAction(CompilerInstance &CI) {
307 if (!CI.getLangOpts().CPlusPlusModules) {
308 CI.getDiagnostics().Report(DiagID: diag::err_module_interface_requires_cpp_modules);
309 return false;
310 }
311 CI.getLangOpts().setCompilingModule(LangOptions::CMK_HeaderUnit);
312 return GenerateModuleAction::BeginSourceFileAction(CI);
313}
314
315std::unique_ptr<raw_pwrite_stream>
316GenerateHeaderUnitAction::CreateOutputFile(CompilerInstance &CI,
317 StringRef InFile) {
318 return CI.createDefaultOutputFile(/*Binary=*/true, BaseInput: InFile, Extension: "pcm");
319}
320
321SyntaxOnlyAction::~SyntaxOnlyAction() {
322}
323
324std::unique_ptr<ASTConsumer>
325SyntaxOnlyAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
326 return std::make_unique<ASTConsumer>();
327}
328
329std::unique_ptr<ASTConsumer>
330DumpModuleInfoAction::CreateASTConsumer(CompilerInstance &CI,
331 StringRef InFile) {
332 return std::make_unique<ASTConsumer>();
333}
334
335std::unique_ptr<ASTConsumer>
336VerifyPCHAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
337 return std::make_unique<ASTConsumer>();
338}
339
340void VerifyPCHAction::ExecuteAction() {
341 CompilerInstance &CI = getCompilerInstance();
342 bool Preamble = CI.getPreprocessorOpts().PrecompiledPreambleBytes.first != 0;
343 const std::string &Sysroot = CI.getHeaderSearchOpts().Sysroot;
344 std::unique_ptr<ASTReader> Reader(new ASTReader(
345 CI.getPreprocessor(), CI.getModuleCache(), &CI.getASTContext(),
346 CI.getPCHContainerReader(), CI.getCodeGenOpts(),
347 CI.getFrontendOpts().ModuleFileExtensions,
348 Sysroot.empty() ? "" : Sysroot.c_str(),
349 DisableValidationForModuleKind::None,
350 /*AllowASTWithCompilerErrors*/ false,
351 /*AllowConfigurationMismatch*/ true,
352 /*ValidateSystemInputs*/ true, /*ForceValidateUserInputs*/ true));
353
354 Reader->ReadAST(FileName: ModuleFileName::makeExplicit(Name: getCurrentFile()),
355 Type: Preamble ? serialization::MK_Preamble : serialization::MK_PCH,
356 ImportLoc: SourceLocation(), ClientLoadCapabilities: ASTReader::ARR_ConfigurationMismatch);
357}
358
359namespace {
360 /// AST reader listener that dumps module information for a module
361 /// file.
362 class DumpModuleInfoListener : public ASTReaderListener {
363 llvm::raw_ostream &Out;
364 FileManager &FileMgr;
365
366 public:
367 DumpModuleInfoListener(llvm::raw_ostream &Out, FileManager &FileMgr)
368 : Out(Out), FileMgr(FileMgr) {}
369
370#define DUMP_BOOLEAN(Value, Text) \
371 Out.indent(4) << Text << ": " << (Value? "Yes" : "No") << "\n"
372
373 bool ReadFullVersionInformation(StringRef FullVersion) override {
374 Out.indent(NumSpaces: 2)
375 << "Generated by "
376 << (FullVersion == getClangFullRepositoryVersion()? "this"
377 : "a different")
378 << " Clang: " << FullVersion << "\n";
379 return ASTReaderListener::ReadFullVersionInformation(FullVersion);
380 }
381
382 void ReadModuleName(StringRef ModuleName) override {
383 Out.indent(NumSpaces: 2) << "Module name: " << ModuleName << "\n";
384 }
385 void ReadModuleMapFile(StringRef ModuleMapPath) override {
386 Out.indent(NumSpaces: 2) << "Module map file: " << ModuleMapPath << "\n";
387 }
388
389 bool ReadLanguageOptions(const LangOptions &LangOpts,
390 StringRef ModuleFilename, bool Complain,
391 bool AllowCompatibleDifferences) override {
392 // FIXME: Replace with C++20 `using enum LangOptions::CompatibilityKind`.
393 using CK = LangOptions::CompatibilityKind;
394
395 Out.indent(NumSpaces: 2) << "Language options:\n";
396#define LANGOPT(Name, Bits, Default, Compatibility, Description) \
397 if constexpr (CK::Compatibility != CK::Benign) \
398 DUMP_BOOLEAN(LangOpts.Name, Description);
399#define ENUM_LANGOPT(Name, Type, Bits, Default, Compatibility, Description) \
400 if constexpr (CK::Compatibility != CK::Benign) \
401 Out.indent(4) << Description << ": " \
402 << static_cast<unsigned>(LangOpts.get##Name()) << "\n";
403#define VALUE_LANGOPT(Name, Bits, Default, Compatibility, Description) \
404 if constexpr (CK::Compatibility != CK::Benign) \
405 Out.indent(4) << Description << ": " << LangOpts.Name << "\n";
406#include "clang/Basic/LangOptions.def"
407
408 if (!LangOpts.ModuleFeatures.empty()) {
409 Out.indent(NumSpaces: 4) << "Module features:\n";
410 for (StringRef Feature : LangOpts.ModuleFeatures)
411 Out.indent(NumSpaces: 6) << Feature << "\n";
412 }
413
414 return false;
415 }
416
417 bool ReadTargetOptions(const TargetOptions &TargetOpts,
418 StringRef ModuleFilename, bool Complain,
419 bool AllowCompatibleDifferences) override {
420 Out.indent(NumSpaces: 2) << "Target options:\n";
421 Out.indent(NumSpaces: 4) << " Triple: " << TargetOpts.Triple << "\n";
422 Out.indent(NumSpaces: 4) << " CPU: " << TargetOpts.CPU << "\n";
423 Out.indent(NumSpaces: 4) << " TuneCPU: " << TargetOpts.TuneCPU << "\n";
424 Out.indent(NumSpaces: 4) << " ABI: " << TargetOpts.ABI << "\n";
425
426 if (!TargetOpts.FeaturesAsWritten.empty()) {
427 Out.indent(NumSpaces: 4) << "Target features:\n";
428 for (unsigned I = 0, N = TargetOpts.FeaturesAsWritten.size();
429 I != N; ++I) {
430 Out.indent(NumSpaces: 6) << TargetOpts.FeaturesAsWritten[I] << "\n";
431 }
432 }
433
434 return false;
435 }
436
437 bool ReadDiagnosticOptions(DiagnosticOptions &DiagOpts,
438 StringRef ModuleFilename,
439 bool Complain) override {
440 Out.indent(NumSpaces: 2) << "Diagnostic options:\n";
441#define DIAGOPT(Name, Bits, Default) DUMP_BOOLEAN(DiagOpts.Name, #Name);
442#define ENUM_DIAGOPT(Name, Type, Bits, Default) \
443 Out.indent(4) << #Name << ": " \
444 << static_cast<unsigned>(DiagOpts.get##Name()) << "\n";
445#define VALUE_DIAGOPT(Name, Bits, Default) \
446 Out.indent(4) << #Name << ": " << DiagOpts.Name << "\n";
447#include "clang/Basic/DiagnosticOptions.def"
448
449 Out.indent(NumSpaces: 4) << "Diagnostic flags:\n";
450 for (const std::string &Warning : DiagOpts.Warnings)
451 Out.indent(NumSpaces: 6) << "-W" << Warning << "\n";
452 for (const std::string &Remark : DiagOpts.Remarks)
453 Out.indent(NumSpaces: 6) << "-R" << Remark << "\n";
454
455 return false;
456 }
457
458 bool ReadHeaderSearchOptions(const HeaderSearchOptions &HSOpts,
459 StringRef ModuleFilename,
460 StringRef ContextHash,
461 bool Complain) override {
462 std::string SpecificModuleCachePath = createSpecificModuleCachePath(
463 FileMgr, ModuleCachePath: HSOpts.ModuleCachePath, DisableModuleHash: HSOpts.DisableModuleHash,
464 ContextHash: std::string(ContextHash));
465
466 Out.indent(NumSpaces: 2) << "Header search options:\n";
467 Out.indent(NumSpaces: 4) << "System root [-isysroot=]: '" << HSOpts.Sysroot << "'\n";
468 Out.indent(NumSpaces: 4) << "Resource dir [ -resource-dir=]: '" << HSOpts.ResourceDir << "'\n";
469 Out.indent(NumSpaces: 4) << "Module Cache: '" << SpecificModuleCachePath << "'\n";
470 DUMP_BOOLEAN(HSOpts.UseBuiltinIncludes,
471 "Use builtin include directories [-nobuiltininc]");
472 DUMP_BOOLEAN(HSOpts.UseStandardSystemIncludes,
473 "Use standard system include directories [-nostdinc]");
474 DUMP_BOOLEAN(HSOpts.UseStandardCXXIncludes,
475 "Use standard C++ include directories [-nostdinc++]");
476 DUMP_BOOLEAN(HSOpts.UseLibcxx,
477 "Use libc++ (rather than libstdc++) [-stdlib=]");
478 return false;
479 }
480
481 bool ReadHeaderSearchPaths(const HeaderSearchOptions &HSOpts,
482 bool Complain) override {
483 Out.indent(NumSpaces: 2) << "Header search paths:\n";
484 Out.indent(NumSpaces: 4) << "User entries:\n";
485 for (const auto &Entry : HSOpts.UserEntries)
486 Out.indent(NumSpaces: 6) << Entry.Path << "\n";
487 Out.indent(NumSpaces: 4) << "System header prefixes:\n";
488 for (const auto &Prefix : HSOpts.SystemHeaderPrefixes)
489 Out.indent(NumSpaces: 6) << Prefix.Prefix << "\n";
490 Out.indent(NumSpaces: 4) << "VFS overlay files:\n";
491 for (const auto &Overlay : HSOpts.VFSOverlayFiles)
492 Out.indent(NumSpaces: 6) << Overlay << "\n";
493 return false;
494 }
495
496 bool ReadPreprocessorOptions(const PreprocessorOptions &PPOpts,
497 StringRef ModuleFilename, bool ReadMacros,
498 bool Complain,
499 std::string &SuggestedPredefines) override {
500 Out.indent(NumSpaces: 2) << "Preprocessor options:\n";
501 DUMP_BOOLEAN(PPOpts.UsePredefines,
502 "Uses compiler/target-specific predefines [-undef]");
503 DUMP_BOOLEAN(PPOpts.DetailedRecord,
504 "Uses detailed preprocessing record (for indexing)");
505
506 if (ReadMacros) {
507 Out.indent(NumSpaces: 4) << "Predefined macros:\n";
508 }
509
510 for (std::vector<std::pair<std::string, bool/*isUndef*/> >::const_iterator
511 I = PPOpts.Macros.begin(), IEnd = PPOpts.Macros.end();
512 I != IEnd; ++I) {
513 Out.indent(NumSpaces: 6);
514 if (I->second)
515 Out << "-U";
516 else
517 Out << "-D";
518 Out << I->first << "\n";
519 }
520 return false;
521 }
522
523 /// Indicates that a particular module file extension has been read.
524 void readModuleFileExtension(
525 const ModuleFileExtensionMetadata &Metadata) override {
526 Out.indent(NumSpaces: 2) << "Module file extension '"
527 << Metadata.BlockName << "' " << Metadata.MajorVersion
528 << "." << Metadata.MinorVersion;
529 if (!Metadata.UserInfo.empty()) {
530 Out << ": ";
531 Out.write_escaped(Str: Metadata.UserInfo);
532 }
533
534 Out << "\n";
535 }
536
537 /// Tells the \c ASTReaderListener that we want to receive the
538 /// input files of the AST file via \c visitInputFile.
539 bool needsInputFileVisitation() override { return true; }
540
541 /// Tells the \c ASTReaderListener that we want to receive the
542 /// input files of the AST file via \c visitInputFile.
543 bool needsSystemInputFileVisitation() override { return true; }
544
545 /// Indicates that the AST file contains particular input file.
546 ///
547 /// \returns true to continue receiving the next input file, false to stop.
548 bool visitInputFileAsRequested(StringRef FilenameAsRequested,
549 StringRef Filename, bool isSystem,
550 bool isOverridden, time_t StoredTime,
551 bool isExplicitModule) override {
552
553 Out.indent(NumSpaces: 2) << "Input file: " << FilenameAsRequested;
554
555 if (isSystem || isOverridden || isExplicitModule) {
556 Out << " [";
557 if (isSystem) {
558 Out << "System";
559 if (isOverridden || isExplicitModule)
560 Out << ", ";
561 }
562 if (isOverridden) {
563 Out << "Overridden";
564 if (isExplicitModule)
565 Out << ", ";
566 }
567 if (isExplicitModule)
568 Out << "ExplicitModule";
569
570 Out << "]";
571 }
572
573 Out << "\n";
574
575 if (StoredTime > 0)
576 Out.indent(NumSpaces: 4) << "MTime: " << llvm::itostr(X: StoredTime) << "\n";
577
578 return true;
579 }
580
581 /// Returns true if this \c ASTReaderListener wants to receive the
582 /// imports of the AST file via \c visitImport, false otherwise.
583 bool needsImportVisitation() const override { return true; }
584
585 /// If needsImportVisitation returns \c true, this is called for each
586 /// AST file imported by this AST file.
587 void visitImport(StringRef ModuleName, StringRef Filename) override {
588 Out.indent(NumSpaces: 2) << "Imports module '" << ModuleName
589 << "': " << Filename.str() << "\n";
590 }
591#undef DUMP_BOOLEAN
592 };
593}
594
595bool DumpModuleInfoAction::BeginInvocation(CompilerInstance &CI) {
596 // The Object file reader also supports raw ast files and there is no point in
597 // being strict about the module file format in -module-file-info mode.
598 CI.getHeaderSearchOpts().ModuleFormat = "obj";
599 return true;
600}
601
602static StringRef ModuleKindName(Module::ModuleKind MK) {
603 switch (MK) {
604 case Module::ModuleMapModule:
605 return "Module Map Module";
606 case Module::ModuleInterfaceUnit:
607 return "Interface Unit";
608 case Module::ModuleImplementationUnit:
609 return "Implementation Unit";
610 case Module::ModulePartitionInterface:
611 return "Partition Interface";
612 case Module::ModulePartitionImplementation:
613 return "Partition Implementation";
614 case Module::ModuleHeaderUnit:
615 return "Header Unit";
616 case Module::ExplicitGlobalModuleFragment:
617 return "Global Module Fragment";
618 case Module::ImplicitGlobalModuleFragment:
619 return "Implicit Module Fragment";
620 case Module::PrivateModuleFragment:
621 return "Private Module Fragment";
622 }
623 llvm_unreachable("unknown module kind!");
624}
625
626void DumpModuleInfoAction::ExecuteAction() {
627 CompilerInstance &CI = getCompilerInstance();
628
629 // Don't process files of type other than module to avoid crash
630 if (!isCurrentFileAST()) {
631 CI.getDiagnostics().Report(DiagID: diag::err_file_is_not_module)
632 << getCurrentFile();
633 return;
634 }
635
636 // Set up the output file.
637 StringRef OutputFileName = CI.getFrontendOpts().OutputFile;
638 if (!OutputFileName.empty() && OutputFileName != "-") {
639 std::error_code EC;
640 OutputStream.reset(p: new llvm::raw_fd_ostream(
641 OutputFileName.str(), EC, llvm::sys::fs::OF_TextWithCRLF));
642 }
643 llvm::raw_ostream &Out = OutputStream ? *OutputStream : llvm::outs();
644
645 Out << "Information for module file '" << getCurrentFile() << "':\n";
646 auto &FileMgr = CI.getFileManager();
647 auto Buffer = FileMgr.getBufferForFile(Filename: getCurrentFile());
648 StringRef Magic = (*Buffer)->getMemBufferRef().getBuffer();
649 bool IsRaw = Magic.starts_with(Prefix: "CPCH");
650 Out << " Module format: " << (IsRaw ? "raw" : "obj") << "\n";
651
652 Preprocessor &PP = CI.getPreprocessor();
653 DumpModuleInfoListener Listener(Out, CI.getFileManager());
654 const HeaderSearchOptions &HSOpts =
655 PP.getHeaderSearchInfo().getHeaderSearchOpts();
656
657 // The FrontendAction::BeginSourceFile () method loads the AST so that much
658 // of the information is already available and modules should have been
659 // loaded.
660
661 const LangOptions &LO = getCurrentASTUnit().getLangOpts();
662 if (LO.CPlusPlusModules && !LO.CurrentModule.empty()) {
663 ASTReader *R = getCurrentASTUnit().getASTReader().get();
664 unsigned SubModuleCount = R->getTotalNumSubmodules();
665 serialization::ModuleFile &MF = R->getModuleManager().getPrimaryModule();
666 Out << " ====== C++20 Module structure ======\n";
667
668 if (MF.ModuleName != LO.CurrentModule)
669 Out << " Mismatched module names : " << MF.ModuleName << " and "
670 << LO.CurrentModule << "\n";
671
672 struct SubModInfo {
673 unsigned Idx;
674 Module *Mod;
675 Module::ModuleKind Kind;
676 std::string &Name;
677 bool Seen;
678 };
679 std::map<std::string, SubModInfo> SubModMap;
680 auto PrintSubMapEntry = [&](std::string Name, Module::ModuleKind Kind) {
681 Out << " " << ModuleKindName(MK: Kind) << " '" << Name << "'";
682 auto I = SubModMap.find(x: Name);
683 if (I == SubModMap.end())
684 Out << " was not found in the sub modules!\n";
685 else {
686 I->second.Seen = true;
687 Out << " is at index #" << I->second.Idx << "\n";
688 }
689 };
690 Module *Primary = nullptr;
691 for (unsigned Idx = 0; Idx <= SubModuleCount; ++Idx) {
692 Module *M = R->getModule(ID: Idx);
693 if (!M)
694 continue;
695 if (M->Name == LO.CurrentModule) {
696 Primary = M;
697 Out << " " << ModuleKindName(MK: M->Kind) << " '" << LO.CurrentModule
698 << "' is the Primary Module at index #" << Idx << "\n";
699 SubModMap.insert(x: {M->Name, {.Idx: Idx, .Mod: M, .Kind: M->Kind, .Name: M->Name, .Seen: true}});
700 } else
701 SubModMap.insert(x: {M->Name, {.Idx: Idx, .Mod: M, .Kind: M->Kind, .Name: M->Name, .Seen: false}});
702 }
703 if (Primary) {
704 if (!Primary->submodules().empty())
705 Out << " Sub Modules:\n";
706 for (Module *MI : Primary->submodules()) {
707 PrintSubMapEntry(MI->Name, MI->Kind);
708 }
709 if (!Primary->Imports.empty())
710 Out << " Imports:\n";
711 for (Module *IMP : Primary->Imports) {
712 PrintSubMapEntry(IMP->Name, IMP->Kind);
713 }
714 if (!Primary->Exports.empty())
715 Out << " Exports:\n";
716 for (unsigned MN = 0, N = Primary->Exports.size(); MN != N; ++MN) {
717 if (Module *M = Primary->Exports[MN].first) {
718 PrintSubMapEntry(M->Name, M->Kind);
719 }
720 }
721 }
722
723 // Emit the macro definitions in the module file so that we can know how
724 // much definitions in the module file quickly.
725 // TODO: Emit the macro definition bodies completely.
726 {
727 std::vector<StringRef> MacroNames;
728 for (const auto &M : R->getPreprocessor().macros()) {
729 if (M.first->isFromAST())
730 MacroNames.push_back(x: M.first->getName());
731 }
732 llvm::sort(C&: MacroNames);
733 if (!MacroNames.empty())
734 Out << " Macro Definitions:\n";
735 for (StringRef Name : MacroNames)
736 Out << " " << Name << "\n";
737 }
738
739 // Now let's print out any modules we did not see as part of the Primary.
740 for (const auto &SM : SubModMap) {
741 if (!SM.second.Seen && SM.second.Mod) {
742 Out << " " << ModuleKindName(MK: SM.second.Kind) << " '" << SM.first
743 << "' at index #" << SM.second.Idx
744 << " has no direct reference in the Primary\n";
745 }
746 }
747 Out << " ====== ======\n";
748 }
749
750 // The reminder of the output is produced from the listener as the AST
751 // FileCcontrolBlock is (re-)parsed.
752 ASTReader::readASTFileControlBlock(
753 Filename: getCurrentFile(), FileMgr, ModCache: CI.getModuleCache(),
754 PCHContainerRdr: CI.getPCHContainerReader(),
755 /*FindModuleFileExtensions=*/true, Listener,
756 ValidateDiagnosticOptions: HSOpts.ModulesValidateDiagnosticOptions);
757}
758
759//===----------------------------------------------------------------------===//
760// Preprocessor Actions
761//===----------------------------------------------------------------------===//
762
763void DumpRawTokensAction::ExecuteAction() {
764 Preprocessor &PP = getCompilerInstance().getPreprocessor();
765 SourceManager &SM = PP.getSourceManager();
766
767 // Start lexing the specified input file.
768 llvm::MemoryBufferRef FromFile = SM.getBufferOrFake(FID: SM.getMainFileID());
769 Lexer RawLex(SM.getMainFileID(), FromFile, SM, PP.getLangOpts());
770 RawLex.SetKeepWhitespaceMode(true);
771
772 Token RawTok;
773 RawLex.LexFromRawLexer(Result&: RawTok);
774 while (RawTok.isNot(K: tok::eof)) {
775 PP.DumpToken(Tok: RawTok, DumpFlags: true);
776 llvm::errs() << "\n";
777 RawLex.LexFromRawLexer(Result&: RawTok);
778 }
779}
780
781void DumpTokensAction::ExecuteAction() {
782 Preprocessor &PP = getCompilerInstance().getPreprocessor();
783 // Start preprocessing the specified input file.
784 Token Tok;
785 PP.EnterMainSourceFile();
786 do {
787 PP.Lex(Result&: Tok);
788 PP.DumpToken(Tok, DumpFlags: true);
789 llvm::errs() << "\n";
790 } while (Tok.isNot(K: tok::eof));
791}
792
793void PreprocessOnlyAction::ExecuteAction() {
794 Preprocessor &PP = getCompilerInstance().getPreprocessor();
795
796 // Ignore unknown pragmas.
797 PP.IgnorePragmas();
798
799 Token Tok;
800 // Start parsing the specified input file.
801 PP.EnterMainSourceFile();
802 do {
803 PP.Lex(Result&: Tok);
804 } while (Tok.isNot(K: tok::eof));
805}
806
807void PrintPreprocessedAction::ExecuteAction() {
808 CompilerInstance &CI = getCompilerInstance();
809 // Output file may need to be set to 'Binary', to avoid converting Unix style
810 // line feeds (<LF>) to Microsoft style line feeds (<CR><LF>) on Windows.
811 //
812 // Look to see what type of line endings the file uses. If there's a
813 // CRLF, then we won't open the file up in binary mode. If there is
814 // just an LF or CR, then we will open the file up in binary mode.
815 // In this fashion, the output format should match the input format, unless
816 // the input format has inconsistent line endings.
817 //
818 // This should be a relatively fast operation since most files won't have
819 // all of their source code on a single line. However, that is still a
820 // concern, so if we scan for too long, we'll just assume the file should
821 // be opened in binary mode.
822
823 bool BinaryMode = false;
824 if (llvm::Triple(LLVM_HOST_TRIPLE).isOSWindows()) {
825 BinaryMode = true;
826 const SourceManager &SM = CI.getSourceManager();
827 if (std::optional<llvm::MemoryBufferRef> Buffer =
828 SM.getBufferOrNone(FID: SM.getMainFileID())) {
829 const char *cur = Buffer->getBufferStart();
830 const char *end = Buffer->getBufferEnd();
831 const char *next = (cur != end) ? cur + 1 : end;
832
833 // Limit ourselves to only scanning 256 characters into the source
834 // file. This is mostly a check in case the file has no
835 // newlines whatsoever.
836 if (end - cur > 256)
837 end = cur + 256;
838
839 while (next < end) {
840 if (*cur == 0x0D) { // CR
841 if (*next == 0x0A) // CRLF
842 BinaryMode = false;
843
844 break;
845 } else if (*cur == 0x0A) // LF
846 break;
847
848 ++cur;
849 ++next;
850 }
851 }
852 }
853
854 std::unique_ptr<raw_ostream> OS =
855 CI.createDefaultOutputFile(Binary: BinaryMode, BaseInput: getCurrentFileOrBufferName());
856 if (!OS) return;
857
858 // If we're preprocessing a module map, start by dumping the contents of the
859 // module itself before switching to the input buffer.
860 auto &Input = getCurrentInput();
861 if (Input.getKind().getFormat() == InputKind::ModuleMap) {
862 if (Input.isFile()) {
863 (*OS) << "# 1 \"";
864 OS->write_escaped(Str: Input.getFile());
865 (*OS) << "\"\n";
866 }
867 getCurrentModule()->print(OS&: *OS);
868 (*OS) << "#pragma clang module contents\n";
869 }
870
871 DoPrintPreprocessedInput(PP&: CI.getPreprocessor(), OS: OS.get(),
872 Opts: CI.getPreprocessorOutputOpts());
873}
874
875void PrintPreambleAction::ExecuteAction() {
876 switch (getCurrentFileKind().getLanguage()) {
877 case Language::C:
878 case Language::CXX:
879 case Language::ObjC:
880 case Language::ObjCXX:
881 case Language::OpenCL:
882 case Language::OpenCLCXX:
883 case Language::CUDA:
884 case Language::HIP:
885 case Language::HLSL:
886 case Language::CIR:
887 break;
888
889 case Language::Unknown:
890 case Language::Asm:
891 case Language::LLVM_IR:
892 // We can't do anything with these.
893 return;
894 }
895
896 // We don't expect to find any #include directives in a preprocessed input.
897 if (getCurrentFileKind().isPreprocessed())
898 return;
899
900 CompilerInstance &CI = getCompilerInstance();
901 auto Buffer = CI.getFileManager().getBufferForFile(Filename: getCurrentFile());
902 if (Buffer) {
903 unsigned Preamble =
904 Lexer::ComputePreamble(Buffer: (*Buffer)->getBuffer(), LangOpts: CI.getLangOpts()).Size;
905 llvm::outs().write(Ptr: (*Buffer)->getBufferStart(), Size: Preamble);
906 }
907}
908
909static void writeCompilerOptionValues(raw_ostream &OS,
910 StringRef CompilerOptionNames,
911 ArrayRef<bool> CompilerOptionValues) {
912 bool FirstValue = true;
913 for (bool CompilerOptionValue : CompilerOptionValues) {
914 auto [CompilerOptionName, RemainingCompilerOptionNames] =
915 CompilerOptionNames.split(Separator: '\0');
916 CompilerOptionNames = RemainingCompilerOptionNames;
917 if (!FirstValue)
918 OS << ",\n";
919 FirstValue = false;
920 OS << "\t{\"" << CompilerOptionName
921 << "\" : " << (CompilerOptionValue ? "true" : "false") << "}";
922 }
923 assert(CompilerOptionNames.empty() && "compiler option name count mismatch");
924}
925
926void DumpCompilerOptionsAction::ExecuteAction() {
927 CompilerInstance &CI = getCompilerInstance();
928 std::unique_ptr<raw_ostream> OSP =
929 CI.createDefaultOutputFile(Binary: false, BaseInput: getCurrentFile());
930 if (!OSP)
931 return;
932
933 raw_ostream &OS = *OSP;
934 const Preprocessor &PP = CI.getPreprocessor();
935 const LangOptions &LangOpts = PP.getLangOpts();
936
937 // FIXME: Rather than manually format the JSON (which is awkward due to
938 // needing to remove trailing commas), this should make use of a JSON library.
939 // FIXME: Instead of printing enums as an integral value and specifying the
940 // type as a separate field, use introspection to print the enumerator.
941
942 OS << "{\n";
943 OS << "\n\"features\" : [\n";
944 {
945 static constexpr char FeatureNames[] = {
946#define FEATURE(Name, Predicate) #Name "\0"
947#include "clang/Basic/Features.def"
948 };
949 const bool FeatureValues[] = {
950#define FEATURE(Name, Predicate) static_cast<bool>(Predicate),
951#include "clang/Basic/Features.def"
952 };
953 writeCompilerOptionValues(
954 OS, CompilerOptionNames: StringRef(FeatureNames, sizeof(FeatureNames) - 1), CompilerOptionValues: FeatureValues);
955 }
956 OS << "\n],\n";
957
958 OS << "\n\"extensions\" : [\n";
959 {
960 static constexpr char ExtensionNames[] = {
961#define EXTENSION(Name, Predicate) #Name "\0"
962#include "clang/Basic/Features.def"
963 };
964 const bool ExtensionValues[] = {
965#define EXTENSION(Name, Predicate) static_cast<bool>(Predicate),
966#include "clang/Basic/Features.def"
967 };
968 writeCompilerOptionValues(
969 OS, CompilerOptionNames: StringRef(ExtensionNames, sizeof(ExtensionNames) - 1),
970 CompilerOptionValues: ExtensionValues);
971 }
972 OS << "\n]\n";
973
974 OS << "}";
975}
976
977void PrintDependencyDirectivesSourceMinimizerAction::ExecuteAction() {
978 CompilerInstance &CI = getCompilerInstance();
979 SourceManager &SM = CI.getPreprocessor().getSourceManager();
980 llvm::MemoryBufferRef FromFile = SM.getBufferOrFake(FID: SM.getMainFileID());
981
982 llvm::SmallVector<dependency_directives_scan::Token, 16> Tokens;
983 llvm::SmallVector<dependency_directives_scan::Directive, 32> Directives;
984 if (scanSourceForDependencyDirectives(
985 Input: FromFile.getBuffer(), Tokens, Directives, Diags: &CI.getDiagnostics(),
986 InputSourceLoc: SM.getLocForStartOfFile(FID: SM.getMainFileID()))) {
987 assert(CI.getDiagnostics().hasErrorOccurred() &&
988 "no errors reported for failure");
989
990 // Preprocess the source when verifying the diagnostics to capture the
991 // 'expected' comments.
992 if (CI.getDiagnosticOpts().VerifyDiagnostics) {
993 // Make sure we don't emit new diagnostics!
994 CI.getDiagnostics().setSuppressAllDiagnostics(true);
995 Preprocessor &PP = getCompilerInstance().getPreprocessor();
996 PP.EnterMainSourceFile();
997 Token Tok;
998 do {
999 PP.Lex(Result&: Tok);
1000 } while (Tok.isNot(K: tok::eof));
1001 }
1002 return;
1003 }
1004 printDependencyDirectivesAsSource(Source: FromFile.getBuffer(), Directives,
1005 OS&: llvm::outs());
1006}
1007
1008//===----------------------------------------------------------------------===//
1009// HLSL Specific Actions
1010//===----------------------------------------------------------------------===//
1011
1012class InjectRootSignatureCallback : public PPCallbacks {
1013private:
1014 Sema &Actions;
1015 StringRef RootSigName;
1016 llvm::dxbc::RootSignatureVersion Version;
1017
1018 std::optional<StringLiteral *> processStringLiteral(ArrayRef<Token> Tokens) {
1019 for (Token Tok : Tokens)
1020 if (!tok::isStringLiteral(K: Tok.getKind()))
1021 return std::nullopt;
1022
1023 ExprResult StringResult = Actions.ActOnUnevaluatedStringLiteral(StringToks: Tokens);
1024 if (StringResult.isInvalid())
1025 return std::nullopt;
1026
1027 if (auto Signature = dyn_cast<StringLiteral>(Val: StringResult.get()))
1028 return Signature;
1029
1030 return std::nullopt;
1031 }
1032
1033public:
1034 void MacroDefined(const Token &MacroNameTok,
1035 const MacroDirective *MD) override {
1036 if (RootSigName != MacroNameTok.getIdentifierInfo()->getName())
1037 return;
1038
1039 const MacroInfo *MI = MD->getMacroInfo();
1040 auto Signature = processStringLiteral(Tokens: MI->tokens());
1041 if (!Signature.has_value()) {
1042 Actions.getDiagnostics().Report(Loc: MI->getDefinitionLoc(),
1043 DiagID: diag::err_expected_string_literal)
1044 << /*in attributes...*/ 4 << "RootSignature";
1045 return;
1046 }
1047
1048 IdentifierInfo *DeclIdent =
1049 hlsl::ParseHLSLRootSignature(Actions, Version, Signature: *Signature);
1050 Actions.HLSL().SetRootSignatureOverride(DeclIdent);
1051 }
1052
1053 InjectRootSignatureCallback(Sema &Actions, StringRef RootSigName,
1054 llvm::dxbc::RootSignatureVersion Version)
1055 : PPCallbacks(), Actions(Actions), RootSigName(RootSigName),
1056 Version(Version) {}
1057};
1058
1059void HLSLFrontendAction::ExecuteAction() {
1060 // Pre-requisites to invoke
1061 CompilerInstance &CI = getCompilerInstance();
1062 if (!CI.hasASTContext() || !CI.hasPreprocessor())
1063 return WrapperFrontendAction::ExecuteAction();
1064
1065 // InjectRootSignatureCallback requires access to invoke Sema to lookup/
1066 // register a root signature declaration. The wrapped action is required to
1067 // account for this by only creating a Sema if one doesn't already exist
1068 // (like we have done, and, ASTFrontendAction::ExecuteAction)
1069 if (!CI.hasSema())
1070 CI.createSema(TUKind: getTranslationUnitKind(),
1071 /*CodeCompleteConsumer=*/CompletionConsumer: nullptr);
1072 Sema &S = CI.getSema();
1073
1074 auto &TargetInfo = CI.getASTContext().getTargetInfo();
1075 bool IsRootSignatureTarget =
1076 TargetInfo.getTriple().getEnvironment() == llvm::Triple::RootSignature;
1077 StringRef HLSLEntry = TargetInfo.getTargetOpts().HLSLEntry;
1078
1079 // Register HLSL specific callbacks
1080 auto LangOpts = CI.getLangOpts();
1081 StringRef RootSigName =
1082 IsRootSignatureTarget ? HLSLEntry : LangOpts.HLSLRootSigOverride;
1083
1084 auto MacroCallback = std::make_unique<InjectRootSignatureCallback>(
1085 args&: S, args&: RootSigName, args&: LangOpts.HLSLRootSigVer);
1086
1087 Preprocessor &PP = CI.getPreprocessor();
1088 PP.addPPCallbacks(C: std::move(MacroCallback));
1089
1090 // If we are targeting a root signature, invoke custom handling
1091 if (IsRootSignatureTarget)
1092 return hlsl::HandleRootSignatureTarget(S, EntryRootSig: HLSLEntry);
1093 else // otherwise, invoke as normal
1094 return WrapperFrontendAction::ExecuteAction();
1095}
1096
1097HLSLFrontendAction::HLSLFrontendAction(
1098 std::unique_ptr<FrontendAction> WrappedAction)
1099 : WrapperFrontendAction(std::move(WrappedAction)) {}
1100