https://github.com/necto updated 
https://github.com/llvm/llvm-project/pull/236747

>From 84eb810d08c8e44bd4a6852ed3091adf0b8dc6c3 Mon Sep 17 00:00:00 2001
From: Arseniy Zaostrovnykh <[email protected]>
Date: Thu, 29 Oct 2026 09:41:05 +0100
Subject: [PATCH 1/3] [analyzer] Add tests for StdLibraryFunctions summaries
 not matching MSVC fopen

The MSVC C runtime (UCRT) declares fopen without the restrict qualifiers
of the standard prototype. From C99 on, the summaries of
StdLibraryFunctionsChecker require restrict on such parameters, so the
fopen summary is not applied: there is no argument constraint check and
no "Assuming that 'fopen' is successful" path note. The same holds for
any summary with restrict parameters, see the new MSVC run of
std-c-library-functions-restrict.c.

The tests also pin that on other targets, including MinGW, a fopen
declaration without restrict is not matched in C99.
---
 ...brary-functions-restrict-msvc-path-notes.c | 37 +++++++++++++++
 .../std-c-library-functions-restrict-msvc.c   | 46 +++++++++++++++++++
 .../std-c-library-functions-restrict.c        | 15 ++++++
 3 files changed, 98 insertions(+)
 create mode 100644 
clang/test/Analysis/std-c-library-functions-restrict-msvc-path-notes.c
 create mode 100644 clang/test/Analysis/std-c-library-functions-restrict-msvc.c

diff --git 
a/clang/test/Analysis/std-c-library-functions-restrict-msvc-path-notes.c 
b/clang/test/Analysis/std-c-library-functions-restrict-msvc-path-notes.c
new file mode 100644
index 00000000000000..10735d82fea095
--- /dev/null
+++ b/clang/test/Analysis/std-c-library-functions-restrict-msvc-path-notes.c
@@ -0,0 +1,37 @@
+// RUN: %clang_analyze_cc1 %s -std=c89 \
+// RUN:   -analyzer-checker=core \
+// RUN:   -analyzer-checker=unix.StdCLibraryFunctions \
+// RUN:   -analyzer-output=text \
+// RUN:   -triple x86_64-pc-windows-msvc -verify=expected,applied
+
+// RUN: %clang_analyze_cc1 %s -std=c99 \
+// RUN:   -analyzer-checker=core \
+// RUN:   -analyzer-checker=unix.StdCLibraryFunctions \
+// RUN:   -analyzer-output=text \
+// RUN:   -triple x86_64-pc-windows-msvc -verify=expected,not-applied
+
+// RUN: %clang_analyze_cc1 %s -std=c11 \
+// RUN:   -analyzer-checker=core \
+// RUN:   -analyzer-checker=unix.StdCLibraryFunctions \
+// RUN:   -analyzer-output=text \
+// RUN:   -triple x86_64-pc-windows-msvc -verify=expected,not-applied
+
+// The MSVC C runtime (UCRT) declares fopen without the restrict qualifiers of
+// the standard prototype. From C99 on, the summary of fopen does not match
+// this declaration, so the path misses the note about the successful call.
+//
+// FIXME: The summary should be applied in C99 and later too, and the note
+// should be present as in C89. See std-c-library-functions-restrict-msvc.c.
+
+typedef struct _iobuf FILE;
+FILE *fopen(char const *_FileName, char const *_Mode);
+
+void test_fopen_note(void) {
+  int *p = 0; // expected-note{{'p' initialized to a null pointer value}}
+  FILE *fp = fopen("file", "r"); // applied-note{{Assuming that 'fopen' is 
successful}}
+  if (fp) // applied-note{{'fp' is non-null}}
+          // not-applied-note@-1{{Assuming 'fp' is non-null}}
+          // expected-note@-2{{Taking true branch}}
+    *p = 1; // expected-warning{{Dereference of null pointer (loaded from 
variable 'p')}}
+            // expected-note@-1{{Dereference of null pointer (loaded from 
variable 'p')}}
+}
diff --git a/clang/test/Analysis/std-c-library-functions-restrict-msvc.c 
b/clang/test/Analysis/std-c-library-functions-restrict-msvc.c
new file mode 100644
index 00000000000000..cdf7af0690d821
--- /dev/null
+++ b/clang/test/Analysis/std-c-library-functions-restrict-msvc.c
@@ -0,0 +1,46 @@
+// RUN: %clang_analyze_cc1 %s -std=c89 \
+// RUN:   -analyzer-checker=core \
+// RUN:   -analyzer-checker=unix.StdCLibraryFunctions \
+// RUN:   -triple x86_64-pc-windows-msvc -verify=applied
+
+// RUN: %clang_analyze_cc1 %s -std=c99 \
+// RUN:   -analyzer-checker=core \
+// RUN:   -analyzer-checker=unix.StdCLibraryFunctions \
+// RUN:   -triple x86_64-pc-windows-msvc -verify=not-applied
+
+// RUN: %clang_analyze_cc1 %s -std=c11 \
+// RUN:   -analyzer-checker=core \
+// RUN:   -analyzer-checker=unix.StdCLibraryFunctions \
+// RUN:   -triple x86_64-pc-windows-msvc -verify=not-applied
+
+// RUN: %clang_analyze_cc1 %s -std=c99 \
+// RUN:   -analyzer-checker=core \
+// RUN:   -analyzer-checker=unix.StdCLibraryFunctions \
+// RUN:   -triple x86_64-unknown-linux-gnu -verify=not-applied
+
+// RUN: %clang_analyze_cc1 %s -std=c99 \
+// RUN:   -analyzer-checker=core \
+// RUN:   -analyzer-checker=unix.StdCLibraryFunctions \
+// RUN:   -triple x86_64-w64-windows-gnu -verify=not-applied
+
+// The MSVC C runtime (UCRT) declares fopen without the restrict qualifiers of
+// the standard prototype. In C89, the summary has no restrict either, so it is
+// applied. From C99 on, the summary requires restrict and does not match, so
+// the summary is not applied to the fopen of MSVC.
+//
+// FIXME: The summary should be applied in C99 and later too when targeting the
+// MSVC environment. Its C runtime does not follow the standard prototype, but
+// this declaration is still the fopen of the C library.
+//
+// On other targets (including MinGW, which has its own C library headers), the
+// C library is expected to follow the standard prototype, so the summary is
+// rightly not applied to a declaration without restrict.
+
+// not-applied-no-diagnostics
+
+typedef struct _iobuf FILE;
+FILE *fopen(char const *_FileName, char const *_Mode);
+
+void test_fopen_null_filename(void) {
+  fopen(0, "r"); // applied-warning{{The 1st argument to 'fopen' is NULL but 
should not be NULL}}
+}
diff --git a/clang/test/Analysis/std-c-library-functions-restrict.c 
b/clang/test/Analysis/std-c-library-functions-restrict.c
index 27e223c6e5b2f2..172e5460b91562 100644
--- a/clang/test/Analysis/std-c-library-functions-restrict.c
+++ b/clang/test/Analysis/std-c-library-functions-restrict.c
@@ -5,10 +5,21 @@
 // RUN:   -analyzer-config 
unix.StdCLibraryFunctions:DisplayLoadedSummaries=true \
 // RUN:   -triple i686-unknown-linux 2>&1 | FileCheck %s
 
+// RUN: %clang_analyze_cc1 %s \
+// RUN:   -analyzer-checker=core \
+// RUN:   -analyzer-checker=unix.StdCLibraryFunctions \
+// RUN:   -analyzer-checker=debug.StdCLibraryFunctionsTester \
+// RUN:   -analyzer-config 
unix.StdCLibraryFunctions:DisplayLoadedSummaries=true \
+// RUN:   -triple x86_64-pc-windows-msvc 2>&1 | FileCheck %s 
--check-prefix=MSVC
+
 // The signatures for these functions are the same and they specify their
 // parameter with the restrict qualifier. In C, the signature should match only
 // if the restrict qualifier is there on the parameter. Thus, the summary
 // should be loaded for the last two declarations only.
+// FIXME: The MSVC environment should be an exception: the Microsoft C runtime
+// omits the restrict qualifiers of the standard prototypes, so the signature
+// should match regardless of the restrict qualifier, and the summary should be
+// loaded for all three declarations.
 void __test_restrict_param_0(void *p);
 void __test_restrict_param_1(void *__restrict p);
 void __test_restrict_param_2(void *restrict p);
@@ -17,6 +28,10 @@ void __test_restrict_param_2(void *restrict p);
 //     CHECK: Loaded summary for: void __test_restrict_param_1(void *restrict 
p)
 //     CHECK: Loaded summary for: void __test_restrict_param_2(void *restrict 
p)
 
+// MSVC-NOT: Loaded summary for: void __test_restrict_param_0
+//     MSVC: Loaded summary for: void __test_restrict_param_1(void *restrict p)
+//     MSVC: Loaded summary for: void __test_restrict_param_2(void *restrict p)
+
 // Must have at least one call expression to initialize the summary map.
 int bar(void);
 void foo(void) {

>From 000dce4ffd7aeb619e1c9270c0fca18337140201 Mon Sep 17 00:00:00 2001
From: Arseniy Zaostrovnykh <[email protected]>
Date: Thu, 29 Oct 2026 11:31:32 +0100
Subject: [PATCH 2/3] Ignore restrict qualifier in MSVC environment

---
 .../Checkers/StdLibraryFunctionsChecker.cpp   | 28 +++++++++++++------
 ...brary-functions-restrict-msvc-path-notes.c | 12 ++++----
 .../std-c-library-functions-restrict-msvc.c   | 13 +++------
 .../std-c-library-functions-restrict.c        | 14 +++++-----
 4 files changed, 35 insertions(+), 32 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
index 1e1c5b50184b53..ef5a2c6bacfd61 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
@@ -1490,6 +1490,23 @@ bool StdLibraryFunctionsChecker::evalCall(const 
CallEvent &Call,
   llvm_unreachable("Unknown invalidation kind!");
 }
 
+/// Returns whether the signatures should match based on the restrict
+/// qualifier.
+/// The "restrict" keyword is illegal in C++, however, many libc
+/// implementations use the "__restrict" compiler intrinsic in functions
+/// prototypes. The "__restrict" keyword qualifies a type as a restricted type
+/// even in C++.
+/// In case of any non-C99 languages, we don't want to match based on the
+/// restrict qualifier because we cannot know if the given libc implementation
+/// qualifies the paramter type or not.
+/// The Microsoft C runtime (e.g. UCRT) omits the restrict qualifiers of the
+/// standard prototypes even in C99 and later, so we don't match based on the
+/// restrict qualifier when targeting the MSVC environment either.
+static bool isRestrictSignificant(const ASTContext &ACtx) {
+  return ACtx.getLangOpts().C99 &&
+         !ACtx.getTargetInfo().getTriple().isWindowsMSVCEnvironment();
+}
+
 bool StdLibraryFunctionsChecker::Signature::matches(
     const FunctionDecl *FD) const {
   assert(!isInvalid());
@@ -1497,15 +1514,8 @@ bool StdLibraryFunctionsChecker::Signature::matches(
   if (FD->param_size() != ArgTys.size())
     return false;
 
-  // The "restrict" keyword is illegal in C++, however, many libc
-  // implementations use the "__restrict" compiler intrinsic in functions
-  // prototypes. The "__restrict" keyword qualifies a type as a restricted type
-  // even in C++.
-  // In case of any non-C99 languages, we don't want to match based on the
-  // restrict qualifier because we cannot know if the given libc implementation
-  // qualifies the paramter type or not.
   auto RemoveRestrict = [&FD](QualType T) {
-    if (!FD->getASTContext().getLangOpts().C99)
+    if (!isRestrictSignificant(FD->getASTContext()))
       T.removeLocalRestrict();
     return T;
   };
@@ -1606,7 +1616,7 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
   public:
     GetRestrictTy(const ASTContext &ACtx) : ACtx(ACtx) {}
     QualType operator()(QualType Ty) {
-      return ACtx.getLangOpts().C99 ? ACtx.getRestrictType(Ty) : Ty;
+      return isRestrictSignificant(ACtx) ? ACtx.getRestrictType(Ty) : Ty;
     }
     std::optional<QualType> operator()(std::optional<QualType> Ty) {
       if (Ty)
diff --git 
a/clang/test/Analysis/std-c-library-functions-restrict-msvc-path-notes.c 
b/clang/test/Analysis/std-c-library-functions-restrict-msvc-path-notes.c
index 10735d82fea095..add8c52ae9c4ce 100644
--- a/clang/test/Analysis/std-c-library-functions-restrict-msvc-path-notes.c
+++ b/clang/test/Analysis/std-c-library-functions-restrict-msvc-path-notes.c
@@ -8,20 +8,18 @@
 // RUN:   -analyzer-checker=core \
 // RUN:   -analyzer-checker=unix.StdCLibraryFunctions \
 // RUN:   -analyzer-output=text \
-// RUN:   -triple x86_64-pc-windows-msvc -verify=expected,not-applied
+// RUN:   -triple x86_64-pc-windows-msvc -verify=expected,applied
 
 // RUN: %clang_analyze_cc1 %s -std=c11 \
 // RUN:   -analyzer-checker=core \
 // RUN:   -analyzer-checker=unix.StdCLibraryFunctions \
 // RUN:   -analyzer-output=text \
-// RUN:   -triple x86_64-pc-windows-msvc -verify=expected,not-applied
+// RUN:   -triple x86_64-pc-windows-msvc -verify=expected,applied
 
 // The MSVC C runtime (UCRT) declares fopen without the restrict qualifiers of
-// the standard prototype. From C99 on, the summary of fopen does not match
-// this declaration, so the path misses the note about the successful call.
-//
-// FIXME: The summary should be applied in C99 and later too, and the note
-// should be present as in C89. See std-c-library-functions-restrict-msvc.c.
+// the standard prototype. The summary of fopen is still applied, so the path
+// has the note about the successful call in all language modes.
+// See std-c-library-functions-restrict-msvc.c.
 
 typedef struct _iobuf FILE;
 FILE *fopen(char const *_FileName, char const *_Mode);
diff --git a/clang/test/Analysis/std-c-library-functions-restrict-msvc.c 
b/clang/test/Analysis/std-c-library-functions-restrict-msvc.c
index cdf7af0690d821..ea74180f9adca7 100644
--- a/clang/test/Analysis/std-c-library-functions-restrict-msvc.c
+++ b/clang/test/Analysis/std-c-library-functions-restrict-msvc.c
@@ -6,12 +6,12 @@
 // RUN: %clang_analyze_cc1 %s -std=c99 \
 // RUN:   -analyzer-checker=core \
 // RUN:   -analyzer-checker=unix.StdCLibraryFunctions \
-// RUN:   -triple x86_64-pc-windows-msvc -verify=not-applied
+// RUN:   -triple x86_64-pc-windows-msvc -verify=applied
 
 // RUN: %clang_analyze_cc1 %s -std=c11 \
 // RUN:   -analyzer-checker=core \
 // RUN:   -analyzer-checker=unix.StdCLibraryFunctions \
-// RUN:   -triple x86_64-pc-windows-msvc -verify=not-applied
+// RUN:   -triple x86_64-pc-windows-msvc -verify=applied
 
 // RUN: %clang_analyze_cc1 %s -std=c99 \
 // RUN:   -analyzer-checker=core \
@@ -24,13 +24,8 @@
 // RUN:   -triple x86_64-w64-windows-gnu -verify=not-applied
 
 // The MSVC C runtime (UCRT) declares fopen without the restrict qualifiers of
-// the standard prototype. In C89, the summary has no restrict either, so it is
-// applied. From C99 on, the summary requires restrict and does not match, so
-// the summary is not applied to the fopen of MSVC.
-//
-// FIXME: The summary should be applied in C99 and later too when targeting the
-// MSVC environment. Its C runtime does not follow the standard prototype, but
-// this declaration is still the fopen of the C library.
+// the standard prototype. When targeting the MSVC environment, the summary
+// does not require restrict, so it is applied in all language modes.
 //
 // On other targets (including MinGW, which has its own C library headers), the
 // C library is expected to follow the standard prototype, so the summary is
diff --git a/clang/test/Analysis/std-c-library-functions-restrict.c 
b/clang/test/Analysis/std-c-library-functions-restrict.c
index 172e5460b91562..9339180c6cbb9b 100644
--- a/clang/test/Analysis/std-c-library-functions-restrict.c
+++ b/clang/test/Analysis/std-c-library-functions-restrict.c
@@ -16,10 +16,10 @@
 // parameter with the restrict qualifier. In C, the signature should match only
 // if the restrict qualifier is there on the parameter. Thus, the summary
 // should be loaded for the last two declarations only.
-// FIXME: The MSVC environment should be an exception: the Microsoft C runtime
-// omits the restrict qualifiers of the standard prototypes, so the signature
-// should match regardless of the restrict qualifier, and the summary should be
-// loaded for all three declarations.
+// The exception is the MSVC environment: the Microsoft C runtime omits the
+// restrict qualifiers of the standard prototypes, so the signature should 
match
+// regardless of the restrict qualifier, and the summary should be loaded for
+// all three declarations.
 void __test_restrict_param_0(void *p);
 void __test_restrict_param_1(void *__restrict p);
 void __test_restrict_param_2(void *restrict p);
@@ -28,9 +28,9 @@ void __test_restrict_param_2(void *restrict p);
 //     CHECK: Loaded summary for: void __test_restrict_param_1(void *restrict 
p)
 //     CHECK: Loaded summary for: void __test_restrict_param_2(void *restrict 
p)
 
-// MSVC-NOT: Loaded summary for: void __test_restrict_param_0
-//     MSVC: Loaded summary for: void __test_restrict_param_1(void *restrict p)
-//     MSVC: Loaded summary for: void __test_restrict_param_2(void *restrict p)
+// MSVC: Loaded summary for: void __test_restrict_param_0(void *p)
+// MSVC: Loaded summary for: void __test_restrict_param_1(void *restrict p)
+// MSVC: Loaded summary for: void __test_restrict_param_2(void *restrict p)
 
 // Must have at least one call expression to initialize the summary map.
 int bar(void);

>From 282b4bfaeee02d622ab6f79d6dd43073f6e15a9f Mon Sep 17 00:00:00 2001
From: Arseniy Zaostrovnykh <[email protected]>
Date: Mon, 2 Nov 2026 10:58:11 +0100
Subject: [PATCH 3/3] Add a release note

---
 clang/docs/ReleaseNotes.md | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 7180696c22fe65..66b2e19e5b1809 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -1423,6 +1423,10 @@ documentation](CIR/index.md) for more information 
regarding ClangIR's status and
 
 #### Improvements
 
+- `unix.StdCLibraryFunctions` now ignores `restrict` qualifiers in C library
+  declarations such as `fopen` when targeting MSVC to avoid false negatives in
+  C99 and later C standards.
+
 - The lock-order-reversal check in ``alpha.unix.PthreadLock`` is now disabled 
by default.
   It can be re-enabled with the ``WarnOnLockOrderReversal`` option.
 

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

Reply via email to