[clang] [clang] Catch missing format attributes (PR #105479)

Aaron Puchert via cfe-commits cfe-commits at lists.llvm.org
Sat Jun 7 16:05:37 PDT 2025


================
@@ -0,0 +1,259 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wmissing-format-attribute %s
+// RUN: %clang_cc1 -fsyntax-only -Wmissing-format-attribute -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
+// RUN: %clang_cc1 -fsyntax-only -x c++ -verify -Wmissing-format-attribute %s
+// RUN: %clang_cc1 -fsyntax-only -x c++ -verify -std=c++23 -Wmissing-format-attribute %s
+// RUN: %clang_cc1 -fsyntax-only -x c++ -Wmissing-format-attribute -fdiagnostics-parseable-fixits %s 2>&1 | FileCheck %s
+
+#ifndef __cplusplus
+typedef __CHAR16_TYPE__ char16_t;
+typedef __CHAR32_TYPE__ char32_t;
+typedef __WCHAR_TYPE__ wchar_t;
+#endif
+
+typedef __SIZE_TYPE__ size_t;
+typedef __builtin_va_list va_list;
+
+__attribute__((__format__(__printf__, 1, 2)))
+int printf(const char *, ...); // #printf
+
+__attribute__((__format__(__scanf__, 1, 2)))
+int scanf(const char *, ...); // #scanf
+
+__attribute__((__format__(__printf__, 1, 0)))
+int vprintf(const char *, va_list); // #vprintf
+
+__attribute__((__format__(__scanf__, 1, 0)))
+int vscanf(const char *, va_list); // #vscanf
+
+__attribute__((__format__(__printf__, 2, 0)))
+int vsprintf(char *, const char *, va_list); // #vsprintf
+
+__attribute__((__format__(__printf__, 3, 0)))
+int vsnprintf(char *, size_t, const char *, va_list); // #vsnprintf
+
+#ifndef __cplusplus
+int vwscanf(const wchar_t *, va_list); // #vwscanf
+#endif
+
+__attribute__((__format__(__scanf__, 1, 4)))
+void f1(char *out, const size_t len, const char *format, ... /* args */) // #f1
+{
+    va_list args;
+    vsnprintf(out, len, format, args);
+}
+
+__attribute__((__format__(__printf__, 1, 4)))
+void f2(char *out, const size_t len, const char *format, ... /* args */) // #f2
+{
+    va_list args;
+    vsnprintf(out, len, format, args); // expected-warning@#f2 {{diagnostic behavior may be improved by adding the 'printf' format attribute to the declaration of 'f2'}}
+                                       // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:6-[[@LINE-4]]:6}:"__attribute__((format(printf, 3, 4)))"
+                                       // expected-note at -2 {{'printf' format function}}
+}
+
+void f3(char *out, va_list args) // #f3
+{
+    vprintf(out, args); // expected-warning@#f3 {{diagnostic behavior may be improved by adding the 'printf' format attribute to the declaration of 'f3'}}
+                        // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:6-[[@LINE-3]]:6}:"__attribute__((format(printf, 1, 0)))"
+                        // expected-note at -2 {{'printf' format function}}
+}
+
+void f4(char* out, ... /* args */) // #f4
+{
+    va_list args;
+    vprintf("test", args);
+
+    const char *ch;
+    vprintf(ch, args);
+}
+
+void f5(va_list args) // #f5
+{
+    char *ch;
+    vscanf(ch, args);
+}
+
+void f6(char *out, va_list args) // #f6
+{
+    char *ch;
+    vprintf(ch, args);
+    vprintf("test", args);
+    vprintf(out, args); // expected-warning@#f6 {{diagnostic behavior may be improved by adding the 'printf' format attribute to the declaration of 'f6'}}
+                        // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:6-[[@LINE-6]]:6}:"__attribute__((format(printf, 1, 0)))"
+                        // expected-note at -2 {{'printf' format function}}
+}
+
+void f7(const char *out, ... /* args */) // #f7
+{
+    va_list args;
+
+    vscanf(out, args); // expected-warning@#f7 {{diagnostic behavior may be improved by adding the 'scanf' format attribute to the declaration of 'f7'}}
+                       // CHECK: fix-it:"{{.*}}":{[[@LINE-5]]:6-[[@LINE-5]]:6}:"__attribute__((format(scanf, 1, 2)))"
+                       // expected-note at -2 {{'scanf' format function}}
+}
+
+void f8(const char *out, ... /* args */) // #f8
+{
+    va_list args;
+
+    vscanf(out, args);
+    vprintf(out, args);
----------------
aaronpuchert wrote:

Here I don't see why we shouldn't warn on each of these calls. If the first call triggers a warning, why does the second call make it go away? This might be seen as leading to contradictory suggestions, but this function doesn't really make sense: the same arguments can't be both arguments to `printf` and `scanf`, unless the list is empty.

https://github.com/llvm/llvm-project/pull/105479


More information about the cfe-commits mailing list