https://github.com/steakhal 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 01/17] [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 02/17] 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 03/17] 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\" } From 1e0eb2525531f9d5fadc5dc6992246cd244c0b22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Mon, 2 Feb 2026 19:12:48 +0100 Subject: [PATCH 04/17] remove out-of-date comment --- .../StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp index 46935d07c05c3..dbfa82d20d348 100644 --- a/clang/lib/StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp @@ -5,13 +5,6 @@ // 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" From 7296482163288c885591d543fe3c974e27efb554 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Mon, 2 Feb 2026 19:19:38 +0100 Subject: [PATCH 05/17] move into apiModeling to be enabled by default --- clang/include/clang/StaticAnalyzer/Checkers/Checkers.td | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td index 99dbae1dbf8b7..b167ee9480b48 100644 --- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -357,6 +357,10 @@ def TrustReturnsNonnullChecker : Checker<"TrustReturnsNonnull">, "are not null">, Documentation<NotDocumented>; +def OpaqueSTLFunctionsModeling : Checker<"OpaqueSTLFunctionsModeling">, + HelpText<"Model opaque, conservative evaluation of some STL functions">, + Documentation<NotDocumented>; + } // end "apiModeling" //===----------------------------------------------------------------------===// @@ -675,11 +679,6 @@ def PureVirtualCallChecker "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 { From 06da20179b3b66c19f63a64ed4e1d89291668562 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Mon, 2 Feb 2026 19:22:17 +0100 Subject: [PATCH 06/17] remove custom tag --- .../Checkers/OpaqueSTLFunctionsModeling.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp index dbfa82d20d348..9fa0a082fdeea 100644 --- a/clang/lib/StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp @@ -36,11 +36,9 @@ bool OpaqueSTLFunctionsModeling::evalCall(const CallEvent &Call, if (!ModeledFunctions.contains(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); + ProgramStateRef InvalidatedRegionsState = + Call.invalidateRegions(C.blockCount(), C.getState()); + C.addTransition(InvalidatedRegionsState); return true; } From f07af7bbbf19de24f33620f7a019ee9be0c28166 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Mon, 2 Feb 2026 19:22:49 +0100 Subject: [PATCH 07/17] update tests --- ...tem-header-simulator-cxx-std-suppression.h | 40 +++++++++++++------ .../implicit-cxx-std-suppression.cpp | 15 ------- .../opaque-stl-functions-modeling-tag.cpp | 16 -------- .../opaque-stl-functions-modeling.cpp | 24 +++++++++++ 4 files changed, 51 insertions(+), 44 deletions(-) delete mode 100644 clang/test/Analysis/diagnostics/opaque-stl-functions-modeling-tag.cpp create mode 100644 clang/test/Analysis/diagnostics/opaque-stl-functions-modeling.cpp 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 e7139987db66f..801cd9750c628 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 @@ -10,6 +10,12 @@ typedef unsigned char uint8_t; typedef __typeof__(sizeof(int)) size_t; void *memmove(void *s1, const void *s2, size_t n); +#define TRIGGER_DIV_BY_ZERO \ +do { \ + int z = 0; \ + z = 5/z; \ +} while (0); + namespace std { template <class _Tp> @@ -41,8 +47,7 @@ namespace std { // Fake use-after-free. // No warning is expected as we are suppressing warning coming // out of std::list. - int z = 0; - z = 5/z; + TRIGGER_DIV_BY_ZERO } bool empty() const; }; @@ -69,8 +74,7 @@ namespace std { // Fake error trigger. // No warning is expected as we are suppressing warning coming // out of std::basic_string. - int z = 0; - z = 5/z; + TRIGGER_DIV_BY_ZERO } _CharT *getBuffer() { @@ -108,8 +112,7 @@ __independent_bits_engine<_Engine, _UIntType> // Fake error trigger. // No warning is expected as we are suppressing warning coming // out of std::__independent_bits_engine. - int z = 0; - z = 5/z; + TRIGGER_DIV_BY_ZERO } #if __has_feature(cxx_decltype) @@ -130,8 +133,7 @@ class shared_ptr // Fake error trigger. // No warning is expected as we are suppressing warning coming // out of std::shared_ptr. - int z = 0; - z = 5/z; + TRIGGER_DIV_BY_ZERO } }; @@ -144,14 +146,26 @@ shared_ptr<_Tp>::shared_ptr(nullptr_t) { #endif // __has_feature(cxx_decltype) template<typename _RandomAccessIterator> -void sort(_RandomAccessIterator __first, _RandomAccessIterator __last) {} +void sort(_RandomAccessIterator __first, _RandomAccessIterator __last) { + // Fake error trigger + // std::sort is expected to be evaluated conservatively. + TRIGGER_DIV_BY_ZERO +} template<typename _RandomAccessIterator> -void stable_sort(_RandomAccessIterator __first, _RandomAccessIterator __last) {} +void stable_sort(_RandomAccessIterator __first, _RandomAccessIterator __last) { + // Fake error trigger + // std::stable_sort is expected to be evaluated conservatively. + TRIGGER_DIV_BY_ZERO +} template<typename _BidirectionalIterator> void inplace_merge(_BidirectionalIterator __first, - _BidirectionalIterator, - _BidirectionalIterator __last) {} -} + _BidirectionalIterator __middle, + _BidirectionalIterator __last) { + // Fake error trigger + // std::inplace_merge is expected to be evaluated conservatively. + TRIGGER_DIV_BY_ZERO + } +} // namespace std diff --git a/clang/test/Analysis/diagnostics/implicit-cxx-std-suppression.cpp b/clang/test/Analysis/diagnostics/implicit-cxx-std-suppression.cpp index e677f2dd06ddd..35f8798c81ae1 100644 --- a/clang/test/Analysis/diagnostics/implicit-cxx-std-suppression.cpp +++ b/clang/test/Analysis/diagnostics/implicit-cxx-std-suppression.cpp @@ -37,18 +37,3 @@ void testSuppression_std_shared_pointer() { p = nullptr; // no-warning } - -void testSuppression_sort() { - int arr[5]; - std::sort(arr, arr + 5); // 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 deleted file mode 100644 index 71ed21b5ef63a..0000000000000 --- a/clang/test/Analysis/diagnostics/opaque-stl-functions-modeling-tag.cpp +++ /dev/null @@ -1,16 +0,0 @@ -// 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::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\" -} - diff --git a/clang/test/Analysis/diagnostics/opaque-stl-functions-modeling.cpp b/clang/test/Analysis/diagnostics/opaque-stl-functions-modeling.cpp new file mode 100644 index 0000000000000..d8d84123da7c1 --- /dev/null +++ b/clang/test/Analysis/diagnostics/opaque-stl-functions-modeling.cpp @@ -0,0 +1,24 @@ +// RUN: %clang --analyze -Xclang -verify %s +// +// RUN: %clang --analyze -Xclang -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 test_sort() { + int arr[5]; + std::sort(arr, arr + 5); // no-warning + // CHECK: \"tag\": \"apiModeling.OpaqueSTLFunctionsModeling +} + +void test_stable_sort() { + int arr[5]; + std::stable_sort(arr, arr + 5); // no-warning + // CHECK: \"tag\": \"apiModeling.OpaqueSTLFunctionsModeling +} + +void test_inplace_merge() { + int arr[5]; + std::inplace_merge(arr, arr + 2, arr + 5); // no-warning + // CHECK: \"tag\": \"apiModeling.OpaqueSTLFunctionsModeling +} From b053d9ef782431f20d3591983ff0d47c8f4f4842 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Mon, 2 Feb 2026 19:59:29 +0100 Subject: [PATCH 08/17] add expected-no-diagnostics --- .../test/Analysis/diagnostics/opaque-stl-functions-modeling.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clang/test/Analysis/diagnostics/opaque-stl-functions-modeling.cpp b/clang/test/Analysis/diagnostics/opaque-stl-functions-modeling.cpp index d8d84123da7c1..e48b8c51be6c9 100644 --- a/clang/test/Analysis/diagnostics/opaque-stl-functions-modeling.cpp +++ b/clang/test/Analysis/diagnostics/opaque-stl-functions-modeling.cpp @@ -5,6 +5,8 @@ #include "../Inputs/system-header-simulator-cxx-std-suppression.h" +// expected-no-diagnostics + void test_sort() { int arr[5]; std::sort(arr, arr + 5); // no-warning From dcaf3fa6446f9b1b997f4f92ce5753664bedeb2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Mon, 2 Feb 2026 20:07:19 +0100 Subject: [PATCH 09/17] restore lexicographic order --- clang/include/clang/StaticAnalyzer/Checkers/Checkers.td | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td index b167ee9480b48..6e968d3dee561 100644 --- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -347,6 +347,10 @@ def ErrnoModeling : Checker<"Errno">, HelpText<"Make the special value 'errno' available to other checkers.">, Documentation<NotDocumented>; +def OpaqueSTLFunctionsModeling : Checker<"OpaqueSTLFunctionsModeling">, + HelpText<"Model opaque, conservative evaluation of some STL functions">, + Documentation<NotDocumented>; + def TrustNonnullChecker : Checker<"TrustNonnull">, HelpText<"Trust that returns from framework methods annotated with _Nonnull " "are not null">, @@ -357,10 +361,6 @@ def TrustReturnsNonnullChecker : Checker<"TrustReturnsNonnull">, "are not null">, Documentation<NotDocumented>; -def OpaqueSTLFunctionsModeling : Checker<"OpaqueSTLFunctionsModeling">, - HelpText<"Model opaque, conservative evaluation of some STL functions">, - Documentation<NotDocumented>; - } // end "apiModeling" //===----------------------------------------------------------------------===// From c0439c9cb58748980fc46e41a31b3ae1ab1510ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Mon, 2 Feb 2026 21:33:08 +0100 Subject: [PATCH 10/17] fix div-by-zero macro --- ...system-header-simulator-cxx-std-suppression.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) 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 801cd9750c628..47f013016debd 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 @@ -14,7 +14,7 @@ void *memmove(void *s1, const void *s2, size_t n); do { \ int z = 0; \ z = 5/z; \ -} while (0); +} while (0) namespace std { @@ -47,7 +47,7 @@ namespace std { // Fake use-after-free. // No warning is expected as we are suppressing warning coming // out of std::list. - TRIGGER_DIV_BY_ZERO + TRIGGER_DIV_BY_ZERO; } bool empty() const; }; @@ -74,7 +74,7 @@ namespace std { // Fake error trigger. // No warning is expected as we are suppressing warning coming // out of std::basic_string. - TRIGGER_DIV_BY_ZERO + TRIGGER_DIV_BY_ZERO; } _CharT *getBuffer() { @@ -112,7 +112,7 @@ __independent_bits_engine<_Engine, _UIntType> // Fake error trigger. // No warning is expected as we are suppressing warning coming // out of std::__independent_bits_engine. - TRIGGER_DIV_BY_ZERO + TRIGGER_DIV_BY_ZERO; } #if __has_feature(cxx_decltype) @@ -133,7 +133,7 @@ class shared_ptr // Fake error trigger. // No warning is expected as we are suppressing warning coming // out of std::shared_ptr. - TRIGGER_DIV_BY_ZERO + TRIGGER_DIV_BY_ZERO; } }; @@ -149,14 +149,14 @@ template<typename _RandomAccessIterator> void sort(_RandomAccessIterator __first, _RandomAccessIterator __last) { // Fake error trigger // std::sort is expected to be evaluated conservatively. - TRIGGER_DIV_BY_ZERO + TRIGGER_DIV_BY_ZERO; } template<typename _RandomAccessIterator> void stable_sort(_RandomAccessIterator __first, _RandomAccessIterator __last) { // Fake error trigger // std::stable_sort is expected to be evaluated conservatively. - TRIGGER_DIV_BY_ZERO + TRIGGER_DIV_BY_ZERO; } template<typename _BidirectionalIterator> @@ -165,7 +165,7 @@ void inplace_merge(_BidirectionalIterator __first, _BidirectionalIterator __last) { // Fake error trigger // std::inplace_merge is expected to be evaluated conservatively. - TRIGGER_DIV_BY_ZERO + TRIGGER_DIV_BY_ZERO; } } // namespace std From ee7318cdb0e1cfcc4d125c2f8367ecefc637025b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Mon, 2 Feb 2026 21:34:12 +0100 Subject: [PATCH 11/17] only 1 run-line is enough --- .../opaque-stl-functions-modeling.cpp | 22 +++++-------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/clang/test/Analysis/diagnostics/opaque-stl-functions-modeling.cpp b/clang/test/Analysis/diagnostics/opaque-stl-functions-modeling.cpp index e48b8c51be6c9..ec7e0bd66b28f 100644 --- a/clang/test/Analysis/diagnostics/opaque-stl-functions-modeling.cpp +++ b/clang/test/Analysis/diagnostics/opaque-stl-functions-modeling.cpp @@ -1,26 +1,16 @@ -// RUN: %clang --analyze -Xclang -verify %s -// -// RUN: %clang --analyze -Xclang -analyzer-dump-egraph=%t.dot -std=c++11 %s +// RUN: %clang --analyze -Xclang -analyzer-dump-egraph=%t.dot -std=c++11 -Xclang -verify %s // RUN: cat %t.dot | FileCheck %s #include "../Inputs/system-header-simulator-cxx-std-suppression.h" // expected-no-diagnostics -void test_sort() { - int arr[5]; +void test_opaque_handling() { + int arr[5] = {}; std::sort(arr, arr + 5); // no-warning - // CHECK: \"tag\": \"apiModeling.OpaqueSTLFunctionsModeling -} - -void test_stable_sort() { - int arr[5]; +// CHECK: \"tag\": \"apiModeling.OpaqueSTLFunctionsModeling\" std::stable_sort(arr, arr + 5); // no-warning - // CHECK: \"tag\": \"apiModeling.OpaqueSTLFunctionsModeling -} - -void test_inplace_merge() { - int arr[5]; +// CHECK: \"tag\": \"apiModeling.OpaqueSTLFunctionsModeling\" std::inplace_merge(arr, arr + 2, arr + 5); // no-warning - // CHECK: \"tag\": \"apiModeling.OpaqueSTLFunctionsModeling +// CHECK: \"tag\": \"apiModeling.OpaqueSTLFunctionsModeling\" } From e0b4e243968685fef2c097e5eda9309a391f0018 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Wed, 4 Feb 2026 11:17:01 +0100 Subject: [PATCH 12/17] fix test case formatting --- .../Inputs/system-header-simulator-cxx-std-suppression.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 47f013016debd..3b3b316469094 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 @@ -166,6 +166,6 @@ void inplace_merge(_BidirectionalIterator __first, // Fake error trigger // std::inplace_merge is expected to be evaluated conservatively. TRIGGER_DIV_BY_ZERO; - } +} } // namespace std From 007fe7a969ddf6356c7294c2b5d9b08564427a69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Wed, 4 Feb 2026 11:04:31 +0100 Subject: [PATCH 13/17] use clang_analyze_cc1 --- .../test/Analysis/diagnostics/opaque-stl-functions-modeling.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/test/Analysis/diagnostics/opaque-stl-functions-modeling.cpp b/clang/test/Analysis/diagnostics/opaque-stl-functions-modeling.cpp index ec7e0bd66b28f..d35f1a91560ef 100644 --- a/clang/test/Analysis/diagnostics/opaque-stl-functions-modeling.cpp +++ b/clang/test/Analysis/diagnostics/opaque-stl-functions-modeling.cpp @@ -1,4 +1,4 @@ -// RUN: %clang --analyze -Xclang -analyzer-dump-egraph=%t.dot -std=c++11 -Xclang -verify %s +// RUN: %clang_analyze_cc1 -analyzer-dump-egraph=%t.dot -std=c++11 -verify %s // RUN: cat %t.dot | FileCheck %s #include "../Inputs/system-header-simulator-cxx-std-suppression.h" From 253d38189eeca16308ec48153ac5c67a123ac69a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Wed, 4 Feb 2026 11:03:48 +0100 Subject: [PATCH 14/17] remove redundant typedef --- clang/lib/StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp index 9fa0a082fdeea..6f6da5a852601 100644 --- a/clang/lib/StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp @@ -23,7 +23,6 @@ class OpaqueSTLFunctionsModeling : public Checker<eval::Call> { bool evalCall(const CallEvent &Call, CheckerContext &C) const; private: - using CDM = CallDescription::Mode; const CallDescriptionSet ModeledFunctions{ {CDM::SimpleFunc, {"std", "sort"}}, {CDM::SimpleFunc, {"std", "stable_sort"}}, From a0a787e893c4f8b7d6798a9ac24e075b46694011 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Wed, 4 Feb 2026 11:15:13 +0100 Subject: [PATCH 15/17] remove headers belonging to the old impl --- .../lib/StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp index 6f6da5a852601..119d3301003a3 100644 --- a/clang/lib/StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp @@ -6,8 +6,6 @@ // //===----------------------------------------------------------------------===// -#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/CallDescription.h" From 0974982321cad646ab9f7d583179923b98d97b3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Endre=20F=C3=BCl=C3=B6p?= <[email protected]> Date: Wed, 4 Feb 2026 10:53:12 +0100 Subject: [PATCH 16/17] add a brief comment --- .../StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/clang/lib/StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp index 119d3301003a3..5483170d0ab1f 100644 --- a/clang/lib/StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/OpaqueSTLFunctionsModeling.cpp @@ -5,6 +5,12 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// +// +// Models STL functions whose best accurate model is to invalidate their +// arguments. Only functions where this simple approach is sufficient and won't +// interfere with the modeling of other checkers should be put here. +// +//===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/Checker.h" From 05aede833c8e77c628a6d5fecd666d728db36307 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Benics?= <[email protected]> Date: Wed, 4 Feb 2026 10:31:24 +0000 Subject: [PATCH 17/17] Drop unrelated line change from checkers.td --- clang/include/clang/StaticAnalyzer/Checkers/Checkers.td | 1 - 1 file changed, 1 deletion(-) diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td index 6e968d3dee561..c0dab071caff6 100644 --- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -678,7 +678,6 @@ def PureVirtualCallChecker HelpText< "Check pure virtual function calls during construction/destruction">, Documentation<HasDocumentation>; - } // end: "cplusplus" let ParentPackage = CplusplusOptIn in { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
