Repository: doctest/doctest (language: cpp)

Issue:
issue introduced in 2.4.7
How to reproduce:
https://godbolt.org/z/5Y87aMW8W

fails with gcc but no problem with clang - doctest version 2.4.7 introduced the problem and it's because of this commit:
https://github.com/doctest/doctest/commit/20dbad670e1a6ebf52d3aa12d55c7784070a115d

Seems to be related to this member and/or the lack of a move constructor for the `stringstream_proxy` class
https://github.com/adishavit/argh/blob/master/argh.h#L67

Logging this to not forget about it - I haven't dug any deeper though.

Original report: https://github.com/adishavit/argh/issues/72



Discussion / hints:
Thank you for writing a reproducer, I was trying to write one as I am also facing this issue using doctest+Boost.Outcome. A fix would be welcome, currently stuck on doctest 2.4.6 because of it.

Fix patch:
diff --git a/examples/all_features/CMakeLists.txt b/examples/all_features/CMakeLists.txt
index 3d6b342f1..80105c98a 100644
--- a/examples/all_features/CMakeLists.txt
+++ b/examples/all_features/CMakeLists.txt
@@ -16,6 +16,7 @@ set(files_with_output
     test_cases_and_suites.cpp
     asserts_used_outside_of_tests.cpp
     enums.cpp
+    decomposition.cpp
 )
 
 set(files_all
diff --git a/examples/all_features/decomposition.cpp b/examples/all_features/decomposition.cpp
new file mode 100644
index 000000000..d610cc72e
--- /dev/null
+++ b/examples/all_features/decomposition.cpp
@@ -0,0 +1,25 @@
+#include <doctest/doctest.h>
+
+class MoveOnly {
+    public:
+        MoveOnly(int iIn) : i(iIn) { }
+        MoveOnly(MoveOnly&&) = default;
+        MoveOnly(const MoveOnly&) = delete;
+        MoveOnly& operator=(MoveOnly&&) = default;
+        MoveOnly& operator=(const MoveOnly&) = default;
+        operator bool() { // NOT const!
+            return i == 42;
+        }
+
+    private:
+        int i;
+};
+
+static MoveOnly genType(bool b) {
+    return MoveOnly(b ? 42 : 0);
+}
+
+TEST_CASE("Move Only Type") {
+    CHECK(genType(true));
+    CHECK(genType(false));
+}


Test patch:
diff --git a/doctest/doctest.h b/doctest/doctest.h
index 30315de87..d576bbd5d 100644
--- a/doctest/doctest.h
+++ b/doctest/doctest.h
@@ -408,9 +408,9 @@ namespace doctest { namespace detail {
     static DOCTEST_CONSTEXPR int consume(const int*, int) { return 0; }
 }}
 
-#define DOCTEST_GLOBAL_NO_WARNINGS(var, ...)                                                       \
-    DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wglobal-constructors")                              \
-    static const int var = doctest::detail::consume(&var, __VA_ARGS__);                            \
+#define DOCTEST_GLOBAL_NO_WARNINGS(var, ...)                                                         \
+    DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wglobal-constructors")                                \
+    static const int var = doctest::detail::consume(&var, __VA_ARGS__); /* NOLINT(cert-err58-cpp) */ \
     DOCTEST_CLANG_SUPPRESS_WARNING_POP
 
 #ifndef DOCTEST_BREAK_INTO_DEBUGGER
@@ -1391,7 +1391,7 @@ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wunused-comparison")
         assertType::Enum m_at;
 
         explicit Expression_lhs(L&& in, assertType::Enum at)
