https://github.com/bozicrHT updated 
https://github.com/llvm/llvm-project/pull/160988

From 4179480b4d3522a8412f2b6bea5997282d2fc41c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Radovan=20Bo=C5=BEi=C4=87?= <[email protected]>
Date: Fri, 26 Sep 2025 16:00:00 +0200
Subject: [PATCH 1/6] [clang][sema] Add nonnull attribute to builtin format
 functions

Annotate printf/scanf and related builtins with the nonnull attribute on
their format string parameters. This enables diagnostics when NULL is
passed, matching GCC behavior. Updated existing Sema tests and added new
one for coverage. Closes issue #33923
---
 clang/include/clang/Basic/Builtins.h       |  4 ++
 clang/include/clang/Basic/Builtins.td      | 31 ++++-----
 clang/include/clang/Basic/BuiltinsBase.td  |  2 +-
 clang/lib/Basic/Builtins.cpp               | 48 +++++++++-----
 clang/lib/Sema/SemaDecl.cpp                |  9 +++
 clang/test/Sema/format-strings-nonnull.c   | 74 ++++++++++++++++++++++
 clang/test/Sema/format-strings-nonnull.cpp | 40 ++++++++++++
 clang/test/Sema/format-strings.c           |  6 +-
 clang/test/SemaCXX/format-strings-0x.cpp   |  2 +
 clang/test/SemaObjC/format-strings-objc.m  |  3 +-
 10 files changed, 183 insertions(+), 36 deletions(-)
 create mode 100644 clang/test/Sema/format-strings-nonnull.c
 create mode 100644 clang/test/Sema/format-strings-nonnull.cpp

diff --git a/clang/include/clang/Basic/Builtins.h 
b/clang/include/clang/Basic/Builtins.h
index 3a5e31de2bc50..6d26a7b92a0fd 100644
--- a/clang/include/clang/Basic/Builtins.h
+++ b/clang/include/clang/Basic/Builtins.h
@@ -392,6 +392,10 @@ class Context {
   bool performsCallback(unsigned ID,
                         llvm::SmallVectorImpl<int> &Encoding) const;
 
+  /// Return true if this builtin has parameters that must be non-null.
+  /// The parameter indices are appended into 'Indxs'.
+  bool isNonNull(unsigned ID, llvm::SmallVectorImpl<int> &Indxs) const;
+
   /// Return true if this function has no side effects and doesn't
   /// read memory, except for possibly errno or raising FP exceptions.
   ///
diff --git a/clang/include/clang/Basic/Builtins.td 
b/clang/include/clang/Basic/Builtins.td
index 9bc70ea5e5858..5f9843f482276 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -3095,104 +3095,105 @@ def StrLen : LibBuiltin<"string.h"> {
 // FIXME: This list is incomplete.
 def Printf : LibBuiltin<"stdio.h"> {
   let Spellings = ["printf"];
-  let Attributes = [PrintfFormat<0>];
+  let Attributes = [PrintfFormat<0>, NonNull<[0]>];
   let Prototype = "int(char const*, ...)";
 }
 
 // FIXME: The builtin and library function should have the same signature.
 def BuiltinPrintf : Builtin {
   let Spellings = ["__builtin_printf"];
-  let Attributes = [NoThrow, PrintfFormat<0>, FunctionWithBuiltinPrefix];
+  let Attributes = [NoThrow, PrintfFormat<0>, FunctionWithBuiltinPrefix,
+                    NonNull<[0]>];
   let Prototype = "int(char const* restrict, ...)";
 }
 
 def FPrintf : LibBuiltin<"stdio.h"> {
   let Spellings = ["fprintf"];
-  let Attributes = [NoThrow, PrintfFormat<1>];
+  let Attributes = [NoThrow, PrintfFormat<1>, NonNull<[1]>];
   let Prototype = "int(FILE* restrict, char const* restrict, ...)";
   let AddBuiltinPrefixedAlias = 1;
 }
 
 def SnPrintf : LibBuiltin<"stdio.h"> {
   let Spellings = ["snprintf"];
-  let Attributes = [NoThrow, PrintfFormat<2>];
+  let Attributes = [NoThrow, PrintfFormat<2>, NonNull<[2]>];
   let Prototype = "int(char* restrict, size_t, char const* restrict, ...)";
   let AddBuiltinPrefixedAlias = 1;
 }
 
 def SPrintf : LibBuiltin<"stdio.h"> {
   let Spellings = ["sprintf"];
-  let Attributes = [NoThrow, PrintfFormat<1>];
+  let Attributes = [NoThrow, PrintfFormat<1>, NonNull<[1]>];
   let Prototype = "int(char* restrict, char const* restrict, ...)";
   let AddBuiltinPrefixedAlias = 1;
 }
 
 def VPrintf : LibBuiltin<"stdio.h"> {
   let Spellings = ["vprintf"];
-  let Attributes = [NoThrow, VPrintfFormat<0>];
+  let Attributes = [NoThrow, VPrintfFormat<0>, NonNull<[0]>];
   let Prototype = "int(char const* restrict, __builtin_va_list)";
   let AddBuiltinPrefixedAlias = 1;
 }
 
 def VfPrintf : LibBuiltin<"stdio.h"> {
   let Spellings = ["vfprintf"];
-  let Attributes = [NoThrow, VPrintfFormat<1>];
+  let Attributes = [NoThrow, VPrintfFormat<1>, NonNull<[1]>];
   let Prototype = "int(FILE* restrict, char const* restrict, 
__builtin_va_list)";
   let AddBuiltinPrefixedAlias = 1;
 }
 
 def VsnPrintf : LibBuiltin<"stdio.h"> {
   let Spellings = ["vsnprintf"];
-  let Attributes = [NoThrow, VPrintfFormat<2>];
+  let Attributes = [NoThrow, VPrintfFormat<2>, NonNull<[2]>];
   let Prototype = "int(char* restrict, size_t, char const* restrict, 
__builtin_va_list)";
   let AddBuiltinPrefixedAlias = 1;
 }
 
 def VsPrintf : LibBuiltin<"stdio.h"> {
   let Spellings = ["vsprintf"];
-  let Attributes = [NoThrow, VPrintfFormat<1>];
+  let Attributes = [NoThrow, VPrintfFormat<1>, NonNull<[1]>];
   let Prototype = "int(char* restrict, char const* restrict, 
__builtin_va_list)";
   let AddBuiltinPrefixedAlias = 1;
 }
 
 def Scanf : LibBuiltin<"stdio.h"> {
   let Spellings = ["scanf"];
-  let Attributes = [ScanfFormat<0>];
+  let Attributes = [ScanfFormat<0>, NonNull<[0]>];
   let Prototype = "int(char const* restrict, ...)";
   let AddBuiltinPrefixedAlias = 1;
 }
 
 def FScanf : LibBuiltin<"stdio.h"> {
   let Spellings = ["fscanf"];
-  let Attributes = [ScanfFormat<1>];
+  let Attributes = [ScanfFormat<1>, NonNull<[1]>];
   let Prototype = "int(FILE* restrict, char const* restrict, ...)";
   let AddBuiltinPrefixedAlias = 1;
 }
 
 def SScanf : LibBuiltin<"stdio.h"> {
   let Spellings = ["sscanf"];
-  let Attributes = [ScanfFormat<1>];
+  let Attributes = [ScanfFormat<1>, NonNull<[1]>];
   let Prototype = "int(char const* restrict, char const* restrict, ...)";
   let AddBuiltinPrefixedAlias = 1;
 }
 
 def VScanf : LibBuiltin<"stdio.h"> {
   let Spellings = ["vscanf"];
-  let Attributes = [VScanfFormat<0>];
+  let Attributes = [VScanfFormat<0>, NonNull<[0]>];
   let Prototype = "int(char const* restrict, __builtin_va_list)";
   let AddBuiltinPrefixedAlias = 1;
 }
 
 def VFScanf : LibBuiltin<"stdio.h"> {
   let Spellings = ["vfscanf"];
-  let Attributes = [VScanfFormat<1>];
+  let Attributes = [VScanfFormat<1>, NonNull<[1]>];
   let Prototype = "int(FILE* restrict, char const* restrict, 
__builtin_va_list)";
   let AddBuiltinPrefixedAlias = 1;
 }
 
 def VSScanf : LibBuiltin<"stdio.h"> {
   let Spellings = ["vsscanf"];
-  let Attributes = [VScanfFormat<1>];
+  let Attributes = [VScanfFormat<1>, NonNull<[1]>];
   let Prototype = "int(char const* restrict, char const* restrict, 
__builtin_va_list)";
   let AddBuiltinPrefixedAlias = 1;
 }
