mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-03-04 16:41:43 +00:00
[clang][cli] Command line round-trip for HeaderSearch options
This patch implements generation of remaining header search arguments. It's done manually in C++ as opposed to TableGen, because we need the flexibility and don't anticipate reuse. This patch also tests the generation of header search options via a round-trip. This way, the code gets exercised whenever Clang is built and tested in asserts mode. All `check-clang` tests pass. Reviewed By: dexonsmith Differential Revision: https://reviews.llvm.org/D94472
This commit is contained in:
parent
e3f02302e3
commit
225ccf0c50
@ -465,6 +465,8 @@ option(CLANG_ENABLE_STATIC_ANALYZER
|
||||
|
||||
option(CLANG_ENABLE_PROTO_FUZZER "Build Clang protobuf fuzzer." OFF)
|
||||
|
||||
option(CLANG_ROUND_TRIP_CC1_ARGS "Round-trip command line arguments in -cc1." OFF)
|
||||
|
||||
if(NOT CLANG_ENABLE_STATIC_ANALYZER AND CLANG_ENABLE_ARCMT)
|
||||
message(FATAL_ERROR "Cannot disable static analyzer while enabling ARCMT or Z3")
|
||||
endif()
|
||||
@ -473,6 +475,10 @@ if(CLANG_ENABLE_ARCMT)
|
||||
set(CLANG_ENABLE_OBJC_REWRITER ON)
|
||||
endif()
|
||||
|
||||
if (CLANG_ROUND_TRIP_CC1_ARGS)
|
||||
add_definitions(-DCLANG_ROUND_TRIP_CC1_ARGS=ON)
|
||||
endif()
|
||||
|
||||
# Clang version information
|
||||
set(CLANG_EXECUTABLE_VERSION
|
||||
"${CLANG_VERSION_MAJOR}" CACHE STRING
|
||||
|
@ -536,4 +536,11 @@ def err_drv_invalid_object_mode : Error<"OBJECT_MODE setting %0 is not recognize
|
||||
|
||||
def err_aix_default_altivec_abi : Error<
|
||||
"The default Altivec ABI on AIX is not yet supported, use '-mabi=vec-extabi' for the extended Altivec ABI">;
|
||||
|
||||
def note_cc1_round_trip_original : Note<"Original arguments in %0 round-trip: %1">;
|
||||
def note_cc1_round_trip_generated : Note<"Generated arguments #%1 in %0 round-trip: %2">;
|
||||
def remark_cc1_round_trip_generated : Remark<"Generated arguments #%1 in %0 round-trip: %2">, InGroup<RoundTripCC1Args>;
|
||||
def err_cc1_round_trip_fail_then_ok : Error<"Original arguments parse failed, then succeeded in %0 round-trip">;
|
||||
def err_cc1_round_trip_ok_then_fail : Error<"Generated arguments parse failed in %0 round-trip">;
|
||||
def err_cc1_round_trip_mismatch : Error<"Generated arguments do not match in %0 round-trip">;
|
||||
}
|
||||
|
@ -437,6 +437,7 @@ def ModuleBuild : DiagGroup<"module-build">;
|
||||
def ModuleImport : DiagGroup<"module-import">;
|
||||
def ModuleConflict : DiagGroup<"module-conflict">;
|
||||
def ModuleFileExtension : DiagGroup<"module-file-extension">;
|
||||
def RoundTripCC1Args : DiagGroup<"round-trip-cc1-args">;
|
||||
def NewlineEOF : DiagGroup<"newline-eof">;
|
||||
def Nullability : DiagGroup<"nullability">;
|
||||
def NullabilityDeclSpec : DiagGroup<"nullability-declspec">;
|
||||
|
@ -520,6 +520,11 @@ def gen_reproducer: Flag<["-"], "gen-reproducer">, InternalDebugOpt,
|
||||
def gen_cdb_fragment_path: Separate<["-"], "gen-cdb-fragment-path">, InternalDebugOpt,
|
||||
HelpText<"Emit a compilation database fragment to the specified directory">;
|
||||
|
||||
def round_trip_args : Flag<["-"], "round-trip-args">, Flags<[CC1Option, NoDriverOption]>,
|
||||
HelpText<"Enable command line arguments round-trip.">;
|
||||
def no_round_trip_args : Flag<["-"], "no-round-trip-args">, Flags<[CC1Option, NoDriverOption]>,
|
||||
HelpText<"Disable command line arguments round-trip.">;
|
||||
|
||||
def _migrate : Flag<["--"], "migrate">, Flags<[NoXarchOption]>,
|
||||
HelpText<"Run the migrator">;
|
||||
def ccc_objcmt_migrate : Separate<["-"], "ccc-objcmt-migrate">,
|
||||
|
@ -260,6 +260,18 @@ private:
|
||||
const llvm::Triple &T,
|
||||
const std::string &OutputFile,
|
||||
const LangOptions &LangOptsRef);
|
||||
|
||||
/// Parse command line options that map to HeaderSearchOptions.
|
||||
static void ParseHeaderSearchArgs(CompilerInvocation &Res,
|
||||
HeaderSearchOptions &Opts,
|
||||
llvm::opt::ArgList &Args,
|
||||
DiagnosticsEngine &Diags,
|
||||
const std::string &WorkingDir);
|
||||
|
||||
/// Generate command line options from HeaderSearchOptions.
|
||||
static void GenerateHeaderSearchArgs(HeaderSearchOptions &Opts,
|
||||
SmallVectorImpl<const char *> &Args,
|
||||
CompilerInvocation::StringAllocator SA);
|
||||
};
|
||||
|
||||
IntrusiveRefCntPtr<llvm::vfs::FileSystem>
|
||||
|
@ -38,6 +38,7 @@
|
||||
#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"
|
||||
@ -48,6 +49,7 @@
|
||||
#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"
|
||||
@ -204,7 +206,7 @@ static void denormalizeStringImpl(SmallVectorImpl<const char *> &Args,
|
||||
const char *Spelling,
|
||||
CompilerInvocation::StringAllocator SA,
|
||||
Option::OptionClass OptClass, unsigned,
|
||||
Twine Value) {
|
||||
const Twine &Value) {
|
||||
switch (OptClass) {
|
||||
case Option::SeparateClass:
|
||||
case Option::JoinedOrSeparateClass:
|
||||
@ -548,9 +550,174 @@ static unsigned getOptimizationLevelSize(ArgList &Args) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static std::string GetOptName(llvm::opt::OptSpecifier OptSpecifier) {
|
||||
static const OptTable &OptTable = getDriverOptTable();
|
||||
return OptTable.getOption(OptSpecifier).getPrefixedName();
|
||||
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 subset of command line arguments into a member of CompilerInvocation.
|
||||
using ParseFn = llvm::function_ref<bool(CompilerInvocation &, ArgList &,
|
||||
DiagnosticsEngine &)>;
|
||||
|
||||
// Generate part of command line arguments from a member of CompilerInvocation.
|
||||
using GenerateFn = llvm::function_ref<void(
|
||||
CompilerInvocation &, SmallVectorImpl<const char *> &,
|
||||
CompilerInvocation::StringAllocator)>;
|
||||
|
||||
// Swap between dummy/real instance of a CompilerInvocation member.
|
||||
using SwapOptsFn = llvm::function_ref<void(CompilerInvocation &)>;
|
||||
|
||||
// Performs round-trip of command line arguments if OriginalArgs contain
|
||||
// "-round-trip-args". Effectively runs the Parse function for a part of
|
||||
// CompilerInvocation on command line arguments that were already once parsed
|
||||
// and generated. This is used to check the Generate function produces arguments
|
||||
// that are semantically equivalent to those that were used to create
|
||||
// CompilerInvocation.
|
||||
static bool RoundTrip(ParseFn Parse, GenerateFn Generate, SwapOptsFn SwapOpts,
|
||||
CompilerInvocation &Res, ArgList &OriginalArgs,
|
||||
DiagnosticsEngine &Diags, StringRef OptsName) {
|
||||
// FIXME: Switch to '#ifndef NDEBUG' when possible.
|
||||
#ifdef CLANG_ROUND_TRIP_CC1_ARGS
|
||||
bool DoRoundTripDefault = true;
|
||||
#else
|
||||
bool DoRoundTripDefault = false;
|
||||
#endif
|
||||
|
||||
bool DoRoundTrip = OriginalArgs.hasFlag(
|
||||
OPT_round_trip_args, OPT_no_round_trip_args, DoRoundTripDefault);
|
||||
|
||||
// If round-trip was not requested, simply run the parser with the original
|
||||
// options and diagnostics.
|
||||
if (!DoRoundTrip)
|
||||
return Parse(Res, OriginalArgs, Diags);
|
||||
|
||||
// Serializes quoted (and potentially escaped) arguments.
|
||||
auto SerializeArgs = [](ArgStringList &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;
|
||||
};
|
||||
|
||||
OriginalArgs.clearQueriedOpts();
|
||||
|
||||
// Setup a dummy DiagnosticsEngine.
|
||||
DiagnosticsEngine DummyDiags(new DiagnosticIDs(), new DiagnosticOptions());
|
||||
DummyDiags.setClient(new TextDiagnosticBuffer());
|
||||
|
||||
// Run the first parse on the original arguments with dummy options and
|
||||
// diagnostics.
|
||||
SwapOpts(Res);
|
||||
if (!Parse(Res, OriginalArgs, DummyDiags)) {
|
||||
// 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 original
|
||||
// options and diagnostics, so all side-effects of parsing are visible.
|
||||
SwapOpts(Res);
|
||||
if (!Parse(Res, OriginalArgs, Diags))
|
||||
return false;
|
||||
|
||||
// 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) << OptsName;
|
||||
ArgStringList OriginalStrings;
|
||||
OriginalArgs.AddAllArgsExcept(OriginalStrings, {});
|
||||
Diags.Report(diag::note_cc1_round_trip_original)
|
||||
<< OptsName << SerializeArgs(OriginalStrings);
|
||||
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. First simply copy any arguments the parser did not
|
||||
// query. Then, use the Generate function that uses the CompilerInvocation
|
||||
// options instance as the source of truth. If Generate is the inverse of
|
||||
// Parse, the newly generated arguments must have the same semantics as the
|
||||
// original.
|
||||
ArgStringList GeneratedStrings1;
|
||||
OriginalArgs.AddAllArgsExcept(GeneratedStrings1,
|
||||
OriginalArgs.getQueriedOpts());
|
||||
Generate(Res, GeneratedStrings1, SA);
|
||||
|
||||
// Process the generated arguments.
|
||||
unsigned MissingArgIndex1, MissingArgCount1;
|
||||
InputArgList GeneratedArgs1 =
|
||||
getDriverOptTable().ParseArgs(GeneratedStrings1, MissingArgIndex1,
|
||||
MissingArgCount1, options::CC1Option);
|
||||
|
||||
// TODO: Once we're responsible for generating all arguments, check that we
|
||||
// didn't create any unknown options or omitted required values.
|
||||
|
||||
// Run the second parse, now on the generated arguments, and with the original
|
||||
// options 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.
|
||||
SwapOpts(Res);
|
||||
bool Success2 = Parse(Res, GeneratedArgs1, Diags);
|
||||
|
||||
// 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) << OptsName;
|
||||
Diags.Report(diag::note_cc1_round_trip_generated)
|
||||
<< OptsName << 1 << SerializeArgs(GeneratedStrings1);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Generate arguments again, this time from the options we will end up using
|
||||
// for the rest of the compilation.
|
||||
ArgStringList GeneratedStrings2;
|
||||
GeneratedArgs1.AddAllArgsExcept(GeneratedStrings2,
|
||||
GeneratedArgs1.getQueriedOpts());
|
||||
Generate(Res, GeneratedStrings2, SA);
|
||||
|
||||
// Compares two lists of generated arguments.
|
||||
auto Equal = [](const ArgStringList &A, const ArgStringList &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(GeneratedStrings1, GeneratedStrings2)) {
|
||||
Diags.Report(diag::err_cc1_round_trip_mismatch) << OptsName;
|
||||
Diags.Report(diag::note_cc1_round_trip_generated)
|
||||
<< OptsName << 1 << SerializeArgs(GeneratedStrings1);
|
||||
Diags.Report(diag::note_cc1_round_trip_generated)
|
||||
<< OptsName << 2 << SerializeArgs(GeneratedStrings2);
|
||||
return false;
|
||||
}
|
||||
|
||||
Diags.Report(diag::remark_cc1_round_trip_generated)
|
||||
<< OptsName << 1 << SerializeArgs(GeneratedStrings1);
|
||||
Diags.Report(diag::remark_cc1_round_trip_generated)
|
||||
<< OptsName << 2 << SerializeArgs(GeneratedStrings2);
|
||||
|
||||
return Success2;
|
||||
}
|
||||
|
||||
static void addDiagnosticArgs(ArgList &Args, OptSpecifier Group,
|
||||
@ -1837,9 +2004,9 @@ std::string CompilerInvocation::GetResourcesPath(const char *Argv0,
|
||||
return Driver::GetResourcesPath(ClangExecutable, CLANG_RESOURCE_DIR);
|
||||
}
|
||||
|
||||
static void GenerateHeaderSearchArgs(const HeaderSearchOptions &Opts,
|
||||
SmallVectorImpl<const char *> &Args,
|
||||
CompilerInvocation::StringAllocator SA) {
|
||||
void CompilerInvocation::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, \
|
||||
@ -1851,9 +2018,119 @@ static void GenerateHeaderSearchArgs(const HeaderSearchOptions &Opts,
|
||||
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::find(Groups, Entry.Group) != Groups.end() &&
|
||||
(!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 void ParseHeaderSearchArgs(HeaderSearchOptions &Opts, ArgList &Args,
|
||||
static bool ParseHeaderSearchArgs(HeaderSearchOptions &Opts, ArgList &Args,
|
||||
DiagnosticsEngine &Diags,
|
||||
const std::string &WorkingDir) {
|
||||
HeaderSearchOptions *HeaderSearchOpts = &Opts;
|
||||
@ -1984,6 +2261,31 @@ static void ParseHeaderSearchArgs(HeaderSearchOptions &Opts, ArgList &Args,
|
||||
|
||||
for (const auto *A : Args.filtered(OPT_ivfsoverlay))
|
||||
Opts.AddVFSOverlayFile(A->getValue());
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
void CompilerInvocation::ParseHeaderSearchArgs(CompilerInvocation &Res,
|
||||
HeaderSearchOptions &Opts,
|
||||
ArgList &Args,
|
||||
DiagnosticsEngine &Diags,
|
||||
const std::string &WorkingDir) {
|
||||
auto DummyOpts = std::make_shared<HeaderSearchOptions>();
|
||||
|
||||
RoundTrip(
|
||||
[&WorkingDir](CompilerInvocation &Res, ArgList &Args,
|
||||
DiagnosticsEngine &Diags) {
|
||||
return ::ParseHeaderSearchArgs(Res.getHeaderSearchOpts(), Args, Diags,
|
||||
WorkingDir);
|
||||
},
|
||||
[](CompilerInvocation &Res, SmallVectorImpl<const char *> &GeneratedArgs,
|
||||
CompilerInvocation::StringAllocator SA) {
|
||||
GenerateHeaderSearchArgs(Res.getHeaderSearchOpts(), GeneratedArgs, SA);
|
||||
},
|
||||
[&DummyOpts](CompilerInvocation &Res) {
|
||||
Res.HeaderSearchOpts.swap(DummyOpts);
|
||||
},
|
||||
Res, Args, Diags, "HeaderSearchOptions");
|
||||
}
|
||||
|
||||
void CompilerInvocation::setLangDefaults(LangOptions &Opts, InputKind IK,
|
||||
@ -2203,9 +2505,9 @@ static void GenerateLangArgs(const LangOptions &Opts,
|
||||
SmallVectorImpl<const char *> &Args,
|
||||
CompilerInvocation::StringAllocator SA) {
|
||||
if (Opts.IncludeDefaultHeader)
|
||||
Args.push_back(SA(GetOptName(OPT_finclude_default_header)));
|
||||
GenerateArg(Args, OPT_finclude_default_header, SA);
|
||||
if (Opts.DeclareOpenCLBuiltins)
|
||||
Args.push_back(SA(GetOptName(OPT_fdeclare_opencl_builtins)));
|
||||
GenerateArg(Args, OPT_fdeclare_opencl_builtins, SA);
|
||||
}
|
||||
|
||||
void CompilerInvocation::ParseLangArgs(LangOptions &Opts, ArgList &Args,
|
||||
@ -2835,7 +3137,7 @@ bool CompilerInvocation::CreateFromArgs(CompilerInvocation &Res,
|
||||
LangOpts.IsHeaderFile);
|
||||
ParseTargetArgs(Res.getTargetOpts(), Args, Diags);
|
||||
llvm::Triple T(Res.getTargetOpts().Triple);
|
||||
ParseHeaderSearchArgs(Res.getHeaderSearchOpts(), Args, Diags,
|
||||
ParseHeaderSearchArgs(Res, Res.getHeaderSearchOpts(), Args, Diags,
|
||||
Res.getFileSystemOpts().WorkingDir);
|
||||
if (DashX.getFormat() == InputKind::Precompiled ||
|
||||
DashX.getLanguage() == Language::LLVM_IR) {
|
||||
|
7
clang/test/Frontend/round-trip-cc1-args.c
Normal file
7
clang/test/Frontend/round-trip-cc1-args.c
Normal file
@ -0,0 +1,7 @@
|
||||
// RUN: %clang_cc1 %s -no-round-trip-args -Rround-trip-cc1-args 2>&1 | FileCheck %s -check-prefix=CHECK-WITHOUT-ROUND-TRIP -allow-empty
|
||||
// RUN: %clang_cc1 %s -round-trip-args 2>&1 | FileCheck %s -check-prefix=CHECK-ROUND-TRIP-WITHOUT-REMARKS -allow-empty
|
||||
// RUN: %clang_cc1 %s -round-trip-args -Rround-trip-cc1-args 2>&1 | FileCheck %s -check-prefix=CHECK-ROUND-TRIP-WITH-REMARKS
|
||||
|
||||
// CHECK-WITHOUT-ROUND-TRIP-NOT: remark:
|
||||
// CHECK-ROUND-TRIP-WITHOUT-REMARKS-NOT: remark:
|
||||
// CHECK-ROUND-TRIP-WITH-REMARKS: remark: Generated arguments {{.*}} in {{.*}} round-trip: {{.*}}
|
@ -203,6 +203,12 @@ int cc1_main(ArrayRef<const char *> Argv, const char *Argv0, void *MainAddr) {
|
||||
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions();
|
||||
TextDiagnosticBuffer *DiagsBuffer = new TextDiagnosticBuffer;
|
||||
DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagsBuffer);
|
||||
|
||||
// Setup round-trip remarks for the DiagnosticsEngine used in CreateFromArgs.
|
||||
if (find(Argv, StringRef("-Rround-trip-cc1-args")) != Argv.end())
|
||||
Diags.setSeverity(diag::remark_cc1_round_trip_generated,
|
||||
diag::Severity::Remark, {});
|
||||
|
||||
bool Success = CompilerInvocation::CreateFromArgs(Clang->getInvocation(),
|
||||
Argv, Diags, Argv0);
|
||||
|
||||
|
@ -137,6 +137,16 @@ private:
|
||||
/// The first and last index of each different OptSpecifier ID.
|
||||
DenseMap<unsigned, OptRange> OptRanges;
|
||||
|
||||
/// The OptSpecifiers that were queried from this argument list.
|
||||
mutable DenseSet<unsigned> QueriedOpts;
|
||||
|
||||
/// Record the queried OptSpecifiers.
|
||||
template <typename... OptSpecifiers>
|
||||
void recordQueriedOpts(OptSpecifiers... Ids) const {
|
||||
SmallVector<unsigned, 4> OptsSpecifiers({toOptSpecifier(Ids).getID()...});
|
||||
QueriedOpts.insert(OptsSpecifiers.begin(), OptsSpecifiers.end());
|
||||
}
|
||||
|
||||
/// Get the range of indexes in which options with the specified IDs might
|
||||
/// reside, or (0, 0) if there are no such options.
|
||||
OptRange getRange(std::initializer_list<OptSpecifier> Ids) const;
|
||||
@ -203,6 +213,7 @@ public:
|
||||
template<typename ...OptSpecifiers>
|
||||
iterator_range<filtered_iterator<sizeof...(OptSpecifiers)>>
|
||||
filtered(OptSpecifiers ...Ids) const {
|
||||
recordQueriedOpts(Ids...);
|
||||
OptRange Range = getRange({toOptSpecifier(Ids)...});
|
||||
auto B = Args.begin() + Range.first;
|
||||
auto E = Args.begin() + Range.second;
|
||||
@ -214,6 +225,7 @@ public:
|
||||
template<typename ...OptSpecifiers>
|
||||
iterator_range<filtered_reverse_iterator<sizeof...(OptSpecifiers)>>
|
||||
filtered_reverse(OptSpecifiers ...Ids) const {
|
||||
recordQueriedOpts(Ids...);
|
||||
OptRange Range = getRange({toOptSpecifier(Ids)...});
|
||||
auto B = Args.rend() - Range.second;
|
||||
auto E = Args.rend() - Range.first;
|
||||
@ -308,6 +320,10 @@ public:
|
||||
A->render(*this, Output);
|
||||
}
|
||||
|
||||
/// AddAllArgsExcept - Render all arguments not matching any of the excluded
|
||||
/// ids.
|
||||
void AddAllArgsExcept(ArgStringList &Output,
|
||||
const DenseSet<unsigned> &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,
|
||||
@ -342,6 +358,12 @@ public:
|
||||
///
|
||||
void ClaimAllArgs() const;
|
||||
|
||||
/// Return the OptSpecifiers queried from this argument list.
|
||||
const DenseSet<unsigned> &getQueriedOpts() const { return QueriedOpts; }
|
||||
|
||||
/// Clear the set of queried OptSpecifiers.
|
||||
void clearQueriedOpts() const { QueriedOpts.clear(); }
|
||||
|
||||
/// @}
|
||||
/// @name Arg Synthesis
|
||||
/// @{
|
||||
|
@ -90,11 +90,22 @@ StringRef ArgList::getLastArgValue(OptSpecifier Id, StringRef Default) const {
|
||||
}
|
||||
|
||||
std::vector<std::string> ArgList::getAllArgValues(OptSpecifier Id) const {
|
||||
recordQueriedOpts(Id);
|
||||
SmallVector<const char *, 16> Values;
|
||||
AddAllArgValues(Values, Id);
|
||||
return std::vector<std::string>(Values.begin(), Values.end());
|
||||
}
|
||||
|
||||
void ArgList::AddAllArgsExcept(ArgStringList &Output,
|
||||
const DenseSet<unsigned> &ExcludeIds) const {
|
||||
for (const Arg *Arg : *this) {
|
||||
if (!ExcludeIds.contains(Arg->getOption().getID())) {
|
||||
Arg->claim();
|
||||
Arg->render(*this, Output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ArgList::AddAllArgsExcept(ArgStringList &Output,
|
||||
ArrayRef<OptSpecifier> Ids,
|
||||
ArrayRef<OptSpecifier> ExcludeIds) const {
|
||||
|
Loading…
x
Reference in New Issue
Block a user