Experimental static analysis support in TEST_CASE and SECTION

Closes #2681
This commit is contained in:
Martin Hořeňovský 2023-05-15 14:33:24 +02:00
parent dba9197ec7
commit bf5aa7b383
No known key found for this signature in database
GPG Key ID: DE48307B8B0D381A
2 changed files with 111 additions and 23 deletions

View File

@ -9,6 +9,7 @@
#define CATCH_SECTION_HPP_INCLUDED #define CATCH_SECTION_HPP_INCLUDED
#include <catch2/internal/catch_compiler_capabilities.hpp> #include <catch2/internal/catch_compiler_capabilities.hpp>
#include <catch2/internal/catch_config_static_analysis_support.hpp>
#include <catch2/internal/catch_noncopyable.hpp> #include <catch2/internal/catch_noncopyable.hpp>
#include <catch2/catch_section_info.hpp> #include <catch2/catch_section_info.hpp>
#include <catch2/catch_timer.hpp> #include <catch2/catch_timer.hpp>
@ -38,16 +39,62 @@ namespace Catch {
} // end namespace Catch } // end namespace Catch
#if !defined(CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT)
# define INTERNAL_CATCH_SECTION( ... ) \ # define INTERNAL_CATCH_SECTION( ... ) \
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::Section( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \ if ( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( \
catch_internal_Section ) = \
Catch::Section( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
# define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \ # define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, (Catch::ReusableStringStream() << __VA_ARGS__).str() ) ) \ if ( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( \
catch_internal_Section ) = \
Catch::SectionInfo( \
CATCH_INTERNAL_LINEINFO, \
( Catch::ReusableStringStream() << __VA_ARGS__ ) \
.str() ) ) \
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
#else
// These section definitions imply that at most one section at one level
// will be intered (because only one section's __LINE__ can be equal to
// the dummy `catchInternalSectionHint` variable from `TEST_CASE`).
namespace Catch {
namespace Detail {
// Intentionally without linkage, as it should only be used as a dummy
// symbol for static analysis.
int GetNewSectionHint();
} // namespace Detail
} // namespace Catch
# define INTERNAL_CATCH_SECTION( ... ) \
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \
if ( [[maybe_unused]] int catchInternalPreviousSectionHint = \
catchInternalSectionHint, \
catchInternalSectionHint = Catch::Detail::GetNewSectionHint(); \
catchInternalPreviousSectionHint == __LINE__ ) \
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
# define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \
if ( [[maybe_unused]] int catchInternalPreviousSectionHint = \
catchInternalSectionHint, \
catchInternalSectionHint = Catch::Detail::GetNewSectionHint(); \
catchInternalPreviousSectionHint == __LINE__ ) \
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
#endif
#endif // CATCH_SECTION_HPP_INCLUDED #endif // CATCH_SECTION_HPP_INCLUDED

View File

@ -8,6 +8,7 @@
#ifndef CATCH_TEST_REGISTRY_HPP_INCLUDED #ifndef CATCH_TEST_REGISTRY_HPP_INCLUDED
#define CATCH_TEST_REGISTRY_HPP_INCLUDED #define CATCH_TEST_REGISTRY_HPP_INCLUDED
#include <catch2/internal/catch_config_static_analysis_support.hpp>
#include <catch2/internal/catch_source_line_info.hpp> #include <catch2/internal/catch_source_line_info.hpp>
#include <catch2/internal/catch_noncopyable.hpp> #include <catch2/internal/catch_noncopyable.hpp>
#include <catch2/interfaces/catch_interfaces_test_invoker.hpp> #include <catch2/interfaces/catch_interfaces_test_invoker.hpp>
@ -72,6 +73,9 @@ struct AutoReg : Detail::NonCopyable {
void TestName::test() void TestName::test()
#endif #endif
#if !defined(CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT)
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \ #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \
static void TestName(); \ static void TestName(); \
@ -84,19 +88,40 @@ struct AutoReg : Detail::NonCopyable {
#define INTERNAL_CATCH_TESTCASE( ... ) \ #define INTERNAL_CATCH_TESTCASE( ... ) \
INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__ ) INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__ )
/////////////////////////////////////////////////////////////////////////////// #else // ^^ !CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT | vv CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT
#define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
// Dummy registrator for the dumy test case macros
namespace Catch {
namespace Detail {
struct DummyUse {
DummyUse( void ( * )( int ) );
};
} // namespace Detail
} // namespace Catch
// Note that both the presence of the argument and its exact name are
// necessary for the section support.
// We provide a shadowed variable so that a `SECTION` inside non-`TEST_CASE`
// tests can compile. The redefined `TEST_CASE` shadows this with param.
static int catchInternalSectionHint = 0;
# define INTERNAL_CATCH_TESTCASE2( fname ) \
static void fname( int ); \
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
namespace { \ static const Catch::Detail::DummyUse INTERNAL_CATCH_UNIQUE_NAME( \
const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( \ dummyUser )( &fname ); \
Catch::makeTestInvoker( &QualifiedMethod ), \ CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \
CATCH_INTERNAL_LINEINFO, \ static void fname( [[maybe_unused]] int catchInternalSectionHint ) \
"&" #QualifiedMethod##_catch_sr, \
Catch::NameAndTags{ __VA_ARGS__ } ); \
} /* NOLINT */ \
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
# define INTERNAL_CATCH_TESTCASE( ... ) \
INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( dummyFunction ) )
#endif // CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\
@ -118,6 +143,22 @@ struct AutoReg : Detail::NonCopyable {
#define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \ #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \
INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), ClassName, __VA_ARGS__ ) INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), ClassName, __VA_ARGS__ )
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
namespace { \
const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( \
Catch::makeTestInvoker( &QualifiedMethod ), \
CATCH_INTERNAL_LINEINFO, \
"&" #QualifiedMethod##_catch_sr, \
Catch::NameAndTags{ __VA_ARGS__ } ); \
} /* NOLINT */ \
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \
do { \ do { \