diff --git a/clang/include/clang/Basic/BuiltinsBase.td 
b/clang/include/clang/Basic/BuiltinsBase.td
index 09bc9f89059fe..73918ab167b8d 100644
--- a/clang/include/clang/Basic/BuiltinsBase.td
+++ b/clang/include/clang/Basic/BuiltinsBase.td
@@ -32,7 +32,6 @@ def Const : Attribute<"c">;
 def NoThrow : Attribute<"n">;
 def Pure : Attribute<"U">;
 def ReturnsTwice : Attribute<"j">;
-//  FIXME: gcc has nonnull
 
 // builtin-specific attributes
 // ---------------------------
@@ -85,6 +84,7 @@ def Consteval : Attribute<"EG">;
 // Callback behavior: the first index argument is called with the arguments
 // indicated by the remaining indices.
 class Callback<list<int> ArgIndices> : MultiIndexAttribute<"C", ArgIndices>;
+class NonNull<list<int> ArgIndices> : MultiIndexAttribute<"N", ArgIndices>;
 
 // Prefixes
 // ========
diff --git a/clang/lib/Basic/Builtins.cpp b/clang/lib/Basic/Builtins.cpp
index acd98fe84adf5..a813726b7b848 100644
--- a/clang/lib/Basic/Builtins.cpp
+++ b/clang/lib/Basic/Builtins.cpp
@@ -293,30 +293,48 @@ bool Builtin::Context::isScanfLike(unsigned ID, unsigned 
&FormatIdx,
   return isLike(ID, FormatIdx, HasVAListArg, "sS");
 }
 
-bool Builtin::Context::performsCallback(unsigned ID,
-                                        SmallVectorImpl<int> &Encoding) const {
-  const char *CalleePos = ::strchr(getAttributesString(ID), 'C');
-  if (!CalleePos)
-    return false;
-
-  ++CalleePos;
-  assert(*CalleePos == '<' &&
-         "Callback callee specifier must be followed by a '<'");
-  ++CalleePos;
+static void parseCommaSeparatedIndices(const char *CurrPos,
+                                       llvm::SmallVectorImpl<int> &Indxs) {
+  assert(*CurrPos == '<' && "Expected '<' to start index list");
+  ++CurrPos;
 
   char *EndPos;
-  int CalleeIdx = ::strtol(CalleePos, &EndPos, 10);
-  assert(CalleeIdx >= 0 && "Callee index is supposed to be positive!");
-  Encoding.push_back(CalleeIdx);
+  int PosIdx = ::strtol(CurrPos, &EndPos, 10);
+  assert(PosIdx >= 0 && "Index is supposed to be positive!");
+  Indxs.push_back(PosIdx);
 
   while (*EndPos == ',') {
     const char *PayloadPos = EndPos + 1;
 
     int PayloadIdx = ::strtol(PayloadPos, &EndPos, 10);
-    Encoding.push_back(PayloadIdx);
+    Indxs.push_back(PayloadIdx);
   }
 
-  assert(*EndPos == '>' && "Callback callee specifier must end with a '>'");
+  assert(*EndPos == '>' && "Index list must end with '>'");
+}
+
+bool Builtin::Context::isNonNull(unsigned ID,
+                                 llvm::SmallVectorImpl<int> &Indxs) const {
+
+  const char *AttrPos = ::strchr(getAttributesString(ID), 'N');
+  if (!AttrPos)
+    return false;
+
+  ++AttrPos;
+  parseCommaSeparatedIndices(AttrPos, Indxs);
+
+  return true;
+}
+
+bool Builtin::Context::performsCallback(unsigned ID,
+                                        SmallVectorImpl<int> &Encoding) const {
+  const char *CalleePos = ::strchr(getAttributesString(ID), 'C');
+  if (!CalleePos)
+    return false;
+
+  ++CalleePos;
+  parseCommaSeparatedIndices(CalleePos, Encoding);
+
   return true;
 }
 
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 9ef7a2698913d..d9df60af114fb 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -17141,6 +17141,15 @@ void Sema::AddKnownFunctionAttributes(FunctionDecl 
*FD) {
       }
     }
 
+    SmallVector<int, 4> Indxs;
+    if (Context.BuiltinInfo.isNonNull(BuiltinID, Indxs) &&
+        !FD->hasAttr<NonNullAttr>()) {
+      llvm::SmallVector<ParamIdx, 4> ParamIndxs;
+      for (int I : Indxs)
+        ParamIndxs.push_back(ParamIdx(I + 1, FD));
+      FD->addAttr(NonNullAttr::CreateImplicit(Context, ParamIndxs.data(),
+                                              ParamIndxs.size()));
+    }
     if (Context.BuiltinInfo.isReturnsTwice(BuiltinID) &&
         !FD->hasAttr<ReturnsTwiceAttr>())
       FD->addAttr(ReturnsTwiceAttr::CreateImplicit(Context,
diff --git a/clang/test/Sema/format-strings-nonnull.c 
b/clang/test/Sema/format-strings-nonnull.c
new file mode 100644
index 0000000000000..86ce24a27ca11
--- /dev/null
+++ b/clang/test/Sema/format-strings-nonnull.c
@@ -0,0 +1,74 @@
+// RUN: %clang_cc1 -fsyntax-only --std=c23 -verify -Wnonnull 
-Wno-format-security %s
+
+#define NULL  (void*)0
+
+typedef struct _FILE FILE;
+typedef __SIZE_TYPE__ size_t;
+typedef __builtin_va_list va_list;
+int printf(char const* restrict, ...);
+int __builtin_printf(char const* restrict, ...);
+int fprintf(FILE* restrict, char const* restrict, ...);
+int snprintf(char* restrict, size_t, char const* restrict, ...);
+int sprintf(char* restrict, char const* restrict, ...);
+int vprintf(char const* restrict, __builtin_va_list);
+int vfprintf(FILE* restrict, char const* restrict, __builtin_va_list);
+int vsnprintf(char* restrict, size_t, char const* restrict, __builtin_va_list);
+int vsprintf(char* restrict, char const* restrict, __builtin_va_list);
+
+int scanf(char const* restrict, ...);
+int fscanf(FILE* restrict, char const* restrict, ...);
+int sscanf(char const* restrict, char const* restrict, ...);
+int vscanf(char const* restrict, __builtin_va_list);
+int vfscanf(FILE* restrict, char const* restrict, __builtin_va_list);
+int vsscanf(char const* restrict, char const* restrict, __builtin_va_list);
+
+
+void check_format_string(FILE *fp, va_list ap) {
+    char buf[256];
+    char* const fmt = NULL;
+
+    printf(fmt);
+    // expected-warning@-1{{null passed to a callee that requires a non-null 
argument}}
+
+    __builtin_printf(NULL, "xxd");
+    // expected-warning@-1{{null passed to a callee that requires a non-null 
argument}}
+
+    fprintf(fp, NULL, 25);
+    // expected-warning@-1{{null passed to a callee that requires a non-null 
argument}}
+
+    sprintf(buf, NULL, 42);
+    // expected-warning@-1{{null passed to a callee that requires a non-null 
argument}}
+
+    snprintf(buf, 10, 0, 42);
+    // expected-warning@-1{{null passed to a callee that requires a non-null 
argument}}
+
+    vprintf(fmt, ap);
+    // expected-warning@-1{{null passed to a callee that requires a non-null 
argument}}
+
+    vfprintf(fp, 0, ap);
+    // expected-warning@-1{{null passed to a callee that requires a non-null 
argument}}
+
+    vsprintf(buf, nullptr, ap);
+    // expected-warning@-1{{null passed to a callee that requires a non-null 
argument}}
+
+    vsnprintf(buf, 10, fmt, ap);
+    // expected-warning@-1{{null passed to a callee that requires a non-null 
argument}}
+
+    scanf(NULL);
+    // expected-warning@-1{{null passed to a callee that requires a non-null 
argument}}
+
+    fscanf(fp, nullptr);
+    // expected-warning@-1{{null passed to a callee that requires a non-null 
argument}}
+
+    sscanf(buf, fmt);
+    // expected-warning@-1{{null passed to a callee that requires a non-null 
argument}}
+
+    vscanf(NULL, ap);
+    // expected-warning@-1{{null passed to a callee that requires a non-null 
argument}}
+
+    vfscanf(fp, fmt, ap);
+    // expected-warning@-1{{null passed to a callee that requires a non-null 
argument}}
+
+    vsscanf(buf, NULL, ap);
+    // expected-warning@-1{{null passed to a callee that requires a non-null 
argument}}
+}
\ No newline at end of file
diff --git a/clang/test/Sema/format-strings-nonnull.cpp 
b/clang/test/Sema/format-strings-nonnull.cpp
new file mode 100644
index 0000000000000..55a3ed1788c79
--- /dev/null
+++ b/clang/test/Sema/format-strings-nonnull.cpp
@@ -0,0 +1,40 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -Wnonnull %s
+
+#ifdef __cplusplus
+# define EXTERN_C extern "C"
+#else
+# define EXTERN_C extern
+#endif
+
+typedef struct _FILE FILE;
+typedef __SIZE_TYPE__ size_t;
+typedef __builtin_va_list va_list;
+
+EXTERN_C int printf(const char *, ...);
+EXTERN_C int fprintf(FILE *, const char *restrict, ...);
+EXTERN_C int sprintf(char* restrict, char const* res, ...);
+
+EXTERN_C int scanf(char const *restrict, ...);
+EXTERN_C int fscanf(FILE* restrict, char const* res, ...);
+
+void test(FILE *fp) {
+  char buf[256];
+
+  __builtin_printf(__null, "x");
+  // expected-warning@-1 {{null passed to a callee that requires a non-null 
argument}}
+
+  printf(__null, "xxd");
+  // expected-warning@-1 {{null passed to a callee that requires a non-null 
argument}}
+
+  fprintf(fp, __null, 42);
+  // expected-warning@-1 {{null passed to a callee that requires a non-null 
argument}}
+
+  sprintf(buf, __null);
+  // expected-warning@-1 {{null passed to a callee that requires a non-null 
argument}}
+
+  scanf(__null);
+  // expected-warning@-1 {{null passed to a callee that requires a non-null 
argument}}
+
+  fscanf(fp, __null);
+  // expected-warning@-1 {{null passed to a callee that requires a non-null 
argument}}
+}
diff --git a/clang/test/Sema/format-strings.c b/clang/test/Sema/format-strings.c
index 103dd8ab5a85c..164db45fa2053 100644
--- a/clang/test/Sema/format-strings.c
+++ b/clang/test/Sema/format-strings.c
@@ -480,11 +480,9 @@ void pr7981(wint_t c, wchar_t c2) {
 #endif
 }
 
-// -Wformat-security says NULL is not a string literal
 void rdar8269537(void) {
-  // This is likely to crash in most cases, but -Wformat-nonliteral technically
-  // doesn't warn in this case.
-  printf(0); // no-warning
+  printf(0);
+  // expected-warning@-1{{null passed to a callee that requires a non-null 
argument}}
 }
 
 // Handle functions with multiple format attributes.
diff --git a/clang/test/SemaCXX/format-strings-0x.cpp 
b/clang/test/SemaCXX/format-strings-0x.cpp
index 7d37f8276f29f..e0ca7a270c993 100644
--- a/clang/test/SemaCXX/format-strings-0x.cpp
+++ b/clang/test/SemaCXX/format-strings-0x.cpp
@@ -14,6 +14,7 @@ void f(char **sp, float *fp) {
   printf("%a", 1.0);
   scanf("%afoobar", fp);
   printf(nullptr);
+  // expected-warning@-1{{null passed to a callee that requires a non-null 
argument}}
   printf(*sp); // expected-warning {{not a string literal}}
   // expected-note@-1{{treat the string as an argument to avoid this}}
 
@@ -32,4 +33,5 @@ void f(char **sp, float *fp) {
   printf("init list: %d", { 0 }); // expected-error {{cannot pass initializer 
list to variadic function; expected type from format string was 'int'}}
   printf("void: %d", f(sp, fp)); // expected-error {{cannot pass expression of 
type 'void' to variadic function; expected type from format string was 'int'}}
   printf(0, { 0 }); // expected-error {{cannot pass initializer list to 
variadic function}}
+  // expected-warning@-1{{null passed to a callee that requires a non-null 
argument}}
 }
diff --git a/clang/test/SemaObjC/format-strings-objc.m 
b/clang/test/SemaObjC/format-strings-objc.m
index 40c1d31b1fd4c..babbb40394267 100644
--- a/clang/test/SemaObjC/format-strings-objc.m
+++ b/clang/test/SemaObjC/format-strings-objc.m
@@ -130,7 +130,7 @@ void rdar10743758(id x) {
   printf(s2); // expected-warning {{more '%' conversions than data arguments}}
 
   const char * const s3 = (const char *)0;
-  printf(s3); // no-warning (NULL is a valid format string)
+  printf(s3); // expected-warning {{null passed to a callee that requires a 
non-null argument}}
 
   NSString * const ns1 = @"constant string %s"; // expected-note {{format 
string is defined here}}
   NSLog(ns1); // expected-warning {{more '%' conversions than data arguments}}
@@ -259,6 +259,7 @@ void testByValueObjectInFormat(Foo *obj) {
   printf("%d %d %d", 1L, *obj, 1L); // expected-error {{cannot pass object 
with interface type 'Foo' by value to variadic function; expected type from 
format string was 'int'}} expected-warning 2 {{format specifies type 'int' but 
the argument has type 'long'}}
   printf("%!", *obj); // expected-error {{cannot pass object with interface 
type 'Foo' by value through variadic function}} expected-warning {{invalid 
conversion specifier}}
   printf(0, *obj); // expected-error {{cannot pass object with interface type 
