Added notion of '*' specified format width/specifiers when checking

printf format strings.  Added type checking to see if the matching
width/precision argument was of type 'int'.

Thanks to Anders Carlsson for reporting this missing feature.

llvm-svn: 42933
This commit is contained in:
Ted Kremenek 2007-10-12 20:51:52 +00:00
parent e0ad9ea7cd
commit 41362cea7b
3 changed files with 57 additions and 4 deletions

View File

@ -313,10 +313,47 @@ Sema::CheckPrintfArguments(Expr *Fn,
// Seen '%'. Now processing a format conversion.
switch (Str[StrIdx]) {
// Handle dynamic precision specifier.
case '*':
if (Str[StrIdx-1] == '.') ++numConversions;
// Handle dynamic precision or width specifier.
case '*': {
++numConversions;
if (!HasVAListArg && numConversions > numDataArgs) {
SourceLocation Loc =
PP.AdvanceToTokenCharacter(Args[format_idx]->getLocStart(),
StrIdx+1);
if (Str[StrIdx-1] == '.')
Diag(Loc, diag::warn_printf_asterisk_precision_missing_arg,
Fn->getSourceRange());
else
Diag(Loc, diag::warn_printf_asterisk_width_missing_arg,
Fn->getSourceRange());
// Don't do any more checking. We'll just emit spurious errors.
return;
}
// Perform type checking on width/precision specifier.
Expr* E = Args[format_idx+numConversions];
QualType T = E->getType().getCanonicalType();
if (BuiltinType *BT = dyn_cast<BuiltinType>(T))
if (BT->getKind() == BuiltinType::Int)
break;
SourceLocation Loc =
PP.AdvanceToTokenCharacter(Args[format_idx]->getLocStart(),
StrIdx+1);
if (Str[StrIdx-1] == '.')
Diag(Loc, diag::warn_printf_asterisk_precision_wrong_type,
T.getAsString(), E->getSourceRange());
else
Diag(Loc, diag::warn_printf_asterisk_width_wrong_type,
T.getAsString(), E->getSourceRange());
break;
}
// Characters which can terminate a format conversion
// (e.g. "%d"). Characters that specify length modifiers or
@ -376,7 +413,7 @@ Sema::CheckPrintfArguments(Expr *Fn,
LastConversionIdx+1);
Diag(Loc, diag::warn_printf_invalid_conversion,
std::string(Str+LastConversionIdx, Str+StrIdx),
std::string(Str+LastConversionIdx, Str+StrIdx),
Fn->getSourceRange());
// This conversion is broken. Advance to the next format

View File

@ -789,6 +789,14 @@ DIAG(warn_printf_format_string_is_wide_literal, WARNING,
"format string should not be a wide string")
DIAG(warn_printf_format_string_contains_null_char, WARNING,
"format string contains '\\0' within the string body")
DIAG(warn_printf_asterisk_width_missing_arg, WARNING,
"'*' specified field width is missing a matching 'int' argument")
DIAG(warn_printf_asterisk_precision_missing_arg, WARNING,
"'.*' specified field precision is missing a matching 'int' argument")
DIAG(warn_printf_asterisk_width_wrong_type, WARNING,
"field width should have type 'int', but argument has type '%0'")
DIAG(warn_printf_asterisk_precision_wrong_type, WARNING,
"field precision should have type 'int', but argument has type '%0'")
// CHECK: returning address/reference of stack memory
DIAG(warn_ret_stack_addr, WARNING,

View File

@ -61,3 +61,11 @@ void check_wide_string(char* b, ...)
printf(L"foo %d",2); // expected-warning {{should not be a wide string}}
vasprintf(&b,L"bar %d",2); // expected-warning {{should not be a wide string}}
}
void check_asterisk_precision_width(int x) {
printf("%*d"); // expected-warning {{'*' specified field width is missing a matching 'int' argument}}
printf("%.*d"); // expected-warning {{'.*' specified field precision is missing a matching 'int' argument}}
printf("%*d",12,x); // no-warning
printf("%*d","foo",x); // expected-warning {{field width should have type 'int', but argument has type 'char *'}}
printf("%.*d","foo",x); // expected-warning {{field precision should have type 'int', but argument has type 'char *'}}
}