https://github.com/karka228 updated https://github.com/llvm/llvm-project/pull/74440
>From a80bf9d03f19d48c0aca4af7758dc49516da8825 Mon Sep 17 00:00:00 2001 From: Karl-Johan Karlsson <karl-johan.karls...@ericsson.com> Date: Tue, 5 Dec 2023 10:03:00 +0100 Subject: [PATCH 1/6] [Sema] Implement support for -Wformat-signedness In gcc there exist a modifier option -Wformat-signedness that turns on additional signedness warnings in the already existing -Wformat warning. This patch implements that support in clang. --- clang/include/clang/AST/FormatString.h | 2 + .../include/clang/Basic/DiagnosticOptions.def | 1 + clang/include/clang/Driver/Options.td | 3 + clang/lib/AST/FormatString.cpp | 29 +++-- clang/lib/Driver/ToolChains/Clang.cpp | 2 + clang/lib/Sema/SemaChecking.cpp | 26 ++++- format-strings-signedness.c | 107 ++++++++++++++++++ 7 files changed, 158 insertions(+), 12 deletions(-) create mode 100644 format-strings-signedness.c diff --git a/clang/include/clang/AST/FormatString.h b/clang/include/clang/AST/FormatString.h index 5c4ad9baaef60..c267a32be4d6f 100644 --- a/clang/include/clang/AST/FormatString.h +++ b/clang/include/clang/AST/FormatString.h @@ -264,6 +264,8 @@ class ArgType { /// The conversion specifier and the argument type are compatible. For /// instance, "%d" and int. Match = 1, + /// The conversion specifier and the argument type have different sign + MatchSignedness, /// The conversion specifier and the argument type are compatible because of /// default argument promotions. For instance, "%hhd" and int. MatchPromotion, diff --git a/clang/include/clang/Basic/DiagnosticOptions.def b/clang/include/clang/Basic/DiagnosticOptions.def index 6d0c1b14acc12..a9562e7d8dcb0 100644 --- a/clang/include/clang/Basic/DiagnosticOptions.def +++ b/clang/include/clang/Basic/DiagnosticOptions.def @@ -44,6 +44,7 @@ DIAGOPT(Name, Bits, Default) #endif SEMANTIC_DIAGOPT(IgnoreWarnings, 1, 0) /// -w +DIAGOPT(FormatSignedness, 1, 0) /// -Wformat-signedness DIAGOPT(NoRewriteMacros, 1, 0) /// -Wno-rewrite-macros DIAGOPT(Pedantic, 1, 0) /// -pedantic DIAGOPT(PedanticErrors, 1, 0) /// -pedantic-errors diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 1d04e4f6e7e6d..04fdf9a7eb92d 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -918,6 +918,9 @@ def Wdeprecated : Flag<["-"], "Wdeprecated">, Group<W_Group>, HelpText<"Enable warnings for deprecated constructs and define __DEPRECATED">; def Wno_deprecated : Flag<["-"], "Wno-deprecated">, Group<W_Group>, Visibility<[ClangOption, CC1Option]>; +def Wformat_signedness : Flag<["-"], "Wformat-signedness">, + Flags<[HelpHidden]>, Visibility<[ClangOption, CC1Option]>, + MarshallingInfoFlag<DiagnosticOpts<"FormatSignedness">>; def Wl_COMMA : CommaJoined<["-"], "Wl,">, Visibility<[ClangOption, FlangOption]>, Flags<[LinkerInput, RenderAsInput]>, HelpText<"Pass the comma separated arguments in <arg> to the linker">, diff --git a/clang/lib/AST/FormatString.cpp b/clang/lib/AST/FormatString.cpp index e0c9e18cfe3a2..670cde017d3ac 100644 --- a/clang/lib/AST/FormatString.cpp +++ b/clang/lib/AST/FormatString.cpp @@ -409,7 +409,7 @@ ArgType::matchesType(ASTContext &C, QualType argTy) const { return Match; if (const auto *BT = argTy->getAs<BuiltinType>()) { // Check if the only difference between them is signed vs unsigned - // if true, we consider they are compatible. + // if true, return match signedness. switch (BT->getKind()) { default: break; @@ -419,44 +419,53 @@ ArgType::matchesType(ASTContext &C, QualType argTy) const { [[fallthrough]]; case BuiltinType::Char_S: case BuiltinType::SChar: + if (T == C.UnsignedShortTy || T == C.ShortTy) + return NoMatchTypeConfusion; + if (T == C.UnsignedCharTy) + return MatchSignedness; + if (T == C.SignedCharTy) + return Match; + break; case BuiltinType::Char_U: case BuiltinType::UChar: if (T == C.UnsignedShortTy || T == C.ShortTy) return NoMatchTypeConfusion; - if (T == C.UnsignedCharTy || T == C.SignedCharTy) + if (T == C.UnsignedCharTy) return Match; + if (T == C.SignedCharTy) + return MatchSignedness; break; case BuiltinType::Short: if (T == C.UnsignedShortTy) - return Match; + return MatchSignedness; break; case BuiltinType::UShort: if (T == C.ShortTy) - return Match; + return MatchSignedness; break; case BuiltinType::Int: if (T == C.UnsignedIntTy) - return Match; + return MatchSignedness; break; case BuiltinType::UInt: if (T == C.IntTy) - return Match; + return MatchSignedness; break; case BuiltinType::Long: if (T == C.UnsignedLongTy) - return Match; + return MatchSignedness; break; case BuiltinType::ULong: if (T == C.LongTy) - return Match; + return MatchSignedness; break; case BuiltinType::LongLong: if (T == C.UnsignedLongLongTy) - return Match; + return MatchSignedness; break; case BuiltinType::ULongLong: if (T == C.LongLongTy) - return Match; + return MatchSignedness; break; } // "Partially matched" because of promotions? diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index f02f7c841b91f..6a6a40293d7d3 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -6054,6 +6054,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back("-fdeprecated-macro"); } + Args.AddLastArg(CmdArgs, options::OPT_Wformat_signedness); + // Translate GCC's misnamer '-fasm' arguments to '-fgnu-keywords'. if (Arg *Asm = Args.getLastArg(options::OPT_fasm, options::OPT_fno_asm)) { if (Asm->getOption().matches(options::OPT_fasm)) diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 07ced5ffc3407..ea8d5a5d6b913 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -11815,6 +11815,18 @@ isArithmeticArgumentPromotion(Sema &S, const ImplicitCastExpr *ICE) { S.Context.getFloatingTypeOrder(From, To) < 0; } +static clang::analyze_format_string::ArgType::MatchKind +handleFormatSignedness(clang::analyze_format_string::ArgType::MatchKind Match, + DiagnosticOptions &DiagOpts) { + if (Match == clang::analyze_format_string::ArgType::MatchSignedness) { + if (DiagOpts.FormatSignedness) + Match = clang::analyze_format_string::ArgType::NoMatch; + else + Match = clang::analyze_format_string::ArgType::Match; + } + return Match; +} + bool CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, const char *StartSpecifier, @@ -11858,6 +11870,8 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, ArgType::MatchKind ImplicitMatch = ArgType::NoMatch; ArgType::MatchKind Match = AT.matchesType(S.Context, ExprTy); + Match = + handleFormatSignedness(Match, S.getDiagnostics().getDiagnosticOptions()); if (Match == ArgType::Match) return true; @@ -11881,6 +11895,8 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, ICE->getType() == S.Context.UnsignedIntTy) { // All further checking is done on the subexpression ImplicitMatch = AT.matchesType(S.Context, ExprTy); + ImplicitMatch = handleFormatSignedness( + ImplicitMatch, S.getDiagnostics().getDiagnosticOptions()); if (ImplicitMatch == ArgType::Match) return true; } @@ -12001,6 +12017,7 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, switch (Match) { case ArgType::Match: case ArgType::MatchPromotion: + case ArgType::MatchSignedness: case ArgType::NoMatchPromotionTypeConfusion: llvm_unreachable("expected non-matching"); case ArgType::NoMatchPedantic: @@ -12037,8 +12054,10 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, CastFix << (S.LangOpts.CPlusPlus ? ">" : ")"); SmallVector<FixItHint,4> Hints; - if (AT.matchesType(S.Context, IntendedTy) != ArgType::Match || - ShouldNotPrintDirectly) + ArgType::MatchKind IntendedMatch = AT.matchesType(S.Context, IntendedTy); + IntendedMatch = handleFormatSignedness( + IntendedMatch, S.getDiagnostics().getDiagnosticOptions()); + if ((IntendedMatch != ArgType::Match) || ShouldNotPrintDirectly) Hints.push_back(FixItHint::CreateReplacement(SpecRange, os.str())); if (const CStyleCastExpr *CCast = dyn_cast<CStyleCastExpr>(E)) { @@ -12106,6 +12125,7 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, switch (Match) { case ArgType::Match: case ArgType::MatchPromotion: + case ArgType::MatchSignedness: case ArgType::NoMatchPromotionTypeConfusion: llvm_unreachable("expected non-matching"); case ArgType::NoMatchPedantic: @@ -12318,6 +12338,8 @@ bool CheckScanfHandler::HandleScanfSpecifier( analyze_format_string::ArgType::MatchKind Match = AT.matchesType(S.Context, Ex->getType()); + Match = + handleFormatSignedness(Match, S.getDiagnostics().getDiagnosticOptions()); bool Pedantic = Match == analyze_format_string::ArgType::NoMatchPedantic; if (Match == analyze_format_string::ArgType::Match) return true; diff --git a/format-strings-signedness.c b/format-strings-signedness.c new file mode 100644 index 0000000000000..02964def10eef --- /dev/null +++ b/format-strings-signedness.c @@ -0,0 +1,107 @@ +// RUN: %clang_cc1 -std=c11 -fsyntax-only -verify -Wformat -Wformat-signedness %s + +int printf(const char *restrict format, ...); +int scanf(const char * restrict, ...); + +void test_printf_bool(_Bool x) +{ + printf("%d", x); // no-warning + printf("%u", x); // expected-warning{{format specifies type 'unsigned int' but the argument has type '_Bool'}} + printf("%x", x); // expected-warning{{format specifies type 'unsigned int' but the argument has type '_Bool'}} +} + +void test_printf_char(char x) +{ + printf("%c", x); // no-warning +} + +void test_printf_unsigned_char(unsigned char x) +{ + printf("%c", x); // no-warning +} + +void test_printf_int(int x) +{ + printf("%d", x); // no-warning + printf("%u", x); // expected-warning{{format specifies type 'unsigned int' but the argument has type 'int'}} + printf("%x", x); // expected-warning{{format specifies type 'unsigned int' but the argument has type 'int'}} +} + +void test_printf_unsigned(unsigned x) +{ + printf("%d", x); // expected-warning{{format specifies type 'int' but the argument has type 'unsigned int'}} + printf("%u", x); // no-warning + printf("%x", x); // no-warning +} + +void test_printf_long(long x) +{ + printf("%ld", x); // no-warning + printf("%lu", x); // expected-warning{{format specifies type 'unsigned long' but the argument has type 'long'}} + printf("%lx", x); // expected-warning{{format specifies type 'unsigned long' but the argument has type 'long'}} +} + +void test_printf_unsigned_long(unsigned long x) +{ + printf("%ld", x); // expected-warning{{format specifies type 'long' but the argument has type 'unsigned long'}} + printf("%lu", x); // no-warning + printf("%lx", x); // no-warning +} + +void test_printf_long_long(long long x) +{ + printf("%lld", x); // no-warning + printf("%llu", x); // expected-warning{{format specifies type 'unsigned long long' but the argument has type 'long long'}} + printf("%llx", x); // expected-warning{{format specifies type 'unsigned long long' but the argument has type 'long long'}} +} + +void test_printf_unsigned_long_long(unsigned long long x) +{ + printf("%lld", x); // expected-warning{{format specifies type 'long long' but the argument has type 'unsigned long long'}} + printf("%llu", x); // no-warning + printf("%llx", x); // no-warning +} + +void test_scanf_char(char *y) { + scanf("%c", y); // no-warning +} + +void test_scanf_unsigned_char(unsigned char *y) { + scanf("%c", y); // no-warning +} + +void test_scanf_int(int *x) { + scanf("%d", x); // no-warning + scanf("%u", x); // expected-warning{{format specifies type 'unsigned int *' but the argument has type 'int *'}} + scanf("%x", x); // expected-warning{{format specifies type 'unsigned int *' but the argument has type 'int *'}} +} + +void test_scanf_unsigned(unsigned *x) { + scanf("%d", x); // expected-warning{{format specifies type 'int *' but the argument has type 'unsigned int *'}} + scanf("%u", x); // no-warning + scanf("%x", x); // no-warning +} + +void test_scanf_long(long *x) { + scanf("%ld", x); // no-warning + scanf("%lu", x); // expected-warning{{format specifies type 'unsigned long *' but the argument has type 'long *'}} + scanf("%lx", x); // expected-warning{{format specifies type 'unsigned long *' but the argument has type 'long *'}} +} + +void test_scanf_unsigned_long(unsigned long *x) { + scanf("%ld", x); // expected-warning{{format specifies type 'long *' but the argument has type 'unsigned long *'}} + scanf("%lu", x); // no-warning + scanf("%lx", x); // no-warning +} + +void test_scanf_longlong(long long *x) { + scanf("%lld", x); // no-warning + scanf("%llu", x); // expected-warning{{format specifies type 'unsigned long long *' but the argument has type 'long long *'}} + scanf("%llx", x); // expected-warning{{format specifies type 'unsigned long long *' but the argument has type 'long long *'}} +} + +void test_scanf_unsigned_longlong(unsigned long long *x) { + scanf("%lld", x); // expected-warning{{format specifies type 'long long *' but the argument has type 'unsigned long long *'}} + scanf("%llu", x); // no-warning + scanf("%llx", x); // no-warning +} >From 1f5fe21d5851e268dfedf909f5ec2832b741a514 Mon Sep 17 00:00:00 2001 From: Karl-Johan Karlsson <karl-johan.karls...@ericsson.com> Date: Tue, 5 Dec 2023 11:17:08 +0100 Subject: [PATCH 2/6] [Sema] Implement support for -Wformat-signedness Move the testcase to its correct dir. --- .../test/Sema/format-strings-signedness.c | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename format-strings-signedness.c => clang/test/Sema/format-strings-signedness.c (100%) diff --git a/format-strings-signedness.c b/clang/test/Sema/format-strings-signedness.c similarity index 100% rename from format-strings-signedness.c rename to clang/test/Sema/format-strings-signedness.c >From 3571a2242d88e4c5b1b6817aa4f5701cf5146c77 Mon Sep 17 00:00:00 2001 From: Karl-Johan Karlsson <karl-johan.karls...@ericsson.com> Date: Fri, 8 Dec 2023 07:58:58 +0100 Subject: [PATCH 3/6] [Sema] Implement support for -Wformat-signedness Move warning option config to DiagnosticSemaKinds.td --- clang/docs/ReleaseNotes.rst | 2 + clang/include/clang/AST/FormatString.h | 4 +- clang/include/clang/Basic/DiagnosticGroups.td | 1 + .../include/clang/Basic/DiagnosticOptions.def | 1 - .../clang/Basic/DiagnosticSemaKinds.td | 6 ++ clang/include/clang/Driver/Options.td | 3 - clang/lib/AST/FormatString.cpp | 20 +++--- clang/lib/Driver/ToolChains/Clang.cpp | 2 - clang/lib/Sema/SemaChecking.cpp | 70 ++++++++++--------- .../Sema/format-strings-signedness-fixit.c | 65 +++++++++++++++++ clang/test/Sema/format-strings-signedness.c | 21 ++++++ 11 files changed, 144 insertions(+), 51 deletions(-) create mode 100644 clang/test/Sema/format-strings-signedness-fixit.c diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 828dd10e3d6db..a9ba5dbfcc512 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -503,6 +503,8 @@ Improvements to Clang's diagnostics 48 | static_assert(1 << 4 == 15); | ~~~~~~~^~~~~ +- New ``-Wformat-signedness`` diagnostic that warn if the format string requires an + unsigned argument and the argument is signed and vice versa. Improvements to Clang's time-trace ---------------------------------- diff --git a/clang/include/clang/AST/FormatString.h b/clang/include/clang/AST/FormatString.h index c267a32be4d6f..33e9f60fd07b3 100644 --- a/clang/include/clang/AST/FormatString.h +++ b/clang/include/clang/AST/FormatString.h @@ -264,8 +264,6 @@ class ArgType { /// The conversion specifier and the argument type are compatible. For /// instance, "%d" and int. Match = 1, - /// The conversion specifier and the argument type have different sign - MatchSignedness, /// The conversion specifier and the argument type are compatible because of /// default argument promotions. For instance, "%hhd" and int. MatchPromotion, @@ -275,6 +273,8 @@ class ArgType { /// The conversion specifier and the argument type are disallowed by the C /// standard, but are in practice harmless. For instance, "%p" and int*. NoMatchPedantic, + /// The conversion specifier and the argument type have different sign. + NoMatchSignedness, /// The conversion specifier and the argument type are compatible, but still /// seems likely to be an error. For instance, "%hd" and _Bool. NoMatchTypeConfusion, diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index ff028bbbf7426..03e6854f32a31 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -968,6 +968,7 @@ def FormatSecurity : DiagGroup<"format-security">; def FormatNonStandard : DiagGroup<"format-non-iso">; def FormatY2K : DiagGroup<"format-y2k">; def FormatPedantic : DiagGroup<"format-pedantic">; +def FormatSignedness : DiagGroup<"format-signedness">; def FormatTypeConfusion : DiagGroup<"format-type-confusion">; def FormatOverflowNonKprintf: DiagGroup<"format-overflow-non-kprintf">; diff --git a/clang/include/clang/Basic/DiagnosticOptions.def b/clang/include/clang/Basic/DiagnosticOptions.def index a9562e7d8dcb0..6d0c1b14acc12 100644 --- a/clang/include/clang/Basic/DiagnosticOptions.def +++ b/clang/include/clang/Basic/DiagnosticOptions.def @@ -44,7 +44,6 @@ DIAGOPT(Name, Bits, Default) #endif SEMANTIC_DIAGOPT(IgnoreWarnings, 1, 0) /// -w -DIAGOPT(FormatSignedness, 1, 0) /// -Wformat-signedness DIAGOPT(NoRewriteMacros, 1, 0) /// -Wno-rewrite-macros DIAGOPT(Pedantic, 1, 0) /// -pedantic DIAGOPT(PedanticErrors, 1, 0) /// -pedantic-errors diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 6dfb2d7195203..038efb868b600 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -9714,6 +9714,9 @@ def warn_format_conversion_argument_type_mismatch : Warning< def warn_format_conversion_argument_type_mismatch_pedantic : Extension< warn_format_conversion_argument_type_mismatch.Summary>, InGroup<FormatPedantic>; +def warn_format_conversion_argument_type_mismatch_signedness : Warning< + warn_format_conversion_argument_type_mismatch.Summary>, + InGroup<FormatSignedness>, DefaultIgnore; def warn_format_conversion_argument_type_mismatch_confusion : Warning< warn_format_conversion_argument_type_mismatch.Summary>, InGroup<FormatTypeConfusion>, DefaultIgnore; @@ -9724,6 +9727,9 @@ def warn_format_argument_needs_cast : Warning< def warn_format_argument_needs_cast_pedantic : Warning< warn_format_argument_needs_cast.Summary>, InGroup<FormatPedantic>, DefaultIgnore; +def warn_format_argument_needs_cast_signedness : Warning< + warn_format_argument_needs_cast.Summary>, + InGroup<FormatSignedness>, DefaultIgnore; def warn_printf_positional_arg_exceeds_data_args : Warning < "data argument position '%0' exceeds the number of data arguments (%1)">, InGroup<Format>; diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 04fdf9a7eb92d..1d04e4f6e7e6d 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -918,9 +918,6 @@ def Wdeprecated : Flag<["-"], "Wdeprecated">, Group<W_Group>, HelpText<"Enable warnings for deprecated constructs and define __DEPRECATED">; def Wno_deprecated : Flag<["-"], "Wno-deprecated">, Group<W_Group>, Visibility<[ClangOption, CC1Option]>; -def Wformat_signedness : Flag<["-"], "Wformat-signedness">, - Flags<[HelpHidden]>, Visibility<[ClangOption, CC1Option]>, - MarshallingInfoFlag<DiagnosticOpts<"FormatSignedness">>; def Wl_COMMA : CommaJoined<["-"], "Wl,">, Visibility<[ClangOption, FlangOption]>, Flags<[LinkerInput, RenderAsInput]>, HelpText<"Pass the comma separated arguments in <arg> to the linker">, diff --git a/clang/lib/AST/FormatString.cpp b/clang/lib/AST/FormatString.cpp index 670cde017d3ac..aba205b56fd9a 100644 --- a/clang/lib/AST/FormatString.cpp +++ b/clang/lib/AST/FormatString.cpp @@ -422,7 +422,7 @@ ArgType::matchesType(ASTContext &C, QualType argTy) const { if (T == C.UnsignedShortTy || T == C.ShortTy) return NoMatchTypeConfusion; if (T == C.UnsignedCharTy) - return MatchSignedness; + return NoMatchSignedness; if (T == C.SignedCharTy) return Match; break; @@ -433,39 +433,39 @@ ArgType::matchesType(ASTContext &C, QualType argTy) const { if (T == C.UnsignedCharTy) return Match; if (T == C.SignedCharTy) - return MatchSignedness; + return NoMatchSignedness; break; case BuiltinType::Short: if (T == C.UnsignedShortTy) - return MatchSignedness; + return NoMatchSignedness; break; case BuiltinType::UShort: if (T == C.ShortTy) - return MatchSignedness; + return NoMatchSignedness; break; case BuiltinType::Int: if (T == C.UnsignedIntTy) - return MatchSignedness; + return NoMatchSignedness; break; case BuiltinType::UInt: if (T == C.IntTy) - return MatchSignedness; + return NoMatchSignedness; break; case BuiltinType::Long: if (T == C.UnsignedLongTy) - return MatchSignedness; + return NoMatchSignedness; break; case BuiltinType::ULong: if (T == C.LongTy) - return MatchSignedness; + return NoMatchSignedness; break; case BuiltinType::LongLong: if (T == C.UnsignedLongLongTy) - return MatchSignedness; + return NoMatchSignedness; break; case BuiltinType::ULongLong: if (T == C.LongLongTy) - return MatchSignedness; + return NoMatchSignedness; break; } // "Partially matched" because of promotions? diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 6a6a40293d7d3..f02f7c841b91f 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -6054,8 +6054,6 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back("-fdeprecated-macro"); } - Args.AddLastArg(CmdArgs, options::OPT_Wformat_signedness); - // Translate GCC's misnamer '-fasm' arguments to '-fgnu-keywords'. if (Arg *Asm = Args.getLastArg(options::OPT_fasm, options::OPT_fno_asm)) { if (Asm->getOption().matches(options::OPT_fasm)) diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index ea8d5a5d6b913..7354fa8529d09 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -11815,18 +11815,6 @@ isArithmeticArgumentPromotion(Sema &S, const ImplicitCastExpr *ICE) { S.Context.getFloatingTypeOrder(From, To) < 0; } -static clang::analyze_format_string::ArgType::MatchKind -handleFormatSignedness(clang::analyze_format_string::ArgType::MatchKind Match, - DiagnosticOptions &DiagOpts) { - if (Match == clang::analyze_format_string::ArgType::MatchSignedness) { - if (DiagOpts.FormatSignedness) - Match = clang::analyze_format_string::ArgType::NoMatch; - else - Match = clang::analyze_format_string::ArgType::Match; - } - return Match; -} - bool CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, const char *StartSpecifier, @@ -11870,8 +11858,6 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, ArgType::MatchKind ImplicitMatch = ArgType::NoMatch; ArgType::MatchKind Match = AT.matchesType(S.Context, ExprTy); - Match = - handleFormatSignedness(Match, S.getDiagnostics().getDiagnosticOptions()); if (Match == ArgType::Match) return true; @@ -11895,8 +11881,6 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, ICE->getType() == S.Context.UnsignedIntTy) { // All further checking is done on the subexpression ImplicitMatch = AT.matchesType(S.Context, ExprTy); - ImplicitMatch = handleFormatSignedness( - ImplicitMatch, S.getDiagnostics().getDiagnosticOptions()); if (ImplicitMatch == ArgType::Match) return true; } @@ -11929,6 +11913,7 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, Match = ArgType::NoMatch; } if (ImplicitMatch == ArgType::NoMatchPedantic || + ImplicitMatch == ArgType::NoMatchSignedness || ImplicitMatch == ArgType::NoMatchTypeConfusion) Match = ImplicitMatch; assert(Match != ArgType::MatchPromotion); @@ -12017,12 +12002,14 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, switch (Match) { case ArgType::Match: case ArgType::MatchPromotion: - case ArgType::MatchSignedness: case ArgType::NoMatchPromotionTypeConfusion: llvm_unreachable("expected non-matching"); case ArgType::NoMatchPedantic: Diag = diag::warn_format_conversion_argument_type_mismatch_pedantic; break; + case ArgType::NoMatchSignedness: + Diag = diag::warn_format_conversion_argument_type_mismatch_signedness; + break; case ArgType::NoMatchTypeConfusion: Diag = diag::warn_format_conversion_argument_type_mismatch_confusion; break; @@ -12054,10 +12041,8 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, CastFix << (S.LangOpts.CPlusPlus ? ">" : ")"); SmallVector<FixItHint,4> Hints; - ArgType::MatchKind IntendedMatch = AT.matchesType(S.Context, IntendedTy); - IntendedMatch = handleFormatSignedness( - IntendedMatch, S.getDiagnostics().getDiagnosticOptions()); - if ((IntendedMatch != ArgType::Match) || ShouldNotPrintDirectly) + if (AT.matchesType(S.Context, IntendedTy) != ArgType::Match || + ShouldNotPrintDirectly) Hints.push_back(FixItHint::CreateReplacement(SpecRange, os.str())); if (const CStyleCastExpr *CCast = dyn_cast<CStyleCastExpr>(E)) { @@ -12093,9 +12078,18 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, Name = TypedefTy->getDecl()->getName(); else Name = CastTyName; - unsigned Diag = Match == ArgType::NoMatchPedantic - ? diag::warn_format_argument_needs_cast_pedantic - : diag::warn_format_argument_needs_cast; + unsigned Diag; + switch (Match) { + default: + Diag = diag::warn_format_argument_needs_cast; + break; + case analyze_format_string::ArgType::NoMatchPedantic: + Diag = diag::warn_format_argument_needs_cast_pedantic; + break; + case analyze_format_string::ArgType::NoMatchSignedness: + Diag = diag::warn_format_argument_needs_cast_signedness; + break; + } EmitFormatDiagnostic(S.PDiag(Diag) << Name << IntendedTy << IsEnum << E->getSourceRange(), E->getBeginLoc(), /*IsStringLocation=*/false, @@ -12104,8 +12098,11 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, // In this case, the expression could be printed using a different // specifier, but we've decided that the specifier is probably correct // and we should cast instead. Just use the normal warning message. + unsigned Diag = Match == ArgType::NoMatchSignedness + ? diag::warn_format_conversion_argument_type_mismatch_signedness + : diag::warn_format_conversion_argument_type_mismatch; EmitFormatDiagnostic( - S.PDiag(diag::warn_format_conversion_argument_type_mismatch) + S.PDiag(Diag) << AT.getRepresentativeTypeName(S.Context) << ExprTy << IsEnum << E->getSourceRange(), E->getBeginLoc(), /*IsStringLocation*/ false, SpecRange, Hints); @@ -12125,12 +12122,14 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, switch (Match) { case ArgType::Match: case ArgType::MatchPromotion: - case ArgType::MatchSignedness: case ArgType::NoMatchPromotionTypeConfusion: llvm_unreachable("expected non-matching"); case ArgType::NoMatchPedantic: Diag = diag::warn_format_conversion_argument_type_mismatch_pedantic; break; + case ArgType::NoMatchSignedness: + Diag = diag::warn_format_conversion_argument_type_mismatch_signedness; + break; case ArgType::NoMatchTypeConfusion: Diag = diag::warn_format_conversion_argument_type_mismatch_confusion; break; @@ -12338,19 +12337,24 @@ bool CheckScanfHandler::HandleScanfSpecifier( analyze_format_string::ArgType::MatchKind Match = AT.matchesType(S.Context, Ex->getType()); - Match = - handleFormatSignedness(Match, S.getDiagnostics().getDiagnosticOptions()); - bool Pedantic = Match == analyze_format_string::ArgType::NoMatchPedantic; if (Match == analyze_format_string::ArgType::Match) return true; ScanfSpecifier fixedFS = FS; bool Success = fixedFS.fixType(Ex->getType(), Ex->IgnoreImpCasts()->getType(), S.getLangOpts(), S.Context); - - unsigned Diag = - Pedantic ? diag::warn_format_conversion_argument_type_mismatch_pedantic - : diag::warn_format_conversion_argument_type_mismatch; + unsigned Diag; + switch (Match) { + default: + Diag = diag::warn_format_conversion_argument_type_mismatch; + break; + case analyze_format_string::ArgType::NoMatchPedantic: + Diag = diag::warn_format_conversion_argument_type_mismatch_pedantic; + break; + case analyze_format_string::ArgType::NoMatchSignedness: + Diag = diag::warn_format_conversion_argument_type_mismatch_signedness; + break; + } if (Success) { // Get the fix string from the fixed format specifier. diff --git a/clang/test/Sema/format-strings-signedness-fixit.c b/clang/test/Sema/format-strings-signedness-fixit.c new file mode 100644 index 0000000000000..2ce794472b752 --- /dev/null +++ b/clang/test/Sema/format-strings-signedness-fixit.c @@ -0,0 +1,65 @@ +// RUN: cp %s %t +// RUN: %clang_cc1 -Wformat -Wformat-signedness -fixit %t +// RUN: %clang_cc1 -fsyntax-only -Wformat -Wformat-signedness -Werror %t +// RUN: %clang_cc1 -E -o - %t | FileCheck %s + +#include <limits.h> + +int printf(const char *restrict format, ...); + +enum foo { + minus_one = -1, + int_val = INT_MAX, + unsigned_val = (unsigned)INT_MIN +}; + +void test_printf_int(int x) +{ + printf("%u", x); +} + +void test_printf_unsigned(unsigned x) +{ + printf("%d", x); +} + +void test_printf_long(long x) +{ + printf("%lu", x); +} + +void test_printf_unsigned_long(unsigned long x) +{ + printf("%ld", x); +} + +void test_printf_long_long(long long x) +{ + printf("%llu", x); +} + +void test_printf_unsigned_long_long(unsigned long long x) +{ + printf("%lld", x); +} + +void test_printf_enum(enum foo x) +{ + printf("%lu", x); +} + +// Validate the fixes. +// CHECK: void test_printf_int(int x) +// CHECK: printf("%d", x); +// CHECK: void test_printf_unsigned(unsigned x) +// CHECK: printf("%u", x); +// CHECK: void test_printf_long(long x) +// CHECK: printf("%ld", x); +// CHECK: void test_printf_unsigned_long(unsigned long x) +// CHECK: printf("%lu", x); +// CHECK: void test_printf_long_long(long long x) +// CHECK: printf("%lld", x); +// CHECK: void test_printf_unsigned_long_long(unsigned long long x) +// CHECK: printf("%llu", x); +// CHECK: void test_printf_enum(enum foo x) +// CHECK: printf("%ld", x); diff --git a/clang/test/Sema/format-strings-signedness.c b/clang/test/Sema/format-strings-signedness.c index 02964def10eef..6e819ed715be3 100644 --- a/clang/test/Sema/format-strings-signedness.c +++ b/clang/test/Sema/format-strings-signedness.c @@ -1,8 +1,16 @@ // RUN: %clang_cc1 -std=c11 -fsyntax-only -verify -Wformat -Wformat-signedness %s +#include <limits.h> + int printf(const char *restrict format, ...); int scanf(const char * restrict, ...); +enum foo { + minus_one = -1, + int_val = INT_MAX, + unsigned_val = (unsigned)INT_MIN +}; + void test_printf_bool(_Bool x) { printf("%d", x); // no-warning @@ -62,6 +70,13 @@ void test_printf_unsigned_long_long(unsigned long long x) printf("%llx", x); // no-warning } +void test_printf_enum(enum foo x) +{ + printf("%ld", x); // no-warning + printf("%lu", x); // expected-warning{{format specifies type 'unsigned long' but the argument has underlying type 'long'}} + printf("%lx", x); // expected-warning{{format specifies type 'unsigned long' but the argument has underlying type 'long'}} +} + void test_scanf_char(char *y) { scanf("%c", y); // no-warning } @@ -105,3 +120,9 @@ void test_scanf_unsigned_longlong(unsigned long long *x) { scanf("%llu", x); // no-warning scanf("%llx", x); // no-warning } + +void test_scanf_enum(enum foo *x) { + scanf("%ld", x); // no-warning + scanf("%lu", x); // expected-warning{{format specifies type 'unsigned long *' but the argument has type 'enum foo *'}} + scanf("%lx", x); // expected-warning{{format specifies type 'unsigned long *' but the argument has type 'enum foo *'}} +} >From 597adefb434306408250a533e8b2941fac0eb466 Mon Sep 17 00:00:00 2001 From: Karl-Johan Karlsson <karl-johan.karls...@ericsson.com> Date: Fri, 8 Dec 2023 09:07:48 +0100 Subject: [PATCH 4/6] [Sema] Implement support for -Wformat-signedness Reformat code with clang-format. --- clang/lib/Sema/SemaChecking.cpp | 48 ++++++++++++++++----------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 7354fa8529d09..848011ef730a8 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -12080,15 +12080,15 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, Name = CastTyName; unsigned Diag; switch (Match) { - default: - Diag = diag::warn_format_argument_needs_cast; - break; - case analyze_format_string::ArgType::NoMatchPedantic: - Diag = diag::warn_format_argument_needs_cast_pedantic; - break; - case analyze_format_string::ArgType::NoMatchSignedness: - Diag = diag::warn_format_argument_needs_cast_signedness; - break; + default: + Diag = diag::warn_format_argument_needs_cast; + break; + case analyze_format_string::ArgType::NoMatchPedantic: + Diag = diag::warn_format_argument_needs_cast_pedantic; + break; + case analyze_format_string::ArgType::NoMatchSignedness: + Diag = diag::warn_format_argument_needs_cast_signedness; + break; } EmitFormatDiagnostic(S.PDiag(Diag) << Name << IntendedTy << IsEnum << E->getSourceRange(), @@ -12098,13 +12098,13 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, // In this case, the expression could be printed using a different // specifier, but we've decided that the specifier is probably correct // and we should cast instead. Just use the normal warning message. - unsigned Diag = Match == ArgType::NoMatchSignedness - ? diag::warn_format_conversion_argument_type_mismatch_signedness - : diag::warn_format_conversion_argument_type_mismatch; + unsigned Diag = + Match == ArgType::NoMatchSignedness + ? diag::warn_format_conversion_argument_type_mismatch_signedness + : diag::warn_format_conversion_argument_type_mismatch; EmitFormatDiagnostic( - S.PDiag(Diag) - << AT.getRepresentativeTypeName(S.Context) << ExprTy << IsEnum - << E->getSourceRange(), + S.PDiag(Diag) << AT.getRepresentativeTypeName(S.Context) << ExprTy + << IsEnum << E->getSourceRange(), E->getBeginLoc(), /*IsStringLocation*/ false, SpecRange, Hints); } } @@ -12345,15 +12345,15 @@ bool CheckScanfHandler::HandleScanfSpecifier( S.getLangOpts(), S.Context); unsigned Diag; switch (Match) { - default: - Diag = diag::warn_format_conversion_argument_type_mismatch; - break; - case analyze_format_string::ArgType::NoMatchPedantic: - Diag = diag::warn_format_conversion_argument_type_mismatch_pedantic; - break; - case analyze_format_string::ArgType::NoMatchSignedness: - Diag = diag::warn_format_conversion_argument_type_mismatch_signedness; - break; + default: + Diag = diag::warn_format_conversion_argument_type_mismatch; + break; + case analyze_format_string::ArgType::NoMatchPedantic: + Diag = diag::warn_format_conversion_argument_type_mismatch_pedantic; + break; + case analyze_format_string::ArgType::NoMatchSignedness: + Diag = diag::warn_format_conversion_argument_type_mismatch_signedness; + break; } if (Success) { >From 79644e36f23efef509e223d21cbe447fe39a991f Mon Sep 17 00:00:00 2001 From: Karl-Johan Karlsson <karl-johan.karls...@ericsson.com> Date: Fri, 8 Dec 2023 09:57:00 +0100 Subject: [PATCH 5/6] [Sema] Implement support for -Wformat-signedness Fixed and extended the enum tests in format-strings-signedness.c and format-strings-signedness-fixit.c --- .../Sema/format-strings-signedness-fixit.c | 55 ++++++++++--- clang/test/Sema/format-strings-signedness.c | 78 ++++++++++++++++--- 2 files changed, 111 insertions(+), 22 deletions(-) diff --git a/clang/test/Sema/format-strings-signedness-fixit.c b/clang/test/Sema/format-strings-signedness-fixit.c index 2ce794472b752..b4e6e975657aa 100644 --- a/clang/test/Sema/format-strings-signedness-fixit.c +++ b/clang/test/Sema/format-strings-signedness-fixit.c @@ -1,18 +1,12 @@ // RUN: cp %s %t -// RUN: %clang_cc1 -Wformat -Wformat-signedness -fixit %t -// RUN: %clang_cc1 -fsyntax-only -Wformat -Wformat-signedness -Werror %t -// RUN: %clang_cc1 -E -o - %t | FileCheck %s +// RUN: %clang_cc1 -triple=x86_64-pc-linux-gnu -Wformat -Wformat-signedness -fixit %t +// RUN: %clang_cc1 -triple=x86_64-pc-linux-gnu -fsyntax-only -Wformat -Wformat-signedness -Werror %t +// RUN: %clang_cc1 -triple=x86_64-pc-linux-gnu -E -o - %t | FileCheck %s #include <limits.h> int printf(const char *restrict format, ...); -enum foo { - minus_one = -1, - int_val = INT_MAX, - unsigned_val = (unsigned)INT_MIN -}; - void test_printf_int(int x) { printf("%u", x); @@ -43,11 +37,44 @@ void test_printf_unsigned_long_long(unsigned long long x) printf("%lld", x); } -void test_printf_enum(enum foo x) +enum enum_int { + minus_1 = -1 +}; + +void test_printf_enum_int(enum enum_int x) +{ + printf("%u", x); +} + +enum enum_unsigned { + zero = 0 +}; + +void test_printf_enum_unsigned(enum enum_unsigned x) +{ + printf("%d", x); +} + +enum enum_long { + minus_one = -1, + int_val = INT_MAX, + unsigned_val = (unsigned)INT_MIN +}; + +void test_printf_enum_long(enum enum_long x) { printf("%lu", x); } +enum enum_unsigned_long { + uint_max_plus = (unsigned long)UINT_MAX+1, +}; + +void test_printf_enum_unsigned_long(enum enum_unsigned_long x) +{ + printf("%ld", x); +} + // Validate the fixes. // CHECK: void test_printf_int(int x) // CHECK: printf("%d", x); @@ -61,5 +88,11 @@ void test_printf_enum(enum foo x) // CHECK: printf("%lld", x); // CHECK: void test_printf_unsigned_long_long(unsigned long long x) // CHECK: printf("%llu", x); -// CHECK: void test_printf_enum(enum foo x) +// CHECK: void test_printf_enum_int(enum enum_int x) +// CHECK: printf("%d", x); +// CHECK: void test_printf_enum_unsigned(enum enum_unsigned x) +// CHECK: printf("%u", x); +// CHECK: void test_printf_enum_long(enum enum_long x) // CHECK: printf("%ld", x); +// CHECK: void test_printf_enum_unsigned_long(enum enum_unsigned_long x) +// CHECK: printf("%lu", x); diff --git a/clang/test/Sema/format-strings-signedness.c b/clang/test/Sema/format-strings-signedness.c index 6e819ed715be3..fe994fa55f4e6 100644 --- a/clang/test/Sema/format-strings-signedness.c +++ b/clang/test/Sema/format-strings-signedness.c @@ -1,16 +1,11 @@ -// RUN: %clang_cc1 -std=c11 -fsyntax-only -verify -Wformat -Wformat-signedness %s +// RUN: %clang_cc1 -triple=x86_64-pc-linux-gnu -std=c11 -fsyntax-only -verify -Wformat -Wformat-signedness %s +// RUN: %clang_cc1 -triple=x86_64-pc-win32 -std=c11 -fsyntax-only -verify -Wformat -Wformat-signedness %s #include <limits.h> int printf(const char *restrict format, ...); int scanf(const char * restrict, ...); -enum foo { - minus_one = -1, - int_val = INT_MAX, - unsigned_val = (unsigned)INT_MIN -}; - void test_printf_bool(_Bool x) { printf("%d", x); // no-warning @@ -70,13 +65,54 @@ void test_printf_unsigned_long_long(unsigned long long x) printf("%llx", x); // no-warning } -void test_printf_enum(enum foo x) +enum enum_int { + minus_1 = -1 +}; + +void test_printf_enum_int(enum enum_int x) +{ + printf("%d", x); // no-warning + printf("%u", x); // expected-warning{{format specifies type 'unsigned int' but the argument has underlying type 'int'}} + printf("%x", x); // expected-warning{{format specifies type 'unsigned int' but the argument has underlying type 'int'}} +} + +#ifndef _WIN32 // Disabled due to enums have different underlying type on _WIN32 +enum enum_unsigned { + zero = 0 +}; + +void test_printf_enum_unsigned(enum enum_unsigned x) +{ + printf("%d", x); // expected-warning{{format specifies type 'int' but the argument has underlying type 'unsigned int'}} + printf("%u", x); // no-warning + printf("%x", x); // no-warning +} + +enum enum_long { + minus_one = -1, + int_val = INT_MAX, + unsigned_val = (unsigned)INT_MIN +}; + +void test_printf_enum_long(enum enum_long x) { printf("%ld", x); // no-warning printf("%lu", x); // expected-warning{{format specifies type 'unsigned long' but the argument has underlying type 'long'}} printf("%lx", x); // expected-warning{{format specifies type 'unsigned long' but the argument has underlying type 'long'}} } +enum enum_unsigned_long { + uint_max_plus = (unsigned long)UINT_MAX+1, +}; + +void test_printf_enum_unsigned_long(enum enum_unsigned_long x) +{ + printf("%ld", x); // expected-warning{{format specifies type 'long' but the argument has underlying type 'unsigned long'}} + printf("%lu", x); // no-warning + printf("%lx", x); // no-warning +} +#endif + void test_scanf_char(char *y) { scanf("%c", y); // no-warning } @@ -121,8 +157,28 @@ void test_scanf_unsigned_longlong(unsigned long long *x) { scanf("%llx", x); // no-warning } -void test_scanf_enum(enum foo *x) { +void test_scanf_enum_int(enum enum_int *x) { + scanf("%d", x); // no-warning + scanf("%u", x); // expected-warning{{format specifies type 'unsigned int *' but the argument has type 'enum enum_int *'}} + scanf("%x", x); // expected-warning{{format specifies type 'unsigned int *' but the argument has type 'enum enum_int *'}} +} + +#ifndef _WIN32 // Disabled due to enums have different underlying type on _WIN32 +void test_scanf_enum_unsigned(enum enum_unsigned *x) { + scanf("%d", x); // expected-warning{{format specifies type 'int *' but the argument has type 'enum enum_unsigned *'}} + scanf("%u", x); // no-warning + scanf("%x", x); // no-warning +} + +void test_scanf_enum_long(enum enum_long *x) { scanf("%ld", x); // no-warning - scanf("%lu", x); // expected-warning{{format specifies type 'unsigned long *' but the argument has type 'enum foo *'}} - scanf("%lx", x); // expected-warning{{format specifies type 'unsigned long *' but the argument has type 'enum foo *'}} + scanf("%lu", x); // expected-warning{{format specifies type 'unsigned long *' but the argument has type 'enum enum_long *'}} + scanf("%lx", x); // expected-warning{{format specifies type 'unsigned long *' but the argument has type 'enum enum_long *'}} +} + +void test_scanf_enum_unsigned_long(enum enum_unsigned_long *x) { + scanf("%ld", x); // expected-warning{{format specifies type 'long *' but the argument has type 'enum enum_unsigned_long *'}} + scanf("%lu", x); // no-warning + scanf("%lx", x); // no-warning } +#endif >From bf24c0f14daf27b07e09d7d04e908ed2b9f8504b Mon Sep 17 00:00:00 2001 From: Karl-Johan Karlsson <karl-johan.karls...@ericsson.com> Date: Tue, 12 Dec 2023 08:09:43 +0100 Subject: [PATCH 6/6] [Sema] Implement support for -Wformat-signedness Add more GCC compatibility. --- .../clang/Basic/DiagnosticSemaKinds.td | 3 - clang/lib/Sema/SemaChecking.cpp | 74 +++++++++---------- clang/test/Sema/format-strings-signedness.c | 12 +++ 3 files changed, 47 insertions(+), 42 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 038efb868b600..55037d61733b4 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -9727,9 +9727,6 @@ def warn_format_argument_needs_cast : Warning< def warn_format_argument_needs_cast_pedantic : Warning< warn_format_argument_needs_cast.Summary>, InGroup<FormatPedantic>, DefaultIgnore; -def warn_format_argument_needs_cast_signedness : Warning< - warn_format_argument_needs_cast.Summary>, - InGroup<FormatSignedness>, DefaultIgnore; def warn_printf_positional_arg_exceeds_data_args : Warning < "data argument position '%0' exceeds the number of data arguments (%1)">, InGroup<Format>; diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 848011ef730a8..259af12735e29 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -11815,6 +11815,20 @@ isArithmeticArgumentPromotion(Sema &S, const ImplicitCastExpr *ICE) { S.Context.getFloatingTypeOrder(From, To) < 0; } +static analyze_format_string::ArgType::MatchKind +handleFormatSignedness(analyze_format_string::ArgType::MatchKind Match, + DiagnosticsEngine &Diags, SourceLocation Loc) { + if (Match == analyze_format_string::ArgType::NoMatchSignedness) { + if (!Diags.isIgnored( + diag::warn_format_conversion_argument_type_mismatch_signedness, + Loc)) + Match = analyze_format_string::ArgType::NoMatch; + else + Match = analyze_format_string::ArgType::Match; + } + return Match; +} + bool CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, const char *StartSpecifier, @@ -11858,6 +11872,7 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, ArgType::MatchKind ImplicitMatch = ArgType::NoMatch; ArgType::MatchKind Match = AT.matchesType(S.Context, ExprTy); + Match = handleFormatSignedness(Match, S.getDiagnostics(), E->getExprLoc()); if (Match == ArgType::Match) return true; @@ -11881,6 +11896,8 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, ICE->getType() == S.Context.UnsignedIntTy) { // All further checking is done on the subexpression ImplicitMatch = AT.matchesType(S.Context, ExprTy); + ImplicitMatch = handleFormatSignedness( + ImplicitMatch, S.getDiagnostics(), E->getExprLoc()); if (ImplicitMatch == ArgType::Match) return true; } @@ -11913,7 +11930,6 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, Match = ArgType::NoMatch; } if (ImplicitMatch == ArgType::NoMatchPedantic || - ImplicitMatch == ArgType::NoMatchSignedness || ImplicitMatch == ArgType::NoMatchTypeConfusion) Match = ImplicitMatch; assert(Match != ArgType::MatchPromotion); @@ -12003,13 +12019,11 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, case ArgType::Match: case ArgType::MatchPromotion: case ArgType::NoMatchPromotionTypeConfusion: + case ArgType::NoMatchSignedness: llvm_unreachable("expected non-matching"); case ArgType::NoMatchPedantic: Diag = diag::warn_format_conversion_argument_type_mismatch_pedantic; break; - case ArgType::NoMatchSignedness: - Diag = diag::warn_format_conversion_argument_type_mismatch_signedness; - break; case ArgType::NoMatchTypeConfusion: Diag = diag::warn_format_conversion_argument_type_mismatch_confusion; break; @@ -12041,8 +12055,10 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, CastFix << (S.LangOpts.CPlusPlus ? ">" : ")"); SmallVector<FixItHint,4> Hints; - if (AT.matchesType(S.Context, IntendedTy) != ArgType::Match || - ShouldNotPrintDirectly) + ArgType::MatchKind IntendedMatch = AT.matchesType(S.Context, IntendedTy); + IntendedMatch = handleFormatSignedness(IntendedMatch, S.getDiagnostics(), + E->getExprLoc()); + if ((IntendedMatch != ArgType::Match) || ShouldNotPrintDirectly) Hints.push_back(FixItHint::CreateReplacement(SpecRange, os.str())); if (const CStyleCastExpr *CCast = dyn_cast<CStyleCastExpr>(E)) { @@ -12078,18 +12094,9 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, Name = TypedefTy->getDecl()->getName(); else Name = CastTyName; - unsigned Diag; - switch (Match) { - default: - Diag = diag::warn_format_argument_needs_cast; - break; - case analyze_format_string::ArgType::NoMatchPedantic: - Diag = diag::warn_format_argument_needs_cast_pedantic; - break; - case analyze_format_string::ArgType::NoMatchSignedness: - Diag = diag::warn_format_argument_needs_cast_signedness; - break; - } + unsigned Diag = Match == ArgType::NoMatchPedantic + ? diag::warn_format_argument_needs_cast_pedantic + : diag::warn_format_argument_needs_cast; EmitFormatDiagnostic(S.PDiag(Diag) << Name << IntendedTy << IsEnum << E->getSourceRange(), E->getBeginLoc(), /*IsStringLocation=*/false, @@ -12098,13 +12105,10 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, // In this case, the expression could be printed using a different // specifier, but we've decided that the specifier is probably correct // and we should cast instead. Just use the normal warning message. - unsigned Diag = - Match == ArgType::NoMatchSignedness - ? diag::warn_format_conversion_argument_type_mismatch_signedness - : diag::warn_format_conversion_argument_type_mismatch; EmitFormatDiagnostic( - S.PDiag(Diag) << AT.getRepresentativeTypeName(S.Context) << ExprTy - << IsEnum << E->getSourceRange(), + S.PDiag(diag::warn_format_conversion_argument_type_mismatch) + << AT.getRepresentativeTypeName(S.Context) << ExprTy << IsEnum + << E->getSourceRange(), E->getBeginLoc(), /*IsStringLocation*/ false, SpecRange, Hints); } } @@ -12123,13 +12127,11 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS, case ArgType::Match: case ArgType::MatchPromotion: case ArgType::NoMatchPromotionTypeConfusion: + case ArgType::NoMatchSignedness: llvm_unreachable("expected non-matching"); case ArgType::NoMatchPedantic: Diag = diag::warn_format_conversion_argument_type_mismatch_pedantic; break; - case ArgType::NoMatchSignedness: - Diag = diag::warn_format_conversion_argument_type_mismatch_signedness; - break; case ArgType::NoMatchTypeConfusion: Diag = diag::warn_format_conversion_argument_type_mismatch_confusion; break; @@ -12337,24 +12339,18 @@ bool CheckScanfHandler::HandleScanfSpecifier( analyze_format_string::ArgType::MatchKind Match = AT.matchesType(S.Context, Ex->getType()); + Match = handleFormatSignedness(Match, S.getDiagnostics(), Ex->getExprLoc()); + bool Pedantic = Match == analyze_format_string::ArgType::NoMatchPedantic; if (Match == analyze_format_string::ArgType::Match) return true; ScanfSpecifier fixedFS = FS; bool Success = fixedFS.fixType(Ex->getType(), Ex->IgnoreImpCasts()->getType(), S.getLangOpts(), S.Context); - unsigned Diag; - switch (Match) { - default: - Diag = diag::warn_format_conversion_argument_type_mismatch; - break; - case analyze_format_string::ArgType::NoMatchPedantic: - Diag = diag::warn_format_conversion_argument_type_mismatch_pedantic; - break; - case analyze_format_string::ArgType::NoMatchSignedness: - Diag = diag::warn_format_conversion_argument_type_mismatch_signedness; - break; - } + + unsigned Diag = + Pedantic ? diag::warn_format_conversion_argument_type_mismatch_pedantic + : diag::warn_format_conversion_argument_type_mismatch; if (Success) { // Get the fix string from the fixed format specifier. diff --git a/clang/test/Sema/format-strings-signedness.c b/clang/test/Sema/format-strings-signedness.c index fe994fa55f4e6..1646dee5a08fe 100644 --- a/clang/test/Sema/format-strings-signedness.c +++ b/clang/test/Sema/format-strings-signedness.c @@ -1,6 +1,10 @@ // RUN: %clang_cc1 -triple=x86_64-pc-linux-gnu -std=c11 -fsyntax-only -verify -Wformat -Wformat-signedness %s // RUN: %clang_cc1 -triple=x86_64-pc-win32 -std=c11 -fsyntax-only -verify -Wformat -Wformat-signedness %s +// Verify that -Wformat-signedness warnings are not reported with only -Wformat +// (gcc compat). +// RUN: %clang_cc1 -triple=x86_64-pc-linux-gnu -std=c11 -fsyntax-only -Wformat -Werror %s + #include <limits.h> int printf(const char *restrict format, ...); @@ -182,3 +186,11 @@ void test_scanf_enum_unsigned_long(enum enum_unsigned_long *x) { scanf("%lx", x); // no-warning } #endif + +// Verify that we can suppress a -Wformat-signedness warning by ignoring +// -Wformat (gcc compat). +void test_suppress(int x) +{ +#pragma GCC diagnostic ignored "-Wformat" + printf("%u", x); +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits