[clang] [clang][analyzer] Add OpaqueSTLFunctionsModeling (PR #178910)
Endre Fülöp via cfe-commits
cfe-commits at lists.llvm.org
Mon Feb 2 06:30:25 PST 2026
https://github.com/gamesh411 updated https://github.com/llvm/llvm-project/pull/178910
>From 7b5b9e715f677dd2a60908327059fc1848d2a39e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fulop at sigmatechnology.com>
Date: Fri, 30 Jan 2026 12:02:57 +0100
Subject: [PATCH 1/2] [clang][analyzer] Add OpaqueSTLFunctionsModeling
This modeling checker is the intended place for suppressing internal
STL-specific implementation-detail functions that cannot be modeled
adequately by the engine.
If possible, using `evalCall` to not even emit the false positives is
more efficient than suppressing them after analysis in
`BugReporterVisitor`s, so this modeling checker takes that route.
This patch introduces the conservative evaluation (no inlining) for
`__uninitialized_construct_buf_dispatch::__ucr`, an STL internal
function used by `std::stable_sort` and `std::inplace_merge` that
causes false positives.
Related to #177804.
---
.../clang/StaticAnalyzer/Checkers/Checkers.td | 6 ++
.../StaticAnalyzer/Checkers/CMakeLists.txt | 1 +
.../Checkers/OpaqueSTLFunctionsModeling.cpp | 76 +++++++++++++++++++
...tem-header-simulator-cxx-std-suppression.h | 28 +++++++
.../implicit-cxx-std-suppression.cpp | 10 +++
.../opaque-stl-functions-modeling-tag.cpp | 14 ++++
6 files changed, 135 insertions(+)
create mode 100644 clang/lib/StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp
create mode 100644 clang/test/Analysis/diagnostics/opaque-stl-functions-modeling-tag.cpp
diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
index 6a409944849e6..99dbae1dbf8b7 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -674,6 +674,12 @@ def PureVirtualCallChecker
HelpText<
"Check pure virtual function calls during construction/destruction">,
Documentation<HasDocumentation>;
+
+def OpaqueSTLFunctionsModeling : Checker<"OpaqueSTLFunctionsModeling">,
+ HelpText<"Force opaque, conservative evaluation for STL internal implementation functions">,
+ Documentation<NotDocumented>,
+ Hidden;
+
} // end: "cplusplus"
let ParentPackage = CplusplusOptIn in {
diff --git a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
index 2df36d8e672ae..3edbd7ebdc1bf 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
+++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
@@ -86,6 +86,7 @@ add_clang_library(clangStaticAnalyzerCheckers
ObjCSelfInitChecker.cpp
ObjCSuperDeallocChecker.cpp
ObjCUnusedIVarsChecker.cpp
+ OpaqueSTLFunctionsModeling.cpp
OSObjectCStyleCast.cpp
PaddingChecker.cpp
PointerArithChecker.cpp
diff --git a/clang/lib/StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp
new file mode 100644
index 0000000000000..0e4f8c4bbc4d3
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp
@@ -0,0 +1,76 @@
+//===--- OpaqueSTLFunctionsModeling.cpp -----------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Forces conservative evaluation for STL internal implementation functions
+// (prefixed with '__') known to cause false positives. This prevents inlining
+// of complex STL internals and avoids wasting analysis time spent in
+// `BugReporterVisitor`s.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/ProgramPoint.h"
+#include "clang/Basic/IdentifierTable.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class OpaqueSTLFunctionsModeling : public Checker<eval::Call> {
+public:
+ bool evalCall(const CallEvent &Call, CheckerContext &C) const;
+
+private:
+ bool shouldForceConservativeEval(const CallEvent &Call) const;
+};
+} // anonymous namespace
+
+bool OpaqueSTLFunctionsModeling::evalCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ if (!shouldForceConservativeEval(Call))
+ return false;
+
+ ProgramStateRef State = C.getState();
+ State = Call.invalidateRegions(C.blockCount(), State);
+ static const SimpleProgramPointTag OpaqueCallTag{getDebugTag(),
+ "Forced Opaque Call"};
+ C.addTransition(State, &OpaqueCallTag);
+ return true;
+}
+
+bool OpaqueSTLFunctionsModeling::shouldForceConservativeEval(
+ const CallEvent &Call) const {
+ const Decl *D = Call.getDecl();
+ if (!D || !AnalysisDeclContext::isInStdNamespace(D))
+ return false;
+
+ // __uninitialized_construct_buf_dispatch::__ucr is used by stable_sort
+ // and inplace_merge.
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) {
+ if (const IdentifierInfo *II = MD->getIdentifier()) {
+ if (II->getName() == "__ucr") {
+ const CXXRecordDecl *RD = MD->getParent();
+ if (RD->getName().starts_with("__uninitialized_construct_buf_dispatch"))
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void ento::registerOpaqueSTLFunctionsModeling(CheckerManager &Mgr) {
+ Mgr.registerChecker<OpaqueSTLFunctionsModeling>();
+}
+
+bool ento::shouldRegisterOpaqueSTLFunctionsModeling(const CheckerManager &Mgr) {
+ return Mgr.getLangOpts().CPlusPlus;
+}
diff --git a/clang/test/Analysis/Inputs/system-header-simulator-cxx-std-suppression.h b/clang/test/Analysis/Inputs/system-header-simulator-cxx-std-suppression.h
index dc53af269c9c2..f4e3454f905b5 100644
--- a/clang/test/Analysis/Inputs/system-header-simulator-cxx-std-suppression.h
+++ b/clang/test/Analysis/Inputs/system-header-simulator-cxx-std-suppression.h
@@ -142,5 +142,33 @@ shared_ptr<_Tp>::shared_ptr(nullptr_t) {
}
#endif // __has_feature(cxx_decltype)
+
+// __uninitialized_construct_buf_dispatch::__ucr is used by stable_sort
+// and inplace_merge.
+template<typename _Tp>
+struct __uninitialized_construct_buf_dispatch {
+ template<typename _ForwardIterator, typename _Allocator>
+ static void __ucr(_ForwardIterator __first, _ForwardIterator __last,
+ _Allocator& __a) {
+ // Fake error trigger
+ int z = 0;
+ z = 5/z;
+ }
+};
+
+template<typename _RandomAccessIterator>
+void stable_sort(_RandomAccessIterator __first, _RandomAccessIterator __last) {
+ allocator<int> alloc;
+ __uninitialized_construct_buf_dispatch<int>::__ucr(__first, __last, alloc);
+}
+
+template<typename _BidirectionalIterator>
+void inplace_merge(_BidirectionalIterator __first,
+ _BidirectionalIterator,
+ _BidirectionalIterator __last) {
+ allocator<int> alloc;
+ __uninitialized_construct_buf_dispatch<int>::__ucr(__first, __last, alloc);
+}
+
}
diff --git a/clang/test/Analysis/diagnostics/implicit-cxx-std-suppression.cpp b/clang/test/Analysis/diagnostics/implicit-cxx-std-suppression.cpp
index 35f8798c81ae1..f49d6f78c165a 100644
--- a/clang/test/Analysis/diagnostics/implicit-cxx-std-suppression.cpp
+++ b/clang/test/Analysis/diagnostics/implicit-cxx-std-suppression.cpp
@@ -37,3 +37,13 @@ void testSuppression_std_shared_pointer() {
p = nullptr; // no-warning
}
+
+void testSuppression_stable_sort() {
+ int arr[5];
+ std::stable_sort(arr, arr + 5); // no-warning
+}
+
+void testSuppression_inplace_merge() {
+ int arr[5];
+ std::inplace_merge(arr, arr + 2, arr + 5); // no-warning
+}
diff --git a/clang/test/Analysis/diagnostics/opaque-stl-functions-modeling-tag.cpp b/clang/test/Analysis/diagnostics/opaque-stl-functions-modeling-tag.cpp
new file mode 100644
index 0000000000000..bab43b0622b8d
--- /dev/null
+++ b/clang/test/Analysis/diagnostics/opaque-stl-functions-modeling-tag.cpp
@@ -0,0 +1,14 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.OpaqueSTLFunctionsModeling \
+// RUN: -analyzer-dump-egraph=%t.dot -std=c++11 %s
+// RUN: cat %t.dot | FileCheck %s
+
+#include "../Inputs/system-header-simulator-cxx-std-suppression.h"
+
+void testOpaqueSTLTags() {
+ int arr[5];
+ std::stable_sort(arr, arr + 5);
+// CHECK: \"tag\": \"cplusplus.OpaqueSTLFunctionsModeling : Forced Opaque Call\"
+ std::inplace_merge(arr, arr + 2, arr + 5);
+// CHECK: \"tag\": \"cplusplus.OpaqueSTLFunctionsModeling : Forced Opaque Call\"
+}
+
>From dd1b74a4ed7824bb45af18253c341f3107313583 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <endre.fulop at sigmatechnology.com>
Date: Mon, 2 Feb 2026 15:23:14 +0100
Subject: [PATCH 2/2] simplify: handle only sort and stable_sort
---
.../Checkers/OpaqueSTLFunctionsModeling.cpp | 29 ++++---------------
...tem-header-simulator-cxx-std-suppression.h | 28 ++----------------
.../implicit-cxx-std-suppression.cpp | 8 ++---
.../opaque-stl-functions-modeling-tag.cpp | 4 +--
4 files changed, 15 insertions(+), 54 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp
index 0e4f8c4bbc4d3..505370f8805c2 100644
--- a/clang/lib/StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp
@@ -17,6 +17,7 @@
#include "clang/Basic/IdentifierTable.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
@@ -29,13 +30,16 @@ class OpaqueSTLFunctionsModeling : public Checker<eval::Call> {
bool evalCall(const CallEvent &Call, CheckerContext &C) const;
private:
- bool shouldForceConservativeEval(const CallEvent &Call) const;
+ using CDM = CallDescription::Mode;
+ const CallDescriptionSet ModeledFunctions{
+ {CDM::SimpleFunc, {"std", "sort"}},
+ {CDM::SimpleFunc, {"std", "stable_sort"}}};
};
} // anonymous namespace
bool OpaqueSTLFunctionsModeling::evalCall(const CallEvent &Call,
CheckerContext &C) const {
- if (!shouldForceConservativeEval(Call))
+ if (!ModeledFunctions.contains(Call))
return false;
ProgramStateRef State = C.getState();
@@ -46,27 +50,6 @@ bool OpaqueSTLFunctionsModeling::evalCall(const CallEvent &Call,
return true;
}
-bool OpaqueSTLFunctionsModeling::shouldForceConservativeEval(
- const CallEvent &Call) const {
- const Decl *D = Call.getDecl();
- if (!D || !AnalysisDeclContext::isInStdNamespace(D))
- return false;
-
- // __uninitialized_construct_buf_dispatch::__ucr is used by stable_sort
- // and inplace_merge.
- if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) {
- if (const IdentifierInfo *II = MD->getIdentifier()) {
- if (II->getName() == "__ucr") {
- const CXXRecordDecl *RD = MD->getParent();
- if (RD->getName().starts_with("__uninitialized_construct_buf_dispatch"))
- return true;
- }
- }
- }
-
- return false;
-}
-
void ento::registerOpaqueSTLFunctionsModeling(CheckerManager &Mgr) {
Mgr.registerChecker<OpaqueSTLFunctionsModeling>();
}
diff --git a/clang/test/Analysis/Inputs/system-header-simulator-cxx-std-suppression.h b/clang/test/Analysis/Inputs/system-header-simulator-cxx-std-suppression.h
index f4e3454f905b5..3f2051f53930d 100644
--- a/clang/test/Analysis/Inputs/system-header-simulator-cxx-std-suppression.h
+++ b/clang/test/Analysis/Inputs/system-header-simulator-cxx-std-suppression.h
@@ -143,32 +143,10 @@ shared_ptr<_Tp>::shared_ptr(nullptr_t) {
#endif // __has_feature(cxx_decltype)
-// __uninitialized_construct_buf_dispatch::__ucr is used by stable_sort
-// and inplace_merge.
-template<typename _Tp>
-struct __uninitialized_construct_buf_dispatch {
- template<typename _ForwardIterator, typename _Allocator>
- static void __ucr(_ForwardIterator __first, _ForwardIterator __last,
- _Allocator& __a) {
- // Fake error trigger
- int z = 0;
- z = 5/z;
- }
-};
-
template<typename _RandomAccessIterator>
-void stable_sort(_RandomAccessIterator __first, _RandomAccessIterator __last) {
- allocator<int> alloc;
- __uninitialized_construct_buf_dispatch<int>::__ucr(__first, __last, alloc);
-}
-
-template<typename _BidirectionalIterator>
-void inplace_merge(_BidirectionalIterator __first,
- _BidirectionalIterator,
- _BidirectionalIterator __last) {
- allocator<int> alloc;
- __uninitialized_construct_buf_dispatch<int>::__ucr(__first, __last, alloc);
-}
+void sort(_RandomAccessIterator __first, _RandomAccessIterator __last) {}
+template<typename _RandomAccessIterator>
+void stable_sort(_RandomAccessIterator __first, _RandomAccessIterator __last) {}
}
diff --git a/clang/test/Analysis/diagnostics/implicit-cxx-std-suppression.cpp b/clang/test/Analysis/diagnostics/implicit-cxx-std-suppression.cpp
index f49d6f78c165a..a54508ee9d6c5 100644
--- a/clang/test/Analysis/diagnostics/implicit-cxx-std-suppression.cpp
+++ b/clang/test/Analysis/diagnostics/implicit-cxx-std-suppression.cpp
@@ -38,12 +38,12 @@ void testSuppression_std_shared_pointer() {
p = nullptr; // no-warning
}
-void testSuppression_stable_sort() {
+void testSuppression_sort() {
int arr[5];
- std::stable_sort(arr, arr + 5); // no-warning
+ std::sort(arr, arr + 5); // no-warning
}
-void testSuppression_inplace_merge() {
+void testSuppression_stable_sort() {
int arr[5];
- std::inplace_merge(arr, arr + 2, arr + 5); // no-warning
+ std::stable_sort(arr, arr + 5); // no-warning
}
diff --git a/clang/test/Analysis/diagnostics/opaque-stl-functions-modeling-tag.cpp b/clang/test/Analysis/diagnostics/opaque-stl-functions-modeling-tag.cpp
index bab43b0622b8d..c14a4583780c6 100644
--- a/clang/test/Analysis/diagnostics/opaque-stl-functions-modeling-tag.cpp
+++ b/clang/test/Analysis/diagnostics/opaque-stl-functions-modeling-tag.cpp
@@ -6,9 +6,9 @@
void testOpaqueSTLTags() {
int arr[5];
- std::stable_sort(arr, arr + 5);
+ std::sort(arr, arr + 5);
// CHECK: \"tag\": \"cplusplus.OpaqueSTLFunctionsModeling : Forced Opaque Call\"
- std::inplace_merge(arr, arr + 2, arr + 5);
+ std::stable_sort(arr, arr + 5);
// CHECK: \"tag\": \"cplusplus.OpaqueSTLFunctionsModeling : Forced Opaque Call\"
}
More information about the cfe-commits
mailing list