From 3caf511b8f5e266787b93530ae2c16542716c93f Mon Sep 17 00:00:00 2001 From: alufers Date: Wed, 26 Apr 2023 14:16:41 +0200 Subject: [PATCH 1/3] fix: Don't smush macros which have an escaped empty line at the end nanopb has a habit of generating macros which end with an escaped empty line (for example for messages which are empty). They look like this: #define some_msg_t_FIELDLIST(X, a) \ #define some_msg_t_CALLBACK NULL This caused CMock to strip all of the newlines after the backslash instead of only one, which the backslash was escaping. This in turn caused both the macros to be in one line, causing a compile error in the generated mock. --- lib/cmock_header_parser.rb | 2 +- test/unit/cmock_header_parser_test.rb | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/cmock_header_parser.rb b/lib/cmock_header_parser.rb index 491e467..1522434 100644 --- a/lib/cmock_header_parser.rb +++ b/lib/cmock_header_parser.rb @@ -137,7 +137,7 @@ class CMockHeaderParser # If the user uses a macro to declare an inline function, # smushing the macros makes it easier to recognize them as a macro and if required, # remove them later on in this function - source.gsub!(/\s*\\\s*/m, ' ') + source.gsub!(/\s*\\(\n|\s*)/m, ' ') # Just looking for static|inline in the gsub is a bit too aggressive (functions that are named like this, ...), so we try to be a bit smarter # Instead, look for an inline pattern (f.e. "static inline") and parse it. diff --git a/test/unit/cmock_header_parser_test.rb b/test/unit/cmock_header_parser_test.rb index 3437de3..43832ff 100644 --- a/test/unit/cmock_header_parser_test.rb +++ b/test/unit/cmock_header_parser_test.rb @@ -2867,5 +2867,19 @@ describe CMockHeaderParser, "Verify CMockHeaderParser Module" do assert_equal(expected, @parser.transform_inline_functions(source)) end + it "Transform macros with escapes of empty lines" do + source = + "#define some_msg_t_FIELDLIST(X, a) \\\n" + + "\n" + + "#define some_msg_t_CALLBACK NULL\n" + + expected = + "#define some_msg_t_FIELDLIST(X, a) \n" + + "#define some_msg_t_CALLBACK NULL\n" + + @parser.treat_inlines = :include + assert_equal(expected, @parser.transform_inline_functions(source)) + end + end From c05e08d99eba77de9e345bc8759b91f9d1c709ab Mon Sep 17 00:00:00 2001 From: "Pascal J. Bourguignon" Date: Fri, 28 Jul 2023 11:10:54 +0200 Subject: [PATCH 2/3] Added a user-friendly error message when processing bad :treat_as When the indentation after :treat_as mappings is wrong, we may get a symbol as ctype. The error signaled by gtype.gsub is not userfriendly, so instead, we test for symbols and issue a user friend error message in that case. --- lib/cmock_unityhelper_parser.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/cmock_unityhelper_parser.rb b/lib/cmock_unityhelper_parser.rb index 9f4beb7..57f2aeb 100644 --- a/lib/cmock_unityhelper_parser.rb +++ b/lib/cmock_unityhelper_parser.rb @@ -35,6 +35,9 @@ class CMockUnityHelperParser def map_c_types c_types = {} @config.treat_as.each_pair do |ctype, expecttype| + if ctype.is_a?(Symbol) + raise ":treat_as expects a list of identifier: identifier mappings, but got a symbol: #{ctype}. Check the indentation in your project.yml" + end c_type = ctype.gsub(/\s+/, '_') if expecttype =~ /\*/ c_types[c_type] = "UNITY_TEST_ASSERT_EQUAL_#{expecttype.delete('*')}_ARRAY" From 5328a5150877b98c2014997c538f9c17a954a629 Mon Sep 17 00:00:00 2001 From: Hannes Bachl Date: Wed, 23 Aug 2023 10:06:34 +0200 Subject: [PATCH 3/3] add check to prevent sizeof(void) generation --- lib/cmock_generator_plugin_return_thru_ptr.rb | 15 ++++++- ...k_generator_plugin_return_thru_ptr_test.rb | 42 +++++++++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/lib/cmock_generator_plugin_return_thru_ptr.rb b/lib/cmock_generator_plugin_return_thru_ptr.rb index 96b2003..ca895c5 100644 --- a/lib/cmock_generator_plugin_return_thru_ptr.rb +++ b/lib/cmock_generator_plugin_return_thru_ptr.rb @@ -2,9 +2,10 @@ class CMockGeneratorPluginReturnThruPtr attr_reader :priority attr_accessor :utils - def initialize(_config, utils) + def initialize(config, utils) @utils = utils @priority = 9 + @config = config end def instance_typedefs(function) @@ -19,6 +20,15 @@ class CMockGeneratorPluginReturnThruPtr lines end + def void_pointer?(type) + # returns true if the provided type is a void, or is supposed to be treated as void + if type.casecmp?('void') + true + else + @config.respond_to?(:treat_as_void) ? @config.treat_as_void.include?(type) : false + end + end + def mock_function_declarations(function) lines = '' function[:args].each do |arg| @@ -27,7 +37,8 @@ class CMockGeneratorPluginReturnThruPtr lines << "#define #{function[:name]}_ReturnThruPtr_#{arg[:name]}(#{arg[:name]})" # If the pointer type actually contains an asterisk, we can do sizeof the type (super safe), otherwise # we need to do a sizeof the dereferenced pointer (which could be a problem if give the wrong size - lines << if arg[:type][-1] == '*' + # however if its a void pointer we are given then we have to use the provided parameter name because sizeof(void) is UB. + lines << if (arg[:type][-1] == '*') && (void_pointer?(arg[:type][0..-2]) == false) " #{function[:name]}_CMockReturnMemThruPtr_#{arg[:name]}(__LINE__, #{arg[:name]}, sizeof(#{arg[:type][0..-2]}))\n" else " #{function[:name]}_CMockReturnMemThruPtr_#{arg[:name]}(__LINE__, #{arg[:name]}, sizeof(*#{arg[:name]}))\n" diff --git a/test/unit/cmock_generator_plugin_return_thru_ptr_test.rb b/test/unit/cmock_generator_plugin_return_thru_ptr_test.rb index 03fc5e4..90afd91 100644 --- a/test/unit/cmock_generator_plugin_return_thru_ptr_test.rb +++ b/test/unit/cmock_generator_plugin_return_thru_ptr_test.rb @@ -39,6 +39,18 @@ describe CMockGeneratorPluginReturnThruPtr, "Verify CMockGeneratorPluginReturnTh :return => test_return[:void], :contains_ptr? => true } + @void_ptr_func = {:name => "Spruce", + :args => [{ :type => "void*", + :name => "pork", + :ptr? => true, + }, + { :type => "MY_FANCY_VOID*", + :name => "salad", + :ptr? => true, + }], + :return => test_return[:void], + :contains_ptr? => true } + #no strict ordering @cmock_generator_plugin_return_thru_ptr = CMockGeneratorPluginReturnThruPtr.new(@config, @utils) end @@ -56,6 +68,13 @@ describe CMockGeneratorPluginReturnThruPtr, "Verify CMockGeneratorPluginReturnTh @utils.expect :ptr_or_str?, true, ['int*'] end + def void_ptr_func_expect + @utils.expect :ptr_or_str?, true, ['void*'] + @utils.expect :ptr_or_str?, true, ['MY_FANCY_VOID*'] + + @config.expect :treat_as_void, ['MY_FANCY_VOID'] + end + it "have set up internal priority correctly on init" do assert_equal(9, @cmock_generator_plugin_return_thru_ptr.priority) end @@ -100,6 +119,29 @@ describe CMockGeneratorPluginReturnThruPtr, "Verify CMockGeneratorPluginReturnTh assert_equal(expected, returned) end + it "add a mock function declaration with sizeof() for void pointer arguments" do + void_ptr_func_expect(); + + expected = + "#define Spruce_ReturnThruPtr_pork(pork)" + + " Spruce_CMockReturnMemThruPtr_pork(__LINE__, pork, sizeof(*pork))\n" + + "#define Spruce_ReturnArrayThruPtr_pork(pork, cmock_len)" + + " Spruce_CMockReturnMemThruPtr_pork(__LINE__, pork, cmock_len * sizeof(*pork))\n" + + "#define Spruce_ReturnMemThruPtr_pork(pork, cmock_size)" + + " Spruce_CMockReturnMemThruPtr_pork(__LINE__, pork, cmock_size)\n" + + "void Spruce_CMockReturnMemThruPtr_pork(UNITY_LINE_TYPE cmock_line, void* pork, size_t cmock_size);\n" + + "#define Spruce_ReturnThruPtr_salad(salad)" + + " Spruce_CMockReturnMemThruPtr_salad(__LINE__, salad, sizeof(*salad))\n" + + "#define Spruce_ReturnArrayThruPtr_salad(salad, cmock_len)" + + " Spruce_CMockReturnMemThruPtr_salad(__LINE__, salad, cmock_len * sizeof(*salad))\n" + + "#define Spruce_ReturnMemThruPtr_salad(salad, cmock_size)" + + " Spruce_CMockReturnMemThruPtr_salad(__LINE__, salad, cmock_size)\n" + + "void Spruce_CMockReturnMemThruPtr_salad(UNITY_LINE_TYPE cmock_line, MY_FANCY_VOID* salad, size_t cmock_size);\n" + + returned = @cmock_generator_plugin_return_thru_ptr.mock_function_declarations(@void_ptr_func) + assert_equal(expected, returned) + end + it "add mock interfaces only for non-const pointer arguments" do complex_func_expect();