From: Ted Kremenek Date: Fri, 12 Oct 2007 20:51:52 +0000 (+0000) Subject: Added notion of '*' specified format width/specifiers when checking X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=580b664e9c2acd3bffddfea79b1ce2863cfd9dd0;p=clang 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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@42933 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/Sema/SemaChecking.cpp b/Sema/SemaChecking.cpp index f764d80df2..c2e3817171 100644 --- a/Sema/SemaChecking.cpp +++ b/Sema/SemaChecking.cpp @@ -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(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 diff --git a/include/clang/Basic/DiagnosticKinds.def b/include/clang/Basic/DiagnosticKinds.def index 81aaa5caf7..dec9343cbb 100644 --- a/include/clang/Basic/DiagnosticKinds.def +++ b/include/clang/Basic/DiagnosticKinds.def @@ -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, diff --git a/test/Sema/format-strings.c b/test/Sema/format-strings.c index 8b3be6856b..2222f79608 100644 --- a/test/Sema/format-strings.c +++ b/test/Sema/format-strings.c @@ -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 *'}} +} \ No newline at end of file