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?= <[email protected]> Date: Fri, 30 Jan 2026 12:02:57 +0100 Subject: [PATCH 1/3] [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?= <[email protected]> Date: Mon, 2 Feb 2026 15:23:14 +0100 Subject: [PATCH 2/3] 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\" } From 6d2d8adaeb4608bc566942f71e7b7bb9775ac762 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Mon, 2 Feb 2026 16:05:39 +0100 Subject: [PATCH 3/3] add inplace_merge back in --- .../StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp | 3 ++- .../Inputs/system-header-simulator-cxx-std-suppression.h | 5 +++++ .../Analysis/diagnostics/implicit-cxx-std-suppression.cpp | 5 +++++ .../diagnostics/opaque-stl-functions-modeling-tag.cpp | 2 ++ 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp index 505370f8805c2..46935d07c05c3 100644 --- a/clang/lib/StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp @@ -33,7 +33,8 @@ class OpaqueSTLFunctionsModeling : public Checker<eval::Call> { using CDM = CallDescription::Mode; const CallDescriptionSet ModeledFunctions{ {CDM::SimpleFunc, {"std", "sort"}}, - {CDM::SimpleFunc, {"std", "stable_sort"}}}; + {CDM::SimpleFunc, {"std", "stable_sort"}}, + {CDM::SimpleFunc, {"std", "inplace_merge"}}}; }; } // anonymous namespace 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 3f2051f53930d..e7139987db66f 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 @@ -148,5 +148,10 @@ void sort(_RandomAccessIterator __first, _RandomAccessIterator __last) {} template<typename _RandomAccessIterator> void stable_sort(_RandomAccessIterator __first, _RandomAccessIterator __last) {} + +template<typename _BidirectionalIterator> +void inplace_merge(_BidirectionalIterator __first, + _BidirectionalIterator, + _BidirectionalIterator __last) {} } diff --git a/clang/test/Analysis/diagnostics/implicit-cxx-std-suppression.cpp b/clang/test/Analysis/diagnostics/implicit-cxx-std-suppression.cpp index a54508ee9d6c5..e677f2dd06ddd 100644 --- a/clang/test/Analysis/diagnostics/implicit-cxx-std-suppression.cpp +++ b/clang/test/Analysis/diagnostics/implicit-cxx-std-suppression.cpp @@ -47,3 +47,8 @@ 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 index c14a4583780c6..71ed21b5ef63a 100644 --- a/clang/test/Analysis/diagnostics/opaque-stl-functions-modeling-tag.cpp +++ b/clang/test/Analysis/diagnostics/opaque-stl-functions-modeling-tag.cpp @@ -9,6 +9,8 @@ void testOpaqueSTLTags() { std::sort(arr, arr + 5); // CHECK: \"tag\": \"cplusplus.OpaqueSTLFunctionsModeling : Forced Opaque Call\" 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\" } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
