Automatically strip out compile-time assertions so they're not confused with function prototypes (Fixes #128)

This commit is contained in:
Mark VanderVoord
2026-06-30 09:50:33 -04:00
parent b01cfdc531
commit dd922f17b0
3 changed files with 28 additions and 1 deletions
+5 -1
View File
@@ -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)
+7
View File
@@ -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.
+16
View File
@@ -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" +