'Foo' by value through variadic function}}
+  // expected-warning@-1{{null passed to a callee that requires a non-null 
argument}}
 
   [Bar log2:@"%d", *obj]; // expected-error {{cannot pass object with 
interface type 'Foo' by value to variadic method; expected type from format 
string was 'int'}}
 }

From c7a6ed9dfa215f6b684535a41cc9bd762599f402 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Radovan=20Bo=C5=BEi=C4=87?= <[email protected]>
Date: Tue, 14 Oct 2025 12:07:20 +0200
Subject: [PATCH 2/6] Add newline at the end of file

---
 clang/test/Sema/format-strings-nonnull.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/test/Sema/format-strings-nonnull.c 
b/clang/test/Sema/format-strings-nonnull.c
index 86ce24a27ca11..14231b1a4e9dc 100644
--- a/clang/test/Sema/format-strings-nonnull.c
+++ b/clang/test/Sema/format-strings-nonnull.c
@@ -71,4 +71,4 @@ void check_format_string(FILE *fp, va_list ap) {
 
     vsscanf(buf, NULL, ap);
     // expected-warning@-1{{null passed to a callee that requires a non-null 
argument}}
-}
\ No newline at end of file
+}

From 775fcf1bd89ceb0d7534b277c7d85357d64bfe50 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Radovan=20Bo=C5=BEi=C4=87?= <[email protected]>
Date: Tue, 14 Oct 2025 15:53:07 +0200
Subject: [PATCH 3/6] Add additional nonnull attributes for printf/scanf family
 functions

Extend nonnull coverage to include not only format string parameters,
but also FILE* and char* arguments (e.g. for sscanf, fprintf, etc.).
---
 clang/include/clang/Basic/Builtins.td      | 20 ++++++++++----------
 clang/test/Analysis/stream.c               |  8 ++++----
 clang/test/Sema/format-strings-nonnull.c   | 15 +++++++++++----
 clang/test/Sema/format-strings-nonnull.cpp | 11 ++++++++++-
 clang/test/Sema/warn-fortify-scanf.c       |  7 +++++++
 5 files changed, 42 insertions(+), 19 deletions(-)

diff --git a/clang/include/clang/Basic/Builtins.td 
b/clang/include/clang/Basic/Builtins.td
index 5f9843f482276..585da2b9a79a0 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -3109,21 +3109,21 @@ def BuiltinPrintf : Builtin {
 
 def FPrintf : LibBuiltin<"stdio.h"> {
   let Spellings = ["fprintf"];
-  let Attributes = [NoThrow, PrintfFormat<1>, NonNull<[1]>];
+  let Attributes = [NoThrow, PrintfFormat<1>, NonNull<[0, 1]>];
   let Prototype = "int(FILE* restrict, char const* restrict, ...)";
   let AddBuiltinPrefixedAlias = 1;
 }
 
 def SnPrintf : LibBuiltin<"stdio.h"> {
   let Spellings = ["snprintf"];
-  let Attributes = [NoThrow, PrintfFormat<2>, NonNull<[2]>];
+  let Attributes = [NoThrow, PrintfFormat<2>, NonNull<[0, 2]>];
   let Prototype = "int(char* restrict, size_t, char const* restrict, ...)";
   let AddBuiltinPrefixedAlias = 1;
 }
 
 def SPrintf : LibBuiltin<"stdio.h"> {
   let Spellings = ["sprintf"];
-  let Attributes = [NoThrow, PrintfFormat<1>, NonNull<[1]>];
+  let Attributes = [NoThrow, PrintfFormat<1>, NonNull<[0, 1]>];
   let Prototype = "int(char* restrict, char const* restrict, ...)";
   let AddBuiltinPrefixedAlias = 1;
 }
@@ -3137,21 +3137,21 @@ def VPrintf : LibBuiltin<"stdio.h"> {
 
 def VfPrintf : LibBuiltin<"stdio.h"> {
   let Spellings = ["vfprintf"];
-  let Attributes = [NoThrow, VPrintfFormat<1>, NonNull<[1]>];
+  let Attributes = [NoThrow, VPrintfFormat<1>, NonNull<[0, 1]>];
   let Prototype = "int(FILE* restrict, char const* restrict, 
__builtin_va_list)";
   let AddBuiltinPrefixedAlias = 1;
 }
 
 def VsnPrintf : LibBuiltin<"stdio.h"> {
   let Spellings = ["vsnprintf"];
-  let Attributes = [NoThrow, VPrintfFormat<2>, NonNull<[2]>];
+  let Attributes = [NoThrow, VPrintfFormat<2>, NonNull<[0, 2]>];
   let Prototype = "int(char* restrict, size_t, char const* restrict, 
__builtin_va_list)";
   let AddBuiltinPrefixedAlias = 1;
 }
 
 def VsPrintf : LibBuiltin<"stdio.h"> {
   let Spellings = ["vsprintf"];
-  let Attributes = [NoThrow, VPrintfFormat<1>, NonNull<[1]>];
+  let Attributes = [NoThrow, VPrintfFormat<1>, NonNull<[0, 1]>];
   let Prototype = "int(char* restrict, char const* restrict, 
__builtin_va_list)";
   let AddBuiltinPrefixedAlias = 1;
 }
@@ -3165,14 +3165,14 @@ def Scanf : LibBuiltin<"stdio.h"> {
 
 def FScanf : LibBuiltin<"stdio.h"> {
   let Spellings = ["fscanf"];
-  let Attributes = [ScanfFormat<1>, NonNull<[1]>];
+  let Attributes = [ScanfFormat<1>, NonNull<[0, 1]>];
   let Prototype = "int(FILE* restrict, char const* restrict, ...)";
   let AddBuiltinPrefixedAlias = 1;
 }
 
 def SScanf : LibBuiltin<"stdio.h"> {
   let Spellings = ["sscanf"];
-  let Attributes = [ScanfFormat<1>, NonNull<[1]>];
+  let Attributes = [ScanfFormat<1>, NonNull<[0, 1]>];
   let Prototype = "int(char const* restrict, char const* restrict, ...)";
   let AddBuiltinPrefixedAlias = 1;
 }
@@ -3186,14 +3186,14 @@ def VScanf : LibBuiltin<"stdio.h"> {
 
 def VFScanf : LibBuiltin<"stdio.h"> {
   let Spellings = ["vfscanf"];
-  let Attributes = [VScanfFormat<1>, NonNull<[1]>];
+  let Attributes = [VScanfFormat<1>, NonNull<[0, 1]>];
   let Prototype = "int(FILE* restrict, char const* restrict, 
__builtin_va_list)";
   let AddBuiltinPrefixedAlias = 1;
 }
 
 def VSScanf : LibBuiltin<"stdio.h"> {
   let Spellings = ["vsscanf"];
-  let Attributes = [VScanfFormat<1>, NonNull<[1]>];
+  let Attributes = [VScanfFormat<1>, NonNull<[0, 1]>];
   let Prototype = "int(char const* restrict, char const* restrict, 
__builtin_va_list)";
   let AddBuiltinPrefixedAlias = 1;
 }
diff --git a/clang/test/Analysis/stream.c b/clang/test/Analysis/stream.c
index 10e6e5381ee6d..4654fdbb80334 100644
--- a/clang/test/Analysis/stream.c
+++ b/clang/test/Analysis/stream.c
@@ -52,13 +52,13 @@ void check_fputs(void) {
 
 void check_fprintf(void) {
   FILE *fp = tmpfile();
-  fprintf(fp, "ABC"); // expected-warning {{Stream pointer might be NULL}}
+  fprintf(fp, "ABC"); // expected-warning {{Null pointer passed to 1st 
parameter expecting 'nonnull'}}
   fclose(fp);
 }
 
 void check_fscanf(void) {
   FILE *fp = tmpfile();
-  fscanf(fp, "ABC"); // expected-warning {{Stream pointer might be NULL}}
+  fscanf(fp, "ABC"); // expected-warning {{Null pointer passed to 1st 
parameter expecting 'nonnull'}}
   fclose(fp);
 }
 
