jansvoboda11 created this revision. jansvoboda11 added reviewers: dexonsmith, Bigcheese. Herald added a subscriber: hiraditya. jansvoboda11 requested review of this revision. Herald added projects: clang, LLVM. Herald added subscribers: llvm-commits, cfe-commits.
This patch tests the manual header search argument generation by performing a "parse, generate, parse" round-trip on each compiler invocation. This way, the manually written C++ code gets exercised by the suite of Clang's end-to-end tests. Moreover, this ensures people adding new command line options are force the implement the generation as well, either via TableGen marshalling infrastructure or manually. It makes sense to enable this only for assert builds, so that we don't perform useless work in release builds. Depending on our roll-out strategy, we might create more patches like this for other groups of options and upstream the work one-by-one, or put all option groups into this patch and enable round-tripping for all of -cc1 in a single commit. Please note this is a work-in-progress. Ideally, we'll be able to get rid of the duplicate methods (parseSimpleArgs, generateCC1CommandLine) and the HeaderSearchOptSpecs array. Depends on D94472 <https://reviews.llvm.org/D94472>. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D94474 Files: clang/include/clang/Frontend/CompilerInvocation.h clang/lib/Frontend/CompilerInvocation.cpp llvm/include/llvm/Option/ArgList.h llvm/lib/Option/ArgList.cpp
Index: llvm/lib/Option/ArgList.cpp =================================================================== --- llvm/lib/Option/ArgList.cpp +++ llvm/lib/Option/ArgList.cpp @@ -95,6 +95,23 @@ return std::vector<std::string>(Values.begin(), Values.end()); } +void ArgList::AddAllArgsExcept(ArgStringList &Output, + ArrayRef<OptSpecifier> ExcludeIds) const { + for (const Arg *Arg : *this) { + bool Excluded = false; + for (OptSpecifier Id : ExcludeIds) { + if (Arg->getOption().matches(Id)) { + Excluded = true; + break; + } + } + if (!Excluded) { + Arg->claim(); + Arg->render(*this, Output); + } + } +} + void ArgList::AddAllArgsExcept(ArgStringList &Output, ArrayRef<OptSpecifier> Ids, ArrayRef<OptSpecifier> ExcludeIds) const { Index: llvm/include/llvm/Option/ArgList.h =================================================================== --- llvm/include/llvm/Option/ArgList.h +++ llvm/include/llvm/Option/ArgList.h @@ -308,6 +308,10 @@ A->render(*this, Output); } + /// AddAllArgsExcept - Render all arguments not matching any of the excluded + /// ids. + void AddAllArgsExcept(ArgStringList &Output, + ArrayRef<OptSpecifier> ExcludeIds = {}) const; /// AddAllArgsExcept - Render all arguments matching any of the given ids /// and not matching any of the excluded ids. void AddAllArgsExcept(ArgStringList &Output, ArrayRef<OptSpecifier> Ids, Index: clang/lib/Frontend/CompilerInvocation.cpp =================================================================== --- clang/lib/Frontend/CompilerInvocation.cpp +++ clang/lib/Frontend/CompilerInvocation.cpp @@ -1439,6 +1439,33 @@ return Success; } +bool CompilerInvocation::parseSimpleArgs( + const ArgList &Args, DiagnosticsEngine &Diags, + ArrayRef<llvm::opt::OptSpecifier> Included) { + bool Success = true; + +#define 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) \ + if ((FLAGS)&options::CC1Option && \ + find(Included, OPT_##ID) != Included.end()) { \ + this->KEYPATH = MERGER(this->KEYPATH, DEFAULT_VALUE); \ + if (IMPLIED_CHECK) \ + this->KEYPATH = MERGER(this->KEYPATH, IMPLIED_VALUE); \ + if (SHOULD_PARSE) \ + if (auto MaybeValue = \ + NORMALIZER(OPT_##ID, TABLE_INDEX, Args, Diags, Success)) \ + this->KEYPATH = MERGER( \ + this->KEYPATH, static_cast<decltype(this->KEYPATH)>(*MaybeValue)); \ + } +#include "clang/Driver/Options.inc" +#undef OPTION_WITH_MARSHALLING + + return Success; +} + bool clang::ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args, DiagnosticsEngine *Diags, bool DefaultDiagColor) { @@ -3099,8 +3126,92 @@ ParseTargetArgs(Res.getTargetOpts(), Args, Diags); Success &= ParseCodeGenArgs(Res.getCodeGenOpts(), Args, DashX, Diags, Res.getTargetOpts(), Res.getFrontendOpts()); + + // Specify all header search options. + OptSpecifier HeaderSearchOptSpecs[] = { + // Marshalled via TableGen. + OPT_fmodules_user_build_path, + OPT_fprebuilt_implicit_modules, + OPT_fno_prebuilt_implicit_modules, + OPT_fmodules_prune_interval, + OPT_fmodules_prune_after, + OPT_fbuild_session_timestamp, + OPT_fmodules_validate_once_per_build_session, + OPT_fmodules_disable_diagnostic_validation, + OPT_fmodules_validate_system_headers, + OPT_fno_modules_validate_system_headers, + OPT_fvalidate_ast_input_files_content, + OPT_fimplicit_module_maps, + OPT_isysroot, + OPT_nobuiltininc, + OPT_nostdincxx, + OPT_resource_dir, + OPT_v, + OPT_fmodule_map_file_home_is_cwd, + OPT_fmodule_format_EQ, + OPT_nostdsysteminc, + OPT_fdisable_module_hash, + OPT_fmodules_hash_content, + OPT_fmodules_strict_context_hash, + + // Marshalled manually. + OPT_stdlib_EQ, + OPT_fmodules_cache_path, + OPT_fmodule_file, + OPT_fprebuilt_module_path, + OPT_fmodules_ignore_macro, + OPT__sysroot_EQ, + OPT_I, + OPT_F, + OPT_index_header_map, + OPT_iprefix, + OPT_iwithprefix, + OPT_iwithprefixbefore, + OPT_idirafter, + OPT_iquote, + OPT_isystem, + OPT_iwithsysroot, + OPT_iframework, + OPT_iframeworkwithsysroot, + OPT_c_isystem, + OPT_cxx_isystem, + OPT_objc_isystem, + OPT_objcxx_isystem, + OPT_internal_isystem, + OPT_internal_externc_isystem, + OPT_system_header_prefix, + OPT_no_system_header_prefix, + OPT_ivfsoverlay, + }; + + // Prepare string allocator. + SmallVector<std::string, 32> GeneratedArgsStorage; + auto SA = [&GeneratedArgsStorage](const Twine &Arg) -> const char * { + return GeneratedArgsStorage.emplace_back(Arg.str()).c_str(); + }; + + // Parse header search options from original arguments. ParseHeaderSearchArgs(Res.getHeaderSearchOpts(), Args, Res.getFileSystemOpts().WorkingDir); + + // Create arg string without header search options. + ArgStringList MarshalledArgStr; + Args.AddAllArgsExcept(MarshalledArgStr, HeaderSearchOptSpecs); + // Fill arg string with header search options. + Res.generateCC1CommandLine(MarshalledArgStr, SA, HeaderSearchOptSpecs); + GenerateHeaderSearchArgs(Res.getHeaderSearchOpts(), MarshalledArgStr, SA); + + InputArgList MarshalledArgs = Opts.ParseArgs( + MarshalledArgStr, MissingArgIndex, MissingArgCount, IncludedFlagsBitmask); + + // Empty the header search options parsed from original arguments. + auto OriginalHeaderSearchOpts = std::make_shared<HeaderSearchOptions>(); + Res.HeaderSearchOpts.swap(OriginalHeaderSearchOpts); + // Parse header search options from generated arguments. + Success &= Res.parseSimpleArgs(MarshalledArgs, Diags, HeaderSearchOptSpecs); + ParseHeaderSearchArgs(Res.getHeaderSearchOpts(), MarshalledArgs, + Res.getFileSystemOpts().WorkingDir); + llvm::Triple T(Res.getTargetOpts().Triple); if (DashX.getFormat() == InputKind::Precompiled || DashX.getLanguage() == Language::LLVM_IR) { @@ -3335,6 +3446,32 @@ GenerateHeaderSearchArgs(getHeaderSearchOpts(), Args, SA); } +void CompilerInvocation::generateCC1CommandLine( + SmallVectorImpl<const char *> &Args, StringAllocator SA, + ArrayRef<OptSpecifier> Included) const { + // Capture the extracted value as a lambda argument to avoid potential issues + // with lifetime extension of the reference. +#define 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) \ + if ((FLAGS)&options::CC1Option && \ + find(Included, OPT_##ID) != Included.end()) { \ + [&](const auto &Extracted) { \ + if (ALWAYS_EMIT || \ + (Extracted != \ + static_cast<decltype(this->KEYPATH)>( \ + (IMPLIED_CHECK) ? (IMPLIED_VALUE) : (DEFAULT_VALUE)))) \ + DENORMALIZER(Args, SPELLING, SA, Option::KIND##Class, TABLE_INDEX, \ + Extracted); \ + }(EXTRACTOR(this->KEYPATH)); \ + } + +#include "clang/Driver/Options.inc" +#undef OPTION_WITH_MARSHALLING +} + IntrusiveRefCntPtr<llvm::vfs::FileSystem> clang::createVFSFromCompilerInvocation(const CompilerInvocation &CI, DiagnosticsEngine &Diags) { Index: clang/include/clang/Frontend/CompilerInvocation.h =================================================================== --- clang/include/clang/Frontend/CompilerInvocation.h +++ clang/include/clang/Frontend/CompilerInvocation.h @@ -22,6 +22,7 @@ #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/Option/OptSpecifier.h" #include <memory> #include <string> @@ -197,6 +198,9 @@ /// The returned pointer is what gets appended to Args. void generateCC1CommandLine(llvm::SmallVectorImpl<const char *> &Args, StringAllocator SA) const; + void generateCC1CommandLine(llvm::SmallVectorImpl<const char *> &Args, + StringAllocator SA, + ArrayRef<llvm::opt::OptSpecifier> Included) const; /// @} /// @name Option Subgroups @@ -246,6 +250,8 @@ /// \returns - True if parsing was successful, false otherwise bool parseSimpleArgs(const llvm::opt::ArgList &Args, DiagnosticsEngine &Diags); + bool parseSimpleArgs(const llvm::opt::ArgList &Args, DiagnosticsEngine &Diags, + ArrayRef<llvm::opt::OptSpecifier> Included); }; IntrusiveRefCntPtr<llvm::vfs::FileSystem>
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits