From dd922f17b02b7a6a59d3f008fe648b7251dc82ce Mon Sep 17 00:00:00 2001 From: Mark VanderVoord Date: Tue, 30 Jun 2026 09:50:33 -0400 Subject: [PATCH] Automatically strip out compile-time assertions so they're not confused with function prototypes (Fixes #128) --- lib/cmock_config.rb | 6 +++++- lib/cmock_header_parser.rb | 7 +++++++ test/unit/cmock_header_parser_test.rb | 16 ++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/lib/cmock_config.rb b/lib/cmock_config.rb index 7ed0154..520a68c 100644 --- a/lib/cmock_config.rb +++ b/lib/cmock_config.rb @@ -52,7 +52,11 @@ class CMockConfig # - The keywords can appear before or after the return type (this is a compiler warning but people do weird stuff), # so we check for word boundaries when searching for them # - We first remove "static inline" combinations and boil down to single inline or static statements - :inline_function_patterns => ['(static\s+inline|inline\s+static)\s*', '(\binline\b)\s*', '(?:static\s*)?(?:__inline__)?__attribute__\s*\([ (]*always_inline[ )]*\)', 'static __inline__'] # Last part (\s*) is just to remove whitespaces (only to prettify the output) + :inline_function_patterns => ['(static\s+inline|inline\s+static)\s*', '(\binline\b)\s*', '(?:static\s*)?(?:__inline__)?__attribute__\s*\([ (]*always_inline[ )]*\)', 'static __inline__'], # Last part (\s*) is just to remove whitespaces (only to prettify the output) + + # Compile-time assertion macro names to strip entirely to avoid being confused with function prototypes + # Common C11, BSD, and embedded RTOS variants are included by default. Add custom names as needed. + :ct_assert_patterns => ['ct_assert', '_?[Ss]tatic_[Aa]ssert', 'STATIC_ASSERT', 'BUILD_ASSERT', 'CTASSERT'] }.freeze def initialize(options = nil) diff --git a/lib/cmock_header_parser.rb b/lib/cmock_header_parser.rb index c4d5aa9..4115427 100644 --- a/lib/cmock_header_parser.rb +++ b/lib/cmock_header_parser.rb @@ -26,6 +26,7 @@ class CMockHeaderParser @treat_externs = cfg.treat_externs @treat_inlines = cfg.treat_inlines @inline_function_patterns = cfg.inline_function_patterns + @ct_assert_patterns = cfg.ct_assert_patterns @c_strippables += ['extern'] if @treat_externs == :include # we'll need to remove the attribute if we're allowing externs @c_strippables += ['inline'] if @treat_inlines == :include # we'll need to remove the attribute if we're allowing inlines end @@ -306,6 +307,12 @@ class CMockHeaderParser source.gsub!(/(\W)(?:register|auto|restrict)(\W)/, '\1\2') source.gsub!(/(\W)(?:static)(\W)/, '\1\2') unless cpp + # strip calls to known compile-time assertion macros by name -- never function prototypes regardless of argument form + source.gsub!(/\b(?:#{@ct_assert_patterns.join('|')})\s*\([^;]*\)/, '') unless @ct_assert_patterns.empty? + # strip any remaining WORD(...==...) etc. -- calls containing comparison operators cannot be C function prototypes + # must run before default-value removal, which would corrupt "!= 0" into "!" by removing "= 0" + source.gsub!(/\b\w+\s*\((?:[^()!=<>]*(?:\([^()]*\))*)*(?:==|!=|<=|>=)[^;]*\)/, '') + source.gsub!(/\s*=\s*['"a-zA-Z0-9_.]+\s*/, '') # remove default value statements from argument lists # strip macro decorator patterns that cannot be C function prototypes. diff --git a/test/unit/cmock_header_parser_test.rb b/test/unit/cmock_header_parser_test.rb index 2ef7922..a6f2051 100644 --- a/test/unit/cmock_header_parser_test.rb +++ b/test/unit/cmock_header_parser_test.rb @@ -27,6 +27,7 @@ describe CMockHeaderParser, "Verify CMockHeaderParser Module" do @config.expect :inline_function_patterns, ['(static\s+inline|inline\s+static)\s*', '(\bstatic\b|\binline\b)\s*'] @config.expect :array_size_type, ['int', 'size_t'] @config.expect :array_size_name, 'size|len' + @config.expect :ct_assert_patterns, ['ct_assert', '_?[Ss]tatic_[Aa]ssert', '_Static_assert', 'STATIC_ASSERT', 'BUILD_ASSERT', 'CTASSERT'] @parser = CMockHeaderParser.new(@config) @@ -371,6 +372,21 @@ describe CMockHeaderParser, "Verify CMockHeaderParser Module" do end + it "ignore compile-time assertions and not treat them as function prototypes" do + source = + "static_assert(BLAH_BOOLEAN_TRAIT);\n" + + "void real_func(int a);\n" + + "ct_assert(COMMIT_ID_H, ID_SIZE == (sizeof(ID)) - 1);\n" + + "CTASSERT(OTHER_HEADER_H, BUF_SIZE != 0);\n" + + "BUILD_ASSERT(MAX_LEN <= 256);\n" + + "STATIC_ASSERT(sizeof(MyStruct) >= 4);\n" + + expected = ["void real_func(int a)"] + + assert_equal(expected, @parser.import_source(source, @test_project).map! { |s| s.strip }) + end + + it "remove struct statements" do source = "struct _NamedStruct1 {\n" +