@@ -152,13 +152,13 @@ void f_dopen(int fd) {
 
 void f_vfprintf(int fd, va_list args) {
   FILE *F = fdopen(fd, "r");
-  vfprintf(F, "%d", args); // expected-warning {{Stream pointer might be NULL}}
+  vfprintf(F, "%d", args); // expected-warning {{Null pointer passed to 1st 
parameter expecting 'nonnull'}}
   fclose(F);
 }
 
 void f_vfscanf(int fd, va_list args) {
   FILE *F = fdopen(fd, "r");
-  vfscanf(F, "%u", args); // expected-warning {{Stream pointer might be NULL}}
+  vfscanf(F, "%u", args); // expected-warning {{Null pointer passed to 1st 
parameter expecting 'nonnull'}}
   fclose(F);
 }
 
diff --git a/clang/test/Sema/format-strings-nonnull.c 
b/clang/test/Sema/format-strings-nonnull.c
index 14231b1a4e9dc..313c10b56d062 100644
--- a/clang/test/Sema/format-strings-nonnull.c
+++ b/clang/test/Sema/format-strings-nonnull.c
@@ -25,6 +25,7 @@ int vsscanf(char const* restrict, char const* restrict, 
__builtin_va_list);
 
 void check_format_string(FILE *fp, va_list ap) {
     char buf[256];
+    int num;
     char* const fmt = NULL;
 
     printf(fmt);
@@ -36,11 +37,13 @@ void check_format_string(FILE *fp, va_list ap) {
     fprintf(fp, NULL, 25);
     // expected-warning@-1{{null passed to a callee that requires a non-null 
argument}}
 
-    sprintf(buf, NULL, 42);
+    sprintf(NULL, NULL, 42);
     // expected-warning@-1{{null passed to a callee that requires a non-null 
argument}}
+    // expected-warning@-2{{null passed to a callee that requires a non-null 
argument}}
 
-    snprintf(buf, 10, 0, 42);
+    snprintf(NULL, 10, 0, 42);
     // expected-warning@-1{{null passed to a callee that requires a non-null 
argument}}
+    // expected-warning@-2{{null passed to a callee that requires a non-null 
argument}}
 
     vprintf(fmt, ap);
     // expected-warning@-1{{null passed to a callee that requires a non-null 
argument}}
@@ -51,15 +54,19 @@ void check_format_string(FILE *fp, va_list ap) {
     vsprintf(buf, nullptr, ap);
     // expected-warning@-1{{null passed to a callee that requires a non-null 
argument}}
 
-    vsnprintf(buf, 10, fmt, ap);
+    vsnprintf(NULL, 10, fmt, ap);
     // expected-warning@-1{{null passed to a callee that requires a non-null 
argument}}
+    // expected-warning@-2{{null passed to a callee that requires a non-null 
argument}}
 
     scanf(NULL);
     // expected-warning@-1{{null passed to a callee that requires a non-null 
argument}}
 
-    fscanf(fp, nullptr);
+    fscanf(nullptr, nullptr);
     // expected-warning@-1{{null passed to a callee that requires a non-null 
argument}}
+    // expected-warning@-2{{null passed to a callee that requires a non-null 
argument}}
 
+    sscanf(NULL, "%d %s", &num, buf);
+    // expected-warning@-1{{null passed to a callee that requires a non-null 
argument}}
     sscanf(buf, fmt);
     // expected-warning@-1{{null passed to a callee that requires a non-null 
argument}}
 
diff --git a/clang/test/Sema/format-strings-nonnull.cpp 
b/clang/test/Sema/format-strings-nonnull.cpp
index 55a3ed1788c79..6c2567999b189 100644
--- a/clang/test/Sema/format-strings-nonnull.cpp
+++ b/clang/test/Sema/format-strings-nonnull.cpp
@@ -13,12 +13,15 @@ typedef __builtin_va_list va_list;
 EXTERN_C int printf(const char *, ...);
 EXTERN_C int fprintf(FILE *, const char *restrict, ...);
 EXTERN_C int sprintf(char* restrict, char const* res, ...);
+EXTERN_C int vfprintf(FILE* restrict, char const* res, __builtin_va_list);
 
 EXTERN_C int scanf(char const *restrict, ...);
 EXTERN_C int fscanf(FILE* restrict, char const* res, ...);
+EXTERN_C int sscanf(char const* restrict, char const* res, ...);
 
-void test(FILE *fp) {
+void test(FILE *fp, va_list ap) {
   char buf[256];
+  int num;
 
   __builtin_printf(__null, "x");
   // expected-warning@-1 {{null passed to a callee that requires a non-null 
argument}}
@@ -37,4 +40,10 @@ void test(FILE *fp) {
 
   fscanf(fp, __null);
   // expected-warning@-1 {{null passed to a callee that requires a non-null 
argument}}
+
+  vfprintf(__null, "xxd", ap);
+  // expected-warning@-1 {{null passed to a callee that requires a non-null 
argument}}
+
+  sscanf(__null, "%d", &num);
+  // expected-warning@-1 {{null passed to a callee that requires a non-null 
argument}}
 }
diff --git a/clang/test/Sema/warn-fortify-scanf.c 
b/clang/test/Sema/warn-fortify-scanf.c
index 16cbfa29b80ab..7b8f4b73438be 100644
--- a/clang/test/Sema/warn-fortify-scanf.c
+++ b/clang/test/Sema/warn-fortify-scanf.c
@@ -59,10 +59,17 @@ void call_fscanf(void) {
   char buf20[20];
   char buf30[30];
   fscanf(0, "%4s %5s %10s", buf20, buf30, buf10); // expected-warning 
{{'fscanf' may overflow; destination buffer in argument 5 has size 10, but the 
corresponding specifier may require size 11}}
+  // expected-warning@-1 {{null passed to a callee that requires a non-null 
argument}}
   fscanf(0, "%4s %5s %11s", buf20, buf30, buf10); // expected-warning 
{{'fscanf' may overflow; destination buffer in argument 5 has size 10, but the 
corresponding specifier may require size 12}}
+  // expected-warning@-1 {{null passed to a callee that requires a non-null 
argument}}
   fscanf(0, "%4s %5s %9s", buf20, buf30, buf10);
+  // expected-warning@-1 {{null passed to a callee that requires a non-null 
argument}}
   fscanf(0, "%20s %5s %9s", buf20, buf30, buf10); // expected-warning 
{{'fscanf' may overflow; destination buffer in argument 3 has size 20, but the 
corresponding specifier may require size 21}}
+  // expected-warning@-1 {{null passed to a callee that requires a non-null 
argument}}
   fscanf(0, "%21s %5s %9s", buf20, buf30, buf10); // expected-warning 
{{'fscanf' may overflow; destination buffer in argument 3 has size 20, but the 
corresponding specifier may require size 22}}
+  // expected-warning@-1 {{null passed to a callee that requires a non-null 
argument}}
   fscanf(0, "%19s %5s %9s", buf20, buf30, buf10);
+  // expected-warning@-1 {{null passed to a callee that requires a non-null 
argument}}
   fscanf(0, "%19s %29s %9s", buf20, buf30, buf10);
