From e8d3be3621241531a146571d55d7e181b1251e31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Ho=C5=99e=C5=88ovsk=C3=BD?= Date: Sat, 26 Aug 2017 15:14:27 +0200 Subject: [PATCH] Workaround raw string literal bug in VS2017 --- docs/configuration.md | 4 ++- docs/limitations.md | 32 +++++++++++++++++++++++ include/internal/catch_capture.hpp | 15 +++++++---- include/internal/catch_capture_matchers.h | 4 +-- 4 files changed, 47 insertions(+), 8 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 82edc55c..ff5811d9 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -72,6 +72,7 @@ This can be useful on certain platforms that do not provide the standard iostrea CATCH_CONFIG_DISABLE_MATCHERS // Do not compile Matchers in this compilation unit CATCH_CONFIG_POSIX_SIGNALS // Enable handling POSIX signals CATCH_CONFIG_WINDOWS_CRTDBG // Enable leak checking using Windows's CRT Debug Heap + CATCH_CONFIG_DISABLE_STRINGIFICATION // Disable stringifying the original expression Currently Catch enables `CATCH_CONFIG_WINDOWS_SEH` only when compiled with MSVC, because some versions of MinGW do not have the necessary Win32 API support. @@ -88,12 +89,13 @@ Defining this flag speeds up compilation of test files by ~20%, by making 2 chan `CATCH_CONFIG_FAST_COMPILE` has to be either defined, or not defined, in all translation units that are linked into single test binary, or the behaviour of setting `-b` flag and throwing unexpected exceptions will be unpredictable. - ## `CATCH_CONFIG_DISABLE_MATCHERS` When `CATCH_CONFIG_DISABLE_MATCHERS` is defined, all mentions of Catch's Matchers are ifdef-ed away from the translation unit. Doing so will speed up compilation of that TU. _Note: If you define `CATCH_CONFIG_DISABLE_MATCHERS` in the same file as Catch's main is implemented, your test executable will fail to link if you use Matchers anywhere._ +## `CATCH_CONFIG_DISABLE_STRINGIFICATION` +This toggle enables a workaround for VS 2017 bug. For details see [known limitations](limitations.md#Visual Studio 2017 -- raw string literal in assert fails to compile) # Windows header clutter diff --git a/docs/limitations.md b/docs/limitations.md index 8c192320..c6ef640e 100644 --- a/docs/limitations.md +++ b/docs/limitations.md @@ -65,6 +65,38 @@ Both of these solutions have their problems, but should let you wring parallelis ## 3rd party bugs This section outlines known bugs in 3rd party components (this means compilers, standard libraries, standard runtimes). +### Visual Studio 2017 -- raw string literal in assert fails to compile +There is a known bug in Visual Studio 2017 (VC 15), that causes compilation error when preprocessor attempts to stringize a raw string literal (`#` preprocessor is applied to it). This snippet is sufficient to trigger the compilation error: +```cpp +#define CATCH_CONFIG_MAIN +#include "catch.hpp" + +TEST_CASE("test") { + CHECK(std::string(R"("\)") == "\"\\"); +} +``` + +Catch provides a workaround, it is possible to disable stringification of original expressions by defining `CATCH_CONFIG_DISABLE_STRINGIFICATION`: +```cpp +#define CATCH_CONFIG_FAST_COMPILE +#define CATCH_CONFIG_DISABLE_STRINGIFICATION +#include "catch.hpp" + +TEST_CASE("test") { + CHECK(std::string(R"("\)") == "\"\\"); +} +``` + +_Do note that this changes the output somewhat_ +``` +catchwork\test1.cpp(6): +PASSED: + CHECK( Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION ) +with expansion: + ""\" == ""\" +``` + + ### Visual Studio 2013 -- do-while loop withing range based for fails to compile (C2059) There is a known bug in Visual Studio 2013 (VC 12), that causes compilation error if range based for is followed by an assertion macro, without enclosing the block in braces. This snippet is sufficient to trigger the error ```cpp diff --git a/include/internal/catch_capture.hpp b/include/internal/catch_capture.hpp index d5d0ad4e..bfa18bfd 100644 --- a/include/internal/catch_capture.hpp +++ b/include/internal/catch_capture.hpp @@ -12,6 +12,11 @@ #include "catch_message.h" #include "catch_interfaces_capture.h" +#if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION) + #define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__ +#else + #define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION" +#endif #if defined(CATCH_CONFIG_FAST_COMPILE) /////////////////////////////////////////////////////////////////////////////// @@ -50,7 +55,7 @@ /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \ do { \ - Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, #__VA_ARGS__, resultDisposition ); \ + Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ INTERNAL_CATCH_TRY( catchAssertionHandler ) { \ CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ catchAssertionHandler.handle( Catch::Decomposer() <= __VA_ARGS__ ); \ @@ -73,7 +78,7 @@ /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, ... ) \ do { \ - Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, #__VA_ARGS__, resultDisposition ); \ + Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ try { \ static_cast(__VA_ARGS__); \ catchAssertionHandler.handle( Catch::ResultWas::Ok ); \ @@ -87,7 +92,7 @@ /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_THROWS( macroName, resultDisposition, ... ) \ do { \ - Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, #__VA_ARGS__, resultDisposition); \ + Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition); \ if( catchAssertionHandler.allowThrows() ) \ try { \ static_cast(__VA_ARGS__); \ @@ -104,7 +109,7 @@ /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \ do { \ - Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, #expr ", " #exceptionType, resultDisposition ); \ + Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr) ", " CATCH_INTERNAL_STRINGIFY(exceptionType), resultDisposition ); \ if( catchAssertionHandler.allowThrows() ) \ try { \ static_cast(expr); \ @@ -138,7 +143,7 @@ // Although this is matcher-based, it can be used with just a string #define INTERNAL_CATCH_THROWS_STR_MATCHES( macroName, resultDisposition, matcher, ... ) \ do { \ - Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, #__VA_ARGS__ ", " #matcher, resultDisposition ); \ + Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ if( catchAssertionHandler.allowThrows() ) \ try { \ static_cast(__VA_ARGS__); \ diff --git a/include/internal/catch_capture_matchers.h b/include/internal/catch_capture_matchers.h index 80262dbf..3520ad2e 100644 --- a/include/internal/catch_capture_matchers.h +++ b/include/internal/catch_capture_matchers.h @@ -57,7 +57,7 @@ namespace Catch { /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CHECK_THAT( macroName, matcher, resultDisposition, arg ) \ do { \ - Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition ); \ + Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ INTERNAL_CATCH_TRY( catchAssertionHandler ) { \ catchAssertionHandler.handle( Catch::makeMatchExpr( arg, matcher, #matcher ) ); \ } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ @@ -68,7 +68,7 @@ namespace Catch { /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_THROWS_MATCHES( macroName, exceptionType, resultDisposition, matcher, ... ) \ do { \ - Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, #__VA_ARGS__ ", " #exceptionType ", " #matcher, resultDisposition ); \ + Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(exceptionType) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ if( catchAssertionHandler.allowThrows() ) \ try { \ static_cast(__VA_ARGS__ ); \