diff options
author | Erik Pilkington <erik.pilkington@gmail.com> | 2019-09-18 19:05:14 +0000 |
---|---|---|
committer | Erik Pilkington <erik.pilkington@gmail.com> | 2019-09-18 19:05:14 +0000 |
commit | 7a6d9c12018b01e8d97ccb88367f24d7f38351cc (patch) | |
tree | 59c3b6a222dfe7452c9ee64fb42c794bb4884fb7 | |
parent | da7c99cf283cf1773550f372cb50d3320db77e92 (diff) | |
download | clang-7a6d9c12018b01e8d97ccb88367f24d7f38351cc.tar.gz |
[Sema] Suppress -Wformat diagnostics for bool types when printed using %hhd
Also, add a diagnostic under -Wformat for printing a boolean value as a
character.
rdar://54579473
Differential revision: https://reviews.llvm.org/D66856
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@372247 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/Basic/DiagnosticSemaKinds.td | 3 | ||||
-rw-r--r-- | lib/AST/FormatString.cpp | 2 | ||||
-rw-r--r-- | lib/Sema/SemaChecking.cpp | 16 | ||||
-rw-r--r-- | test/Sema/format-bool.c | 46 |
4 files changed, 67 insertions, 0 deletions
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index b89bd36757..8dd72d27e3 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -8145,6 +8145,9 @@ def warn_printf_invalid_objc_flag: Warning< def warn_scanf_scanlist_incomplete : Warning< "no closing ']' for '%%[' in scanf format string">, InGroup<Format>; +def warn_format_bool_as_character : Warning< + "using '%0' format specifier, but argument has boolean value">, + InGroup<Format>; def note_format_string_defined : Note<"format string is defined here">; def note_format_fix_specifier : Note<"did you mean to use '%0'?">; def note_printf_c_str: Note<"did you mean to call the %0 method?">; diff --git a/lib/AST/FormatString.cpp b/lib/AST/FormatString.cpp index ff439f9921..3c96c80e4c 100644 --- a/lib/AST/FormatString.cpp +++ b/lib/AST/FormatString.cpp @@ -359,6 +359,7 @@ ArgType::matchesType(ASTContext &C, QualType argTy) const { case BuiltinType::SChar: case BuiltinType::UChar: case BuiltinType::Char_U: + case BuiltinType::Bool: return Match; } return NoMatch; @@ -386,6 +387,7 @@ ArgType::matchesType(ASTContext &C, QualType argTy) const { case BuiltinType::SChar: case BuiltinType::Char_U: case BuiltinType::UChar: + case BuiltinType::Bool: if (T == C.UnsignedShortTy || T == C.ShortTy) return NoMatchPedantic; return T == C.UnsignedCharTy || T == C.SignedCharTy ? Match diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp index c7639b4a40..af69c231f1 100644 --- a/lib/Sema/SemaChecking.cpp +++ b/lib/Sema/SemaChecking.cpp @@ -8109,6 +8109,22 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, ExprTy = TET->getUnderlyingExpr()->getType(); } + // Diagnose attempts to print a boolean value as a character. Unlike other + // -Wformat diagnostics, this is fine from a type perspective, but it still + // doesn't make sense. + if (FS.getConversionSpecifier().getKind() == ConversionSpecifier::cArg && + E->isKnownToHaveBooleanValue()) { + const CharSourceRange &CSR = + getSpecifierRange(StartSpecifier, SpecifierLen); + SmallString<4> FSString; + llvm::raw_svector_ostream os(FSString); + FS.toString(os); + EmitFormatDiagnostic(S.PDiag(diag::warn_format_bool_as_character) + << FSString, + E->getExprLoc(), false, CSR); + return true; + } + const analyze_printf::ArgType::MatchKind Match = AT.matchesType(S.Context, ExprTy); bool Pedantic = Match == analyze_printf::ArgType::NoMatchPedantic; diff --git a/test/Sema/format-bool.c b/test/Sema/format-bool.c new file mode 100644 index 0000000000..53c2c7fd12 --- /dev/null +++ b/test/Sema/format-bool.c @@ -0,0 +1,46 @@ +// RUN: %clang_cc1 -xc %s -verify -DBOOL=_Bool +// RUN: %clang_cc1 -xc++ %s -verify -DBOOL=bool +// RUN: %clang_cc1 -xobjective-c %s -verify -DBOOL=_Bool +// RUN: %clang_cc1 -xc %s -verify -DBOOL=_Bool -Wformat-pedantic -DPEDANTIC +// RUN: %clang_cc1 -xc++ %s -verify -DBOOL=bool -Wformat-pedantic -DPEDANTIC + +__attribute__((format(__printf__, 1, 2))) +int p(const char *fmt, ...); + +BOOL b; + +#ifdef __OBJC__ +@interface NSString ++(NSString *)stringWithFormat:(NSString *)fmt, ... + __attribute__((format(__NSString__, 1, 2))); +@end + +#define YES __objc_yes +#define NO __objc_no +#endif + +int main() { + p("%d", b); + p("%hd", b); +#ifdef PEDANTIC + // expected-warning@-2 {{format specifies type 'short' but the argument has type}} +#endif + p("%hhd", b); + p("%u", b); + p("%hu", b); +#ifdef PEDANTIC + // expected-warning@-2 {{format specifies type 'unsigned short' but the argument has type}} +#endif + p("%hhu", b); + p("%c", b); // expected-warning {{using '%c' format specifier, but argument has boolean value}} + p("%lc", b); // expected-warning {{using '%lc' format specifier, but argument has boolean value}} + p("%c", 1 == 1); // expected-warning {{using '%c' format specifier, but argument has boolean value}} + p("%f", b); // expected-warning{{format specifies type 'double' but the argument has type}} + p("%ld", b); // expected-warning{{format specifies type 'long' but the argument has type}} + p("%lld", b); // expected-warning{{format specifies type 'long long' but the argument has type}} + +#ifdef __OBJC__ + [NSString stringWithFormat: @"%c", 0]; // probably fine? + [NSString stringWithFormat: @"%c", NO]; // expected-warning {{using '%c' format specifier, but argument has boolean value}} +#endif +} |