+  // expected-warning@-1 {{null passed to a callee that requires a non-null 
argument}}
 }

From 265c9a6187f3e5f2875bc93e81cfffdcd6211618 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Radovan=20Bo=C5=BEi=C4=87?= <[email protected]>
Date: Wed, 15 Oct 2025 15:36:46 +0200
Subject: [PATCH 4/6] Remove unnecessary nonnull attribute for snprintf and
 vsnprintf

According to the C standard, the first argument to snprintf and
vsnprintf may be null if the size argument is zero.
---
 clang/include/clang/Basic/Builtins.td    | 4 ++--
 clang/test/Sema/format-strings-nonnull.c | 6 ++----
 2 files changed, 4 insertions(+), 6 deletions(-)

diff --git a/clang/include/clang/Basic/Builtins.td 
b/clang/include/clang/Basic/Builtins.td
index 585da2b9a79a0..8ee2e933d30a7 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -3116,7 +3116,7 @@ def FPrintf : LibBuiltin<"stdio.h"> {
 
 def SnPrintf : LibBuiltin<"stdio.h"> {
   let Spellings = ["snprintf"];
-  let Attributes = [NoThrow, PrintfFormat<2>, NonNull<[0, 2]>];
+  let Attributes = [NoThrow, PrintfFormat<2>, NonNull<[2]>];
   let Prototype = "int(char* restrict, size_t, char const* restrict, ...)";
   let AddBuiltinPrefixedAlias = 1;
 }
@@ -3144,7 +3144,7 @@ def VfPrintf : LibBuiltin<"stdio.h"> {
 
 def VsnPrintf : LibBuiltin<"stdio.h"> {
   let Spellings = ["vsnprintf"];
-  let Attributes = [NoThrow, VPrintfFormat<2>, NonNull<[0, 2]>];
+  let Attributes = [NoThrow, VPrintfFormat<2>, NonNull<[2]>];
   let Prototype = "int(char* restrict, size_t, char const* restrict, 
__builtin_va_list)";
   let AddBuiltinPrefixedAlias = 1;
 }
diff --git a/clang/test/Sema/format-strings-nonnull.c 
b/clang/test/Sema/format-strings-nonnull.c
index 313c10b56d062..b9eeb5954ffb6 100644
--- a/clang/test/Sema/format-strings-nonnull.c
+++ b/clang/test/Sema/format-strings-nonnull.c
@@ -41,9 +41,8 @@ void check_format_string(FILE *fp, va_list ap) {
     // expected-warning@-1{{null passed to a callee that requires a non-null 
argument}}
     // expected-warning@-2{{null passed to a callee that requires a non-null 
argument}}
 
-    snprintf(NULL, 10, 0, 42);
+    snprintf(buf, 10, 0, 42);
     // expected-warning@-1{{null passed to a callee that requires a non-null 
argument}}
-    // expected-warning@-2{{null passed to a callee that requires a non-null 
argument}}
 
     vprintf(fmt, ap);
     // expected-warning@-1{{null passed to a callee that requires a non-null 
argument}}
@@ -54,9 +53,8 @@ void check_format_string(FILE *fp, va_list ap) {
     vsprintf(buf, nullptr, ap);
     // expected-warning@-1{{null passed to a callee that requires a non-null 
argument}}
 
-    vsnprintf(NULL, 10, fmt, ap);
+    vsnprintf(buf, 10, fmt, ap);
     // expected-warning@-1{{null passed to a callee that requires a non-null 
argument}}
-    // expected-warning@-2{{null passed to a callee that requires a non-null 
argument}}
 
     scanf(NULL);
     // expected-warning@-1{{null passed to a callee that requires a non-null 
argument}}

From 70f1cb6e6019ff33ed8872d6a09dc4f4bb04626f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Radovan=20Bo=C5=BEi=C4=87?= <[email protected]>
Date: Tue, 25 Nov 2025 08:57:55 +0100
Subject: [PATCH 5/6] Add optimization mode for builtin nonnull attribute

Introduce two modes for the builtin nonnull attribute. Instead of always
emitting the GNU-style `nonnull` attribute, Clang now distinguishes
between:

* NonOptimizing: attaches Clang's `_Nonnull` type qualifier.
* Optimizing: emits GNU-style `nonnull` attribute.
---
 clang/include/clang/Basic/Builtins.h      |  9 ++++++-
 clang/include/clang/Basic/Builtins.td     | 30 +++++++++++------------
 clang/include/clang/Basic/BuiltinsBase.td | 13 +++++++++-
 clang/lib/Basic/Builtins.cpp              | 14 ++++++++---
 clang/lib/Sema/SemaDecl.cpp               | 22 ++++++++++++-----
 clang/test/Analysis/stream.c              |  8 +++---
 6 files changed, 66 insertions(+), 30 deletions(-)

diff --git a/clang/include/clang/Basic/Builtins.h 
b/clang/include/clang/Basic/Builtins.h
index 6d26a7b92a0fd..34da6ae18e313 100644
--- a/clang/include/clang/Basic/Builtins.h
+++ b/clang/include/clang/Basic/Builtins.h
@@ -94,6 +94,12 @@ struct Info {
   ///
   /// Must be provided the `Shard` for this `Info` object.
   std::string getName(const InfosShard &Shard) const;
+
+  // Builtin non-null attribute modes.
+  // NonOptimizing: attaches Clang's `_Nonnull` type qualifier to parameters.
+  // Optimizing: emits the classic GNU-style `nonnull` attribute for
+  // optimization.
+  enum class NonNullMode { NonOptimizing, Optimizing };
 };
 
 /// A constexpr function to construct an infos array from X-macros.
@@ -394,7 +400,8 @@ class Context {
 
   /// Return true if this builtin has parameters that must be non-null.
   /// The parameter indices are appended into 'Indxs'.
-  bool isNonNull(unsigned ID, llvm::SmallVectorImpl<int> &Indxs) const;
+  bool isNonNull(unsigned ID, llvm::SmallVectorImpl<int> &Indxs,
+                 Info::NonNullMode &Mode) const;
 
   /// Return true if this function has no side effects and doesn't
   /// read memory, except for possibly errno or raising FP exceptions.
diff --git a/clang/include/clang/Basic/Builtins.td 
b/clang/include/clang/Basic/Builtins.td
index 8ee2e933d30a7..35742c095ef9f 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -3095,7 +3095,7 @@ def StrLen : LibBuiltin<"string.h"> {
 // FIXME: This list is incomplete.
 def Printf : LibBuiltin<"stdio.h"> {
   let Spellings = ["printf"];
-  let Attributes = [PrintfFormat<0>, NonNull<[0]>];
+  let Attributes = [PrintfFormat<0>, NonNull<NonOptimizing, [0]>];
   let Prototype = "int(char const*, ...)";
 }
 
@@ -3103,97 +3103,97 @@ def Printf : LibBuiltin<"stdio.h"> {
 def BuiltinPrintf : Builtin {
   let Spellings = ["__builtin_printf"];
   let Attributes = [NoThrow, PrintfFormat<0>, FunctionWithBuiltinPrefix,
-                    NonNull<[0]>];
+                    NonNull<NonOptimizing, [0]>];
   let Prototype = "int(char const* restrict, ...)";
 }
 
 def FPrintf : LibBuiltin<"stdio.h"> {
   let Spellings = ["fprintf"];
-  let Attributes = [NoThrow, PrintfFormat<1>, NonNull<[0, 1]>];
+  let Attributes = [NoThrow, PrintfFormat<1>, NonNull<NonOptimizing, [0, 1]>];
   let Prototype = "int(FILE* restrict, char const* restrict, ...)";
   let AddBuiltinPrefixedAlias = 1;
 }
 
 def SnPrintf : LibBuiltin<"stdio.h"> {
   let Spellings = ["snprintf"];
-  let Attributes = [NoThrow, PrintfFormat<2>, NonNull<[2]>];
+  let Attributes = [NoThrow, PrintfFormat<2>, NonNull<NonOptimizing, [2]>];
   let Prototype = "int(char* restrict, size_t, char const* restrict, ...)";
   let AddBuiltinPrefixedAlias = 1;
 }
 
 def SPrintf : LibBuiltin<"stdio.h"> {
   let Spellings = ["sprintf"];
-  let Attributes = [NoThrow, PrintfFormat<1>, NonNull<[0, 1]>];
+  let Attributes = [NoThrow, PrintfFormat<1>, NonNull<NonOptimizing, [0, 1]>];
   let Prototype = "int(char* restrict, char const* restrict, ...)";
   let AddBuiltinPrefixedAlias = 1;
 }
 
 def VPrintf : LibBuiltin<"stdio.h"> {
   let Spellings = ["vprintf"];
-  let Attributes = [NoThrow, VPrintfFormat<0>, NonNull<[0]>];
+  let Attributes = [NoThrow, VPrintfFormat<0>, NonNull<NonOptimizing, [0]>];
   let Prototype = "int(char const* restrict, __builtin_va_list)";
   let AddBuiltinPrefixedAlias = 1;
 }
 
 def VfPrintf : LibBuiltin<"stdio.h"> {
   let Spellings = ["vfprintf"];
-  let Attributes = [NoThrow, VPrintfFormat<1>, NonNull<[0, 1]>];
+  let Attributes = [NoThrow, VPrintfFormat<1>, NonNull<NonOptimizing, [0, 1]>];
   let Prototype = "int(FILE* restrict, char const* restrict, 
__builtin_va_list)";
   let AddBuiltinPrefixedAlias = 1;
 }
 
 def VsnPrintf : LibBuiltin<"stdio.h"> {
   let Spellings = ["vsnprintf"];
-  let Attributes = [NoThrow, VPrintfFormat<2>, NonNull<[2]>];
+  let Attributes = [NoThrow, VPrintfFormat<2>, NonNull<NonOptimizing, [2]>];
   let Prototype = "int(char* restrict, size_t, char const* restrict, 
__builtin_va_list)";
   let AddBuiltinPrefixedAlias = 1;
 }
 
 def VsPrintf : LibBuiltin<"stdio.h"> {
   let Spellings = ["vsprintf"];
-  let Attributes = [NoThrow, VPrintfFormat<1>, NonNull<[0, 1]>];
+  let Attributes = [NoThrow, VPrintfFormat<1>, NonNull<NonOptimizing, [0, 1]>];
   let Prototype = "int(char* restrict, char const* restrict, 
__builtin_va_list)";
   let AddBuiltinPrefixedAlias = 1;
 }
 
 def Scanf : LibBuiltin<"stdio.h"> {
   let Spellings = ["scanf"];
-  let Attributes = [ScanfFormat<0>, NonNull<[0]>];
+  let Attributes = [ScanfFormat<0>, NonNull<NonOptimizing, [0]>];
   let Prototype = "int(char const* restrict, ...)";
   let AddBuiltinPrefixedAlias = 1;
 }
 
 def FScanf : LibBuiltin<"stdio.h"> {
   let Spellings = ["fscanf"];
-  let Attributes = [ScanfFormat<1>, NonNull<[0, 1]>];
+  let Attributes = [ScanfFormat<1>, NonNull<NonOptimizing, [0, 1]>];
   let Prototype = "int(FILE* restrict, char const* restrict, ...)";
   let AddBuiltinPrefixedAlias = 1;
 }
 
 def SScanf : LibBuiltin<"stdio.h"> {
   let Spellings = ["sscanf"];
-  let Attributes = [ScanfFormat<1>, NonNull<[0, 1]>];
+  let Attributes = [ScanfFormat<1>, NonNull<NonOptimizing, [0, 1]>];
   let Prototype = "int(char const* restrict, char const* restrict, ...)";
   let AddBuiltinPrefixedAlias = 1;
 }
 
 def VScanf : LibBuiltin<"stdio.h"> {
   let Spellings = ["vscanf"];
-  let Attributes = [VScanfFormat<0>, NonNull<[0]>];
+  let Attributes = [VScanfFormat<0>, NonNull<NonOptimizing, [0]>];
   let Prototype = "int(char const* restrict, __builtin_va_list)";
   let AddBuiltinPrefixedAlias = 1;
 }
 
 def VFScanf : LibBuiltin<"stdio.h"> {
   let Spellings = ["vfscanf"];
-  let Attributes = [VScanfFormat<1>, NonNull<[0, 1]>];
+  let Attributes = [VScanfFormat<1>, NonNull<NonOptimizing, [0, 1]>];
   let Prototype = "int(FILE* restrict, char const* restrict, 
__builtin_va_list)";
   let AddBuiltinPrefixedAlias = 1;
 }
 
 def VSScanf : LibBuiltin<"stdio.h"> {
   let Spellings = ["vsscanf"];
-  let Attributes = [VScanfFormat<1>, NonNull<[0, 1]>];
+  let Attributes = [VScanfFormat<1>, NonNull<NonOptimizing, [0, 1]>];
   let Prototype = "int(char const* restrict, char const* restrict, 
__builtin_va_list)";
   let AddBuiltinPrefixedAlias = 1;
 }
diff --git a/clang/include/clang/Basic/BuiltinsBase.td 
b/clang/include/clang/Basic/BuiltinsBase.td
index 73918ab167b8d..be733e84fca71 100644
--- a/clang/include/clang/Basic/BuiltinsBase.td
+++ b/clang/include/clang/Basic/BuiltinsBase.td
@@ -33,6 +33,18 @@ def NoThrow : Attribute<"n">;
 def Pure : Attribute<"U">;
 def ReturnsTwice : Attribute<"j">;
 
+class NonNullOptMode {
+  int value;
+}
+
+def NonOptimizing : NonNullOptMode { let value = 0; }
+def Optimizing    : NonNullOptMode { let value = 1; }
+
+class NonNull<NonNullOptMode Mode, list<int> Is> : MultiIndexAttribute<"N", 
Is> {
+  int optMode = Mode.value;
+  let Mangling = "N:" # Mode.value # ":";
+}
+
 // builtin-specific attributes
 // ---------------------------
 
@@ -84,7 +96,6 @@ def Consteval : Attribute<"EG">;
 // Callback behavior: the first index argument is called with the arguments
 // indicated by the remaining indices.
 class Callback<list<int> ArgIndices> : MultiIndexAttribute<"C", ArgIndices>;
-class NonNull<list<int> ArgIndices> : MultiIndexAttribute<"N", ArgIndices>;
 
 // Prefixes
 // ========
diff --git a/clang/lib/Basic/Builtins.cpp b/clang/lib/Basic/Builtins.cpp
index a813726b7b848..ba6896a9a26d8 100644
--- a/clang/lib/Basic/Builtins.cpp
+++ b/clang/lib/Basic/Builtins.cpp
@@ -313,14 +313,22 @@ static void parseCommaSeparatedIndices(const char 
*CurrPos,
   assert(*EndPos == '>' && "Index list must end with '>'");
 }
 
