Refactor -fsanitize, -f*-sanitizer arguments parsing. Provide a more careful diagnostic for invalid sets of sanitizers

llvm-svn: 168794
This commit is contained in:
Alexey Samsonov 2012-11-28 17:34:24 +00:00
parent df5f3028e2
commit 53f7e12909
3 changed files with 72 additions and 46 deletions

View File

@ -71,18 +71,78 @@ class SanitizerArgs {
/// Parse a -fsanitize= or -fno-sanitize= argument's values, diagnosing any
/// invalid components.
static unsigned parse(const Driver &D, const Arg *A) {
static unsigned parse(const Driver &D, const Arg *A, bool DiagnoseErrors) {
unsigned Kind = 0;
for (unsigned I = 0, N = A->getNumValues(); I != N; ++I) {
if (unsigned K = parse(A->getValue(I)))
Kind |= K;
else
else if (DiagnoseErrors)
D.Diag(diag::err_drv_unsupported_option_argument)
<< A->getOption().getName() << A->getValue(I);
}
return Kind;
}
/// Parse a single flag of the form -f[no]sanitize=, or
/// -f*-sanitizer. Sets the masks defining required change of Kind value.
/// Returns true if the flag was parsed successfully.
static bool parse(const Driver &D, const ArgList &Args, const Arg *A,
unsigned &Add, unsigned &Remove, bool DiagnoseErrors) {
Add = 0;
Remove = 0;
const char *DeprecatedReplacement = 0;
if (A->getOption().matches(options::OPT_faddress_sanitizer)) {
Add = Address;
DeprecatedReplacement = "-fsanitize=address";
} else if (A->getOption().matches(options::OPT_fno_address_sanitizer)) {
Remove = Address;
DeprecatedReplacement = "-fno-sanitize=address";
} else if (A->getOption().matches(options::OPT_fthread_sanitizer)) {
Add = Thread;
DeprecatedReplacement = "-fsanitize=thread";
} else if (A->getOption().matches(options::OPT_fno_thread_sanitizer)) {
Remove = Thread;
DeprecatedReplacement = "-fno-sanitize=thread";
} else if (A->getOption().matches(options::OPT_fcatch_undefined_behavior)) {
Add = Undefined;
DeprecatedReplacement = "-fsanitize=undefined";
} else if (A->getOption().matches(options::OPT_fbounds_checking) ||
A->getOption().matches(options::OPT_fbounds_checking_EQ)) {
Add = Bounds;
DeprecatedReplacement = "-fsanitize=bounds";
} else if (A->getOption().matches(options::OPT_fsanitize_EQ)) {
Add = parse(D, A, DiagnoseErrors);
} else if (A->getOption().matches(options::OPT_fno_sanitize_EQ)) {
Remove = parse(D, A, DiagnoseErrors);
} else {
// Flag is not relevant to sanitizers.
return false;
}
// If this is a deprecated synonym, produce a warning directing users
// towards the new spelling.
if (DeprecatedReplacement && DiagnoseErrors)
D.Diag(diag::warn_drv_deprecated_arg)
<< A->getAsString(Args) << DeprecatedReplacement;
return true;
}
/// Produce an argument string from ArgList \p Args, which shows how it
/// provides a sanitizer kind in \p Mask. For example, the argument list
/// "-fsanitize=thread,vptr -faddress-sanitizer" with mask \c NeedsUbsanRt
/// would produce "-fsanitize=vptr".
static std::string lastArgumentForKind(const Driver &D, const ArgList &Args,
unsigned Kind) {
for (ArgList::const_reverse_iterator I = Args.rbegin(), E = Args.rend();
I != E; ++I) {
unsigned Add, Remove;
if (parse(D, Args, *I, Add, Remove, false) &&
(Add & Kind))
return describeSanitizeArg(Args, *I, Kind);
Kind &= ~Remove;
}
llvm_unreachable("arg list didn't provide expected value");
}
/// Produce an argument string from argument \p A, which shows how it provides
/// a value in \p Mask. For instance, the argument
/// "-fsanitize=address,alignment" with mask \c NeedsUbsanRt would produce

View File

@ -1456,51 +1456,13 @@ static bool UseRelaxAll(Compilation &C, const ArgList &Args) {
SanitizerArgs::SanitizerArgs(const Driver &D, const ArgList &Args) {
Kind = 0;
const Arg *AsanArg, *TsanArg, *UbsanArg;
for (ArgList::const_iterator I = Args.begin(), E = Args.end(); I != E; ++I) {
unsigned Add = 0, Remove = 0;
const char *DeprecatedReplacement = 0;
if ((*I)->getOption().matches(options::OPT_faddress_sanitizer)) {
Add = Address;
DeprecatedReplacement = "-fsanitize=address";
} else if ((*I)->getOption().matches(options::OPT_fno_address_sanitizer)) {
Remove = Address;
DeprecatedReplacement = "-fno-sanitize=address";
} else if ((*I)->getOption().matches(options::OPT_fthread_sanitizer)) {
Add = Thread;
DeprecatedReplacement = "-fsanitize=thread";
} else if ((*I)->getOption().matches(options::OPT_fno_thread_sanitizer)) {
Remove = Thread;
DeprecatedReplacement = "-fno-sanitize=thread";
} else if ((*I)->getOption().matches(options::OPT_fcatch_undefined_behavior)) {
Add = Undefined;
DeprecatedReplacement = "-fsanitize=undefined";
} else if ((*I)->getOption().matches(options::OPT_fsanitize_EQ)) {
Add = parse(D, *I);
} else if ((*I)->getOption().matches(options::OPT_fno_sanitize_EQ)) {
Remove = parse(D, *I);
} else if ((*I)->getOption().matches(options::OPT_fbounds_checking) ||
(*I)->getOption().matches(options::OPT_fbounds_checking_EQ)) {
Add = Bounds;
DeprecatedReplacement = "-fsanitize=bounds";
} else {
unsigned Add, Remove;
if (!parse(D, Args, *I, Add, Remove, true))
continue;
}
(*I)->claim();
Kind |= Add;
Kind &= ~Remove;
if (Add & NeedsAsanRt) AsanArg = *I;
if (Add & NeedsTsanRt) TsanArg = *I;
if (Add & NeedsUbsanRt) UbsanArg = *I;
// If this is a deprecated synonym, produce a warning directing users
// towards the new spelling.
if (DeprecatedReplacement)
D.Diag(diag::warn_drv_deprecated_arg)
<< (*I)->getAsString(Args) << DeprecatedReplacement;
}
// Only one runtime library can be used at once.
@ -1510,10 +1472,8 @@ SanitizerArgs::SanitizerArgs(const Driver &D, const ArgList &Args) {
bool NeedsUbsan = needsUbsanRt();
if (NeedsAsan + NeedsTsan + NeedsUbsan > 1)
D.Diag(diag::err_drv_argument_not_allowed_with)
<< describeSanitizeArg(Args, NeedsAsan ? AsanArg : TsanArg,
NeedsAsan ? NeedsAsanRt : NeedsTsanRt)
<< describeSanitizeArg(Args, NeedsUbsan ? UbsanArg : TsanArg,
NeedsUbsan ? NeedsUbsanRt : NeedsTsanRt);
<< lastArgumentForKind(D, Args, NeedsAsan ? NeedsAsanRt : NeedsTsanRt)
<< lastArgumentForKind(D, Args, NeedsUbsan ? NeedsUbsanRt : NeedsTsanRt);
}
/// If AddressSanitizer is enabled, add appropriate linker flags (Linux).

View File

@ -20,6 +20,12 @@
// RUN: %clang -target x86_64-linux-gnu -faddress-sanitizer -fthread-sanitizer -fno-rtti %s -c -o /dev/null 2>&1 | FileCheck %s --check-prefix=CHECK-ASAN-TSAN
// CHECK-ASAN-TSAN: '-faddress-sanitizer' not allowed with '-fthread-sanitizer'
// RUN: %clang -target x86_64-linux-gnu -fsanitize=address -fsanitize=alignment -fsanitize=vptr -fno-sanitize=vptr %s -c -o /dev/null 2>&1 | FileCheck %s --check-prefix=CHECK-UBSAN-ASAN
// CHECK-UBSAN-ASAN: '-fsanitize=address' not allowed with '-fsanitize=alignment'
// RUN: %clang -target x86_64-linux-gnu -fsanitize=vptr -fno-sanitize=vptr -fsanitize=undefined,address %s -c -o /dev/null 2>&1 | FileCheck %s --check-prefix=CHECK-VPTR-UBSAN-ASAN
// CHECK-VPTR-UBSAN-ASAN: '-fsanitize=address' not allowed with '-fsanitize=undefined'
// RUN: %clang -target x86_64-linux-gnu -fcatch-undefined-behavior -fthread-sanitizer -fno-thread-sanitizer -faddress-sanitizer -fno-address-sanitizer -fbounds-checking -c -o /dev/null %s 2>&1 | FileCheck %s --check-prefix=CHECK-DEPRECATED
// CHECK-DEPRECATED: argument '-fcatch-undefined-behavior' is deprecated, use '-fsanitize=undefined' instead
// CHECK-DEPRECATED: argument '-fthread-sanitizer' is deprecated, use '-fsanitize=thread' instead