https://github.com/DeanSturtevant1 updated https://github.com/llvm/llvm-project/pull/150962
>From 1c6029240e6a19981105fc7561453fc2857e4703 Mon Sep 17 00:00:00 2001 From: Dean Sturtevant <dsturtev...@google.com> Date: Mon, 28 Jul 2025 10:30:50 -0400 Subject: [PATCH 1/3] Fix Clang bug that -Wformat-signedness is not reported properly. The goal is to correctly identify diagnostics that are emitted by virtue of -Wformat-signedness. Before this change, diagnostic messages triggered by -Wformat-signedness might look like: format specifies type 'unsigned int' but the argument has type 'int' [-Wformat] signedness of format specifier 'u' is incompatible with 'c' [-Wformat] With this change: format specifies type 'unsigned int' but the argument has type 'int', which differs in signedness [-Wformat-signedness] signedness of format specifier 'u' is incompatible with 'c' [-Wformat-signedness] Fix: - handleFormatSignedness can now return NoMatchSignedness. Callers handle this. - warn_format_conversion_argument_type extends the message it used to emit by a string that mentions "signedness". - warn_format_cmp_specifier_sign_mismatch is now correctly categorized as a diagnostic controlled by -Wformat-signedness. --- .../clang/Basic/DiagnosticSemaKinds.td | 4 +- clang/lib/Sema/SemaChecking.cpp | 41 +++++++------ clang/test/Sema/format-strings-signedness.c | 60 +++++++++---------- 3 files changed, 56 insertions(+), 49 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index da4216dbda4c9..dfca6577c7701 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10400,6 +10400,7 @@ def warn_format_conversion_argument_type_mismatch_pedantic : Extension< InGroup<FormatPedantic>; def warn_format_conversion_argument_type_mismatch_signedness : Warning< warn_format_conversion_argument_type_mismatch.Summary>, + "format specifies type %0 but the argument has %select{type|underlying type}2 %1, which differs in signedness">, InGroup<FormatSignedness>, DefaultIgnore; def warn_format_conversion_argument_type_mismatch_confusion : Warning< warn_format_conversion_argument_type_mismatch.Summary>, @@ -10513,7 +10514,8 @@ def warn_format_cmp_sensitivity_mismatch : Warning< def warn_format_cmp_specifier_mismatch : Warning< "format specifier '%0' is incompatible with '%1'">, InGroup<Format>; def warn_format_cmp_specifier_sign_mismatch : Warning< - "signedness of format specifier '%0' is incompatible with '%1'">, InGroup<Format>; + "signedness of format specifier '%0' is incompatible with '%1'">, InGroup<FormatSignedness>, + DefaultIgnore; def warn_format_cmp_specifier_mismatch_pedantic : Extension< warn_format_cmp_specifier_sign_mismatch.Summary>, InGroup<FormatPedantic>; def note_format_cmp_with : Note< diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index c74b67106ad74..bd8a8f6264d16 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -40,6 +40,7 @@ #include "clang/AST/UnresolvedSet.h" #include "clang/Basic/AddressSpaces.h" #include "clang/Basic/Diagnostic.h" +#include "clang/Basic/DiagnosticSema.h" #include "clang/Basic/IdentifierTable.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/LangOptions.h" @@ -7645,17 +7646,13 @@ bool EquatableFormatArgument::VerifyCompatible( break; case MK::NoMatchSignedness: - if (!S.getDiagnostics().isIgnored( - diag::warn_format_conversion_argument_type_mismatch_signedness, - ElementLoc)) { - EmitDiagnostic(S, - S.PDiag(diag::warn_format_cmp_specifier_sign_mismatch) - << buildFormatSpecifier() - << Other.buildFormatSpecifier(), - FmtExpr, InFunctionCall); - HadError = S.Diag(Other.ElementLoc, diag::note_format_cmp_with) - << 0 << Other.Range; - } + EmitDiagnostic(S, + S.PDiag(diag::warn_format_cmp_specifier_sign_mismatch) + << buildFormatSpecifier() + << Other.buildFormatSpecifier(), + FmtExpr, InFunctionCall); + HadError = S.Diag(Other.ElementLoc, diag::note_format_cmp_with) + << 0 << Other.Range; break; } return !HadError; @@ -8189,11 +8186,14 @@ static analyze_format_string::ArgType::MatchKind handleFormatSignedness(analyze_format_string::ArgType::MatchKind Match, DiagnosticsEngine &Diags, SourceLocation Loc) { if (Match == analyze_format_string::ArgType::NoMatchSignedness) { - Match = + if (Diags.isIgnored( + diag::warn_format_conversion_argument_type_mismatch_signedness, + Loc) || Diags.isIgnored( - diag::warn_format_conversion_argument_type_mismatch_signedness, Loc) - ? analyze_format_string::ArgType::Match - : analyze_format_string::ArgType::NoMatch; + // Arbitrary -Wformat diagnostic to detect -Wno-format: + diag::warn_format_conversion_argument_type_mismatch, Loc)) { + return analyze_format_string::ArgType::Match; + } } return Match; } @@ -8407,8 +8407,10 @@ 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::NoMatchSignedness: + Diag = diag::warn_format_conversion_argument_type_mismatch_signedness; + break; case ArgType::NoMatchPedantic: Diag = diag::warn_format_conversion_argument_type_mismatch_pedantic; break; @@ -8733,9 +8735,10 @@ 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; + bool Pedantic = Match == analyze_format_string::ArgType::NoMatchPedantic; + bool Signedness = Match == analyze_format_string::ArgType::NoMatchSignedness; ScanfSpecifier fixedFS = FS; bool Success = fixedFS.fixType(Ex->getType(), Ex->IgnoreImpCasts()->getType(), @@ -8743,7 +8746,9 @@ bool CheckScanfHandler::HandleScanfSpecifier( unsigned Diag = Pedantic ? diag::warn_format_conversion_argument_type_mismatch_pedantic - : diag::warn_format_conversion_argument_type_mismatch; + : Signedness + ? diag::warn_format_conversion_argument_type_mismatch_signedness + : 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 d5a8140d9ef8a..773ff412ac177 100644 --- a/clang/test/Sema/format-strings-signedness.c +++ b/clang/test/Sema/format-strings-signedness.c @@ -39,13 +39,13 @@ void test_printf_unsigned_char(unsigned char x) 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'}} + printf("%u", x); // expected-warning{{format specifies type 'unsigned int' but the argument has type 'int', which differs in signedness}} + printf("%x", x); // expected-warning{{format specifies type 'unsigned int' but the argument has type 'int', which differs in signedness}} } void test_printf_unsigned(unsigned x) { - printf("%d", x); // expected-warning{{format specifies type 'int' but the argument has type 'unsigned int'}} + printf("%d", x); // expected-warning{{format specifies type 'int' but the argument has type 'unsigned int', which differs in signedness}} printf("%u", x); // no-warning printf("%x", x); // no-warning } @@ -53,13 +53,13 @@ void test_printf_unsigned(unsigned x) 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'}} + printf("%lu", x); // expected-warning{{format specifies type 'unsigned long' but the argument has type 'long', which differs in signedness}} + printf("%lx", x); // expected-warning{{format specifies type 'unsigned long' but the argument has type 'long', which differs in signedness}} } 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("%ld", x); // expected-warning{{format specifies type 'long' but the argument has type 'unsigned long', which differs in signedness}} printf("%lu", x); // no-warning printf("%lx", x); // no-warning } @@ -67,13 +67,13 @@ void test_printf_unsigned_long(unsigned long x) 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'}} + printf("%llu", x); // expected-warning{{format specifies type 'unsigned long long' but the argument has type 'long long', which differs in signedness}} + printf("%llx", x); // expected-warning{{format specifies type 'unsigned long long' but the argument has type 'long long', which differs in signedness}} } 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("%lld", x); // expected-warning{{format specifies type 'long long' but the argument has type 'unsigned long long', which differs in signedness}} printf("%llu", x); // no-warning printf("%llx", x); // no-warning } @@ -85,8 +85,8 @@ enum enum_int { 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'}} + printf("%u", x); // expected-warning{{format specifies type 'unsigned int' but the argument has underlying type 'int', which differs in signedness}} + printf("%x", x); // expected-warning{{format specifies type 'unsigned int' but the argument has underlying type 'int', which differs in signedness}} } #ifndef _WIN32 // Disabled due to enums have different underlying type on _WIN32 @@ -96,7 +96,7 @@ enum enum_unsigned { 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("%d", x); // expected-warning{{format specifies type 'int' but the argument has underlying type 'unsigned int', which differs in signedness}} printf("%u", x); // no-warning printf("%x", x); // no-warning } @@ -110,8 +110,8 @@ enum enum_long { 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'}} + printf("%lu", x); // expected-warning{{format specifies type 'unsigned long' but the argument has underlying type 'long', which differs in signedness}} + printf("%lx", x); // expected-warning{{format specifies type 'unsigned long' but the argument has underlying type 'long', which differs in signedness}} } enum enum_unsigned_long { @@ -120,7 +120,7 @@ enum enum_unsigned_long { 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("%ld", x); // expected-warning{{format specifies type 'long' but the argument has underlying type 'unsigned long', which differs in signedness}} printf("%lu", x); // no-warning printf("%lx", x); // no-warning } @@ -136,61 +136,61 @@ void test_scanf_unsigned_char(unsigned char *y) { 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 *'}} + scanf("%u", x); // expected-warning{{format specifies type 'unsigned int *' but the argument has type 'int *', which differs in signedness}} + scanf("%x", x); // expected-warning{{format specifies type 'unsigned int *' but the argument has type 'int *', which differs in signedness}} } void test_scanf_unsigned(unsigned *x) { - scanf("%d", x); // expected-warning{{format specifies type 'int *' but the argument has type 'unsigned int *'}} + scanf("%d", x); // expected-warning{{format specifies type 'int *' but the argument has type 'unsigned int *', which differs in signedness}} 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 *'}} + scanf("%lu", x); // expected-warning{{format specifies type 'unsigned long *' but the argument has type 'long *', which differs in signedness}} + scanf("%lx", x); // expected-warning{{format specifies type 'unsigned long *' but the argument has type 'long *', which differs in signedness}} } 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("%ld", x); // expected-warning{{format specifies type 'long *' but the argument has type 'unsigned long *', which differs in signedness}} 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 *'}} + scanf("%llu", x); // expected-warning{{format specifies type 'unsigned long long *' but the argument has type 'long long *', which differs in signedness}} + scanf("%llx", x); // expected-warning{{format specifies type 'unsigned long long *' but the argument has type 'long long *', which differs in signedness}} } 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("%lld", x); // expected-warning{{format specifies type 'long long *' but the argument has type 'unsigned long long *', which differs in signedness}} scanf("%llu", x); // no-warning scanf("%llx", x); // no-warning } 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 *'}} + scanf("%u", x); // expected-warning{{format specifies type 'unsigned int *' but the argument has type 'enum enum_int *', which differs in signedness}} + scanf("%x", x); // expected-warning{{format specifies type 'unsigned int *' but the argument has type 'enum enum_int *', which differs in signedness}} } #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("%d", x); // expected-warning{{format specifies type 'int *' but the argument has type 'enum enum_unsigned *', which differs in signedness}} 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 enum_long *'}} - scanf("%lx", x); // expected-warning{{format specifies type 'unsigned long *' but the argument has type 'enum enum_long *'}} + scanf("%lu", x); // expected-warning{{format specifies type 'unsigned long *' but the argument has type 'enum enum_long *', which differs in signedness}} + scanf("%lx", x); // expected-warning{{format specifies type 'unsigned long *' but the argument has type 'enum enum_long *', which differs in signedness}} } 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("%ld", x); // expected-warning{{format specifies type 'long *' but the argument has type 'enum enum_unsigned_long *', which differs in signedness}} scanf("%lu", x); // no-warning scanf("%lx", x); // no-warning } >From 906679f9ef987c6e35110d0722f500b150a7cde3 Mon Sep 17 00:00:00 2001 From: Dean Sturtevant <dsturtev...@google.com> Date: Mon, 28 Jul 2025 10:36:14 -0400 Subject: [PATCH 2/3] clang-format --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 16 +++++++++------- clang/lib/Sema/SemaChecking.cpp | 4 ++-- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index dfca6577c7701..752ce93190d37 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10398,10 +10398,11 @@ 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>, - "format specifies type %0 but the argument has %select{type|underlying type}2 %1, which differs in signedness">, - InGroup<FormatSignedness>, DefaultIgnore; +def warn_format_conversion_argument_type_mismatch_signedness + : Warning<warn_format_conversion_argument_type_mismatch.Summary>, + "format specifies type %0 but the argument has %select{type|underlying " + "type}2 %1, which differs in signedness" > + , InGroup<FormatSignedness>, DefaultIgnore; def warn_format_conversion_argument_type_mismatch_confusion : Warning< warn_format_conversion_argument_type_mismatch.Summary>, InGroup<FormatTypeConfusion>, DefaultIgnore; @@ -10513,9 +10514,10 @@ def warn_format_cmp_sensitivity_mismatch : Warning< "it should be %select{unspecified|private|public|sensitive}1">, InGroup<Format>; def warn_format_cmp_specifier_mismatch : Warning< "format specifier '%0' is incompatible with '%1'">, InGroup<Format>; -def warn_format_cmp_specifier_sign_mismatch : Warning< - "signedness of format specifier '%0' is incompatible with '%1'">, InGroup<FormatSignedness>, - DefaultIgnore; +def warn_format_cmp_specifier_sign_mismatch + : Warning<"signedness of format specifier '%0' is incompatible with '%1'">, + InGroup<FormatSignedness>, + DefaultIgnore; def warn_format_cmp_specifier_mismatch_pedantic : Extension< warn_format_cmp_specifier_sign_mismatch.Summary>, InGroup<FormatPedantic>; def note_format_cmp_with : Note< diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index bd8a8f6264d16..a9584f8273406 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -7648,8 +7648,8 @@ bool EquatableFormatArgument::VerifyCompatible( case MK::NoMatchSignedness: EmitDiagnostic(S, S.PDiag(diag::warn_format_cmp_specifier_sign_mismatch) - << buildFormatSpecifier() - << Other.buildFormatSpecifier(), + << buildFormatSpecifier() + << Other.buildFormatSpecifier(), FmtExpr, InFunctionCall); HadError = S.Diag(Other.ElementLoc, diag::note_format_cmp_with) << 0 << Other.Range; >From 2789319f4e38b35c4906aa89784e89248513f05d Mon Sep 17 00:00:00 2001 From: Dean Sturtevant <dsturtev...@google.com> Date: Mon, 28 Jul 2025 10:50:36 -0400 Subject: [PATCH 3/3] Fix copy error. --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 752ce93190d37..c02da7dba96fe 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10398,8 +10398,7 @@ 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>, +def warn_format_conversion_argument_type_mismatch_signedness: Warning< "format specifies type %0 but the argument has %select{type|underlying " "type}2 %1, which differs in signedness" > , InGroup<FormatSignedness>, DefaultIgnore; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits