Improve the array handling by making the public interfaces still use arrays.

This commit is contained in:
Mark VanderVoord
2026-06-16 16:18:22 -04:00
parent 3d4ddfdc4c
commit a0de150635
6 changed files with 59 additions and 18 deletions
+2 -4
View File
@@ -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?]
+15 -2
View File
@@ -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" \
+30 -3
View File
@@ -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 }
@@ -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
@@ -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
+6 -9
View File
@@ -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])