approvals / ApprovalTests.cpp

Native ApprovalTests for C++ on Linux, Mac and Windows
https://approvaltestscpp.readthedocs.io/en/latest/
Apache License 2.0
312 stars 51 forks source link

Confusing error message when porting to Options #140

Closed claremacrae closed 4 years ago

claremacrae commented 4 years ago

With v.10.0.01, compiling code that passes a reporter to verify() or verifyAll() gives this error message:

In file included from /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp.Demos/catch2_demo/Test07TestScenarios.cpp:2:
In file included from /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp.Demos/third_party/approval_tests/ApprovalTests.hpp:1:
/Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp.Demos/third_party/approval_tests/ApprovalTests.v.10.0.1.hpp:3249:13:
error: type 'ApprovalTests::QuietReporter' does not provide a call operator
            converter(contents, s);
            ^~~~~~~~~
/Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp.Demos/catch2_demo/Test07TestScenarios.cpp:133:16:
note: in instantiation of function template specialization
'ApprovalTests::TApprovals<ApprovalTests::ToStringCompileTimeOptions<ApprovalTests::StringMaker> >
::verify<(anonymous namespace)::QImageWriter, ApprovalTests::QuietReporter, void>' requested here
    Approvals::verify(imageWriter, QuietReporter{});
               ^
1 error generated.

It is saying that this code is invalid:

        template <typename T,
                  typename Function,
                  typename = Detail::EnableIfNotOptions<Function>>
        static void
        verify(const T& contents, Function converter, const Options& options = Options())
        {
            std::stringstream s;
            converter(contents, s);
            verify(s.str(), options);
        }

This overload is considered because we only disallow Options as the 3rd argument.

If we reinstated "not options and not Reporter", users doing porting might get a better porting experience.