-                : lhs(doctest::detail::forward<L>(in))
+                : lhs(static_cast<L&&>(in))
                 , m_at(at) {}
 
         DOCTEST_NOINLINE operator Result() {
@@ -1465,8 +1465,8 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP
         // https://github.com/catchorg/Catch2/issues/870
         // https://github.com/catchorg/Catch2/issues/565
         template <typename L>
-        Expression_lhs<const L> operator<<(const L &&operand) {
-            return Expression_lhs<const L>(doctest::detail::forward<const L>(operand), m_at);
+        Expression_lhs<L> operator<<(L&& operand) {
+            return Expression_lhs<L>(static_cast<L&&>(operand), m_at);
         }
 
         template <typename L,typename enable_if<!doctest::detail::is_rvalue_reference<L>::value,void >::type* = nullptr>
diff --git a/doctest/parts/doctest_fwd.h b/doctest/parts/doctest_fwd.h
index 23f75ba43..031621505 100644
--- a/doctest/parts/doctest_fwd.h
+++ b/doctest/parts/doctest_fwd.h
@@ -1388,7 +1388,7 @@ DOCTEST_CLANG_SUPPRESS_WARNING_WITH_PUSH("-Wunused-comparison")
         assertType::Enum m_at;
 
         explicit Expression_lhs(L&& in, assertType::Enum at)
-                : lhs(doctest::detail::forward<L>(in))
+                : lhs(static_cast<L&&>(in))
                 , m_at(at) {}
 
         DOCTEST_NOINLINE operator Result() {
@@ -1462,8 +1462,8 @@ DOCTEST_CLANG_SUPPRESS_WARNING_POP
         // https://github.com/catchorg/Catch2/issues/870
         // https://github.com/catchorg/Catch2/issues/565
         template <typename L>
-        Expression_lhs<const L> operator<<(const L &&operand) {
-            return Expression_lhs<const L>(doctest::detail::forward<const L>(operand), m_at);
+        Expression_lhs<L> operator<<(L&& operand) {
+            return Expression_lhs<L>(static_cast<L&&>(operand), m_at);
         }
 
         template <typename L,typename enable_if<!doctest::detail::is_rvalue_reference<L>::value,void >::type* = nullptr>
diff --git a/examples/all_features/test_output/decomposition.cpp.txt b/examples/all_features/test_output/decomposition.cpp.txt
new file mode 100644
index 000000000..7eba247c7
--- /dev/null
+++ b/examples/all_features/test_output/decomposition.cpp.txt
@@ -0,0 +1,13 @@
+[doctest] run with "--help" for options
+===============================================================================
+decomposition.cpp(0):
+TEST CASE:  Move Only Type
+
+decomposition.cpp(0): ERROR: CHECK( genType(false) ) is NOT correct!
+  values: CHECK( {?} )
+
+===============================================================================
+[doctest] test cases: 1 | 0 passed | 1 failed |
+[doctest] assertions: 2 | 1 passed | 1 failed |
+[doctest] Status: FAILURE!
+Program code.
diff --git a/examples/all_features/test_output/decomposition.cpp_junit.txt b/examples/all_features/test_output/decomposition.cpp_junit.txt
new file mode 100644
index 000000000..8ab87d46f
--- /dev/null
+++ b/examples/all_features/test_output/decomposition.cpp_junit.txt
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<testsuites>
+  <testsuite name="all_features" errors="0" failures="1" tests="2">
+    <testcase classname="decomposition.cpp" name="Move Only Type" status="run">
+      <failure message="{?}" type="CHECK">
+decomposition.cpp(0):
+CHECK( genType(false) ) is NOT correct!
+  values: CHECK( {?} )
+
+      </failure>
+    </testcase>
+  </testsuite>
+</testsuites>
+Program code.
diff --git a/examples/all_features/test_output/decomposition.cpp_xml.txt b/examples/all_features/test_output/decomposition.cpp_xml.txt
new file mode 100644
index 000000000..584f85be7
--- /dev/null
+++ b/examples/all_features/test_output/decomposition.cpp_xml.txt
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<doctest binary="all_features">
+  <Options order_by="file" rand_seed="324" first="0" last="4294967295" abort_after="0" subcase_filter_levels="2147483647" case_sensitive="false" no_throw="false" no_skip="false"/>
+  <TestSuite>
+    <TestCase name="Move Only Type" filename="decomposition.cpp" line="0">
+      <Expression success="false" type="CHECK" filename="decomposition.cpp" line="0">
+        <Original>
+          genType(false)
+        </Original>
+        <Expanded>
+          {?}
+        </Expanded>
+      </Expression>
+      <OverallResultsAsserts successes="1" failures="1" test_case_success="false"/>
+    </TestCase>
+  </TestSuite>
+  <OverallResultsAsserts successes="1" failures="1"/>
+  <OverallResultsTestCases successes="0" failures="1"/>
+</doctest>
+Program code.
diff --git a/examples/all_features/test_output/filter_2.txt b/examples/all_features/test_output/filter_2.txt
index a076fd20b..c734cf8f5 100644
--- a/examples/all_features/test_output/filter_2.txt
+++ b/examples/all_features/test_output/filter_2.txt
@@ -1,6 +1,6 @@
 [doctest] run with "--help" for options
 ===============================================================================
-[doctest] test cases: 0 | 0 passed | 0 failed | 98 skipped
+[doctest] test cases: 0 | 0 passed | 0 failed | 99 skipped
 [doctest] assertions: 0 | 0 passed | 0 failed |
 [doctest] Status: SUCCESS!
 Program code.
diff --git a/examples/all_features/test_output/filter_2_xml.txt b/examples/all_features/test_output/filter_2_xml.txt
index 804fa4f11..e36071cd4 100644
--- a/examples/all_features/test_output/filter_2_xml.txt
+++ b/examples/all_features/test_output/filter_2_xml.txt
@@ -4,6 +4,7 @@
   <TestSuite>
     <TestCase name="  Scenario: vectors can be sized and resized" filename="subcases.cpp" line="0" skipped="true"/>
     <TestCase name="CHECK level of asserts fail the test case but don't abort it" filename="assertion_macros.cpp" line="0" skipped="true"/>
+    <TestCase name="Move Only Type" filename="decomposition.cpp" line="0" skipped="true"/>
     <TestCase name="Nested - related to https://github.com/doctest/doctest/issues/282" filename="subcases.cpp" line="0" skipped="true"/>
     <TestCase name="REQUIRE level of asserts fail and abort the test case - 1" filename="assertion_macros.cpp" line="0" skipped="true"/>
     <TestCase name="REQUIRE level of asserts fail and abort the test case - 10" filename="assertion_macros.cpp" line="0" skipped="true"/>
@@ -140,6 +141,6 @@
     <TestCase name="without a funny name:" filename="subcases.cpp" line="0" skipped="true"/>
   </TestSuite>
   <OverallResultsAsserts successes="0" failures="0"/>
-  <OverallResultsTestCases successes="0" failures="0" skipped="98"/>
+  <OverallResultsTestCases successes="0" failures="0" skipped="99"/>
 </doctest>
 Program code.
diff --git a/examples/all_features/test_output/no_multi_lane_atomics.txt b/examples/all_features/test_output/no_multi_lane_atomics.txt
index 1d3a309f0..490dd742f 100644
--- a/examples/all_features/test_output/no_multi_lane_atomics.txt
+++ b/examples/all_features/test_output/no_multi_lane_atomics.txt
@@ -52,6 +52,13 @@ assertion_macros.cpp(0): ERROR: CHECK_UNARY_FALSE( 1 ) is NOT correct!
 
 assertion_macros.cpp(0): MESSAGE: reached!
 
+===============================================================================
+decomposition.cpp(0):
+TEST CASE:  Move Only Type
+
+decomposition.cpp(0): ERROR: CHECK( genType(false) ) is NOT correct!
+  values: CHECK( {?} )
+
 ===============================================================================
 assertion_macros.cpp(0):
 TEST CASE:  REQUIRE level of asserts fail and abort the test case - 1
@@ -807,7 +814,7 @@ TEST CASE:  without a funny name:
 subcases.cpp(0): MESSAGE: Nooo
 
 ===============================================================================
-[doctest] test cases:  78 | 30 passed |  48 failed |
-[doctest] assertions: 213 | 99 passed | 114 failed |
+[doctest] test cases:  79 |  30 passed |  49 failed |
+[doctest] assertions: 215 | 100 passed | 115 failed |
 [doctest] Status: FAILURE!
 Program code.
diff --git a/examples/all_features/test_output/no_multithreading.txt b/examples/all_features/test_output/no_multithreading.txt
index 1d3a309f0..490dd742f 100644
--- a/examples/all_features/test_output/no_multithreading.txt
+++ b/examples/all_features/test_output/no_multithreading.txt
@@ -52,6 +52,13 @@ assertion_macros.cpp(0): ERROR: CHECK_UNARY_FALSE( 1 ) is NOT correct!
 
 assertion_macros.cpp(0): MESSAGE: reached!
 
+===============================================================================
+decomposition.cpp(0):
+TEST CASE:  Move Only Type
+
+decomposition.cpp(0): ERROR: CHECK( genType(false) ) is NOT correct!
+  values: CHECK( {?} )
+
 ===============================================================================
 assertion_macros.cpp(0):
 TEST CASE:  REQUIRE level of asserts fail and abort the test case - 1
@@ -807,7 +814,7 @@ TEST CASE:  without a funny name:
 subcases.cpp(0): MESSAGE: Nooo
 
 ===============================================================================
-[doctest] test cases:  78 | 30 passed |  48 failed |
-[doctest] assertions: 213 | 99 passed | 114 failed |
+[doctest] test cases:  79 |  30 passed |  49 failed |
+[doctest] assertions: 215 | 100 passed | 115 failed |
 [doctest] Status: FAILURE!
 Program code.