From 911d6eb98a780c084ff4c096be10b8e04301349c Mon Sep 17 00:00:00 2001 From: Jean-Louis Leroy Date: Mon, 19 Oct 2026 16:43:33 -0400 Subject: [PATCH 1/9] Coverity CID 504986: document why error() copies the handler Co-Authored-By: Claude Sonnet 5.5 --- include/boost/openmethod/policies/default_error_handler.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/boost/openmethod/policies/default_error_handler.hpp b/include/boost/openmethod/policies/default_error_handler.hpp index b9706e0c..89011c69 100644 --- a/include/boost/openmethod/policies/default_error_handler.hpp +++ b/include/boost/openmethod/policies/default_error_handler.hpp @@ -104,6 +104,9 @@ struct default_error_handler : error_handler { //! @param error The error object. template static auto error(const Error& error) -> void { + // Copy on purpose: the handler may call set(), which would destroy + // the function object while it is running. + // coverity[auto_causes_copy] auto handler = st().handler ? st().handler : default_handler; handler(error_variant(error)); } From 4c62910bf374d31b7fa6cded324c5343d8a155a1 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leroy Date: Mon, 19 Oct 2026 16:44:22 -0400 Subject: [PATCH 2/9] Coverity CID 504989: move the previous handler out of set() The conditional expression copied it; return it by implicit move instead. Co-Authored-By: Claude Sonnet 5.5 --- include/boost/openmethod/policies/default_error_handler.hpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/boost/openmethod/policies/default_error_handler.hpp b/include/boost/openmethod/policies/default_error_handler.hpp index 89011c69..eb0c2f93 100644 --- a/include/boost/openmethod/policies/default_error_handler.hpp +++ b/include/boost/openmethod/policies/default_error_handler.hpp @@ -122,7 +122,11 @@ struct default_error_handler : error_handler { static auto set(function_type new_handler) -> function_type { auto prev = std::exchange(st().handler, std::move(new_handler)); - return prev ? prev : default_handler; + if (!prev) { + return default_handler; + } + + return prev; } //! The default error handler function. From 8371a8e84a04c29904cd542aea44a77b5ea2c919 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leroy Date: Mon, 19 Oct 2026 16:45:19 -0400 Subject: [PATCH 3/9] Coverity CIDs 504988, 504991: bind the directory entry by reference Co-Authored-By: Claude Sonnet 5.5 --- test/dynamic_loading/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/dynamic_loading/main.cpp b/test/dynamic_loading/main.cpp index 0f863cd5..9489cbc2 100644 --- a/test/dynamic_loading/main.cpp +++ b/test/dynamic_loading/main.cpp @@ -34,7 +34,7 @@ BOOST_OPENMETHOD_TEST_CLASSES(Animal, Dog, Cat); boost::filesystem::path find_lib( const boost::filesystem::path& dir, const char* name_fragment) { - for (auto entry : boost::filesystem::directory_iterator(dir)) { + for (const auto& entry : boost::filesystem::directory_iterator(dir)) { auto fname = entry.path().filename().string(); if (fname.find(name_fragment) != std::string::npos) { return entry.path(); From 260a1c7052b4481f01881c365cd1f8bf3cf16e53 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leroy Date: Mon, 19 Oct 2026 16:51:59 -0400 Subject: [PATCH 4/9] Coverity CID 504987: annotate method::fn as order-independent Co-Authored-By: Claude Sonnet 5.5 --- include/boost/openmethod/core.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/boost/openmethod/core.hpp b/include/boost/openmethod/core.hpp index 226a5e1e..cdc77064 100644 --- a/include/boost/openmethod/core.hpp +++ b/include/boost/openmethod/core.hpp @@ -2939,6 +2939,9 @@ typename method::FunctionPointer template< typename Id, typename... Parameters, typename ReturnType, class Registry> method + // The constructor uses only zero- or constant-initialized statics, so the + // order of initialization does not matter. + // coverity[global_init_order] method::fn; template< From a305300a9389abda6a68b664896e842e0e17e659 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leroy Date: Mon, 19 Oct 2026 16:52:59 -0400 Subject: [PATCH 5/9] Coverity CID 504992: annotate the one-past-the-end pointer in type_id_end Co-Authored-By: Claude Sonnet 5.5 --- include/boost/openmethod/preamble.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/boost/openmethod/preamble.hpp b/include/boost/openmethod/preamble.hpp index f3e75bbd..707c5fff 100644 --- a/include/boost/openmethod/preamble.hpp +++ b/include/boost/openmethod/preamble.hpp @@ -361,6 +361,9 @@ struct class_info : static_list::static_link { } auto type_id_end() const { + // One past the end of a one-element range: a valid pointer, never + // dereferenced. + // coverity[array_vs_singleton] return &type + 1; } }; From e68f2a4aeb59e590f4effcf66ac04169892184f3 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leroy Date: Mon, 19 Oct 2026 16:54:28 -0400 Subject: [PATCH 6/9] Coverity CID 506031: track the best bit as a mask, not an int that starts at -1 Co-Authored-By: Claude Sonnet 5.5 --- include/boost/openmethod/policies/minimal_cover_hash.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/boost/openmethod/policies/minimal_cover_hash.hpp b/include/boost/openmethod/policies/minimal_cover_hash.hpp index 7c19b9e3..4e97a384 100644 --- a/include/boost/openmethod/policies/minimal_cover_hash.hpp +++ b/include/boost/openmethod/policies/minimal_cover_hash.hpp @@ -405,7 +405,7 @@ auto minimal_cover_hash::fn::minimal_cover( std::uint64_t by_add = 0; while (!injective(ids, by_add, scratch)) { - int best_bit = -1; + std::uint64_t best_bit = 0; std::size_t best_count = (std::numeric_limits::max)(); for (const auto& info : bits) { @@ -419,11 +419,11 @@ auto minimal_cover_hash::fn::minimal_cover( if (count < best_count) { best_count = count; - best_bit = info.bit; + best_bit = bit; } } - by_add |= std::uint64_t(1) << best_bit; + by_add |= best_bit; } by_add = drop(by_add); From 9b90fe30e8ef5164865b6f21dbd8375f8ab5d0e3 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leroy Date: Mon, 19 Oct 2026 16:56:54 -0400 Subject: [PATCH 7/9] Coverity CIDs 506030, 505810-505817, 490457, 505813, 490469: tests The tests that check a moved-from object do so on purpose: annotate the first use after each move. Move from the last use in test_slot_allocator. Co-Authored-By: Claude Sonnet 5.5 --- test/test_dispatch_boost_any.cpp | 3 +++ test/test_dispatch_std_any.cpp | 3 +++ test/test_dispatch_type_erasure.cpp | 2 ++ test/test_slot_allocator.cpp | 4 ++-- test/test_virtual_any_type_erasure.cpp | 2 ++ test/test_virtual_ptr_value_semantics.cpp | 2 +- test/test_weak_virtual_ptr.cpp | 3 +++ 7 files changed, 16 insertions(+), 3 deletions(-) diff --git a/test/test_dispatch_boost_any.cpp b/test/test_dispatch_boost_any.cpp index e79cf8ef..602228fc 100644 --- a/test/test_dispatch_boost_any.cpp +++ b/test/test_dispatch_boost_any.cpp @@ -197,17 +197,20 @@ BOOST_AUTO_TEST_CASE(boost_any_by_xvalue_ref) { boost::any spot(Dog{"Spot"}); BOOST_TEST(steal(std::move(spot)) == "Spot the dog"); // the overrider moved the name out; the `any` still owns the Dog + // coverity[use_after_move] BOOST_TEST(!spot.empty()); BOOST_TEST(boost::any_cast(spot).name == ""); boost::any felix(std::string{"Felix the cat"}); BOOST_TEST(steal(std::move(felix)) == "Felix the cat"); + // coverity[use_after_move] BOOST_TEST(!felix.empty()); BOOST_TEST(boost::any_cast(felix) == ""); // moving an int copies it boost::any answer(42); BOOST_TEST(steal(std::move(answer)) == "42 the integer"); + // coverity[use_after_move] BOOST_TEST(boost::any_cast(answer) == 42); } } // namespace BOOST_OPENMETHOD_GENSYM diff --git a/test/test_dispatch_std_any.cpp b/test/test_dispatch_std_any.cpp index ac54b5ef..fe4026b4 100644 --- a/test/test_dispatch_std_any.cpp +++ b/test/test_dispatch_std_any.cpp @@ -213,17 +213,20 @@ BOOST_AUTO_TEST_CASE(std_any_by_xvalue_ref) { std::any spot(Dog{"Spot"}); BOOST_TEST(steal(std::move(spot)) == "Spot the dog"); // the overrider moved the name out; the `any` still owns the Dog + // coverity[use_after_move] BOOST_TEST(spot.has_value()); BOOST_TEST(std::any_cast(spot).name == ""); std::any felix(std::string{"Felix the cat"}); BOOST_TEST(steal(std::move(felix)) == "Felix the cat"); + // coverity[use_after_move] BOOST_TEST(felix.has_value()); BOOST_TEST(std::any_cast(felix) == ""); // moving an int copies it std::any answer(42); BOOST_TEST(steal(std::move(answer)) == "42 the integer"); + // coverity[use_after_move] BOOST_TEST(std::any_cast(answer) == 42); } } // namespace BOOST_OPENMETHOD_GENSYM diff --git a/test/test_dispatch_type_erasure.cpp b/test/test_dispatch_type_erasure.cpp index da44b1ee..9e586374 100644 --- a/test/test_dispatch_type_erasure.cpp +++ b/test/test_dispatch_type_erasure.cpp @@ -173,11 +173,13 @@ BOOST_AUTO_TEST_CASE(type_erasure_by_xvalue_ref) { erased spot(Dog{"Spot"}); BOOST_TEST(steal(std::move(spot)) == "Spot the dog"); // the overrider moved the name out; the `any` still owns the Dog + // coverity[use_after_move] BOOST_TEST(!te::is_empty(spot)); BOOST_TEST(te::any_cast(spot).name == ""); erased felix(std::string{"Felix the cat"}); BOOST_TEST(steal(std::move(felix)) == "Felix the cat"); + // coverity[use_after_move] BOOST_TEST(te::any_cast(felix) == ""); } } // namespace BOOST_OPENMETHOD_GENSYM diff --git a/test/test_slot_allocator.cpp b/test/test_slot_allocator.cpp index 0e4e36ea..dd3b05fb 100644 --- a/test/test_slot_allocator.cpp +++ b/test/test_slot_allocator.cpp @@ -56,7 +56,7 @@ auto str(const Container& container) { template auto sstr(Ts... args) { - std::vector vec{args...}; + std::vector vec{std::move(args)...}; std::sort(vec.begin(), vec.end()); return str(vec); @@ -360,7 +360,7 @@ auto sampled_orders(std::size_t n, std::size_t samples) -> std::vector { std::vector orders{listed}; orders.emplace_back(listed.rbegin(), listed.rend()); std::mt19937 rng(20250510u); - auto shuffled = listed; + auto shuffled = std::move(listed); while (samples--) { for (auto i = shuffled.size(); i > 1; --i) { diff --git a/test/test_virtual_any_type_erasure.cpp b/test/test_virtual_any_type_erasure.cpp index fb8916c1..080d8583 100644 --- a/test/test_virtual_any_type_erasure.cpp +++ b/test/test_virtual_any_type_erasure.cpp @@ -164,6 +164,7 @@ BOOST_AUTO_TEST_CASE(virtual_any_by_xvalue_ref) { virtual_erased spot = Dog{"Spot"}; BOOST_TEST(steal(std::move(spot)) == "Spot the dog"); // the overrider moved the name out; the virtual_any still owns the Dog + // coverity[use_after_move] BOOST_TEST(!te::is_empty(spot.get())); BOOST_TEST(te::any_cast(spot.get()).name == ""); @@ -203,6 +204,7 @@ BOOST_AUTO_TEST_CASE(virtual_any_value_semantics) { // move: the source's v-table pointer is nulled auto moved = std::move(copy); BOOST_TEST(moved.vptr() == default_registry::static_vptr); + // coverity[use_after_move] BOOST_TEST(copy.vptr() == nullptr); BOOST_TEST(name(moved) == "Rex the dog"); diff --git a/test/test_virtual_ptr_value_semantics.cpp b/test/test_virtual_ptr_value_semantics.cpp index c5e716f0..811bb599 100644 --- a/test/test_virtual_ptr_value_semantics.cpp +++ b/test/test_virtual_ptr_value_semantics.cpp @@ -166,9 +166,9 @@ BOOST_AUTO_TEST_CASE_TEMPLATE( Dog snoopy; virtual_ptr p(snoopy); virtual_ptr q(std::move(p)); - // coverity[use_after_move] BOOST_TEST(q.get() == &snoopy); BOOST_TEST(q.vptr() == Registry::template static_vptr); + // coverity[use_after_move] BOOST_TEST(p.get() == &snoopy); BOOST_TEST(p.vptr() == Registry::template static_vptr); } diff --git a/test/test_weak_virtual_ptr.cpp b/test/test_weak_virtual_ptr.cpp index 29e413ac..a7020308 100644 --- a/test/test_weak_virtual_ptr.cpp +++ b/test/test_weak_virtual_ptr.cpp @@ -264,6 +264,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE( weak_virtual_ptr moved(std::move(source)); BOOST_TEST(moved.lock().get() == snoopy.get()); BOOST_TEST(moved.vptr() == Registry::template static_vptr); + // coverity[use_after_move] BOOST_TEST(source.expired()); BOOST_TEST(source.vptr() == nullptr); } @@ -274,6 +275,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE( weak_virtual_ptr moved(std::move(source)); BOOST_TEST(moved.lock().get() == snoopy.get()); BOOST_TEST(moved.vptr() == Registry::template static_vptr); + // coverity[use_after_move] BOOST_TEST(source.expired()); BOOST_TEST(source.vptr() == nullptr); } @@ -284,6 +286,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE( moved = std::move(source); BOOST_TEST(moved.lock().get() == snoopy.get()); BOOST_TEST(moved.vptr() == Registry::template static_vptr); + // coverity[use_after_move] BOOST_TEST(source.expired()); BOOST_TEST(source.vptr() == nullptr); } From 0308875cd730ff5082d0b9861f7a5711d5f62811 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leroy Date: Mon, 19 Oct 2026 17:01:06 -0400 Subject: [PATCH 8/9] Coverity CIDs 505640, 505641: b2 skips compile-fail tests in the Coverity step Boost.CI sets COVERITY_SCAN_TOKEN only there. Coverity analyzes the half-parsed translation units of tests meant not to compile. Co-Authored-By: Claude Sonnet 5.5 --- test/Jamfile | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/test/Jamfile b/test/Jamfile index 791eb6fb..782dbd91 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -8,6 +8,7 @@ import-search /boost/config/checks ; +import os ; import testing ; import config : requires ; @@ -107,10 +108,17 @@ for local src in $(bmi2-sources) run mix_release_debug/main.cpp mix_release_debug/lib.cpp unit_test_framework ; -for local src in [ glob compile_fail_*.cpp - ../doc/modules/ROOT/examples/troubleshooting/compile_fail_*.cpp ] +# Boost.CI sets COVERITY_SCAN_TOKEN only in its Coverity step. These sources +# are meant not to compile; Coverity analyzes the half-parsed translation unit +# and reports defects (a missing return, an uncaught exception) that are not +# in the code. +if ! [ os.environ COVERITY_SCAN_TOKEN ] { - compile-fail $(src) ; + for local src in [ glob compile_fail_*.cpp + ../doc/modules/ROOT/examples/troubleshooting/compile_fail_*.cpp ] + { + compile-fail $(src) ; + } } build-project dynamic_loading ; From 18594e1ba0e50406fa56d1756ec4bb954cad4d7c Mon Sep 17 00:00:00 2001 From: Jean-Louis Leroy Date: Mon, 19 Oct 2026 17:04:41 -0400 Subject: [PATCH 9/9] Coverity CIDs 490461, 490475, 501245: annotate the deliberate lvalue copy Co-Authored-By: Claude Sonnet 5.5 --- test/test_shared_virtual_ptr_value_semantics.cpp | 2 ++ test/test_smart_virtual_ptr_value_semantics.cpp | 2 ++ test/test_virtual_ptr_value_semantics.cpp | 2 ++ 3 files changed, 6 insertions(+) diff --git a/test/test_shared_virtual_ptr_value_semantics.cpp b/test/test_shared_virtual_ptr_value_semantics.cpp index e79eaf6d..5ac3b952 100644 --- a/test/test_shared_virtual_ptr_value_semantics.cpp +++ b/test/test_shared_virtual_ptr_value_semantics.cpp @@ -179,6 +179,8 @@ BOOST_AUTO_TEST_CASE_TEMPLATE( // shared_virtual_ptr(shared_virtual_ptr&) auto snoopy = std::make_shared(); shared_virtual_ptr p(snoopy); + // The copy is the point: this tests the non-const lvalue overload. + // coverity[copy_instead_of_move] shared_virtual_ptr q(p); BOOST_TEST(q.get() == snoopy.get()); BOOST_TEST(q.vptr() == Registry::template static_vptr); diff --git a/test/test_smart_virtual_ptr_value_semantics.cpp b/test/test_smart_virtual_ptr_value_semantics.cpp index 47b6333d..eacb7d78 100644 --- a/test/test_smart_virtual_ptr_value_semantics.cpp +++ b/test/test_smart_virtual_ptr_value_semantics.cpp @@ -249,6 +249,8 @@ BOOST_AUTO_TEST_CASE_TEMPLATE( // dog_virtual_ptr(dog_virtual_ptr&) auto snoopy = smart::template make(); dog_virtual_ptr p(snoopy); + // The copy is the point: this tests the non-const lvalue overload. + // coverity[copy_instead_of_move] dog_virtual_ptr q(p); BOOST_TEST(q.get() == snoopy.get()); BOOST_TEST(q.vptr() == default_registry::template static_vptr); diff --git a/test/test_virtual_ptr_value_semantics.cpp b/test/test_virtual_ptr_value_semantics.cpp index 811bb599..f677cd35 100644 --- a/test/test_virtual_ptr_value_semantics.cpp +++ b/test/test_virtual_ptr_value_semantics.cpp @@ -156,6 +156,8 @@ BOOST_AUTO_TEST_CASE_TEMPLATE( // virtual_ptr(virtual_ptr&) Dog snoopy; virtual_ptr p(snoopy); + // The copy is the point: this tests the non-const lvalue overload. + // coverity[copy_instead_of_move] virtual_ptr q(p); BOOST_TEST(q.get() == &snoopy); BOOST_TEST(q.vptr() == Registry::template static_vptr);