(Those using custom writers can't use intermediate earlier releases, due to #139)

claremacrae commented 4 years ago

Similarly, for CombinationApprovals::verifyAllCombinations(), if I pass in a Reporter instead of an Options, I get:

In file included from /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/DocTest_Tests/CombinationTests.cpp:7:
In file included from /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/ApprovalTests/../ApprovalTests/CombinationApprovals.h:8:
/Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/ApprovalTests/../ApprovalTests/utilities/CartesianProduct.h:190:49: error: no matching function for call to 'begin'
            const auto begins = std::make_tuple(begin(ranges)...);
                                                ^~~~~
/Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/ApprovalTests/../ApprovalTests/CombinationApprovals.h:56:31: note: in instantiation of function template specialization 'ApprovalTests::CartesianProduct::cartesian_product<ApprovalTests::TCombinationApprovals<ApprovalTests::ToStringCompileTimeOptions<ApprovalTests::StringMaker> >::serialize<FakeReporter &>, (lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/DocTest_Tests/CombinationTests.cpp:42:23), std::__1::vector<std::__1::basic_string<char>, std::__1::allocator<std::__1::basic_string<char> > > >' requested here
            CartesianProduct::cartesian_product(
                              ^
/Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/ApprovalTests/../ApprovalTests/CombinationApprovals.h:67:13: note: in instantiation of function template specialization 'ApprovalTests::TCombinationApprovals<ApprovalTests::ToStringCompileTimeOptions<ApprovalTests::StringMaker> >::verifyAllCombinations<FakeReporter &, (lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/DocTest_Tests/CombinationTests.cpp:42:23), std::__1::vector<std::__1::basic_string<char>, std::__1::allocator<std::__1::basic_string<char> > > >' requested here
            verifyAllCombinations(
            ^
/Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/DocTest_Tests/CombinationTests.cpp:41:31: note: in instantiation of function template specialization 'ApprovalTests::TCombinationApprovals<ApprovalTests::ToStringCompileTimeOptions<ApprovalTests::StringMaker> >::verifyAllCombinations<FakeReporter &, (lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/DocTest_Tests/CombinationTests.cpp:42:23), std::__1::vector<std::__1::basic_string<char>, std::__1::allocator<std::__1::basic_string<char> > > >' requested here
        CombinationApprovals::verifyAllCombinations(
                              ^
/opt/local/libexec/llvm-9.0/bin/../include/c++/v1/iterator:1703:1: note: candidate template ignored: substitution failure [with _Cp = const (lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/DocTest_Tests/CombinationTests.cpp:42:23)]: no member named 'begin' in '(lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/DocTest_Tests/CombinationTests.cpp:42:23)'
begin(_Cp& __c) -> decltype(__c.begin())
^                               ~~~~~
/opt/local/libexec/llvm-9.0/bin/../include/c++/v1/iterator:1711:1: note: candidate template ignored: substitution failure [with _Cp = (lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/DocTest_Tests/CombinationTests.cpp:42:23)]: no member named 'begin' in '(lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/DocTest_Tests/CombinationTests.cpp:42:23)'
begin(const _Cp& __c) -> decltype(__c.begin())
^                                     ~~~~~
/opt/local/libexec/llvm-9.0/bin/../include/c++/v1/initializer_list:99:1: note: candidate template ignored: could not match 'initializer_list<type-parameter-0-0>' against '(lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/DocTest_Tests/CombinationTests.cpp:42:23)'
begin(initializer_list<_Ep> __il) _NOEXCEPT
^
/opt/local/libexec/llvm-9.0/bin/../include/c++/v1/iterator:1685:1: note: candidate template ignored: could not match '_Tp [_Np]' against 'const (lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/DocTest_Tests/CombinationTests.cpp:42:23)'
begin(_Tp (&__array)[_Np])
^
In file included from /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/DocTest_Tests/CombinationTests.cpp:7:
In file included from /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/ApprovalTests/../ApprovalTests/CombinationApprovals.h:8:
/Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/ApprovalTests/../ApprovalTests/utilities/CartesianProduct.h:191:47: error: no matching function for call to 'end'
            const auto ends = std::make_tuple(end(ranges)...);
                                              ^~~
/opt/local/libexec/llvm-9.0/bin/../include/c++/v1/iterator:1719:1: note: candidate template ignored: substitution failure [with _Cp = const (lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/DocTest_Tests/CombinationTests.cpp:42:23)]: no member named 'end' in '(lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/DocTest_Tests/CombinationTests.cpp:42:23)'
end(_Cp& __c) -> decltype(__c.end())
^                             ~~~
/opt/local/libexec/llvm-9.0/bin/../include/c++/v1/iterator:1727:1: note: candidate template ignored: substitution failure [with _Cp = (lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/DocTest_Tests/CombinationTests.cpp:42:23)]: no member named 'end' in '(lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/DocTest_Tests/CombinationTests.cpp:42:23)'
end(const _Cp& __c) -> decltype(__c.end())
^                                   ~~~
/opt/local/libexec/llvm-9.0/bin/../include/c++/v1/initializer_list:108:1: note: candidate template ignored: could not match 'initializer_list<type-parameter-0-0>' against '(lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/DocTest_Tests/CombinationTests.cpp:42:23)'
end(initializer_list<_Ep> __il) _NOEXCEPT
^
/opt/local/libexec/llvm-9.0/bin/../include/c++/v1/iterator:1693:1: note: candidate template ignored: could not match '_Tp [_Np]' against 'const (lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/DocTest_Tests/CombinationTests.cpp:42:23)'
end(_Tp (&__array)[_Np])
^
In file included from /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/DocTest_Tests/CombinationTests.cpp:7:
In file included from /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/ApprovalTests/../ApprovalTests/CombinationApprovals.h:8:
/Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/ApprovalTests/../ApprovalTests/utilities/CartesianProduct.h:143:28: error: no matching function for call to 'begin'
                    return begin(range) == end(range);
                           ^~~~~
/Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/ApprovalTests/../ApprovalTests/utilities/CartesianProduct.h:114:25: note: in instantiation of function template specialization 'ApprovalTests::CartesianProduct::Detail::is_range_empty::operator()<(lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/DocTest_Tests/CombinationTests.cpp:42:23)>' requested here
                    if (pred(std::forward<T>(value)))
                        ^
/Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/ApprovalTests/../ApprovalTests/utilities/CartesianProduct.h:71:22: note: in instantiation of function template specialization 'ApprovalTests::CartesianProduct::Detail::find_if_body<ApprovalTests::CartesianProduct::Detail::is_range_empty>::operator()<const (lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/DocTest_Tests/CombinationTests.cpp:42:23) &>' requested here
                    (std::forward<F>(f)(std::get<Is>(std::forward<Tuple>(t))), 0)...};
                     ^
/Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/ApprovalTests/../ApprovalTests/utilities/CartesianProduct.h:76:17: note: in instantiation of function template specialization 'ApprovalTests::CartesianProduct::Detail::for_each_impl<std::__1::tuple<const (lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/DocTest_Tests/CombinationTests.cpp:42:23) &, const std::__1::vector<std::__1::basic_string<char>, std::__1::allocator<std::__1::basic_string<char> > > &>, ApprovalTests::CartesianProduct::Detail::find_if_body<ApprovalTests::CartesianProduct::Detail::is_range_empty>, 0, 1>' requested here
                for_each_impl(
                ^
/Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/ApprovalTests/../ApprovalTests/utilities/CartesianProduct.h:127:17: note: in instantiation of function template specialization 'ApprovalTests::CartesianProduct::Detail::for_each<std::__1::tuple<const (lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/DocTest_Tests/CombinationTests.cpp:42:23) &, const std::__1::vector<std::__1::basic_string<char>, std::__1::allocator<std::__1::basic_string<char> > > &>, ApprovalTests::CartesianProduct::Detail::find_if_body<ApprovalTests::CartesianProduct::Detail::is_range_empty> >' requested here
                for_each(std::forward<Tuple>(tuple), find_if_body<Predicate>(pred, idx));
                ^
/Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/ApprovalTests/../ApprovalTests/utilities/CartesianProduct.h:134:24: note: in instantiation of function template specialization 'ApprovalTests::CartesianProduct::Detail::find_if<ApprovalTests::CartesianProduct::Detail::is_range_empty, std::__1::tuple<const (lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/DocTest_Tests/CombinationTests.cpp:42:23) &, const std::__1::vector<std::__1::basic_string<char>, std::__1::allocator<std::__1::basic_string<char> > > &> >' requested here
                return find_if(std::forward<Tuple>(tuple), pred) != tuple_size<Tuple>();
                       ^
/Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/ApprovalTests/../ApprovalTests/utilities/CartesianProduct.h:187:25: note: in instantiation of function template specialization 'ApprovalTests::CartesianProduct::Detail::any_of<ApprovalTests::CartesianProduct::Detail::is_range_empty, std::__1::tuple<const (lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/DocTest_Tests/CombinationTests.cpp:42:23) &, const std::__1::vector<std::__1::basic_string<char>, std::__1::allocator<std::__1::basic_string<char> > > &> >' requested here
            if (Detail::any_of<Detail::is_range_empty>(std::forward_as_tuple(ranges...)))
                        ^
/Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/ApprovalTests/../ApprovalTests/CombinationApprovals.h:56:31: note: in instantiation of function template specialization 'ApprovalTests::CartesianProduct::cartesian_product<ApprovalTests::TCombinationApprovals<ApprovalTests::ToStringCompileTimeOptions<ApprovalTests::StringMaker> >::serialize<FakeReporter &>, (lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/DocTest_Tests/CombinationTests.cpp:42:23), std::__1::vector<std::__1::basic_string<char>, std::__1::allocator<std::__1::basic_string<char> > > >' requested here
            CartesianProduct::cartesian_product(
                              ^
/Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/ApprovalTests/../ApprovalTests/CombinationApprovals.h:67:13: note: in instantiation of function template specialization 'ApprovalTests::TCombinationApprovals<ApprovalTests::ToStringCompileTimeOptions<ApprovalTests::StringMaker> >::verifyAllCombinations<FakeReporter &, (lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/DocTest_Tests/CombinationTests.cpp:42:23), std::__1::vector<std::__1::basic_string<char>, std::__1::allocator<std::__1::basic_string<char> > > >' requested here
            verifyAllCombinations(
            ^
/Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/DocTest_Tests/CombinationTests.cpp:41:31: note: in instantiation of function template specialization 'ApprovalTests::TCombinationApprovals<ApprovalTests::ToStringCompileTimeOptions<ApprovalTests::StringMaker> >::verifyAllCombinations<FakeReporter &, (lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/DocTest_Tests/CombinationTests.cpp:42:23), std::__1::vector<std::__1::basic_string<char>, std::__1::allocator<std::__1::basic_string<char> > > >' requested here
        CombinationApprovals::verifyAllCombinations(
                              ^
/opt/local/libexec/llvm-9.0/bin/../include/c++/v1/iterator:1703:1: note: candidate template ignored: substitution failure [with _Cp = const (lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/DocTest_Tests/CombinationTests.cpp:42:23)]: no member named 'begin' in '(lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/DocTest_Tests/CombinationTests.cpp:42:23)'
begin(_Cp& __c) -> decltype(__c.begin())
^                               ~~~~~
/opt/local/libexec/llvm-9.0/bin/../include/c++/v1/iterator:1711:1: note: candidate template ignored: substitution failure [with _Cp = (lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/DocTest_Tests/CombinationTests.cpp:42:23)]: no member named 'begin' in '(lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/DocTest_Tests/CombinationTests.cpp:42:23)'
begin(const _Cp& __c) -> decltype(__c.begin())
^                                     ~~~~~
/opt/local/libexec/llvm-9.0/bin/../include/c++/v1/initializer_list:99:1: note: candidate template ignored: could not match 'initializer_list<type-parameter-0-0>' against '(lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/DocTest_Tests/CombinationTests.cpp:42:23)'
begin(initializer_list<_Ep> __il) _NOEXCEPT
^
/opt/local/libexec/llvm-9.0/bin/../include/c++/v1/iterator:1685:1: note: candidate template ignored: could not match '_Tp [_Np]' against 'const (lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/DocTest_Tests/CombinationTests.cpp:42:23)'
begin(_Tp (&__array)[_Np])
^
3 errors generated.
claremacrae commented 4 years ago

If I reinstate the old check to prevent Reporter being passed in:

        template <typename T, typename R = void>
        using EnableIfNotOptions = typename std::enable_if<
            (!std::is_same<Options, typename std::decay<T>::type>::value) &&
            (!std::is_base_of<Reporter, typename std::decay<T>::type>::value),
            R>::type;

the errors produced are:

/Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/DocTest_Tests/ApprovalsTests.cpp:19:5: error: no matching function for call to 'verify'
    Approvals::verify(42, DiffReporter());
    ^~~~~~~~~~~~~~~~~
/Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/ApprovalTests/../ApprovalTests/Approvals.h:53:21: note: candidate function template not viable: no known conversion from 'ApprovalTests::DiffReporter' to 'const ApprovalTests::Options' for 2nd argument
        static void verify(const T& contents, const Options& options = Options())
                    ^
/Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/ApprovalTests/../ApprovalTests/Approvals.h:44:21: note: candidate function not viable: no known conversion from 'int' to 'const std::string' (aka 'const basic_string<char, char_traits<char>, allocator<char> >') for 1st argument
        static void verify(const std::string& contents,
                    ^
/Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/ApprovalTests/../ApprovalTests/Approvals.h:70:21: note: candidate function not viable: no known conversion from 'int' to 'const ApprovalTests::ApprovalWriter' for 1st argument
        static void verify(const ApprovalWriter& writer,
                    ^
/Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/ApprovalTests/../ApprovalTests/Approvals.h:62:9: note: candidate template ignored: requirement '!std::is_base_of<ApprovalTests::Reporter, ApprovalTests::DiffReporter>::value' was not satisfied [with T = int, Function = ApprovalTests::DiffReporter]
        verify(const T& contents, Function converter, const Options& options = Options())
        ^
1 error generated.

and for combinations:

/Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/DocTest_Tests/CombinationTests.cpp:41:9: error: no matching function for call to 'verifyAllCombinations'
        CombinationApprovals::verifyAllCombinations(
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/ApprovalTests/../ApprovalTests/CombinationApprovals.h:50:21: note: candidate function template not viable: no known conversion from 'FakeReporter' to 'const ApprovalTests::Options' for 1st argument
        static void verifyAllCombinations(const Options& options,
                    ^
/Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/ApprovalTests/../ApprovalTests/CombinationApprovals.h:64:69: note: candidate template ignored: requirement '!std::is_base_of<ApprovalTests::Reporter, FakeReporter>::value' was not satisfied [with Converter = FakeReporter &, Containers = <(lambda at /Users/clare/Documents/develop/ApprovalTests/ApprovalTests.cpp/tests/DocTest_Tests/CombinationTests.cpp:42:23), std::__1::vector<std::__1::basic_string<char>, std::__1::allocator<std::__1::basic_string<char> > >>]
        ApprovalTests::Detail::EnableIfNotOptions<Converter> static verifyAllCombinations(
                                                                    ^
1 error generated.
claremacrae commented 4 years ago

I measured the difference in build speed with and without this change and it was negligible - so I'll go ahead and check this in...

claremacrae commented 4 years ago

Closing, as this will be included in the next release - and change log has been updated