[clang] [clang] Fix FP -Wformat in functions with 2+ attribute((format)) (PR #129954)

via cfe-commits cfe-commits at lists.llvm.org
Wed Mar 5 15:05:34 PST 2025


https://github.com/apple-fcloutier created https://github.com/llvm/llvm-project/pull/129954

When defining functions with two or more format attributes, if the format strings don't have the same format family, there is a false positive warning that the incorrect kind of format string is being passed at forwarded format string call sites.

This happens because we check that the format string family of each format attribute is compatible before we check that we're using the associated format parameter. The fix is to move the check down one scope, after we've established that we are checking the right parameter.

Tests are updated to include a true negative and a true positive of this situation.

>From 64b571f81c7fa4379e41c811680f83765dd7376b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?F=C3=A9lix=20Cloutier?= <fcloutier at apple.com>
Date: Wed, 5 Mar 2025 14:58:50 -0800
Subject: [PATCH] [clang] Fix FP -Wformat in functions with 2+
 attribute((format))

When defining functions with two or more format attributes, if the
format strings don't have the same format family, there is a false
positive warning that the incorrect kind of format string is being
passed at forwarded format string call sites.
---
 clang/lib/Sema/SemaChecking.cpp  | 23 ++++++++++++-----------
 clang/test/Sema/format-strings.c | 20 ++++++++++++++++++++
 2 files changed, 32 insertions(+), 11 deletions(-)

diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index f9926c6b4adab..9cac9cf5c4df7 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -6149,18 +6149,19 @@ static StringLiteralCheckType checkFormatStringExpr(
             if (!Sema::getFormatStringInfo(D, PVFormat->getFormatIdx(),
                                            PVFormat->getFirstArg(), &CallerFSI))
               continue;
-            // We also check if the formats are compatible.
-            // We can't pass a 'scanf' string to a 'printf' function.
-            if (Type != S.GetFormatStringType(PVFormat)) {
-              S.Diag(Args[format_idx]->getBeginLoc(),
-                     diag::warn_format_string_type_incompatible)
-                  << PVFormat->getType()->getName()
-                  << S.GetFormatStringTypeName(Type);
-              if (!InFunctionCall) {
-                S.Diag(E->getBeginLoc(), diag::note_format_string_defined);
+            if (PV->getFunctionScopeIndex() == CallerFSI.FormatIdx) {
+              // We also check if the formats are compatible.
+              // We can't pass a 'scanf' string to a 'printf' function.
+              if (Type != S.GetFormatStringType(PVFormat)) {
+                S.Diag(Args[format_idx]->getBeginLoc(),
+                       diag::warn_format_string_type_incompatible)
+                    << PVFormat->getType()->getName()
+                    << S.GetFormatStringTypeName(Type);
+                if (!InFunctionCall) {
+                  S.Diag(E->getBeginLoc(), diag::note_format_string_defined);
+                }
+                return SLCT_UncheckedLiteral;
               }
-              return SLCT_UncheckedLiteral;
-            } else if (PV->getFunctionScopeIndex() == CallerFSI.FormatIdx) {
               // Lastly, check that argument passing kinds transition in a
               // way that makes sense:
               // from a caller with FAPK_VAList, allow FAPK_VAList
diff --git a/clang/test/Sema/format-strings.c b/clang/test/Sema/format-strings.c
index efd88d5c18e66..af30ad5d15fe2 100644
--- a/clang/test/Sema/format-strings.c
+++ b/clang/test/Sema/format-strings.c
@@ -496,6 +496,26 @@ void rdar8332221(va_list ap, int *x, long *y) {
   rdar8332221_vprintf_scanf("%", ap, "%d", x); // expected-warning{{incomplete format specifier}}
 }
 
+void rdar8332221_vprintf_scanf(const char *p, va_list ap, const char *s, ...) {
+  vprintf(p, ap);
+
+  va_list vs;
+  va_start(vs, s);
+  vscanf(s, vs);
+  va_end(vs);
+}
+
+__attribute__((__format__(__printf__, 1, 0)))
+__attribute__((__format__(__scanf__, 3, 4)))
+void vprintf_scanf_bad(const char *p, va_list ap, const char *s, ...) {
+  vscanf(p, ap); // expected-warning{{passing 'printf' format string where 'scanf' format string is expected}}
+
+  va_list vs;
+  va_start(vs, s);
+  vprintf(s, vs); // expected-warning{{passing 'scanf' format string where 'printf' format string is expected}}
+  va_end(vs);
+}
+
 // PR8641
 void pr8641(void) {
   printf("%#x\n", 10);



More information about the cfe-commits mailing list