[clang] [clang][analyzer] Add OpaqueSTLFunctionsModeling (PR #178910)
Endre Fülöp via cfe-commits
cfe-commits at lists.llvm.org
Fri Jan 30 08:03:50 PST 2026
https://github.com/gamesh411 created https://github.com/llvm/llvm-project/pull/178910
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.
>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] [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\"
+}
+
More information about the cfe-commits
mailing list