From a0de150635da0e6d373a2fb279ab07d70e30e5c3 Mon Sep 17 00:00:00 2001 From: Mark VanderVoord Date: Tue, 16 Jun 2026 16:18:22 -0400 Subject: [PATCH] Improve the array handling by making the public interfaces still use arrays. --- lib/cmock_generator_plugin_array.rb | 6 ++-- lib/cmock_generator_utils.rb | 17 ++++++++-- lib/cmock_header_parser.rb | 33 +++++++++++++++++-- ...erator_no_error_stubs_plugin_array_test.rb | 3 ++ .../unit/cmock_generator_plugin_array_test.rb | 3 ++ test/unit/cmock_header_parser_test.rb | 15 ++++----- 6 files changed, 59 insertions(+), 18 deletions(-) diff --git a/lib/cmock_generator_plugin_array.rb b/lib/cmock_generator_plugin_array.rb index 8cd8e55..da05bf6 100644 --- a/lib/cmock_generator_plugin_array.rb +++ b/lib/cmock_generator_plugin_array.rb @@ -31,8 +31,7 @@ class CMockGeneratorPluginArray args_call_i = function[:args].map { |m| m[:ptr?] ? "#{m[:name]}, #{m[:name]}_Depth" : (m[:name]).to_s }.join(', ') args_call_o = function[:args].map { |m| m[:ptr?] ? "#{m[:name]}, (#{m[:name]}_Depth)" : (m[:name]).to_s }.join(', ') args_string = function[:args].map do |m| - type = @utils.arg_type_with_const(m) - m[:ptr?] ? "#{type} #{m[:name]}, int #{m[:name]}_Depth" : "#{type} #{m[:name]}" + m[:ptr?] ? "#{@utils.arg_declaration(m)}, int #{m[:name]}_Depth" : @utils.arg_declaration(m) end.join(', ') lines = '' if function[:return][:void?] @@ -53,8 +52,7 @@ class CMockGeneratorPluginArray lines = [] func_name = function[:name] args_string = function[:args].map do |m| - type = @utils.arg_type_with_const(m) - m[:ptr?] ? "#{type} #{m[:name]}, int #{m[:name]}_Depth" : "#{type} #{m[:name]}" + m[:ptr?] ? "#{@utils.arg_declaration(m)}, int #{m[:name]}_Depth" : @utils.arg_declaration(m) end.join(', ') call_string = function[:args].map { |m| m[:ptr?] ? "#{m[:name]}, #{m[:name]}_Depth" : m[:name] }.join(', ') lines << if function[:return][:void?] diff --git a/lib/cmock_generator_utils.rb b/lib/cmock_generator_utils.rb index 455c4c3..4d1193d 100644 --- a/lib/cmock_generator_utils.rb +++ b/lib/cmock_generator_utils.rb @@ -36,6 +36,20 @@ class CMockGeneratorUtils self.class.arg_type_with_const(arg) end + def self.arg_declaration(arg) + if arg[:array_dims] + base_type = arg_type_with_const(arg).sub(/\*$/, '').strip + dims_str = arg[:array_dims].map { |d| "[#{d}]" }.join + "#{base_type} #{arg[:name]}#{dims_str}" + else + "#{arg_type_with_const(arg)} #{arg[:name]}" + end + end + + def arg_declaration(arg) + self.class.arg_declaration(arg) + end + def code_verify_an_arg_expectation(function, arg) if @arrays case @ptr_handling @@ -85,8 +99,7 @@ class CMockGeneratorUtils if function[:args_string] != 'void' if @arrays args_string = function[:args].map do |m| - type = arg_type_with_const(m) - m[:ptr?] ? "#{type} #{m[:name]}, int #{m[:name]}_Depth" : "#{type} #{m[:name]}" + m[:ptr?] ? "#{arg_declaration(m)}, int #{m[:name]}_Depth" : arg_declaration(m) end.join(', ') "void CMockExpectParameters_#{function[:name]}(CMOCK_#{function[:name]}_CALL_INSTANCE* cmock_call_instance, #{args_string});\n" \ "void CMockExpectParameters_#{function[:name]}(CMOCK_#{function[:name]}_CALL_INSTANCE* cmock_call_instance, #{args_string})\n{\n" \ diff --git a/lib/cmock_header_parser.rb b/lib/cmock_header_parser.rb index f587263..4b3a253 100644 --- a/lib/cmock_header_parser.rb +++ b/lib/cmock_header_parser.rb @@ -408,7 +408,16 @@ class CMockHeaderParser arg_info end - def parse_args(arg_list) + def extract_array_dims(arg_list) + dims = {} + arg_list.scan(/(\w+)\s*((?:\s*\[[^\[\]]*\])+)/) do |name, all_dims| + dim_list = all_dims.scan(/\[([^\[\]]*)\]/).map { |d| d[0].strip } + dims[name] = dim_list + end + dims + end + + def parse_args(arg_list, array_dims_by_name = {}) args = [] arg_list.split(',').each do |arg| arg.strip! @@ -418,6 +427,8 @@ class CMockHeaderParser arg_info.delete(:modifier) # don't care about this arg_info.delete(:c_calling_convention) # don't care about this + arg_info[:array_dims] = array_dims_by_name[arg_info[:name]] if array_dims_by_name.key?(arg_info[:name]) + # in C, array arguments implicitly degrade to pointers # make the translation explicit here to simplify later logic if @treat_as_array[arg_info[:type]] && !(arg_info[:ptr?]) @@ -591,10 +602,26 @@ class CMockHeaderParser decl[:var_arg] = nil end + # Extract array dimensions before cleaning converts them to pointer notation + array_dims_by_name = extract_array_dims(args) + # parse out and clean up the remainder of the arguments args = clean_args(args, parse_project) - decl[:args_string] = args - decl[:args] = parse_args(args) + decl[:args] = parse_args(args, array_dims_by_name) + # Rebuild args_string using original array notation where applicable + if args == 'void' + decl[:args_string] = args + else + arg_parts = args.split(/,\s*/) + decl[:args].each_with_index do |arg, i| + next unless arg[:array_dims] + + base_type = arg[:type].sub(/\*$/, '').strip + dims_str = arg[:array_dims].map { |d| "[#{d}]" }.join + arg_parts[i] = "#{base_type} #{arg[:name]}#{dims_str}" + end + decl[:args_string] = arg_parts.join(', ') + end decl[:args_call] = decl[:args].map { |a| a[:name] }.join(', ') decl[:contains_ptr?] = decl[:args].inject(false) { |ptr, arg| arg[:ptr?] ? true : ptr } diff --git a/test/unit/cmock_generator_no_error_stubs_plugin_array_test.rb b/test/unit/cmock_generator_no_error_stubs_plugin_array_test.rb index 462e86d..0850113 100644 --- a/test/unit/cmock_generator_no_error_stubs_plugin_array_test.rb +++ b/test/unit/cmock_generator_no_error_stubs_plugin_array_test.rb @@ -16,6 +16,9 @@ class UtilsStub def arg_type_with_const(arg) CMockGeneratorUtils.arg_type_with_const(arg) end + def arg_declaration(arg) + CMockGeneratorUtils.arg_declaration(arg) + end def code_add_base_expectation(func) "mock_retval_0" end diff --git a/test/unit/cmock_generator_plugin_array_test.rb b/test/unit/cmock_generator_plugin_array_test.rb index ceedfcf..ebea6e4 100644 --- a/test/unit/cmock_generator_plugin_array_test.rb +++ b/test/unit/cmock_generator_plugin_array_test.rb @@ -16,6 +16,9 @@ class UtilsStub def arg_type_with_const(arg) CMockGeneratorUtils.arg_type_with_const(arg) end + def arg_declaration(arg) + CMockGeneratorUtils.arg_declaration(arg) + end def code_add_base_expectation(func) "mock_retval_0" end diff --git a/test/unit/cmock_header_parser_test.rb b/test/unit/cmock_header_parser_test.rb index 05e3c02..4cf1819 100644 --- a/test/unit/cmock_header_parser_test.rb +++ b/test/unit/cmock_header_parser_test.rb @@ -1406,13 +1406,11 @@ describe CMockHeaderParser, "Verify CMockHeaderParser Module" do source = 'void KeyOperated(CUSTOM_TYPE thing1[], int thing2 [ ], ' \ 'char thing3 [][2 ][ 3], int* thing4[4], u8 thing5[((u8)((5 + 5*2)/3))])' expected_args = [ - { type: 'CUSTOM_TYPE*', name: 'thing1', ptr?: true, const?: false, const_ptr?: false }, - { type: 'int*', name: 'thing2', ptr?: true, const?: false, const_ptr?: false }, - # this one will likely change in the future when we improve multidimensional array support - { type: 'char*', name: 'thing3', ptr?: false, const?: false, const_ptr?: false }, - # this one will likely change in the future when we improve multidimensional array support - { type: 'int**', name: 'thing4', ptr?: true, const?: false, const_ptr?: false }, - { type: 'u8*', name: 'thing5', ptr?: true, const?: false, const_ptr?: false } + { type: 'CUSTOM_TYPE*', name: 'thing1', ptr?: true, const?: false, const_ptr?: false, array_dims: [''] }, + { type: 'int*', name: 'thing2', ptr?: true, const?: false, const_ptr?: false, array_dims: [''] }, + { type: 'char*', name: 'thing3', ptr?: false, const?: false, const_ptr?: false, array_dims: ['', '2', '3'] }, + { type: 'int**', name: 'thing4', ptr?: true, const?: false, const_ptr?: false, array_dims: ['4'] }, + { type: 'u8*', name: 'thing5', ptr?: true, const?: false, const_ptr?: false, array_dims: ['((u8)((5 + 5*2)/3))'] } ] expected = [{:var_arg=>nil, :return=>{ :type => "void", @@ -1430,8 +1428,7 @@ describe CMockHeaderParser, "Verify CMockHeaderParser Module" do :modifier=>"", :contains_ptr? => true, :args => expected_args, - :args_string => 'CUSTOM_TYPE* thing1, int* thing2, ' \ - 'char* thing3, int** thing4, u8* thing5', + :args_string => 'CUSTOM_TYPE thing1[], int thing2[], char thing3[][2][3], int* thing4[4], u8 thing5[((u8)((5 + 5*2)/3))]', :args_call => 'thing1, thing2, thing3, thing4, thing5' }] result = @parser.parse("module", source) assert_equal(expected, result[:functions])