-bool Builtin::Context::isNonNull(unsigned ID,
-                                 llvm::SmallVectorImpl<int> &Indxs) const {
+bool Builtin::Context::isNonNull(unsigned ID, llvm::SmallVectorImpl<int> 
&Indxs,
+                                 Info::NonNullMode &Mode) const {
 
   const char *AttrPos = ::strchr(getAttributesString(ID), 'N');
   if (!AttrPos)
     return false;
 
-  ++AttrPos;
+  AttrPos += 2; // skip 'N' and ':'
+  if (*AttrPos == '0')
+    Mode = Info::NonNullMode::NonOptimizing;
+  else if (*AttrPos == '1')
+    Mode = Info::NonNullMode::Optimizing;
+  else
+    llvm_unreachable("Unrecognized NonNull optimization mode");
+  AttrPos += 2; // skip mode and ':'
+
   parseCommaSeparatedIndices(AttrPos, Indxs);
 
   return true;
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index d9df60af114fb..ba24505ecd953 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -17142,13 +17142,23 @@ void Sema::AddKnownFunctionAttributes(FunctionDecl 
*FD) {
     }
 
     SmallVector<int, 4> Indxs;
-    if (Context.BuiltinInfo.isNonNull(BuiltinID, Indxs) &&
+    Builtin::Info::NonNullMode OptMode;
+    if (Context.BuiltinInfo.isNonNull(BuiltinID, Indxs, OptMode) &&
         !FD->hasAttr<NonNullAttr>()) {
-      llvm::SmallVector<ParamIdx, 4> ParamIndxs;
-      for (int I : Indxs)
-        ParamIndxs.push_back(ParamIdx(I + 1, FD));
-      FD->addAttr(NonNullAttr::CreateImplicit(Context, ParamIndxs.data(),
-                                              ParamIndxs.size()));
+      if (OptMode == Builtin::Info::NonNullMode::NonOptimizing) {
+        for (int I : Indxs) {
+          ParmVarDecl *PVD = FD->getParamDecl(I);
+          QualType T = PVD->getType();
+          T = Context.getAttributedType(attr::TypeNonNull, T, T);
+          PVD->setType(T);
+        }
+      } else if (OptMode == Builtin::Info::NonNullMode::Optimizing) {
+        llvm::SmallVector<ParamIdx, 4> ParamIndxs;
+        for (int I : Indxs)
+          ParamIndxs.push_back(ParamIdx(I + 1, FD));
+        FD->addAttr(NonNullAttr::CreateImplicit(Context, ParamIndxs.data(),
+                                                ParamIndxs.size()));
+      }
     }
     if (Context.BuiltinInfo.isReturnsTwice(BuiltinID) &&
         !FD->hasAttr<ReturnsTwiceAttr>())
diff --git a/clang/test/Analysis/stream.c b/clang/test/Analysis/stream.c
index 4654fdbb80334..10e6e5381ee6d 100644
--- a/clang/test/Analysis/stream.c
+++ b/clang/test/Analysis/stream.c
@@ -52,13 +52,13 @@ void check_fputs(void) {
 
 void check_fprintf(void) {
   FILE *fp = tmpfile();
-  fprintf(fp, "ABC"); // expected-warning {{Null pointer passed to 1st 
parameter expecting 'nonnull'}}
+  fprintf(fp, "ABC"); // expected-warning {{Stream pointer might be NULL}}
   fclose(fp);
 }
 
 void check_fscanf(void) {
   FILE *fp = tmpfile();
-  fscanf(fp, "ABC"); // expected-warning {{Null pointer passed to 1st 
parameter expecting 'nonnull'}}
+  fscanf(fp, "ABC"); // expected-warning {{Stream pointer might be NULL}}
   fclose(fp);
 }
 
@@ -152,13 +152,13 @@ void f_dopen(int fd) {
 
 void f_vfprintf(int fd, va_list args) {
   FILE *F = fdopen(fd, "r");
-  vfprintf(F, "%d", args); // expected-warning {{Null pointer passed to 1st 
parameter expecting 'nonnull'}}
+  vfprintf(F, "%d", args); // expected-warning {{Stream pointer might be NULL}}
   fclose(F);
 }
 
 void f_vfscanf(int fd, va_list args) {
   FILE *F = fdopen(fd, "r");
-  vfscanf(F, "%u", args); // expected-warning {{Null pointer passed to 1st 
parameter expecting 'nonnull'}}
+  vfscanf(F, "%u", args); // expected-warning {{Stream pointer might be NULL}}
   fclose(F);
 }
 

From e055def1e4b9a7c3f6fe7a2cacd52700a70dc90a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Radovan=20Bo=C5=BEi=C4=87?= <[email protected]>
Date: Wed, 26 Nov 2025 15:03:04 +0100
Subject: [PATCH 6/6] Make TableGen definitions clearer and add additional
 assertions

---
 clang/include/clang/Basic/BuiltinsBase.td | 11 +++++------
 clang/lib/Basic/Builtins.cpp              |  8 ++++++--
 2 files changed, 11 insertions(+), 8 deletions(-)

diff --git a/clang/include/clang/Basic/BuiltinsBase.td 
b/clang/include/clang/Basic/BuiltinsBase.td
index be733e84fca71..7394f0efd8b0a 100644
--- a/clang/include/clang/Basic/BuiltinsBase.td
+++ b/clang/include/clang/Basic/BuiltinsBase.td
@@ -33,16 +33,15 @@ def NoThrow : Attribute<"n">;
 def Pure : Attribute<"U">;
 def ReturnsTwice : Attribute<"j">;
 
-class NonNullOptMode {
-  int value;
+class NonNullOptMode<int mode> {
+  int value = mode;
 }
 
-def NonOptimizing : NonNullOptMode { let value = 0; }
-def Optimizing    : NonNullOptMode { let value = 1; }
+def NonOptimizing : NonNullOptMode<0>;
+def Optimizing    : NonNullOptMode<1>;
 
-class NonNull<NonNullOptMode Mode, list<int> Is> : MultiIndexAttribute<"N", 
Is> {
+class NonNull<NonNullOptMode Mode, list<int> Is> : MultiIndexAttribute<"N:" # 
Mode.value # ":", Is> {
   int optMode = Mode.value;
-  let Mangling = "N:" # Mode.value # ":";
 }
 
 // builtin-specific attributes
diff --git a/clang/lib/Basic/Builtins.cpp b/clang/lib/Basic/Builtins.cpp
index ba6896a9a26d8..2dcf6a2f8940d 100644
--- a/clang/lib/Basic/Builtins.cpp
+++ b/clang/lib/Basic/Builtins.cpp
@@ -320,14 +320,18 @@ bool Builtin::Context::isNonNull(unsigned ID, 
llvm::SmallVectorImpl<int> &Indxs,
   if (!AttrPos)
     return false;
 
-  AttrPos += 2; // skip 'N' and ':'
+  ++AttrPos;
+  assert(*AttrPos == ':' && "Format specifier must be followed by a ':'");
+  ++AttrPos;
   if (*AttrPos == '0')
     Mode = Info::NonNullMode::NonOptimizing;
   else if (*AttrPos == '1')
     Mode = Info::NonNullMode::Optimizing;
   else
     llvm_unreachable("Unrecognized NonNull optimization mode");
-  AttrPos += 2; // skip mode and ':'
+  ++AttrPos; // skip mode
+  assert(*AttrPos == ':' && "Mode must be followed by a ':'");
+  ++AttrPos;
 
   parseCommaSeparatedIndices(AttrPos, Indxs);
 

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to