diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -248,7 +248,7 @@ class TargetOpts : KeyPathAndMacro<"TargetOpts->", base> {} class FrontendOpts - : KeyPathAndMacro<"FrontendOpts.", base> {} + : KeyPathAndMacro<"FrontendOpts.", base, "FRONTEND_"> {} class PreprocessorOutputOpts : KeyPathAndMacro<"PreprocessorOutputOpts.", base> {} class DependencyOutputOpts diff --git a/clang/include/clang/Frontend/CommandLineSourceLoc.h b/clang/include/clang/Frontend/CommandLineSourceLoc.h --- a/clang/include/clang/Frontend/CommandLineSourceLoc.h +++ b/clang/include/clang/Frontend/CommandLineSourceLoc.h @@ -48,6 +48,13 @@ return PSL; } + + /// Serialize ParsedSourceLocation back to a string. + std::string ToString() const { + return (llvm::Twine(FileName == "" ? "-" : FileName) + ":" + + Twine(Line) + ":" + Twine(Column)) + .str(); + } }; /// A source range that has been parsed on the command line. diff --git a/clang/include/clang/Serialization/ModuleFileExtension.h b/clang/include/clang/Serialization/ModuleFileExtension.h --- a/clang/include/clang/Serialization/ModuleFileExtension.h +++ b/clang/include/clang/Serialization/ModuleFileExtension.h @@ -60,9 +60,20 @@ /// custom writer that can then be accessed via a custom reader when /// the module file or precompiled header is loaded. class ModuleFileExtension { +protected: + /// Discriminator for LLVM-style RTTI. + enum ModuleFileExtensionKind { + MFEK_Test, + }; + + const ModuleFileExtensionKind Kind; public: + ModuleFileExtension(ModuleFileExtensionKind Kind) : Kind(Kind) {} + virtual ~ModuleFileExtension(); + ModuleFileExtensionKind getKind() const { return Kind; } + /// Retrieves the metadata for this module file extension. virtual ModuleFileExtensionMetadata getExtensionMetadata() const = 0; diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -386,7 +386,7 @@ template static T extractMaskValue(T KeyPath) { - return KeyPath & Value; + return ((KeyPath & Value) == Value) ? Value : T(); } #define PARSE_OPTION_WITH_MARSHALLING(ARGS, DIAGS, SUCCESS, ID, FLAGS, PARAM, \ @@ -2217,9 +2217,188 @@ return None; } -static bool ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, - DiagnosticsEngine &Diags, bool &IsHeaderFile) { +/// Maps frontend action to command line option. +static Optional +getProgramActionOpt(frontend::ActionKind ProgramAction) { + for (const auto &ActionOpt : getFrontendActionTable()) + if (ActionOpt.first == ProgramAction) + return ActionOpt.second; + + return None; +} + +static void GenerateFrontendArgs(const FrontendOptions &Opts, + SmallVectorImpl &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 ProgramActionOpt = + getProgramActionOpt(Opts.ProgramAction); + + // Generating a simple flag covers most frontend actions. + std::function 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) + for (const auto &PluginArg : PluginArgs.second) + GenerateArg(Args, OPT_plugin_arg, PluginArgs.first + PluginArg, SA); + + for (const auto &Ext : Opts.ModuleFileExtensions) { + if (auto *TestExt = dyn_cast_or_null(Ext.get())) { + std::string Buffer; + llvm::raw_string_ostream OS(Buffer); + OS << *TestExt; + GenerateArg(Args, OPT_ftest_module_file_extension_EQ, OS.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::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 ParseFrontendArgsImpl(FrontendOptions &Opts, ArgList &Args, + DiagnosticsEngine &Diags, + bool &IsHeaderFile) { + FrontendOptions &FrontendOpts = Opts; + bool Success = true; unsigned NumErrorsBefore = Diags.getNumErrors(); +#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, Success, 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)) { @@ -2424,6 +2603,34 @@ return Diags.getNumErrors() == NumErrorsBefore; } +static bool ParseFrontendArgs(CompilerInvocation &Res, FrontendOptions &Opts, + ArgList &Args, DiagnosticsEngine &Diags, + bool &IsHeaderFile) { + FrontendOptions DummyOpts; + + return RoundTrip( + [&IsHeaderFile](CompilerInvocation &Res, ArgList &Args, + DiagnosticsEngine &Diags) { + // ParseFrontendArgsImpl handles frontend action without querying the + // options. Let's do it now so RoundTrip considers us responsible for + // generating it. + for (const auto &Pair : getFrontendActionTable()) + Args.hasArg(Pair.second); + + return ParseFrontendArgsImpl(Res.getFrontendOpts(), Args, Diags, + IsHeaderFile); + }, + [&IsHeaderFile](CompilerInvocation &Res, + SmallVectorImpl &Args, + CompilerInvocation::StringAllocator SA) { + GenerateFrontendArgs(Res.getFrontendOpts(), Args, SA, IsHeaderFile); + }, + [&DummyOpts](CompilerInvocation &Res) { + std::swap(Res.getFrontendOpts(), DummyOpts); + }, + Res, Args, Diags, "FrontendOptions"); +} + std::string CompilerInvocation::GetResourcesPath(const char *Argv0, void *MainAddr) { std::string ClangExecutable = @@ -3955,7 +4162,7 @@ } Success &= ParseDiagnosticArgs(Res.getDiagnosticOpts(), Args, &Diags, /*DefaultDiagColor=*/false); - Success &= ParseFrontendArgs(Res.getFrontendOpts(), Args, Diags, + Success &= ParseFrontendArgs(Res, Res.getFrontendOpts(), Args, Diags, LangOpts.IsHeaderFile); // FIXME: We shouldn't have to pass the DashX option around here InputKind DashX = Res.getFrontendOpts().DashX; @@ -4177,6 +4384,7 @@ GeneratePreprocessorArgs(*PreprocessorOpts, Args, SA, *LangOpts, FrontendOpts, CodeGenOpts); GenerateAnalyzerArgs(*AnalyzerOpts, Args, SA); + GenerateFrontendArgs(FrontendOpts, Args, SA, LangOpts->IsHeaderFile); GenerateHeaderSearchArgs(*HeaderSearchOpts, Args, SA); GenerateLangArgs(*LangOpts, Args, SA, T); GenerateCodeGenArgs(CodeGenOpts, Args, SA, T, FrontendOpts.OutputFile, diff --git a/clang/lib/Frontend/TestModuleFileExtension.h b/clang/lib/Frontend/TestModuleFileExtension.h --- a/clang/lib/Frontend/TestModuleFileExtension.h +++ b/clang/lib/Frontend/TestModuleFileExtension.h @@ -48,7 +48,8 @@ unsigned MinorVersion, bool Hashed, StringRef UserInfo) - : BlockName(BlockName), + : ModuleFileExtension(ModuleFileExtensionKind::MFEK_Test), + BlockName(BlockName), MajorVersion(MajorVersion), MinorVersion(MinorVersion), Hashed(Hashed), UserInfo(UserInfo) { } ~TestModuleFileExtension() override; @@ -64,6 +65,14 @@ createExtensionReader(const ModuleFileExtensionMetadata &Metadata, ASTReader &Reader, serialization::ModuleFile &Mod, const llvm::BitstreamCursor &Stream) override; + + static bool classof(const ModuleFileExtension *E) { + return E->getKind() == MFEK_Test; + } + + /// Serialize the extension. + friend llvm::raw_ostream & + operator<<(llvm::raw_ostream &OS, const TestModuleFileExtension &Extension); }; } // end namespace clang diff --git a/clang/lib/Frontend/TestModuleFileExtension.cpp b/clang/lib/Frontend/TestModuleFileExtension.cpp --- a/clang/lib/Frontend/TestModuleFileExtension.cpp +++ b/clang/lib/Frontend/TestModuleFileExtension.cpp @@ -127,3 +127,10 @@ return std::unique_ptr( new TestModuleFileExtension::Reader(this, Stream)); } + +llvm::raw_ostream &clang::operator<<(llvm::raw_ostream &OS, + const TestModuleFileExtension &Extension) { + return OS << Extension.BlockName << ":" << Extension.MajorVersion << ":" + << Extension.MinorVersion << ":" << Extension.Hashed << ":" + << Extension.UserInfo; +}