From cd30dd1a70923c6a264d07a6c88229447e425590 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 | 24 +++++++++++++--------- 3 files changed, 51 insertions(+), 9 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 6ac12c91..df06285c 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -107,6 +107,7 @@ TEST_CASE ("Error in streamable check") { CATCH_CONFIG_FAST_COMPILE // Sacrifices some (rather minor) features for compilation speed 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. @@ -123,6 +124,9 @@ 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_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 On Windows Catch includes `windows.h`. To minimize global namespace clutter in the implementation file, it defines `NOMINMAX` and `WIN32_LEAN_AND_MEAN` before including it. You can control this behaviour via two macros: diff --git a/docs/limitations.md b/docs/limitations.md index bee4a296..4897ec39 100644 --- a/docs/limitations.md +++ b/docs/limitations.md @@ -50,6 +50,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 b64cb965..92229df7 100644 --- a/include/internal/catch_capture.hpp +++ b/include/internal/catch_capture.hpp @@ -18,6 +18,12 @@ #include "catch_compiler_capabilities.h" +#if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION) +# define CATCH_INTERNAL_STRINGIFY(expr) #expr +#else +# define CATCH_INTERNAL_STRINGIFY(expr) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION" +#endif + #if defined(CATCH_CONFIG_FAST_COMPILE) /////////////////////////////////////////////////////////////////////////////// // We can speedup compilation significantly by breaking into debugger lower in @@ -33,7 +39,7 @@ // the exception before it propagates back up to the runner. #define INTERNAL_CATCH_TEST_NO_TRY( macroName, resultDisposition, expr ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr), resultDisposition ); \ __catchResult.setExceptionGuard(); \ CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ ( __catchResult <= expr ).endExpression(); \ @@ -45,9 +51,9 @@ #define INTERNAL_CHECK_THAT_NO_TRY( macroName, matcher, resultDisposition, arg ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition ); \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ __catchResult.setExceptionGuard(); \ - __catchResult.captureMatch( arg, matcher, #matcher ); \ + __catchResult.captureMatch( arg, matcher, CATCH_INTERNAL_STRINGIFY(matcher) ); \ __catchResult.unsetExceptionGuard(); \ INTERNAL_CATCH_REACT( __catchResult ) \ } while( Catch::alwaysFalse() ) @@ -67,7 +73,7 @@ /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr), resultDisposition ); \ try { \ CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ ( __catchResult <= expr ).endExpression(); \ @@ -93,7 +99,7 @@ /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, expr ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr), resultDisposition ); \ try { \ static_cast(expr); \ __catchResult.captureResult( Catch::ResultWas::Ok ); \ @@ -107,7 +113,7 @@ /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_THROWS( macroName, resultDisposition, matcher, expr ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition, #matcher ); \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr), resultDisposition, CATCH_INTERNAL_STRINGIFY(matcher) ); \ if( __catchResult.allowThrows() ) \ try { \ static_cast(expr); \ @@ -124,7 +130,7 @@ /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr ", " #exceptionType, resultDisposition ); \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr) ", " CATCH_INTERNAL_STRINGIFY(exceptionType), resultDisposition ); \ if( __catchResult.allowThrows() ) \ try { \ static_cast(expr); \ @@ -168,9 +174,9 @@ /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CHECK_THAT( macroName, matcher, resultDisposition, arg ) \ do { \ - Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition ); \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ try { \ - __catchResult.captureMatch( arg, matcher, #matcher ); \ + __catchResult.captureMatch( arg, matcher, CATCH_INTERNAL_STRINGIFY(matcher) ); \ } catch( ... ) { \ __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \ } \