From 9322e7caabcd09d32683828db48b1db4acb77334 Mon Sep 17 00:00:00 2001 From: Hannes Bachl Date: Wed, 23 Aug 2023 10:06:34 +0200 Subject: [PATCH 01/21] 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 96b20035..ca895c5e 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 @@ def instance_typedefs(function) 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 @@ def mock_function_declarations(function) 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 03fc5e4e..90afd913 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 @@ :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 @@ def complex_func_expect @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 @@ def complex_func_expect 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(); From 9ecc167742bef0915f896922fe28921ffd409125 Mon Sep 17 00:00:00 2001 From: "Pascal J. Bourguignon" Date: Thu, 10 Aug 2023 12:44:31 +0200 Subject: [PATCH 02/21] Removed including standard libraries from mock sources. - Introduced CMock_memset and CMock_memcpy - Updated tests. --- lib/cmock_file_writer.rb | 2 ++ lib/cmock_generator.rb | 9 +++------ lib/cmock_generator_plugin_return_thru_ptr.rb | 2 +- lib/cmock_generator_utils.rb | 4 ++-- src/cmock.c | 17 +++++++++++++++++ src/cmock.h | 2 ++ test/unit/cmock_generator_main_test.rb | 7 ++----- ...ock_generator_plugin_return_thru_ptr_test.rb | 2 +- test/unit/cmock_generator_utils_test.rb | 14 +++++++------- 9 files changed, 37 insertions(+), 22 deletions(-) diff --git a/lib/cmock_file_writer.rb b/lib/cmock_file_writer.rb index f30c44b2..d7849d08 100644 --- a/lib/cmock_file_writer.rb +++ b/lib/cmock_file_writer.rb @@ -22,6 +22,8 @@ def create_file(filename, subdir) full_file_name_temp = "#{@config.mock_path}/#{subdir + '/' if subdir}#{filename}.new" full_file_name_done = "#{@config.mock_path}/#{subdir + '/' if subdir}#{filename}" + $stderr.puts "Creating #{full_file_name_done.inspect}" unless (@config.verbosity < 2) + File.open(full_file_name_temp, 'w') do |file| yield(file, filename) end diff --git a/lib/cmock_generator.rb b/lib/cmock_generator.rb index c3896dfb..815e1b81 100644 --- a/lib/cmock_generator.rb +++ b/lib/cmock_generator.rb @@ -100,6 +100,8 @@ def create_using_statement(file, function) def create_mock_header_file(mock_project) if @include_inline == :include @file_writer.create_file(mock_project[:module_name] + (mock_project[:module_ext]), mock_project[:folder]) do |file, _filename| + file << "/* Source File: #{mock_project[:source]} */\n" + file << "/* Normalized source (without inlines). */\n" file << mock_project[:parsed_stuff][:normalized_source] end end @@ -209,11 +211,6 @@ def create_mock_header_footer(header) def create_source_header_section(file, filename, mock_project) header_file = (mock_project[:folder] || '') + filename.gsub('.c', mock_project[:module_ext]) file << "/* AUTOGENERATED FILE. DO NOT EDIT. */\n" unless mock_project[:parsed_stuff][:functions].empty? - file << "#include \n" - file << "#include \n" - unless @exclude_setjmp_h - file << "#include \n" - end file << "#include \"cmock.h\"\n" @includes_c_pre_header.each { |inc| file << "#include #{inc}\n" } file << "#include \"#{header_file}\"\n" @@ -283,7 +280,7 @@ def create_mock_init_function(file, mock_project) def create_mock_destroy_function(file, mock_project) file << "void #{mock_project[:clean_name]}_Destroy(void)\n{\n" file << " CMock_Guts_MemFreeAll();\n" - file << " memset(&Mock, 0, sizeof(Mock));\n" + file << " CMock_memset(&Mock, 0, sizeof(Mock));\n" file << mock_project[:parsed_stuff][:functions].collect { |function| @plugins.run(:mock_destroy, function) }.join unless @fail_on_unexpected_calls diff --git a/lib/cmock_generator_plugin_return_thru_ptr.rb b/lib/cmock_generator_plugin_return_thru_ptr.rb index ca895c5e..f6c72c20 100644 --- a/lib/cmock_generator_plugin_return_thru_ptr.rb +++ b/lib/cmock_generator_plugin_return_thru_ptr.rb @@ -81,7 +81,7 @@ def mock_implementation(function) lines << " if (cmock_call_instance->ReturnThruPtr_#{arg_name}_Used)\n" lines << " {\n" lines << " UNITY_TEST_ASSERT_NOT_NULL(#{arg_name}, cmock_line, CMockStringPtrIsNULL);\n" - lines << " memcpy((void*)#{arg_name}, (void*)cmock_call_instance->ReturnThruPtr_#{arg_name}_Val,\n" + lines << " CMock_memcpy((void*)#{arg_name}, (void*)cmock_call_instance->ReturnThruPtr_#{arg_name}_Val,\n" lines << " cmock_call_instance->ReturnThruPtr_#{arg_name}_Size);\n" lines << " }\n" end diff --git a/lib/cmock_generator_utils.rb b/lib/cmock_generator_utils.rb index ecbc37e5..5af37afe 100644 --- a/lib/cmock_generator_utils.rb +++ b/lib/cmock_generator_utils.rb @@ -51,7 +51,7 @@ def code_add_base_expectation(func_name, global_ordering_supported = true) lines = " CMOCK_MEM_INDEX_TYPE cmock_guts_index = CMock_Guts_MemNew(sizeof(CMOCK_#{func_name}_CALL_INSTANCE));\n" lines << " CMOCK_#{func_name}_CALL_INSTANCE* cmock_call_instance = (CMOCK_#{func_name}_CALL_INSTANCE*)CMock_Guts_GetAddressFor(cmock_guts_index);\n" lines << " UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringOutOfMemory);\n" - lines << " memset(cmock_call_instance, 0, sizeof(*cmock_call_instance));\n" + lines << " CMock_memset(cmock_call_instance, 0, sizeof(*cmock_call_instance));\n" lines << " Mock.#{func_name}_CallInstance = CMock_Guts_MemChain(Mock.#{func_name}_CallInstance, cmock_guts_index);\n" lines << " Mock.#{func_name}_IgnoreBool = (char)0;\n" if @ignore || @ignore_stateless lines << " cmock_call_instance->LineNumber = cmock_line;\n" @@ -75,7 +75,7 @@ def code_assign_argument_quickly(dest, arg) else assert_expr = "sizeof(#{arg[:name]}) == sizeof(#{arg[:type]}) ? 1 : -1" comment = "/* add #{arg[:type]} to :treat_as_array if this causes an error */" - " memcpy((void*)(&#{dest}), (void*)(&#{arg[:name]}),\n" \ + " CMock_memcpy((void*)(&#{dest}), (void*)(&#{arg[:name]}),\n" \ " sizeof(#{arg[:type]}[#{assert_expr}])); #{comment}\n" end end diff --git a/src/cmock.c b/src/cmock.c index e6a11971..94d73043 100644 --- a/src/cmock.c +++ b/src/cmock.c @@ -229,3 +229,20 @@ void CMock_Guts_MemFreeFinal(void) #endif } +void CMock_memset(void* ptr, int value, size_t num) +{ + size_t i; + for (i = 0; i < num; i++) + { + ((char*)ptr)[i] = (char)value; + } +} + +void CMock_memcpy(void* ptr, const void* src, size_t num) +{ + size_t i; + for (i = 0; i < num; i++) + { + ((char*)ptr)[i] = ((char*)src)[i]; + } +} diff --git a/src/cmock.h b/src/cmock.h index 45bab182..1f943fcf 100644 --- a/src/cmock.h +++ b/src/cmock.h @@ -43,5 +43,7 @@ CMOCK_MEM_INDEX_TYPE CMock_Guts_MemBytesFree(void) CMOCK_FUNCTION_ATTR(pure); CMOCK_MEM_INDEX_TYPE CMock_Guts_MemBytesUsed(void) CMOCK_FUNCTION_ATTR(pure); void CMock_Guts_MemFreeAll(void); void CMock_Guts_MemFreeFinal(void); +void CMock_memset(void* ptr, int value, size_t num); +void CMock_memcpy(void* ptr, const void* src, size_t num); #endif /* end of CMOCK_FRAMEWORK_H */ diff --git a/test/unit/cmock_generator_main_test.rb b/test/unit/cmock_generator_main_test.rb index 1a66c0a5..f7229ba8 100644 --- a/test/unit/cmock_generator_main_test.rb +++ b/test/unit/cmock_generator_main_test.rb @@ -343,9 +343,6 @@ def helper_create_header_top_with_opt_includes_form_config_and_plugin(ext) { :name => "tres", :args => [] } ] expected = [ "/* AUTOGENERATED FILE. DO NOT EDIT. */\n", - "#include \n", - "#include \n", - "#include \n", "#include \"cmock.h\"\n", "#include \"MockPoutPoutFish.h\"\n", "\n", @@ -486,7 +483,7 @@ def helper_create_header_top_with_opt_includes_form_config_and_plugin(ext) output = [] expected = [ "void MockPoutPoutFish_Destroy(void)\n{\n", " CMock_Guts_MemFreeAll();\n", - " memset(&Mock, 0, sizeof(Mock));\n", + " CMock_memset(&Mock, 0, sizeof(Mock));\n", "}\n\n" ] @@ -503,7 +500,7 @@ def helper_create_header_top_with_opt_includes_form_config_and_plugin(ext) output = [] expected = [ "void MockPoutPoutFish_Destroy(void)\n{\n", " CMock_Guts_MemFreeAll();\n", - " memset(&Mock, 0, sizeof(Mock));\n", + " CMock_memset(&Mock, 0, sizeof(Mock));\n", " uno", " GlobalExpectCount = 0;\n", " GlobalVerifyOrder = 0;\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 90afd913..e8fa436b 100644 --- a/test/unit/cmock_generator_plugin_return_thru_ptr_test.rb +++ b/test/unit/cmock_generator_plugin_return_thru_ptr_test.rb @@ -167,7 +167,7 @@ def void_ptr_func_expect " if (cmock_call_instance->ReturnThruPtr_tofu_Used)\n" + " {\n" + " UNITY_TEST_ASSERT_NOT_NULL(tofu, cmock_line, CMockStringPtrIsNULL);\n" + - " memcpy((void*)tofu, (void*)cmock_call_instance->ReturnThruPtr_tofu_Val,\n" + + " CMock_memcpy((void*)tofu, (void*)cmock_call_instance->ReturnThruPtr_tofu_Val,\n" + " cmock_call_instance->ReturnThruPtr_tofu_Size);\n" + " }\n" diff --git a/test/unit/cmock_generator_utils_test.rb b/test/unit/cmock_generator_utils_test.rb index 15356638..a1268218 100644 --- a/test/unit/cmock_generator_utils_test.rb +++ b/test/unit/cmock_generator_utils_test.rb @@ -61,7 +61,7 @@ " CMOCK_MEM_INDEX_TYPE cmock_guts_index = CMock_Guts_MemNew(sizeof(CMOCK_Apple_CALL_INSTANCE));\n" + " CMOCK_Apple_CALL_INSTANCE* cmock_call_instance = (CMOCK_Apple_CALL_INSTANCE*)CMock_Guts_GetAddressFor(cmock_guts_index);\n" + " UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringOutOfMemory);\n" + - " memset(cmock_call_instance, 0, sizeof(*cmock_call_instance));\n" + + " CMock_memset(cmock_call_instance, 0, sizeof(*cmock_call_instance));\n" + " Mock.Apple_CallInstance = CMock_Guts_MemChain(Mock.Apple_CallInstance, cmock_guts_index);\n" + " cmock_call_instance->LineNumber = cmock_line;\n" output = @cmock_generator_utils_simple.code_add_base_expectation("Apple") @@ -73,7 +73,7 @@ " CMOCK_MEM_INDEX_TYPE cmock_guts_index = CMock_Guts_MemNew(sizeof(CMOCK_Apple_CALL_INSTANCE));\n" + " CMOCK_Apple_CALL_INSTANCE* cmock_call_instance = (CMOCK_Apple_CALL_INSTANCE*)CMock_Guts_GetAddressFor(cmock_guts_index);\n" + " UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringOutOfMemory);\n" + - " memset(cmock_call_instance, 0, sizeof(*cmock_call_instance));\n" + + " CMock_memset(cmock_call_instance, 0, sizeof(*cmock_call_instance));\n" + " Mock.Apple_CallInstance = CMock_Guts_MemChain(Mock.Apple_CallInstance, cmock_guts_index);\n" + " Mock.Apple_IgnoreBool = (char)0;\n" + " cmock_call_instance->LineNumber = cmock_line;\n" + @@ -88,7 +88,7 @@ " CMOCK_MEM_INDEX_TYPE cmock_guts_index = CMock_Guts_MemNew(sizeof(CMOCK_Apple_CALL_INSTANCE));\n" + " CMOCK_Apple_CALL_INSTANCE* cmock_call_instance = (CMOCK_Apple_CALL_INSTANCE*)CMock_Guts_GetAddressFor(cmock_guts_index);\n" + " UNITY_TEST_ASSERT_NOT_NULL(cmock_call_instance, cmock_line, CMockStringOutOfMemory);\n" + - " memset(cmock_call_instance, 0, sizeof(*cmock_call_instance));\n" + + " CMock_memset(cmock_call_instance, 0, sizeof(*cmock_call_instance));\n" + " Mock.Apple_CallInstance = CMock_Guts_MemChain(Mock.Apple_CallInstance, cmock_guts_index);\n" + " Mock.Apple_IgnoreBool = (char)0;\n" + " cmock_call_instance->LineNumber = cmock_line;\n" + @@ -108,7 +108,7 @@ expected3 = " cmock_call_instance->Expected_Kiwi = Kiwi;\n" arg4 = { :name => "Lime", :const? => false, :type => 'LIME_T', :ptr? => false } - expected4 = " memcpy((void*)(&cmock_call_instance->Expected_Lime), (void*)(&Lime),\n" + + expected4 = " CMock_memcpy((void*)(&cmock_call_instance->Expected_Lime), (void*)(&Lime),\n" + " sizeof(LIME_T[sizeof(Lime) == sizeof(LIME_T) ? 1 : -1])); /* add LIME_T to :treat_as_array if this causes an error */\n" assert_equal(expected1, @cmock_generator_utils_simple.code_add_an_arg_expectation(arg1)) @@ -134,7 +134,7 @@ " cmock_call_instance->ReturnThruPtr_Kiwi_Used = 0;\n" arg4 = { :name => "Lime", :const? => false, :type => 'LIME_T', :ptr? => false } - expected4 = " memcpy((void*)(&cmock_call_instance->Expected_Lime), (void*)(&Lime),\n" + + expected4 = " CMock_memcpy((void*)(&cmock_call_instance->Expected_Lime), (void*)(&Lime),\n" + " sizeof(LIME_T[sizeof(Lime) == sizeof(LIME_T) ? 1 : -1])); /* add LIME_T to :treat_as_array if this causes an error */\n" + " cmock_call_instance->IgnoreArg_Lime = 0;\n" @@ -158,7 +158,7 @@ expected = "void CMockExpectParameters_Melon(CMOCK_Melon_CALL_INSTANCE* cmock_call_instance, stuff);\n" + "void CMockExpectParameters_Melon(CMOCK_Melon_CALL_INSTANCE* cmock_call_instance, stuff)\n{\n" + " cmock_call_instance->Expected_MyIntPtr = MyIntPtr;\n" + - " memcpy((void*)(&cmock_call_instance->Expected_MyMyType), (void*)(&MyMyType),\n" + + " CMock_memcpy((void*)(&cmock_call_instance->Expected_MyMyType), (void*)(&MyMyType),\n" + " sizeof(MY_TYPE[sizeof(MyMyType) == sizeof(MY_TYPE) ? 1 : -1])); /* add MY_TYPE to :treat_as_array if this causes an error */\n" + " cmock_call_instance->Expected_MyStr = MyStr;\n" + "}\n\n" @@ -176,7 +176,7 @@ " cmock_call_instance->Expected_MyIntPtr_Depth = MyIntPtr_Depth;\n" + " cmock_call_instance->IgnoreArg_MyIntPtr = 0;\n" + " cmock_call_instance->ReturnThruPtr_MyIntPtr_Used = 0;\n" + - " memcpy((void*)(&cmock_call_instance->Expected_MyMyType), (void*)(&MyMyType),\n" + + " CMock_memcpy((void*)(&cmock_call_instance->Expected_MyMyType), (void*)(&MyMyType),\n" + " sizeof(MY_TYPE[sizeof(MyMyType) == sizeof(MY_TYPE) ? 1 : -1])); /* add MY_TYPE to :treat_as_array if this causes an error */\n" + " cmock_call_instance->IgnoreArg_MyMyType = 0;\n" + " cmock_call_instance->Expected_MyStr = MyStr;\n" + From 92d3f9afc585cad815c128f9623004aa935f5d80 Mon Sep 17 00:00:00 2001 From: "Pascal J. Bourguignon" Date: Fri, 11 Aug 2023 10:59:05 +0200 Subject: [PATCH 03/21] Write message to stdout instead of stderr. --- lib/cmock_file_writer.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/cmock_file_writer.rb b/lib/cmock_file_writer.rb index d7849d08..98c0b812 100644 --- a/lib/cmock_file_writer.rb +++ b/lib/cmock_file_writer.rb @@ -22,8 +22,8 @@ def create_file(filename, subdir) full_file_name_temp = "#{@config.mock_path}/#{subdir + '/' if subdir}#{filename}.new" full_file_name_done = "#{@config.mock_path}/#{subdir + '/' if subdir}#{filename}" - $stderr.puts "Creating #{full_file_name_done.inspect}" unless (@config.verbosity < 2) - + puts "Creating #{full_file_name_done.inspect}" unless (@config.verbosity < 2) + File.open(full_file_name_temp, 'w') do |file| yield(file, filename) end From ef23c3c93f7488389cb90270e191172e93ecfee6 Mon Sep 17 00:00:00 2001 From: "Pascal J. Bourguignon" Date: Fri, 11 Aug 2023 11:28:18 +0200 Subject: [PATCH 04/21] Removed parentheses following rubocop advice. --- lib/cmock_file_writer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cmock_file_writer.rb b/lib/cmock_file_writer.rb index 98c0b812..ee198061 100644 --- a/lib/cmock_file_writer.rb +++ b/lib/cmock_file_writer.rb @@ -22,7 +22,7 @@ def create_file(filename, subdir) full_file_name_temp = "#{@config.mock_path}/#{subdir + '/' if subdir}#{filename}.new" full_file_name_done = "#{@config.mock_path}/#{subdir + '/' if subdir}#{filename}" - puts "Creating #{full_file_name_done.inspect}" unless (@config.verbosity < 2) + puts "Creating #{full_file_name_done.inspect}" unless @config.verbosity < 2 File.open(full_file_name_temp, 'w') do |file| yield(file, filename) From 07443f16e32792536b669b11187824aaaa27a00f Mon Sep 17 00:00:00 2001 From: "Pascal J. Bourguignon" Date: Thu, 4 May 2023 17:20:53 +0200 Subject: [PATCH 05/21] Ignore backup files. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 57fce96f..e93ff38b 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ Gemfile.lock *.swp examples/make_example/build examples/temp_sensor/build +*~ From 6a6816a3429dcf03f0c1279cb663cee64cc04568 Mon Sep 17 00:00:00 2001 From: "Pascal J. Bourguignon" Date: Thu, 4 May 2023 17:24:07 +0200 Subject: [PATCH 06/21] Added generation of noreturn exits. Factorized out the generation of returns with noreturn exits in generate_return and generate_template_return methods. --- lib/cmock_generator.rb | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/lib/cmock_generator.rb b/lib/cmock_generator.rb index 815e1b81..142b2b25 100644 --- a/lib/cmock_generator.rb +++ b/lib/cmock_generator.rb @@ -89,6 +89,34 @@ def create_skeleton(module_name, parsed_stuff) private if $ThisIsOnlyATest.nil? ############################## + def is_noreturn?(function) + if function[:attributes].nil? + return nil; + else + return function[:attributes].include?('noreturn'); + end + end + + def generate_return(function) + if is_noreturn?(function) + return " TEST_DO_NOT_RETURN();\n" + elsif function[:return][:void?] + return " return;\n" + else + return " return cmock_call_instance->ReturnVal;\n" + end + end + + def generate_template_return(function) + if is_noreturn?(function) + return " TEST_DO_NOT_RETURN();\n" + elsif function[:return][:void?] + return " return;\n" + else + return " return (#{(function[:return][:type])})0;\n" + end + end + def create_mock_subdir(mock_project) @file_writer.create_subdir(mock_project[:folder]) end @@ -339,7 +367,7 @@ def create_mock_implementation(file, function) end file << @plugins.run(:mock_implementation, function) file << " UNITY_CLR_DETAILS();\n" - file << " return cmock_call_instance->ReturnVal;\n" unless function[:return][:void?] + file << generate_return(function); file << "}\n" # Close any namespace(s) opened above @@ -371,7 +399,7 @@ def create_function_skeleton(file, function, existing) file << "{\n" file << " /*TODO: Implement Me!*/\n" function[:args].each { |arg| file << " (void)#{arg[:name]};\n" } - file << " return (#{(function[:return][:type])})0;\n" unless function[:return][:void?] + file << generate_template_return(function); file << "}\n\n" end end From 814a908cd0775c877522f0091ec989ac53faee4b Mon Sep 17 00:00:00 2001 From: "Pascal J. Bourguignon" Date: Thu, 4 May 2023 17:21:17 +0200 Subject: [PATCH 07/21] Added parsing of noreturn attributes. Added parsing of noreturn attributes in various syntaxes (C syntax, C++ syntax, GNU gcc syntax). To do so safely, added a C lexer, and a quick-and-dirty parenthesis parser (including brackets, braces and angle-brackets for C++). Using the scanned list of tokens, processing parenthesised source code is easier and safer than with regular expressions. --- lib/CLexer.rb | 153 ++ lib/cmock_config.rb | 6 + lib/cmock_header_parser.rb | 1027 ++++++++-- test/rakefile_helper.rb | 2 +- test/unit/cmock_header_parser_test.rb | 2618 ++++++++++++++----------- 5 files changed, 2538 insertions(+), 1268 deletions(-) create mode 100644 lib/CLexer.rb diff --git a/lib/CLexer.rb b/lib/CLexer.rb new file mode 100644 index 00000000..1cc00bb2 --- /dev/null +++ b/lib/CLexer.rb @@ -0,0 +1,153 @@ +# +# This is a simple lexer for the C programming language. +# MIT license. (c) 2023 Pascal Bourguignon +# + +class CLexer + + KEYWORDS = %w[auto break case char const continue default do double else enum + extern float for goto if int long register return short signed + sizeof static struct switch typedef union unsigned void volatile while].freeze + + STAR = :mul_op + ADDRESS = :logical_and_op + + OPERATOR_SYMBOLS = { + + '...' => :ellipsis, + '->*' => :ptr_mem_op, + '>>=' => :right_assign, + '<<=' => :left_assign, + + '==' => :eq, + '!=' => :ne, + '<=' => :le, + '>=' => :ge, + '>>' => :right_op, + '<<' => :left_op, + '+=' => :add_assign, + '-=' => :sub_assign, + '*=' => :mul_assign, + '/=' => :div_assign, + '%=' => :mod_assign, + '&=' => :and_assign, + '^=' => :xor_assign, + '|=' => :or_assign, + '->' => :ptr_op, + '&&' => :and_op, + '||' => :or_op, + '++' => :increment, + '--' => :decrement, + + '<:' => :open_bracket, + ':>' => :close_bracket, + '<%' => :open_brace, + '%>' => :close_brace, + + '!' => :logical_not_op, + '%' => :mod_op, + '&' => :logical_and_op, + '(' => :open_paren, + ')' => :close_paren, + '*' => :mul_op, + '+' => :add_op, + ',' => :comma, + '-' => :sub_op, + '.' => :dot, + '/' => :div_op, + ':' => :colon, + ';' => :semicolon, + '<' => :lt, + '=' => :assign, + '>' => :gt, + '?' => :question, + '[' => :open_bracket, + ']' => :close_bracket, + '^' => :logical_xor_op, + '{' => :open_brace, + '|' => :logical_or_op, + '}' => :close_brace, + '~' => :bitwise_not_op, + + }.freeze + + + OPERATOR_REGEX = Regexp.new('\A(' + OPERATOR_SYMBOLS.keys.map { |op| Regexp.escape(op) }.join('|') + ')') + OPERATOR_SYMS = OPERATOR_SYMBOLS.values.freeze + KEYWORDS_SYMS = KEYWORDS.map{ |n| n.to_sym }.freeze + + def initialize(input) + @input = input + @tokens = [] + end + + def tokenize + while @input.size > 0 + case @input + when /\A[[:space:]]+/m + @input = $' + when /\A\/\/[^\n]*/ + @input = $' + when /\A\/\*/ + consume_multiline_comment + when /\A[_a-zA-Z][_a-zA-Z0-9]*/ + identifier_or_keyword = $& ; + @input = $' + if KEYWORDS.include?(identifier_or_keyword) + @tokens << identifier_or_keyword.to_sym + else + @tokens << [:identifier, identifier_or_keyword] + end + when /\A\d+\.\d*([eE][+-]?\d+)?[fFlL]?|\.\d+([eE][+-]?\d+)?[fFlL]?|\d+[eE][+-]?\d+[fFlL]?/ + float_constant = $& ; + @input = $' + @tokens << [:float_literal, float_constant] + when /\A\d+/ + integer_constant = $& ; + @input = $' + @tokens << [:integer_literal, integer_constant] + when /\A0[xX][0-9a-fA-F]+/ + hex_constant = $& ; + @input = $' + @tokens << [:hex_literal, hex_constant] + when /\A'((\\.|[^\\'])*)'/ + char_literal = $& ; + @input = $' + @tokens << [:char_literal, char_literal] + when /\A"((\\.|[^\\"])*)"/ + string_literal = $& ; + @input = $' + @tokens << [:string_literal, string_literal] + when OPERATOR_REGEX + operator = $& ; + @input = $' + @tokens << OPERATOR_SYMBOLS[operator] + else + raise "Unexpected character: #{@input[0]}" + end + end + + @tokens + end + + private + + def consume_multiline_comment + while @input.size > 0 + case @input + when /\A\*\// + @input = $' + break + when /\A./m + @input = $' + end + end + end +end + +def example + input = File.read("/home/pbourguignon/src/c-tidbits/pipes/tee.out.c") + lexer = CLexer.new(input) + tokens = lexer.tokenize + puts tokens.inspect +end diff --git a/lib/cmock_config.rb b/lib/cmock_config.rb index c7db9451..c925420d 100644 --- a/lib/cmock_config.rb +++ b/lib/cmock_config.rb @@ -16,7 +16,13 @@ class CMockConfig :subdir => nil, :plugins => [], :strippables => ['(?:__attribute__\s*\([ (]*.*?[ )]*\)+)'], + :process_gcc_attributes => false, # __attribute__((...)) ; also remove it from strippables. + :process_cpp_attributes => false, # [[ ... ]] :attributes => %w[__ramfunc __irq __fiq register extern], + :c_noreturn_attributes => [], # simple keyword, before the function name + :gcc_noreturn_attributes => [], # put 'noreturn' for __attribute__((noreturn)), + # # before or after the function name, + # # also remove it from :strippables :c_calling_conventions => %w[__stdcall __cdecl __fastcall], :enforce_strict_ordering => false, :fail_on_unexpected_calls => true, diff --git a/lib/cmock_header_parser.rb b/lib/cmock_header_parser.rb index 15224349..0f560637 100644 --- a/lib/cmock_header_parser.rb +++ b/lib/cmock_header_parser.rb @@ -1,20 +1,30 @@ +# coding: utf-8 # ========================================== # CMock Project - Automatic Mock Generation for C # Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams # [Released under MIT License. Please refer to license.txt for details] # ========================================== +require "#{__dir__}/CLexer" + class CMockHeaderParser attr_accessor :funcs, :c_attr_noconst, :c_attributes, :treat_as_void, :treat_externs, :treat_inlines, :inline_function_patterns + attr_reader :c_noreturn_attributes, :c_calling_conventions def initialize(cfg) @c_strippables = cfg.strippables + @process_gcc_attributes = cfg.process_gcc_attributes + @process_cpp_attributes = cfg.process_cpp_attributes @c_attr_noconst = cfg.attributes.uniq - ['const'] - @c_attributes = ['const'] + c_attr_noconst + @c_attributes = ['const'] + @c_attr_noconst + @c_noreturn_attributes = cfg.c_noreturn_attributes.uniq @c_calling_conventions = cfg.c_calling_conventions.uniq @treat_as_array = cfg.treat_as_array @treat_as_void = (['void'] + cfg.treat_as_void).uniq - @function_declaration_parse_base_match = '([\w\s\*\(\),\[\]]*?\w[\w\s\*\(\),\[\]]*?)\(([\w\s\*\(\),\.\[\]+\-\/]*)\)' + attribute_regexp = '((?:\s*__attribute__\s*\(\s*\(.*?\)\s*\))*)' + type_and_name_regexp = '([\w\s\*\(\),\[\]]*?\w[\w\s\*\(\),\[\]]*?)' + args_regexp = '([\w\s\*\(\),\.\[\]+\-\/]*)' + @function_declaration_parse_base_match = type_and_name_regexp+'\('+ args_regexp + '\)' + attribute_regexp @declaration_parse_matcher = /#{@function_declaration_parse_base_match}$/m @standards = (%w[int short char long unsigned signed] + cfg.treat_as.keys).uniq @array_size_name = cfg.array_size_name @@ -27,6 +37,11 @@ def initialize(cfg) @inline_function_patterns = cfg.inline_function_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 + @c = 0 + end + + def raise_parse_error(message) + raise "Failed Parsing Declaration Prototype!" + "\n" + message end def parse(name, source) @@ -61,7 +76,7 @@ def parse(name, source) :normalized_source => parse_project[:normalized_source] } end - private if $ThisIsOnlyATest.nil? ################ + # REMVOVE BEFORE COMMIT # private if $ThisIsOnlyATest.nil? ################ # Remove C/C++ comments from a string # +source+:: String which will have the comments removed @@ -241,8 +256,10 @@ def import_source(source, parse_project, cpp = false) # remove assembler pragma sections source.gsub!(/^\s*#\s*pragma\s+asm\s+.*?#\s*pragma\s+endasm/m, '') - # remove gcc's __attribute__ tags - source.gsub!(/__attribute(?:__)?\s*\(\(+.*\)\)+/, '') + if @c_noreturn_attributes.nil? + # remove gcc's __attribute__ tags + source.gsub!(/__attribute(?:__)?\s*\(\(+.*\)\)+/, '') + end # remove preprocessor statements and extern "C" source.gsub!(/extern\s+\"C\"\s*\{/, '') @@ -306,6 +323,7 @@ def import_source(source, parse_project, cpp = false) end src_lines.delete_if(&:empty?) # drop empty lines + src_lines end # Rudimentary C++ parser - does not handle all situations - e.g.: @@ -371,182 +389,861 @@ def parse_functions(source) funcs end - def parse_type_and_name(arg) - # Split up words and remove known attributes. For pointer types, make sure - # to remove 'const' only when it applies to the pointer itself, not when it - # applies to the type pointed to. For non-pointer types, remove any - # occurrence of 'const'. - arg.gsub!(/(\w)\*/, '\1 *') # pull asterisks away from preceding word - arg.gsub!(/\*(\w)/, '* \1') # pull asterisks away from following word - arg_array = arg.split - arg_info = divine_ptr_and_const(arg) - arg_info[:name] = arg_array[-1] - - attributes = arg.include?('*') ? @c_attr_noconst : @c_attributes - attr_array = [] - type_array = [] - - arg_array[0..-2].each do |word| - if attributes.include?(word) - attr_array << word - elsif @c_calling_conventions.include?(word) - arg_info[:c_calling_convention] = word - else - type_array << word + # This grammar is quite ambiguous: + # + # fun_declaration : type name parameters { attributes | c_calling_convention } ; + # + # type : stuffs ; + # + # stuffs : | stuff stuffs ; + # + # stuff : token + # | :open_paren stuffs :close_paren + # | :open_bracket stuffs :close_bracket + # | :open_brace stuffs :close_brace + # | :lt stuffs :gt' -- angle brackets + # ; + # -- Note: we will also scan char_literal and string_literal + # -- because they could appear in constant expressions (eg. enums) + # -- and contain parentheses. + # -- Note: angle brackets for templates are very ambiguous, because + # -- we may also have '<' tokens in constant expressions (eg. in a enum). + # -- So we'd need a real parser to handle this correctly. + # + # token : identifier | literals_and_other_tokens ; + # + # name : identifier ; + # + # parameters : :open_paren stuffs :close_paren ; + # + # attributes : '__attributes__' :open_paren stuffs :close_paren ; + # -- we won't parse macro calls in attributes because of the ambiguity. + # + # + # Therefore we will parse in two phases: + # Phase 1: + # we parse fun_declaration_1 : { stuff } ; + # -- this takes care of parentheses, et al. + # Phase 2: + # then match from the end of the list of stuffs, + # for c_calling_conventions, __attributes__ (...) + # then '(' parameters ')' = '(' stuffs ')' + # then name identifier, + # then the rest is type. + + def eos?(src,pos) + src.length<=pos + end + + def validate_identifier(token,what) + if token[0] == :identifier + token + else + raise_parse_error "Expected #{what} identifier, got #{token[0]} #{token[1]}" + end + end + + def parse_token(src,pos) + if eos?(src,pos) + raise_parse_error "Expected a token, not end of source at position #{pos}" + end + [ src[pos], pos+1 ] + end + + def parse_stuff(src,pos) + # stuff : token + # | '(' stuffs ')' + # | '[' stuffs ']' + # | '{' stuffs '}' + # | '<' stuffs '>' + # ; + stuff = nil + if not eos?(src,pos) + case src[pos] + when :open_paren then stuff, pos = parse_delimited_stuffs(src, pos, :close_paren) + when :open_bracket then stuff, pos = parse_delimited_stuffs(src, pos, :close_bracket) + when :open_brace then stuff, pos = parse_delimited_stuffs(src, pos, :close_brace) + when :lt then stuff, pos = parse_delimited_stuffs(src, pos, :gt) + else stuff, pos = parse_token(src, pos) end end + [stuff, pos] + end - if arg_info[:const_ptr?] - attr_array << 'const' - type_array.delete_at(type_array.rindex('const')) + def parse_delimited_stuffs(src,pos,closing) + pos += 1 # eat the opening tokenn + stuffs = [] + while not eos?(src, pos) and src[pos] != closing + item, pos = parse_stuff(src, pos) + stuffs << item + end + if not eos?(src, pos) + pos += 1 # skip closing token + end + op = case closing + when :close_paren then :parens + when :close_bracket then :brackets + when :close_brace then :braces + when :gt then :angle_brackets + end + [ [op, stuffs], pos ] + end + + def parse_stuffs(src,pos) + # stuffs : | stuff stuffs ; + stuffs = [] + while not eos?(src, pos) + stuff, pos = parse_stuff(src, pos) + stuffs << stuff unless stuff.nil? end + [ stuffs, pos ] + end - arg_info[:modifier] = attr_array.join(' ') - arg_info[:type] = type_array.join(' ').gsub(/\s+\*/, '*') # remove space before asterisks - arg_info + def is_parens(stuff) + stuff.is_a?(Array) and (stuff.length == 2) and (stuff[0] == :parens) end - def parse_args(arg_list) - args = [] - arg_list.split(',').each do |arg| - arg.strip! - return args if arg =~ /^\s*((\.\.\.)|(void))\s*$/ # we're done if we reach void by itself or ... - - arg_info = parse_type_and_name(arg) - arg_info.delete(:modifier) # don't care about this - arg_info.delete(:c_calling_convention) # don't care about this - - # 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?]) - arg_info[:type] = "#{@treat_as_array[arg_info[:type]]}*" - arg_info[:type] = "const #{arg_info[:type]}" if arg_info[:const?] - arg_info[:ptr?] = true + def parens_list(stuff) + stuff[1] if is_parens(stuff) + end + + def is_brackets(stuff) + stuff.is_a?(Array) and (stuff.length == 2) and (stuff[0] == :brackets) + end + + def brackets_list(stuff) + stuff[1] if is_brackets(stuff) + end + + def is_token(token) + token.is_a?(Symbol) and (CLexer::OPERATOR_SYMS.index(token) or CLexer::KEYWORDS_SYMS.index(token)) + end + + def is_identifier(token,name=nil) + if token.is_a?(Array) and (token.length == 2) + if name.nil? + (token[0] == :identifier) + else + (token[0] == :identifier) and (token[1] == name) end + else + false + end + end - args << arg_info + def identifier_name(token) + token[1] if token.is_a?(Array) and (token[0] == :identifier) + end + + def token_name(token) + if is_identifier(token) + identifier_name(token) + elsif token.is_a?(Symbol) + token.to_s + elsif token.is_a?(String) + token + else + raise_parse_error "Invalid token #{token.inspect}" end + end - # Try to find array pair in parameters following this pattern : * , <@array_size_type> <@array_size_name> - args.each_with_index do |val, index| - next_index = index + 1 - next unless args.length > next_index + def is_c_calling_convention(stuff) + # whether stuff is a C calling convention (listed in @c_calling_conventions). + # note: stuff may be either a symbol, a string or an :identifier array. + res = if stuff.is_a?(Symbol) or is_token(stuff) or is_identifier(stuff) + not @c_calling_conventions.index(token_name(stuff)).nil? + else + false + end + res + end - if (val[:ptr?] == true) && args[next_index][:name].match(@array_size_name) && @array_size_type.include?(args[next_index][:type]) - val[:array_data?] = true - args[next_index][:array_size?] = true - end + def is_c_attribute(token) + # whether the token is a C attribute (listed in @c_attributes or in @c_noreturn_attributes). + # note: token may be either a symbol, a string or an :identifier array. + if token.is_a?(String) + name = token + elsif token.is_a?(Symbol) + name = token.to_s + elsif is_token(token) or is_identifier(token) + name = token_name(token) + elsif is_attribute(token) + name = attribute_name(token) + else + return false end - args + res = (@c_attributes.index(name) or + ((not @c_noreturn_attributes.nil?) and + (@c_noreturn_attributes.any? { |attr_regexp| name =~ /^#{attr_regexp}$/ }))) + res end - def divine_ptr(arg) - return false unless arg.include? '*' - # treat "const char *" and similar as a string, not a pointer - return false if /(^|\s)(const\s+)?char(\s+const)?\s*\*(?!.*\*)/ =~ arg + def make_attribute(namespace,name,arguments,kind) + if name.nil? + raise_parse_error "Attribute name should not be nil! #{namespace.inspect}, #{name.inspect}, #{arguments.inspect}" + end + [:attribute,namespace,name,arguments,kind] + end - true + def is_attribute(object) + (object.is_a?(Array)) and (object.length == 5) and (object[0] == :attribute) end - def divine_const(arg) - # a non-pointer arg containing "const" is a constant - # an arg containing "const" before the last * is a pointer to a constant - if arg.include?('*') ? (/(^|\s|\*)const(\s(\w|\s)*)?\*(?!.*\*)/ =~ arg) : (/(^|\s)const(\s|$)/ =~ arg) - true + def attribute_namespace(attribute) + raise_parse_error "Not an normalized attribute: #{attribute}" unless is_attribute(attribute) + attribute[1] + end + + def attribute_name(attribute) + raise_parse_error "Not an normalized attribute: #{attribute}" unless is_attribute(attribute) + attribute[2] + end + + def attribute_qualified_name(attribute) + if attribute_namespace(attribute) + attribute_namespace(attribute) + "::" + attribute_name(attribute) + else + attribute_name(attribute) + end + end + + def attribute_arguments(attribute) + raise_parse_error "Not an normalized attribute: #{attribute}" unless is_attribute(attribute) + attribute[3] + end + + def attribute_kind(attribute) + raise_parse_error "Not an normalized attribute: #{attribute}" unless is_attribute(attribute) + attribute[4] + end + + def is_noreturn(attribute) + if is_identifier(attribute) + @c_noreturn_attributes.include?(identifier_name(attribute)) + elsif is_attribute(attribute) + @c_noreturn_attributes.include?(attribute_qualified_name(attribute)) else false end end - def divine_ptr_and_const(arg) - divination = {} + def has_noreturn_attribute(attributes) + attributes.any? do |attribute| + is_noreturn(attribute) + end + end - divination[:ptr?] = divine_ptr(arg) - divination[:const?] = divine_const(arg) + def is_gcc_attribute_syntax(operator, parameters) + # gcc atributes are all of the syntax __attribute__ (...) + # where ... is a list of stuffs. + # so is_gcc_attribute_syntax([:identifier,"__attribute__"],[:parens,stuff_list]) + is_identifier(operator,'__attribute__') and is_parens(parameters) + # see parse_gcc_attribute + end - # an arg containing "const" after the last * is a constant pointer - divination[:const_ptr?] = /\*(?!.*\*)\s*const(\s|$)/ =~ arg ? true : false + def is_processable_gcc_attribute(name) + is_c_attribute(name) + end + + def parse_gcc_attribute(op,stuff) + # gcc atributes are all of the syntax __attribute__ (...) + # where ... is a list of stuffs. + # Here, attribute = [:attribute, [:parens,stuff_list]] + # We want to normalize attribute into a list of atributes: + # + # [:attribute,[:parens,[[:parens,[:identifier,"foo"],:comma,[:identifier,"bar"]]]]] + # --> [[:attribute,[:identifier,"foo"],[:parens,[[:identifier,"bar"]])]]] + # + # [:attribute,[:parens,[[:parens,[[:identifier,"foo"]]],:comma,[:parens,[[:identifier,"bar"]]]]]] + # --> [[:attribute,[:identifier "foo"]],nil],[[:attribute,[:identifier,"bar"]],nil]] + # + # [:attribute, [:parens,[[:parens, + # [[:identifier,"access"],[:parens,[[:identifier,"read_write"],:comma,[:integer_literal,"1"]]],:comma, + # [:identifier,"access"],[:parens,[[:identifier,"read_only"],:comma,[:integer_literal,"2"]]]]]]]]] + # + # --> [[:attribute,[:identifier,"access"],[:parens,[[:identifier,"read_write"],[:integer_literal,"1"]]]], + # [:attribute,[:identifier,"access"],[:parens,[[:identifier,"read_only"],[:integer_literal,"2"]]]]] + + if not (is_identifier(op,'__attribute__') and is_parens(stuff) and is_parens(parens_list(stuff)[0])) + raise_parse_error "Unexpected attribute syntax #{[op,stuff].inspect}" + end + normalized = [] + j=0 + chunks = parens_list(stuff) + while j ':' ] { ',' } ']]' ; + # attribute = [ '::' ] [ '(' ')' ] ; + attributes = [] + + if not (is_brackets(stuff) and (1 == brackets_list(stuff).length) and is_brackets(brackets_list(stuff)[0])) + raise_parse_error "Unexpected C++ attribute syntax #{stuff.inspect}" + + "\nis_brackets(stuff) = #{is_brackets(stuff)}" + + "\nbrackets_list(stuff).length = #{is_brackets(stuff) ? brackets_list(stuff).length : nil}" + + "\nis_brackets(brackets_list(stuff)[0]) = #{is_brackets(stuff) and brackets_list(stuff).length>1 ? is_brackets(brackets_list(stuff)[0]) : nil}" + end + + stuff = brackets_list(brackets_list(stuff)[0]) + + # Note: for better support for C++, we'd have to update CLexer for C++ tokens. + # so using would be :using, and :: would be :double-colon insead of :colon :colon + # etc (but C++ lexers must be context-sensitive). + + default_namespace = nil + start=0 + if 30 else - c = 0 - # magically turn brackets into asterisks, also match for parentheses that come from macros - arg_list.gsub!(/(\w+)(?:\s*\[[^\[\]]*\])+/, '*\1') - # remove space to place asterisks with type (where they belong) - arg_list.gsub!(/\s+\*/, '*') - # pull asterisks away from arg to place asterisks with type (where they belong) - arg_list.gsub!(/\*(\w)/, '* \1') - - # scan argument list for function pointers and replace them with custom types - arg_list.gsub!(/([\w\s\*]+)\(+([\w\s]*)\*[\*\s]*([\w\s]*)\s*\)+\s*\(((?:[\w\s\*]*,?)*)\s*\)*/) do |_m| - functype = "cmock_#{parse_project[:module_name]}_func_ptr#{parse_project[:typedefs].size + 1}" - funcret = Regexp.last_match(1).strip - funcdecl = Regexp.last_match(2).strip - funcname = Regexp.last_match(3).strip - funcargs = Regexp.last_match(4).strip - funconst = '' - if funcname.include? 'const' - funcname.gsub!('const', '').strip! - funconst = 'const ' + # char* are "strings", not "pointers". + guess[:ptr?] = starc>1 + end + + if first_const.nil? + # no const: + guess[:const?] = false + elsif starc == 0 + # const, no star + guess[:const?] = true + else + # const, some star: + before_last_star = type[0..last_star-1].rindex(:mul_op) + + if before_last_star.nil? + # a single star: + guess[:const?] = (first_const0) and (not last_const.nil?) and (last_star < last_const)) + + guess + end + + def parse_function_signature(src,pos) + + # Phase 1: + # we parse fun_declaration_1 : { stuff } ; + # -- this takes care of parentheses, et al. + items, pos = parse_stuffs(src, pos) + raise_parse_error "Unparsed characters from position #{pos}" unless pos == src.length + + # Phase 2: + # then match from the end of the list of stuffs, + # for c_calling_conventions, __attributes__ (...) + # then '(' parameters ')' = '(' stuffs ')' + # then name identifier, + # then the rest is type. + + ccc = [] + attributes = [] + parameters = nil + + # match from the end of the list of stuffs, + # for c_calling_conventions, __attributes__ (...) + i = items.length-1 + while is_c_calling_convention(items[i]) or ((3<=i) and is_gcc_attribute_syntax(items[i-1],items[i])) + if is_c_calling_convention(items[i]) + ccc << [:c_calling_convention, token_name(items[i])] + i -= 1 + else + attributes += parse_gcc_attribute(items[i-1],items[i]) + i -= 2 + end + end + + # then '(' parameters ')' = '(' stuffs ')' + if is_parens(items[i]) + parameters = parens_list(items[i]) + i -= 1 + end + + # then name identifier, + if not is_identifier(items[i]) + raise_parse_error "Expected an identifier but got #{items[i].inspect} as function name in #{items.inspect}" + end + name = identifier_name(items[i]) + i -= 1 + + # then the rest is type. + type = items[0..i] + + [type, name, parameters, attributes, ccc] + end + + def parse_type(stuff) + # Split up words and remove known attributes. For pointer types, make sure + # to remove 'const' only when it applies to the pointer itself, not when it + # applies to the type pointed to. For non-pointer types, remove any + # occurrence of 'const'. + + arg_info = guess_ptr_and_const(stuff) + + @attributes = (stuff.any?{|item| item == :mul_op}) ? @c_attr_noconst : @c_attributes + + type = [] + attributes = [] + ccc = [] + i = 0 + while i" + when :attribute then + case attribute_kind(stuff) + when :gcc then "__attribute__((#{unparse_inner(attribute_qualified_name(stuff))}))" + when :cpp then "[[#{unparse_inner(attribute_qualified_name(stuff))}]]" + when :c then "#{unparse_inner(attribute_qualified_name(stuff))}" end - parse_project[:typedefs] << "typedef #{funcret}(*#{functype})(#{funcargs});" - funcname = "cmock_arg#{c += 1}" if funcname.empty? - "#{functype} #{funconst}#{funcname}" + else stuff.map{|item| unparse_inner(item)}.join(' ') end + elsif stuff.is_a?(String) + stuff + else + raise_parse_error "Unexpected stuff #{stuff.inspect} while unparsing #{@unparsing.inspect}" + end + end + + def unparse(stuff) + @unparsing = stuff + unparse_inner(stuff) + end + + + def replace_arrays_by_pointers_in_parameters(parameters) + # parameter is now a list of parameter declarations each being a lists of tokens. + # + # eg. for (int* b, int c[5], int (*(*farr)[4])(int x),int a=42) we'd have now (=42 has been removed earlier): + # + # [ [ :int, :mul_op, [:identifier, "b"] ], + # [ :int, [:identifier, "c"], [:brackets, [[:integer_literal, "5"]]] ], + # [ :int, [:parens, [:mul_op, [:parens, [:mul_op, [:identifier, "farr"]]], + # [:brackets, [[:integer_literal, "4"]]]]], + # [:parens, [:int, [:identifier, "x"]]]] ], + # [ :int, [:identifier, "a"] ] ] + + # we want to turn instances of: [..., stuff, [:brackets, ... ], ...] into: [..., :mul_op, stuff] + # Note: a single pointer for multidimensionnal arrays: + # foo_t foo[][][] --> foo_t* foo + # foo_t* foo[][][] --> foo_t** foo + if parameters == [[]] + parameters + else + parameters.map do |parameter| + if is_parens(parameter) + [:parens] + replace_arrays_by_pointers_in_parameters([parens_list(parameter)]) + elsif parameter.is_a?(Array) + i = parameter.rindex{|item| not is_brackets(item)} + if i.nil? then + # all items are brackets + raise_parse_error "All items are brackets parameter=#{parameter.inspect}" + elsif i == parameter.length-1 then + # no item is a brackets + parameter + else + # some brackets, remove them and insert * before the name + # Note: int foo[3][4][5] --> int* foo + parameter[0,i] + [:mul_op] + [parameter[i]] + end.map do |item| + # recurse into parens groups: + if is_parens(item) + [:parens] + replace_arrays_by_pointers_in_parameters([parens_list(item)]) + else + item + end + end + else + parameter + end + end + end + end + +# replace_arrays_by_pointers_in_parameters([ [ :int, :mul_op, [:identifier, "b"] ], +# [ :int, [:identifier, "c"], [:brackets, [[:integer_literal, "5"]]] ], +# [ :int, [:parens, [:mul_op, [:parens, [:mul_op, [:identifier, "farr"]]], +# [:brackets, [[:integer_literal, "4"]]]]], +# [:parens, [:int, [:identifier, "x"]]] ], +# [ :int, [:identifier, "a"] ] ]) +# ==> +# [[:int, :mul_op, [:identifier, "b"]], +# [:int, :mul_op, [:identifier, "c"]], +# [:int, +# [:parens, [:mul_op, :mul_op, [:parens, [:mul_op, [:identifier, "farr"]]]]], +# [:parens, [:int, [:identifier, "x"]]]], +# [:int, [:identifier, "a"]]] + + + def replace_function_pointers_by_custom_types(parameters, parse_project) + parameters.map do |parameter| + plen = parameter.length + if 20) ? spec[0..ptrindex-1] : [] + funcname = spec[ptrindex+1..-1] + constindex = funcname.index(:const) + if constindex + funcname.delete_at(constindex) + funcconst = [:const] + else + funcconst = [] + end + + else + raise_parse_error "Invalid syntax for function parameter #{parameter.inspect}" + end + + elsif is_identifier(parameter[-2]) # ...foo() + + funcdecl = [] + funcname = [parameter[-2]] + funcconst = [] - # automatically name unnamed arguments (those that only had a type) - arg_list.split(/\s*,\s*/).map do |arg| - parts = (arg.split - ['struct', 'union', 'enum', 'const', 'const*']) - if (parts.size < 2) || (parts[-1][-1].chr == '*') || @standards.include?(parts[-1]) - "#{arg} cmock_arg#{c += 1}" else - arg + raise_parse_error "Invalid syntax for function parameter #{parameter.inspect}" end - end.join(', ') + + functype = [:identifier,"cmock_#{parse_project[:module_name]}_func_ptr#{parse_project[:typedefs].size + 1}"] + funcret = parameter[0..-3] + funcargs = parameter[-1] + + # add typedef for function pointer + parse_project[:typedefs] << "typedef #{unparse(funcret)}(#{unparse(funcdecl+[:mul_op]+[functype])})#{unparse(funcargs)};".gsub(/\(\*\s+/,'(*').gsub(/\s+\*/,'*').gsub(/\s+,/,',') + funcname = [[:identifier, "cmock_arg#{@c += 1}"]] if funcname.empty? + [functype] + funcconst + funcname + else + parameter + end end end + def is_anonymous_parameter(parameter) + parameter = parameter.reject { |token| [:struct, :union, :enum, :const, :mul_op].include?(token) } + if (parameter.length == 0) + true + elsif (parameter.length == 1) + not (parameter[0] == :ellipsis) + else + not is_identifier(parameter[-1]) + end + end + + def add_names_to_anonymous_parameters(parameters) + parameters.map do |parameter| + if parameter.nil? + nil + elsif is_anonymous_parameter(parameter) + parameter << [:identifier, "cmock_arg#{@c += 1}"] + else + parameter + end + end + end + + def parameter_unwrap_superfluous_parentheses(parameter) + pc = parameter.count { |item| is_parens(item) } + if (pc == 1) and is_parens(parameter[-1]) and + (parens_list(parameter[-1]).length == 1) and + is_parens(parens_list(parameter[-1])[0]) + # ... ((...)) --> unwrap ... (...) + parameter_unwrap_superfluous_parentheses(parameter[0..-2] + parens_list(parameter[-1])) + elsif (pc == 1) and is_parens(parameter[-1]) and + (parens_list(parameter[-1]).length == 2) and + is_parens(parens_list(parameter[-1])[0]) and + is_parens(parens_list(parameter[-1])[1]) + # ... ((...)(...)) --> unwrap ... (...)(...) + parameter_unwrap_superfluous_parentheses(parameter[0..-2] + + [parens_list(parameter[-1])[0]] + + [parens_list(parameter[-1])[1]]) + elsif (pc == 2) and is_parens(parameter[-2]) and is_parens(parameter[-1]) and + (parens_list(parameter[-2]).length == 1) and + is_parens(parens_list(parameter[-2])[0]) + # ... ((...)) (...) --> unwrap ... (...) (...) + parameter_unwrap_superfluous_parentheses(parameter[0..-3] + parens_list(parameter[-2]) + parameter[-1]) + else + parameter + end + end + + def clean_args(parameters, parse_project) + # parameter is now a list of parameter declarations each being a lists of tokens. + # eg. for (int* b, int c[5], int a=42) we'd have now (=42 has been removed earlier): + # [ [ :int, :mul_op, [:identifier, "b"] ], + # [ :int, [:identifier, "c"], [:brackets, [:integer_literal, "5"]] ], + # [ :int, [:identifier, "a"] ] ] + + if parameters.empty? or ((parameters.length == 1) and @local_as_void.include?(unparse(parameters[0]))) + [:void] + else + @c = 0 + + # unwrap superfluous parentheses, eg.: + # + # [:int, [:parens, [[:parens, [[:identity, "foo"],[:parens,[[:int,:comma,:int]]]]]]]] + # --> [:int, [:identity, "foo"], [:parens,[[:int,:comma,:int]]]] + # + # [:int, [:parens, [[:parens, [[:parens, [[:mul_op,[:identity, "foo"]]]], [:parens,[[:int,:comma,:int]]]]]]]] + # --> [:int, [:parens, [[:mul_op,[:identity, "foo"]]]], [:parens,[[:int,:comma,:int]]]] + + parameters = parameters.map { |parameter| parameter_unwrap_superfluous_parentheses(parameter) } + + # magically turn brackets into asterisks, also match for parentheses that come from macros + parameters = replace_arrays_by_pointers_in_parameters(parameters) + + # scan argument list for function pointers and replace them with custom types + # scan argument list for function pointers with shorthand notation and replace them with custom types + # Note: if I'm not wrong, this new code using tokens handles both cases, with and without funcdecl. + # parameters=[[:unsigned, :int, [:parens, [:mul_op, [:identifier, "func_ptr"]]], [:parens, [:int, :comma, :char]]]] + parameters=replace_function_pointers_by_custom_types(parameters, parse_project) + + # automatically name unnamed arguments (those that only had a type) + parameters = add_names_to_anonymous_parameters(parameters) + + if parameters.any?(nil) + raise_parse_error "Invalid parameters #{parameters.inspect}" + end + parameters + end + end + + def is_string_type(type) + (type.length>=2) and (type[0]==:char) and (type[1]==:mul_op) + end + + def parse_args(parameters) + # parameters have been cleaned (clean_args) + # so they're each of the form :void, :ellipsis, or [type... name] + args = [] + parameters.each do |parameter| + return args if (parameter == :void) or (parameter == [:ellipsis]) + + if parameter.nil? or (parameter.length<2) + raise_parse_error "Invalid parameter #{parameter.inspect} in #{parameters.inspect}" + else + type=parameter[0..-2] + name=parameter[-1] + type, _, _, arg_info = parse_type(type) + arg_info[:name]=identifier_name(name) + arg_info.delete(:modifier) # don't care about this + arg_info.delete(:noreturn) # don't care about this + arg_info.delete(:c_calling_convention) # don't care about this + + # 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?]) + arg_info[:type] = "#{@treat_as_array[arg_info[:type]]}*" + arg_info[:ptr?] = true + arg_info[:type] = "const #{arg_info[:type]}" if arg_info[:const?] + elsif arg_info[:ptr?] or is_string_type(type) + if arg_info[:const?] + arg_info[:type] = "const #{arg_info[:type]}" + end + end + + args << arg_info + end + end + + # Try to find array pair in parameters following this pattern : * , <@array_size_type> <@array_size_name> + args.each_with_index do |val, index| + next_index = index + 1 + next unless args.length > next_index + + if (val[:ptr?] == true) && args[next_index][:name].match(@array_size_name) && @array_size_type.include?(args[next_index][:type]) + val[:array_data?] = true + args[next_index][:array_size?] = true + end + end + + args + end + def parse_declaration(parse_project, declaration, namespace = [], classname = nil) decl = {} decl[:namespace] = namespace decl[:class] = classname - regex_match = @declaration_parse_matcher.match(declaration) - raise "Failed parsing function declaration: '#{declaration}'" if regex_match.nil? + lexer = CLexer.new(declaration) + decl_tokens = lexer.tokenize - # grab argument list - args = regex_match[2].strip + # Split declaration into type, name, parameters, attributes, and calling convention - # process function attributes, return type, and name - parsed = parse_type_and_name(regex_match[1]) + type, name, parameters, p_attributes, p_ccc = parse_function_signature(decl_tokens, 0) + + # Process function attributes, return type, and name + # Some attributes may be written after the parameter list, so we need to + # check for them and move them to the front of the declaration. + type, attributes, ccc, parsed = parse_type(type) + attributes += p_attributes + ccc += p_ccc # Record original name without scope prefix - decl[:unscoped_name] = parsed[:name] + decl[:unscoped_name] = name # Prefix name with namespace scope (if any) and then class decl[:name] = namespace.join('_') @@ -558,12 +1255,22 @@ def parse_declaration(parse_project, declaration, namespace = [], classname = ni decl[:name] << '_' unless decl[:name].empty? decl[:name] << decl[:unscoped_name] - decl[:modifier] = parsed[:modifier] - unless parsed[:c_calling_convention].nil? - decl[:c_calling_convention] = parsed[:c_calling_convention] + decl[:modifier] = parsed[:modifier] + if parsed[:ptr?] + if parsed[:const?] + type = [:const] + type + attributes.delete_if{|attr| attribute_name(attr) == "const"} + decl[:modifier] = unparse(attributes) + end + end + + if not (parsed[:c_calling_convention].nil? or parsed[:c_calling_convention].empty?) + decl[:c_calling_convention] = parsed[:c_calling_convention] end - rettype = parsed[:type] + decl[:noreturn] = has_noreturn_attribute(attributes) + + rettype = unparse(type).gsub(/\s+\*/,'*') rettype = 'void' if @local_as_void.include?(rettype.strip) decl[:return] = { :type => rettype, :name => 'cmock_to_return', @@ -573,34 +1280,51 @@ def parse_declaration(parse_project, declaration, namespace = [], classname = ni :const? => parsed[:const?] || false, :const_ptr? => parsed[:const_ptr?] || false } + parameters = parameters.slice_before { |element| element == :comma }.map { |subarray| subarray.reject { |e| e == :comma } }.to_a + # parameter is now a list of parameter declarations each being a lists of tokens. + # eg. for (int* b, int c[5], int a=42) we'd have now (=42 has been removed earlier): + # [ [ :int, :mul_op, [:identifier, "b"] ], + # [ :int, [:identifier, "c"], [:brackets, [:integer_literal, "5"]] ], + # [ :int, [:identifier, "a"], :assign, [:integer_literal, "42"] ] ] + # remove default argument statements from mock definitions - args.gsub!(/=\s*[a-zA-Z0-9_\.]+\s*/, ' ') + parameters = parameters.map { |parameter| + # type name [ = expression ] + passign = parameter.index(:assign) + if passign.nil? + parameter + else + parameter[0..passign-1] + end } # check for var args - if args =~ /\.\.\./ - decl[:var_arg] = args.match(/[\w\s]*\.\.\./).to_s.strip - args = if args =~ /\,[\w\s]*\.\.\./ - args.gsub!(/\,[\w\s]*\.\.\./, '') - else - 'void' - end + if parameters[-1] == [:ellipsis] + decl[:var_arg] = "..." + parameters.pop + if parameters.empty? + parameters = [:void] + end else decl[:var_arg] = nil end - args = clean_args(args, parse_project) - decl[:args_string] = args - decl[:args] = parse_args(args) + + if parameters.any?(nil) + raise_parse_error "Invalid parameters #{parameters.inspect}" + end + parameters = clean_args(parameters, parse_project) + decl[:args_string] = parameters.map{|parameter| unparse(parameter)}.join(', ').gsub(/\s+\*/, '*') + decl[:args] = parse_args(parameters) decl[:args_call] = decl[:args].map { |a| a[:name] }.join(', ') decl[:contains_ptr?] = decl[:args].inject(false) { |ptr, arg| arg[:ptr?] ? true : ptr } if decl[:return][:type].nil? || decl[:name].nil? || decl[:args].nil? || decl[:return][:type].empty? || decl[:name].empty? - raise "Failed Parsing Declaration Prototype!\n" \ - " declaration: '#{declaration}'\n" \ - " modifier: '#{decl[:modifier]}'\n" \ - " return: #{prototype_inspect_hash(decl[:return])}\n" \ - " function: '#{decl[:name]}'\n" \ - " args: #{prototype_inspect_array_of_hashes(decl[:args])}\n" + raise_parse_error " declaration: '#{declaration}'\n" \ + " modifier: '#{decl[:modifier]}'\n" \ + " noreturn: '#{decl[:noreturn]}'\n" \ + " return: #{prototype_inspect_hash(decl[:return])}\n" \ + " function: '#{decl[:name]}'\n" \ + " args: #{prototype_inspect_array_of_hashes(decl[:args])}\n" end decl @@ -624,4 +1348,5 @@ def prototype_inspect_array_of_hashes(array) return "[\n #{hashes.join("\n ")}\n ]\n" end end + end diff --git a/test/rakefile_helper.rb b/test/rakefile_helper.rb index 3d898905..dd0b6541 100644 --- a/test/rakefile_helper.rb +++ b/test/rakefile_helper.rb @@ -21,7 +21,7 @@ module RakefileHelpers def load_configuration(config_file) $cfg_file = config_file - $cfg = YAML.load(File.read('./targets/' + $cfg_file)) + $cfg = YAML.load(File.read('./targets/' + $cfg_file),aliases: true) $colour_output = false unless $cfg['colour'] end diff --git a/test/unit/cmock_header_parser_test.rb b/test/unit/cmock_header_parser_test.rb index 43832ff8..27f710f1 100644 --- a/test/unit/cmock_header_parser_test.rb +++ b/test/unit/cmock_header_parser_test.rb @@ -1,3 +1,4 @@ +# coding: utf-8 # ========================================== # CMock Project - Automatic Mock Generation for C # Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams @@ -8,14 +9,18 @@ require File.expand_path(File.dirname(__FILE__)) + "/../test_helper" require File.expand_path(File.dirname(__FILE__)) + '/../../lib/cmock_header_parser' +require File.expand_path(File.dirname(__FILE__)) + '/../../lib/CLexer' describe CMockHeaderParser, "Verify CMockHeaderParser Module" do before do create_mocks :config @config.expect :strippables, ["STRIPPABLE"] - @config.expect :attributes, ['__ramfunc', 'funky_attrib', 'SQLITE_API'] + @config.expect :attributes, ['__ramfunc', 'funky_attrib', 'SQLITE_API','access','rt::access','deprecated'] @config.expect :c_calling_conventions, ['__stdcall'] + @config.expect :c_noreturn_attributes,['_Noreturn', 'noreturn', '__noreturn__'] + @config.expect :process_gcc_attributes, true + @config.expect :process_cpp_attributes, true @config.expect :treat_as_void, ['MY_FUNKY_VOID'] @config.expect :treat_as, { "BANJOS" => "INT", "TUBAS" => "HEX16"} @config.expect :treat_as_array, {"IntArray" => "int", "Book" => "Page"} @@ -42,7 +47,7 @@ it "create and initialize variables to defaults appropriately" do assert_nil(@parser.funcs) - assert_equal(['const', '__ramfunc', 'funky_attrib', 'SQLITE_API'], @parser.c_attributes) + assert_equal(['const', '__ramfunc', 'funky_attrib', 'SQLITE_API','access','rt::access','deprecated'], @parser.c_attributes) assert_equal(['void','MY_FUNKY_VOID'], @parser.treat_as_void) end @@ -53,10 +58,10 @@ "who // is you\n" expected = - [ - "abcd", - "who" - ] + [ + "abcd", + "who" + ] assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip}) end @@ -83,13 +88,13 @@ "//*/\n" expected = - [ - "no_comments", - "pre_block", - "shown_because_block_comment_invalid_from_line_comment", - "shown_because_block_comment_invalid_from_shorter_line_comment", - "shown_because_line_above_ended_comment_this_time" - ] + [ + "no_comments", + "pre_block", + "shown_because_block_comment_invalid_from_line_comment", + "shown_because_block_comment_invalid_from_shorter_line_comment", + "shown_because_line_above_ended_comment_this_time" + ] assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip}) end @@ -105,16 +110,16 @@ " void STRIPPABLE universal_handler ();\n" expected = - [ - "void* my_calloc(size_t, size_t)", - "void my_realloc(void*, size_t)", - "void universal_handler()" - ] + [ + "void* my_calloc(size_t, size_t)", + "void my_realloc(void*, size_t)", + "void universal_handler()" + ] assert_equal(expected, @parser.import_source(source, @test_project)) end - it "remove gcc's function __attribute__'s" do + it "keep gcc's function __attribute__'s" do source = "void* my_calloc(size_t, size_t) __attribute__((alloc_size(1,2)));\n" + "void\n" + @@ -125,11 +130,11 @@ " void __attribute__ ((interrupt)) universal_handler ();\n" expected = - [ - "void* my_calloc(size_t, size_t)", - "void my_realloc(void*, size_t)", - "void universal_handler()" - ] + [ # attributes will be removed later, depending on their name. + "void* my_calloc(size_t, size_t)__attribute__((alloc_size(1,2)))", + "void my_realloc(void*, size_t)__attribute__((alloc_size(2)))", + "void __attribute__((interrupt))universal_handler()" + ] assert_equal(expected, @parser.import_source(source, @test_project)) end @@ -167,9 +172,9 @@ "when \\ \n" expected = - [ - "hoo hah when" - ] + [ + "hoo hah when" + ] assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip}) end @@ -212,11 +217,11 @@ "};\n" expected = - [ - "int notatypedef", - "int typedef_isnt_me", - "this should remain!" - ] + [ + "int notatypedef", + "int typedef_isnt_me", + "this should remain!" + ] assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip}) end @@ -291,10 +296,10 @@ "void kinda_ugly_on_the_next_line(unsigned int);\n" expected = - [ - "uint32 extern_name_func(unsigned int)", - "uint32 funcinline(unsigned int)" - ] + [ + "uint32 extern_name_func(unsigned int)", + "uint32 funcinline(unsigned int)" + ] assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip}) end @@ -311,12 +316,12 @@ "}\n" expected = - [ - "uint32 func_with_decl_a(unsigned int)", - "uint32 func_with_decl_a", #okay. it's not going to be interpretted as another function - "uint32 func_with_decl_b(unsigned int)", - "uint32 func_with_decl_b", #okay. it's not going to be interpretted as another function - ] + [ + "uint32 func_with_decl_a(unsigned int)", + "uint32 func_with_decl_a", #okay. it's not going to be interpretted as another function + "uint32 func_with_decl_b(unsigned int)", + "uint32 func_with_decl_b", #okay. it's not going to be interpretted as another function + ] assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip}) end @@ -351,14 +356,14 @@ "}\n" expected = - [ - "uint32 func_with_decl_a(unsigned int)", - "uint32 func_with_decl_a", #okay. it's not going to be interpretted as another function - "uint32 func_with_decl_b(unsigned int)", - "uint32 func_with_decl_b", #okay. it's not going to be interpretted as another function - "uint32 func_with_decl_c(unsigned int)", - "uint32 func_with_decl_c", #okay. it's not going to be interpretted as another function - ] + [ + "uint32 func_with_decl_a(unsigned int)", + "uint32 func_with_decl_a", #okay. it's not going to be interpretted as another function + "uint32 func_with_decl_b(unsigned int)", + "uint32 func_with_decl_b", #okay. it's not going to be interpretted as another function + "uint32 func_with_decl_c(unsigned int)", + "uint32 func_with_decl_c", #okay. it's not going to be interpretted as another function + ] assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip}) end @@ -450,12 +455,12 @@ "void kinda_ugly_on_the_next_line(unsigned int);\n" expected = - [ "extern uint32 foobar(unsigned int)", - "uint32 extern_name_func(unsigned int)", - "uint32 funcinline(unsigned int)", - "extern void bar(unsigned int)", - "extern void kinda_ugly_on_the_next_line(unsigned int)" - ] + [ "extern uint32 foobar(unsigned int)", + "uint32 extern_name_func(unsigned int)", + "uint32 funcinline(unsigned int)", + "extern void bar(unsigned int)", + "extern void kinda_ugly_on_the_next_line(unsigned int)" + ] @parser.treat_externs = :include assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip}) @@ -476,12 +481,12 @@ "}\n" expected = - [ "uint32 extern_name_func(unsigned int)", - "uint32 funcinline(unsigned int)", - "void inlineBar(unsigned int)", - "void staticinlineBar(unsigned int)", - "void bar(unsigned int)" - ] + [ "uint32 extern_name_func(unsigned int)", + "uint32 funcinline(unsigned int)", + "void inlineBar(unsigned int)", + "void staticinlineBar(unsigned int)", + "void bar(unsigned int)" + ] @parser.treat_inlines = :include assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip}) @@ -502,14 +507,14 @@ "}\n" expected = - [ "extern uint32 foobar(unsigned int)", - "uint32 extern_name_func(unsigned int)", - "uint32 funcinline(unsigned int)", - "void inlineBar(unsigned int)", - "extern int extern_bar(void)", - "void staticinlineBar(unsigned int)", - "void bar(unsigned int)" - ] + [ "extern uint32 foobar(unsigned int)", + "uint32 extern_name_func(unsigned int)", + "uint32 funcinline(unsigned int)", + "void inlineBar(unsigned int)", + "extern int extern_bar(void)", + "void staticinlineBar(unsigned int)", + "void bar(unsigned int)" + ] @parser.treat_externs = :include @parser.treat_inlines = :include @@ -535,11 +540,11 @@ expected = [ - "uint32 foo(unsigned int)", - "uint32 bar(unsigned int)", - "void inlineBar(void)", - "int alwaysinlinefunc(int a)", - "void inlinebar(unsigned int)" + "uint32 foo(unsigned int)", + "uint32 bar(unsigned int)", + "void inlineBar(void)", + "int alwaysinlinefunc(int a)", + "void inlinebar(unsigned int)" ] @parser.treat_inlines = :include @@ -556,9 +561,9 @@ "#define get_foo() \\\n ((Thing)foo.bar)" # exercise multiline define expected = - [ - "void hello(void)", - ] + [ + "void hello(void)", + ] assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip}) end @@ -569,9 +574,9 @@ "const int TheMatrix(register int Trinity, unsigned int *restrict Neo)" expected = - [ - "const int TheMatrix(int Trinity, unsigned int * Neo)", - ] + [ + "const int TheMatrix(int Trinity, unsigned int * Neo)", + ] assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip}) end @@ -581,70 +586,73 @@ # since cmock treats void specially, we can't let void be obfuscated it "handle odd case of typedef'd void returned" do source = "MY_FUNKY_VOID FunkyVoidReturned(int a)" - expected = { :var_arg=>nil, - :name=>"FunkyVoidReturned", - :unscoped_name=>"FunkyVoidReturned", - :namespace=>[], - :class=>nil, - :return=>{ :type => "void", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "void cmock_to_return", - :void? => true - }, - :modifier=>"", + expected = { :var_arg => nil, + :name => "FunkyVoidReturned", + :unscoped_name => "FunkyVoidReturned", + :namespace => [], + :class => nil, + :noreturn => false, + :return => { :type => "void", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "void cmock_to_return", + :void? => true + }, + :modifier => "", :contains_ptr? => false, - :args=>[{:type=>"int", :name=>"a", :ptr? => false, :const? => false, :const_ptr? => false}], - :args_string=>"int a", - :args_call=>"a"} + :args => [{:type => "int", :name => "a", :ptr? => false, :const? => false, :const_ptr? => false}], + :args_string => "int a", + :args_call => "a"} assert_equal(expected, @parser.parse_declaration(@test_project, source)) end it "handle odd case of typedef'd void as arg" do source = "int FunkyVoidAsArg(MY_FUNKY_VOID)" - expected = { :var_arg=>nil, - :name=>"FunkyVoidAsArg", - :unscoped_name=>"FunkyVoidAsArg", - :namespace=>[], - :class=>nil, - :return=>{ :type => "int", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "int cmock_to_return", - :void? => false - }, - :modifier=>"", + expected = { :var_arg => nil, + :name => "FunkyVoidAsArg", + :unscoped_name => "FunkyVoidAsArg", + :namespace => [], + :class => nil, + :noreturn => false, + :return => { :type => "int", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :modifier => "", :contains_ptr? => false, - :args=>[], - :args_string=>"void", - :args_call=>"" } + :args => [], + :args_string => "void", + :args_call => "" } assert_equal(expected, @parser.parse_declaration(@test_project, source)) end it "handle odd case of typedef'd void as arg pointer" do source = "char FunkyVoidPointer(MY_FUNKY_VOID* bluh)" - expected = { :var_arg=>nil, - :name=>"FunkyVoidPointer", - :unscoped_name=>"FunkyVoidPointer", - :namespace=>[], - :class=>nil, - :return=>{ :type => "char", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "char cmock_to_return", - :void? => false - }, - :modifier=>"", + expected = { :var_arg => nil, + :name => "FunkyVoidPointer", + :unscoped_name => "FunkyVoidPointer", + :namespace => [], + :class => nil, + :noreturn => false, + :return => { :type => "char", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "char cmock_to_return", + :void? => false + }, + :modifier => "", :contains_ptr? => true, - :args=>[{:type=>"MY_FUNKY_VOID*", :name=>"bluh", :ptr? => true, :const? => false, :const_ptr? => false}], - :args_string=>"MY_FUNKY_VOID* bluh", - :args_call=>"bluh" } + :args => [{:type => "MY_FUNKY_VOID*", :name => "bluh", :ptr? => true, :const? => false, :const_ptr? => false}], + :args_string => "MY_FUNKY_VOID* bluh", + :args_call => "bluh" } assert_equal(expected, @parser.parse_declaration(@test_project, source)) end @@ -654,9 +662,9 @@ "void Foo(int a = 57, float b=37.52, char c= 'd', char* e=\"junk\");\n" expected = - [ - "void Foo(int a, float b, char c, char* e)" - ] + [ + "void Foo(int a, float b, char c, char* e)" + ] assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip}) end @@ -729,155 +737,161 @@ it "extract and return function declarations with retval and args" do source = "int Foo(int a, unsigned int b)" - expected = { :var_arg=>nil, - :name=>"Foo", - :unscoped_name=>"Foo", - :namespace=>[], - :class=>nil, - :return=>{ :type => "int", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "int cmock_to_return", - :void? => false - }, - :modifier=>"", + expected = { :var_arg => nil, + :name => "Foo", + :unscoped_name => "Foo", + :namespace => [], + :class => nil, + :noreturn => false, + :return => { :type => "int", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :modifier => "", :contains_ptr? => false, - :args=>[ {:type=>"int", :name=>"a", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"unsigned int", :name=>"b", :ptr? => false, :const? => false, :const_ptr? => false} - ], - :args_string=>"int a, unsigned int b", - :args_call=>"a, b" } + :args => [ {:type => "int", :name => "a", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "unsigned int", :name => "b", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "int a, unsigned int b", + :args_call => "a, b" } assert_equal(expected, @parser.parse_declaration(@test_project, source)) end it "extract and return function declarations with no retval" do source = "void FunkyChicken( uint la, int de, bool da)" - expected = { :var_arg=>nil, - :return=>{ :type => "void", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "void cmock_to_return", - :void? => true - }, - :name=>"FunkyChicken", - :unscoped_name=>"FunkyChicken", - :namespace=>[], - :class=>nil, - :modifier=>"", + expected = { :var_arg => nil, + :noreturn => false, + :return => { :type => "void", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "void cmock_to_return", + :void? => true + }, + :name => "FunkyChicken", + :unscoped_name => "FunkyChicken", + :namespace => [], + :class => nil, + :modifier => "", :contains_ptr? => false, - :args=>[ {:type=>"uint", :name=>"la", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"int", :name=>"de", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"bool", :name=>"da", :ptr? => false, :const? => false, :const_ptr? => false} - ], - :args_string=>"uint la, int de, bool da", - :args_call=>"la, de, da" } + :args => [ {:type => "uint", :name => "la", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "int", :name => "de", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "bool", :name => "da", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "uint la, int de, bool da", + :args_call => "la, de, da" } assert_equal(expected, @parser.parse_declaration(@test_project, source)) end it "extract and return function declarations with implied voids" do source = "void tat()" - expected = { :var_arg=>nil, - :return=>{ :type => "void", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "void cmock_to_return", - :void? => true - }, - :name=>"tat", - :unscoped_name=>"tat", - :namespace=>[], - :class=>nil, - :modifier=>"", + expected = { :var_arg => nil, + :noreturn => false, + :return => { :type => "void", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "void cmock_to_return", + :void? => true + }, + :name => "tat", + :unscoped_name => "tat", + :namespace => [], + :class => nil, + :modifier => "", :contains_ptr? => false, - :args=>[ ], - :args_string=>"void", - :args_call=>"" } + :args => [ ], + :args_string => "void", + :args_call => "" } assert_equal(expected, @parser.parse_declaration(@test_project, source)) end it "extract modifiers properly" do source = "const int TheMatrix(int Trinity, unsigned int * Neo)" - expected = { :var_arg=>nil, - :return=>{ :type => "int", - :name => 'cmock_to_return', - :ptr? => false, - :const? => true, - :const_ptr? => false, - :str => "int cmock_to_return", - :void? => false - }, - :name=>"TheMatrix", - :unscoped_name=>"TheMatrix", - :namespace=>[], - :class=>nil, - :modifier=>"const", + expected = { :var_arg => nil, + :noreturn => false, + :return => { :type => "int", + :name => 'cmock_to_return', + :ptr? => false, + :const? => true, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :name => "TheMatrix", + :unscoped_name => "TheMatrix", + :namespace => [], + :class => nil, + :modifier => "const", :contains_ptr? => true, - :args=>[ {:type=>"int", :name=>"Trinity", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"unsigned int*", :name=>"Neo", :ptr? => true, :const? => false, :const_ptr? => false} - ], - :args_string=>"int Trinity, unsigned int* Neo", - :args_call=>"Trinity, Neo" } + :args => [ {:type => "int", :name => "Trinity", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "unsigned int*", :name => "Neo", :ptr? => true, :const? => false, :const_ptr? => false} + ], + :args_string => "int Trinity, unsigned int* Neo", + :args_call => "Trinity, Neo" } assert_equal(expected, @parser.parse_declaration(@test_project, source)) end it "extract c calling conventions properly" do source = "const int __stdcall TheMatrix(int Trinity, unsigned int * Neo)" - expected = { :var_arg=>nil, - :return=>{ :type => "int", - :name => 'cmock_to_return', - :ptr? => false, - :const? => true, - :const_ptr? => false, - :str => "int cmock_to_return", - :void? => false - }, - :name=>"TheMatrix", - :unscoped_name=>"TheMatrix", - :namespace=>[], - :class=>nil, - :modifier=>"const", - :c_calling_convention=>"__stdcall", + expected = { :var_arg => nil, + :noreturn => false, + :return => { :type => "int", + :name => 'cmock_to_return', + :ptr? => false, + :const? => true, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :name => "TheMatrix", + :unscoped_name => "TheMatrix", + :namespace => [], + :class => nil, + :modifier => "const", + :c_calling_convention => "__stdcall", :contains_ptr? => true, - :args=>[ {:type=>"int", :name=>"Trinity", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"unsigned int*", :name=>"Neo", :ptr? => true, :const? => false, :const_ptr? => false} - ], - :args_string=>"int Trinity, unsigned int* Neo", - :args_call=>"Trinity, Neo" } + :args => [ {:type => "int", :name => "Trinity", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "unsigned int*", :name => "Neo", :ptr? => true, :const? => false, :const_ptr? => false} + ], + :args_string => "int Trinity, unsigned int* Neo", + :args_call => "Trinity, Neo" } assert_equal(expected, @parser.parse_declaration(@test_project, source)) end it "extract and return function declarations inside namespace and class" do source = "int Foo(int a, unsigned int b)" - expected = { :var_arg=>nil, - :name=>"ns1_ns2_Bar_Foo", - :unscoped_name=>"Foo", - :class=>"Bar", - :namespace=>["ns1", "ns2"], - :return=>{ :type => "int", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "int cmock_to_return", - :void? => false - }, - :modifier=>"", + expected = { :var_arg => nil, + :name => "ns1_ns2_Bar_Foo", + :unscoped_name => "Foo", + :class => "Bar", + :namespace => ["ns1", "ns2"], + :noreturn => false, + :return => { :type => "int", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :modifier => "", :contains_ptr? => false, - :args=>[ {:type=>"int", :name=>"a", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"unsigned int", :name=>"b", :ptr? => false, :const? => false, :const_ptr? => false} - ], - :args_string=>"int a, unsigned int b", - :args_call=>"a, b" } + :args => [ {:type => "int", :name => "a", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "unsigned int", :name => "b", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "int a, unsigned int b", + :args_call => "a, b" } assert_equal(expected, @parser.parse_declaration(@test_project, source, ["ns1", "ns2"], "Bar")) end @@ -886,46 +900,48 @@ source = "const int TheMatrix(int Trinity, unsigned int * Neo);\n" + "int Morpheus(int, unsigned int*);\n" - expected = [{ :var_arg=>nil, - :return=> { :type => "int", - :name => 'cmock_to_return', - :ptr? => false, - :const? => true, - :const_ptr? => false, - :str => "int cmock_to_return", - :void? => false - }, - :name=>"TheMatrix", - :unscoped_name=>"TheMatrix", - :namespace=>[], - :class=>nil, - :modifier=>"const", + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "int", + :name => 'cmock_to_return', + :ptr? => false, + :const? => true, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :name => "TheMatrix", + :unscoped_name => "TheMatrix", + :namespace => [], + :class => nil, + :modifier => "const", :contains_ptr? => true, - :args=>[ {:type=>"int", :name=>"Trinity", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"unsigned int*", :name=>"Neo", :ptr? => true, :const? => false, :const_ptr? => false} - ], - :args_string=>"int Trinity, unsigned int* Neo", - :args_call=>"Trinity, Neo" }, - { :var_arg=>nil, - :return=> { :type => "int", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "int cmock_to_return", - :void? => false - }, - :name=>"Morpheus", - :unscoped_name=>"Morpheus", - :namespace=>[], - :class=>nil, - :modifier=>"", + :args => [ {:type => "int", :name => "Trinity", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "unsigned int*", :name => "Neo", :ptr? => true, :const? => false, :const_ptr? => false} + ], + :args_string => "int Trinity, unsigned int* Neo", + :args_call => "Trinity, Neo" }, + { :var_arg => nil, + :noreturn => false, + :return => { :type => "int", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :name => "Morpheus", + :unscoped_name => "Morpheus", + :namespace => [], + :class => nil, + :modifier => "", :contains_ptr? => true, - :args=>[ {:type=>"int", :name=>"cmock_arg1", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"unsigned int*", :name=>"cmock_arg2", :ptr? => true, :const? => false, :const_ptr? => false} - ], - :args_string=>"int cmock_arg1, unsigned int* cmock_arg2", - :args_call=>"cmock_arg1, cmock_arg2" + :args => [ {:type => "int", :name => "cmock_arg1", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "unsigned int*", :name => "cmock_arg2", :ptr? => true, :const? => false, :const_ptr? => false} + ], + :args_string => "int cmock_arg1, unsigned int* cmock_arg2", + :args_call => "cmock_arg1, cmock_arg2" }] assert_equal(expected, @parser.parse("module", source)[:functions]) end @@ -935,26 +951,27 @@ source = "const int TheMatrix(int Trinity, unsigned int * Neo);\n" + "const int TheMatrix(int, unsigned int*);\n" - expected = [{ :var_arg=>nil, - :name=>"TheMatrix", - :unscoped_name=>"TheMatrix", - :namespace=>[], - :class=>nil, - :return=> { :type => "int", - :name => 'cmock_to_return', - :ptr? => false, - :const? => true, - :const_ptr? => false, - :str => "int cmock_to_return", - :void? => false - }, - :modifier=>"const", + expected = [{ :var_arg => nil, + :name => "TheMatrix", + :unscoped_name => "TheMatrix", + :namespace => [], + :class => nil, + :noreturn => false, + :return => { :type => "int", + :name => 'cmock_to_return', + :ptr? => false, + :const? => true, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :modifier => "const", :contains_ptr? => true, - :args=>[ {:type=>"int", :name=>"Trinity", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"unsigned int*", :name=>"Neo", :ptr? => true, :const? => false, :const_ptr? => false} - ], - :args_string=>"int Trinity, unsigned int* Neo", - :args_call=>"Trinity, Neo" + :args => [ {:type => "int", :name => "Trinity", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "unsigned int*", :name => "Neo", :ptr? => true, :const? => false, :const_ptr? => false} + ], + :args_string => "int Trinity, unsigned int* Neo", + :args_call => "Trinity, Neo" }] assert_equal(expected, @parser.parse("module", source)[:functions]) end @@ -969,8 +986,9 @@ expected = [{ :var_arg => nil, :name => "PorkRoast", :unscoped_name => "PorkRoast", - :namespace=>[], - :class=>nil, + :namespace => [], + :class => nil, + :noreturn => false, :return => { :type => "const int*", :name => 'cmock_to_return', :ptr? => true, @@ -1001,14 +1019,15 @@ expected = [{ :var_arg => nil, :name => "PorkRoast", :unscoped_name => "PorkRoast", - :namespace=>[], - :class=>nil, - :return => { :type => "int const*", + :namespace => [], + :class => nil, + :noreturn => false, + :return => { :type => "const int*", :name => 'cmock_to_return', :ptr? => true, :const? => true, :const_ptr? => false, - :str => "int const* cmock_to_return", + :str => "const int* cmock_to_return", :void? => false }, :modifier => "", @@ -1027,24 +1046,25 @@ source = "int * const PorkRoast(void);\n" - expected = [{ :var_arg=>nil, - :name=>"PorkRoast", - :unscoped_name=>"PorkRoast", - :namespace=>[], - :class=>nil, - :return=> { :type => "int*", - :name => 'cmock_to_return', - :ptr? => true, - :const? => false, - :const_ptr? => true, - :str => "int* cmock_to_return", - :void? => false - }, - :modifier=>"const", + expected = [{ :var_arg => nil, + :name => "PorkRoast", + :unscoped_name => "PorkRoast", + :namespace => [], + :class => nil, + :noreturn => false, + :return => { :type => "int*", + :name => 'cmock_to_return', + :ptr? => true, + :const? => false, + :const_ptr? => true, + :str => "int* cmock_to_return", + :void? => false + }, + :modifier => "const", :contains_ptr? => false, - :args=>[], - :args_string=>"void", - :args_call=>"" + :args => [], + :args_string => "void", + :args_call => "" }] assert_equal(expected, @parser.parse("module", source)[:functions]) end @@ -1055,9 +1075,10 @@ expected = [{ :name => "foo", :unscoped_name => "foo", - :namespace=>[], - :class=>nil, + :namespace => [], + :class => nil, :modifier => "", + :noreturn => false, :return => { :type => "void", :name => "cmock_to_return", :str => "void cmock_to_return", @@ -1069,14 +1090,14 @@ :var_arg => nil, :args_string => "int const* cmock_arg1, int* const cmock_arg2, const int* cmock_arg3, const int* const cmock_arg4, " + "int const* const cmock_arg5, int* cmock_arg6, int cmock_arg7, const int cmock_arg8", - :args => [{ :type=>"int const*", :name => "cmock_arg1", :ptr? => true, :const? => true, :const_ptr? => false }, - { :type=>"int*", :name => "cmock_arg2", :ptr? => true, :const? => false, :const_ptr? => true }, - { :type=>"const int*", :name => "cmock_arg3", :ptr? => true, :const? => true, :const_ptr? => false }, - { :type=>"const int*", :name => "cmock_arg4", :ptr? => true, :const? => true, :const_ptr? => true }, - { :type=>"int const*", :name => "cmock_arg5", :ptr? => true, :const? => true, :const_ptr? => true }, - { :type=>"int*", :name => "cmock_arg6", :ptr? => true, :const? => false, :const_ptr? => false }, - { :type=>"int", :name => "cmock_arg7", :ptr? => false, :const? => false, :const_ptr? => false }, - { :type=>"int", :name => "cmock_arg8", :ptr? => false, :const? => true, :const_ptr? => false }], + :args => [{ :type => "const int*", :name => "cmock_arg1", :ptr? => true, :const? => true, :const_ptr? => false }, + { :type => "int*", :name => "cmock_arg2", :ptr? => true, :const? => false, :const_ptr? => true }, + { :type => "const int*", :name => "cmock_arg3", :ptr? => true, :const? => true, :const_ptr? => false }, + { :type => "const int*", :name => "cmock_arg4", :ptr? => true, :const? => true, :const_ptr? => true }, + { :type => "const int*", :name => "cmock_arg5", :ptr? => true, :const? => true, :const_ptr? => true }, + { :type => "int*", :name => "cmock_arg6", :ptr? => true, :const? => false, :const_ptr? => false }, + { :type => "int", :name => "cmock_arg7", :ptr? => false, :const? => false, :const_ptr? => false }, + { :type => "int", :name => "cmock_arg8", :ptr? => false, :const? => true, :const_ptr? => false }], :args_call => "cmock_arg1, cmock_arg2, cmock_arg3, cmock_arg4, cmock_arg5, cmock_arg6, cmock_arg7, cmock_arg8", :contains_ptr? => true }] @@ -1090,9 +1111,10 @@ expected = [{ :name => "bar", :unscoped_name => "bar", - :namespace=>[], - :class=>nil, + :namespace => [], + :class => nil, :modifier => "", + :noreturn => false, :return => { :type => "void", :name => "cmock_to_return", :str => "void cmock_to_return", @@ -1104,14 +1126,14 @@ :var_arg => nil, :args_string => "int const* param1, int* const param2, const int* param3, const int* const param4, " + "int const* const param5, int* param6, int param7, const int param8", - :args => [{ :type=>"int const*", :name => "param1", :ptr? => true, :const? => true, :const_ptr? => false }, - { :type=>"int*", :name => "param2", :ptr? => true, :const? => false, :const_ptr? => true }, - { :type=>"const int*", :name => "param3", :ptr? => true, :const? => true, :const_ptr? => false }, - { :type=>"const int*", :name => "param4", :ptr? => true, :const? => true, :const_ptr? => true }, - { :type=>"int const*", :name => "param5", :ptr? => true, :const? => true, :const_ptr? => true }, - { :type=>"int*", :name => "param6", :ptr? => true, :const? => false, :const_ptr? => false }, - { :type=>"int", :name => "param7", :ptr? => false, :const? => false, :const_ptr? => false }, - { :type=>"int", :name => "param8", :ptr? => false, :const? => true, :const_ptr? => false }], + :args => [{ :type => "const int*", :name => "param1", :ptr? => true, :const? => true, :const_ptr? => false }, + { :type => "int*", :name => "param2", :ptr? => true, :const? => false, :const_ptr? => true }, + { :type => "const int*", :name => "param3", :ptr? => true, :const? => true, :const_ptr? => false }, + { :type => "const int*", :name => "param4", :ptr? => true, :const? => true, :const_ptr? => true }, + { :type => "const int*", :name => "param5", :ptr? => true, :const? => true, :const_ptr? => true }, + { :type => "int*", :name => "param6", :ptr? => true, :const? => false, :const_ptr? => false }, + { :type => "int", :name => "param7", :ptr? => false, :const? => false, :const_ptr? => false }, + { :type => "int", :name => "param8", :ptr? => false, :const? => true, :const_ptr? => false }], :args_call => "param1, param2, param3, param4, param5, param6, param7, param8", :contains_ptr? => true }].freeze @@ -1124,9 +1146,10 @@ expected = [{ :name => "AddToBook", :unscoped_name => "AddToBook", - :namespace=>[], - :class=>nil, - :modifier=>"", + :namespace => [], + :class => nil, + :modifier => "", + :noreturn => false, :return => { :type => "Book", :name => "cmock_to_return", :str => "Book cmock_to_return", @@ -1154,11 +1177,12 @@ "FUNKY_VOID_T DrHorrible(int SingAlong);\n" + "int CaptainHammer(CHUNKY_VOID_T);\n" - expected = [{ :var_arg=>nil, - :name=>"DrHorrible", - :unscoped_name=>"DrHorrible", - :namespace=>[], - :class=>nil, + expected = [{ :var_arg => nil, + :name => "DrHorrible", + :unscoped_name => "DrHorrible", + :namespace => [], + :class => nil, + :noreturn => false, :return => { :type => "void", :name => 'cmock_to_return', :ptr? => false, @@ -1167,30 +1191,31 @@ :str => "void cmock_to_return", :void? => true }, - :modifier=>"", + :modifier => "", :contains_ptr? => false, - :args=>[ {:type=>"int", :name=>"SingAlong", :ptr? => false, :const? => false, :const_ptr? => false} ], - :args_string=>"int SingAlong", - :args_call=>"SingAlong" + :args => [ {:type => "int", :name => "SingAlong", :ptr? => false, :const? => false, :const_ptr? => false} ], + :args_string => "int SingAlong", + :args_call => "SingAlong" }, - { :var_arg=>nil, - :return=> { :type => "int", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "int cmock_to_return", - :void? => false - }, - :name=>"CaptainHammer", - :unscoped_name=>"CaptainHammer", - :namespace=>[], - :class=>nil, - :modifier=>"", + { :var_arg => nil, + :noreturn => false, + :return => { :type => "int", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :name => "CaptainHammer", + :unscoped_name => "CaptainHammer", + :namespace => [], + :class => nil, + :modifier => "", :contains_ptr? => false, - :args=>[ ], - :args_string=>"void", - :args_call=>"" + :args => [ ], + :args_string => "void", + :args_call => "" }] assert_equal(expected, @parser.parse("module", source)[:functions]) end @@ -1201,7 +1226,8 @@ "void Penny(struct const _KeepYourHeadUp_ * const BillyBuddy);\n" + "struct TheseArentTheHammer CaptainHammer(void);\n" - expected = [{ :var_arg=>nil, + expected = [{ :var_arg => nil, + :noreturn => false, :return =>{ :type => "int", :name => 'cmock_to_return', :ptr? => false, @@ -1210,193 +1236,200 @@ :str => "int cmock_to_return", :void? => false }, - :name=>"DrHorrible", - :unscoped_name=>"DrHorrible", - :namespace=>[], - :class=>nil, - :modifier=>"", + :name => "DrHorrible", + :unscoped_name => "DrHorrible", + :namespace => [], + :class => nil, + :modifier => "", :contains_ptr? => false, - :args=>[ {:type=>"struct SingAlong", :name=>"Blog", :ptr? => false, :const? => false, :const_ptr? => false} ], - :args_string=>"struct SingAlong Blog", - :args_call=>"Blog" + :args => [ {:type => "struct SingAlong", :name => "Blog", :ptr? => false, :const? => false, :const_ptr? => false} ], + :args_string => "struct SingAlong Blog", + :args_call => "Blog" }, - { :var_arg=>nil, - :return=> { :type => "void", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "void cmock_to_return", - :void? => true - }, - :name=>"Penny", - :unscoped_name=>"Penny", - :namespace=>[], - :class=>nil, - :modifier=>"", + { :var_arg => nil, + :noreturn => false, + :return => { :type => "void", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "void cmock_to_return", + :void? => true + }, + :name => "Penny", + :unscoped_name => "Penny", + :namespace => [], + :class => nil, + :modifier => "", :contains_ptr? => true, - :args=>[ {:type=>"struct const _KeepYourHeadUp_*", :name=>"BillyBuddy", :ptr? => true, :const? => true, :const_ptr? => true} ], - :args_string=>"struct const _KeepYourHeadUp_* const BillyBuddy", - :args_call=>"BillyBuddy" + :args => [ {:type => "const struct _KeepYourHeadUp_*", :name => "BillyBuddy", :ptr? => true, :const? => true, :const_ptr? => true} ], + :args_string => "struct const _KeepYourHeadUp_* const BillyBuddy", + :args_call => "BillyBuddy" }, - { :var_arg=>nil, - :return=> { :type => "struct TheseArentTheHammer", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "struct TheseArentTheHammer cmock_to_return", - :void? => false - }, - :name=>"CaptainHammer", - :unscoped_name=>"CaptainHammer", - :namespace=>[], - :class=>nil, - :modifier=>"", + { :var_arg => nil, + :noreturn => false, + :return => { :type => "struct TheseArentTheHammer", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "struct TheseArentTheHammer cmock_to_return", + :void? => false + }, + :name => "CaptainHammer", + :unscoped_name => "CaptainHammer", + :namespace => [], + :class => nil, + :modifier => "", :contains_ptr? => false, - :args=>[ ], - :args_string=>"void", - :args_call=>"" + :args => [ ], + :args_string => "void", + :args_call => "" }] assert_equal(expected, @parser.parse("module", source)[:functions]) end it "extract functions containing unions with union specifier" do source = "void OrangePeel(union STARS_AND_STRIPES * a, union AFL_CIO b)" - expected = [{ :var_arg=>nil, - :return=>{ :type => "void", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "void cmock_to_return", - :void? => true - }, - :name=>"OrangePeel", - :unscoped_name=>"OrangePeel", - :namespace=>[], - :class=>nil, - :modifier=>"", - :contains_ptr? => true, - :args=>[ {:type=>"union STARS_AND_STRIPES*", :name=>"a", :ptr? => true, :const? => false, :const_ptr? => false}, - {:type=>"union AFL_CIO", :name=>"b", :ptr? => false, :const? => false, :const_ptr? => false} - ], - :args_string=>"union STARS_AND_STRIPES* a, union AFL_CIO b", - :args_call=>"a, b" }] + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "void", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "void cmock_to_return", + :void? => true + }, + :name => "OrangePeel", + :unscoped_name => "OrangePeel", + :namespace => [], + :class => nil, + :modifier => "", + :contains_ptr? => true, + :args => [ {:type => "union STARS_AND_STRIPES*", :name => "a", :ptr? => true, :const? => false, :const_ptr? => false}, + {:type => "union AFL_CIO", :name => "b", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "union STARS_AND_STRIPES* a, union AFL_CIO b", + :args_call => "a, b" }] result = @parser.parse("module", source) assert_equal(expected, result[:functions]) end it "not be thwarted by variables named with primitive types as part of the name" do source = "void ApplePeel(const unsigned int const_param, int int_param, int integer, char character, int* const constant)" - expected = [{ :var_arg=>nil, - :return=>{ :type => "void", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "void cmock_to_return", - :void? => true - }, - :name=>"ApplePeel", - :unscoped_name=>"ApplePeel", - :namespace=>[], - :class=>nil, - :modifier=>"", - :contains_ptr? => true, - :args=>[ {:type=> "unsigned int", :name=>"const_param", :ptr? => false, :const? => true, :const_ptr? => false}, - {:type=>"int", :name=>"int_param", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"int", :name=>"integer", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"char", :name=>"character", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"int*", :name=>"constant", :ptr? => true, :const? => false, :const_ptr? => true} - ], - :args_string=>"const unsigned int const_param, int int_param, int integer, char character, int* const constant", - :args_call=>"const_param, int_param, integer, character, constant" }] + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "void", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "void cmock_to_return", + :void? => true + }, + :name => "ApplePeel", + :unscoped_name => "ApplePeel", + :namespace => [], + :class => nil, + :modifier => "", + :contains_ptr? => true, + :args => [ {:type => "unsigned int", :name => "const_param", :ptr? => false, :const? => true, :const_ptr? => false}, + {:type => "int", :name => "int_param", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "int", :name => "integer", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "char", :name => "character", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "int*", :name => "constant", :ptr? => true, :const? => false, :const_ptr? => true} + ], + :args_string => "const unsigned int const_param, int int_param, int integer, char character, int* const constant", + :args_call => "const_param, int_param, integer, character, constant" }] result = @parser.parse("module", source) assert_equal(expected, result[:functions]) end it "not be thwarted by custom types named similarly to primitive types" do source = "void LemonPeel(integer param, character thing, longint * junk, constant value, int32_t const number)" - expected = [{:var_arg=>nil, - :return=>{ :type => "void", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "void cmock_to_return", - :void? => true - }, - :name=>"LemonPeel", - :unscoped_name=>"LemonPeel", - :namespace=>[], - :class=>nil, - :modifier=>"", + expected = [{:var_arg => nil, + :noreturn => false, + :return => { :type => "void", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "void cmock_to_return", + :void? => true + }, + :name => "LemonPeel", + :unscoped_name => "LemonPeel", + :namespace => [], + :class => nil, + :modifier => "", :contains_ptr? => true, - :args=>[ {:type=>"integer", :name=>"param", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"character", :name=>"thing", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"longint*", :name=>"junk", :ptr? => true, :const? => false, :const_ptr? => false}, - {:type=>"constant", :name=>"value", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"int32_t", :name=>"number", :ptr? => false, :const? => true, :const_ptr? => false} - ], - :args_string=>"integer param, character thing, longint* junk, constant value, int32_t const number", - :args_call=>"param, thing, junk, value, number" }] + :args => [ {:type => "integer", :name => "param", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "character", :name => "thing", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "longint*", :name => "junk", :ptr? => true, :const? => false, :const_ptr? => false}, + {:type => "constant", :name => "value", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "int32_t", :name => "number", :ptr? => false, :const? => true, :const_ptr? => false} + ], + :args_string => "integer param, character thing, longint* junk, constant value, int32_t const number", + :args_call => "param, thing, junk, value, number" }] result = @parser.parse("module", source) assert_equal(expected, result[:functions]) end it "handle some of those chains of C name specifiers naturally" do source = "void CoinOperated(signed char abc, const unsigned long int xyz_123, unsigned int const abc_123, long long arm_of_the_law)" - expected = [{:var_arg=>nil, - :return=>{ :type => "void", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "void cmock_to_return", - :void? => true - }, - :name=>"CoinOperated", - :unscoped_name=>"CoinOperated", - :namespace=>[], - :class=>nil, - :modifier=>"", + expected = [{:var_arg => nil, + :noreturn => false, + :return => { :type => "void", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "void cmock_to_return", + :void? => true + }, + :name => "CoinOperated", + :unscoped_name => "CoinOperated", + :namespace => [], + :class => nil, + :modifier => "", :contains_ptr? => false, - :args=>[ {:type=>"signed char", :name=>"abc", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"unsigned long int", :name=>"xyz_123", :ptr? => false, :const? => true, :const_ptr? => false}, - {:type=>"unsigned int", :name=>"abc_123", :ptr? => false, :const? => true, :const_ptr? => false}, - {:type=>"long long", :name=>"arm_of_the_law", :ptr? => false, :const? => false, :const_ptr? => false} - ], - :args_string=>"signed char abc, const unsigned long int xyz_123, unsigned int const abc_123, long long arm_of_the_law", - :args_call=>"abc, xyz_123, abc_123, arm_of_the_law" }] + :args => [ {:type => "signed char", :name => "abc", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "unsigned long int", :name => "xyz_123", :ptr? => false, :const? => true, :const_ptr? => false}, + {:type => "unsigned int", :name => "abc_123", :ptr? => false, :const? => true, :const_ptr? => false}, + {:type => "long long", :name => "arm_of_the_law", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "signed char abc, const unsigned long int xyz_123, unsigned int const abc_123, long long arm_of_the_law", + :args_call => "abc, xyz_123, abc_123, arm_of_the_law" }] result = @parser.parse("module", source) assert_equal(expected, result[:functions]) end it "handle custom types of various formats" do source = "void CardOperated(CUSTOM_TYPE abc, CUSTOM_TYPE* xyz_123, CUSTOM_TYPE const abcxyz, struct CUSTOM_TYPE const * const abc123)" - expected = [{:var_arg=>nil, - :return=>{ :type => "void", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "void cmock_to_return", - :void? => true - }, - :name=>"CardOperated", - :unscoped_name=>"CardOperated", - :namespace=>[], - :class=>nil, - :modifier=>"", + expected = [{:var_arg => nil, + :noreturn => false, + :return => { :type => "void", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "void cmock_to_return", + :void? => true + }, + :name => "CardOperated", + :unscoped_name => "CardOperated", + :namespace => [], + :class => nil, + :modifier => "", :contains_ptr? => true, - :args=>[ {:type=>"CUSTOM_TYPE", :name=>"abc", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"CUSTOM_TYPE*", :name=>"xyz_123", :ptr? => true, :const? => false, :const_ptr? => false}, - {:type=>"CUSTOM_TYPE", :name=>"abcxyz", :ptr? => false, :const? => true, :const_ptr? => false}, - {:type=>"struct CUSTOM_TYPE const*", :name=>"abc123", :ptr? => true, :const? => true, :const_ptr? => true} - ], - :args_string=>"CUSTOM_TYPE abc, CUSTOM_TYPE* xyz_123, CUSTOM_TYPE const abcxyz, struct CUSTOM_TYPE const* const abc123", - :args_call=>"abc, xyz_123, abcxyz, abc123" }] + :args => [ {:type => "CUSTOM_TYPE", :name => "abc", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "CUSTOM_TYPE*", :name => "xyz_123", :ptr? => true, :const? => false, :const_ptr? => false}, + {:type => "CUSTOM_TYPE", :name => "abcxyz", :ptr? => false, :const? => true, :const_ptr? => false}, + {:type => "const struct CUSTOM_TYPE*", :name => "abc123", :ptr? => true, :const? => true, :const_ptr? => true} + ], + :args_string => "CUSTOM_TYPE abc, CUSTOM_TYPE* xyz_123, CUSTOM_TYPE const abcxyz, struct CUSTOM_TYPE const* const abc123", + :args_call => "abc, xyz_123, abcxyz, abc123" }] result = @parser.parse("module", source) assert_equal(expected, result[:functions]) end @@ -1413,20 +1446,21 @@ { type: 'int**', name: 'thing4', ptr?: true, const?: false, const_ptr?: false }, { type: 'u8*', name: 'thing5', ptr?: true, const?: false, const_ptr?: false } ] - expected = [{:var_arg=>nil, - :return=>{ :type => "void", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "void cmock_to_return", - :void? => true - }, - :name=>"KeyOperated", - :unscoped_name=>"KeyOperated", - :namespace=>[], - :class=>nil, - :modifier=>"", + expected = [{:var_arg => nil, + :noreturn => false, + :return => { :type => "void", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "void cmock_to_return", + :void? => true + }, + :name => "KeyOperated", + :unscoped_name => "KeyOperated", + :namespace => [], + :class => nil, + :modifier => "", :contains_ptr? => true, :args => expected_args, :args_string => 'CUSTOM_TYPE* thing1, int* thing2, ' \ @@ -1438,52 +1472,54 @@ it "give a reasonable guess when dealing with weird combinations of custom types and modifiers" do source = "void Cheese(unsigned CUSTOM_TYPE abc, unsigned xyz, CUSTOM_TYPE1 CUSTOM_TYPE2 pdq)" - expected = [{:var_arg=>nil, - :return=>{ :type => "void", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "void cmock_to_return", - :void? => true - }, - :name=>"Cheese", - :unscoped_name=>"Cheese", - :namespace=>[], - :class=>nil, - :modifier=>"", + expected = [{:var_arg => nil, + :noreturn => false, + :return => { :type => "void", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "void cmock_to_return", + :void? => true + }, + :name => "Cheese", + :unscoped_name => "Cheese", + :namespace => [], + :class => nil, + :modifier => "", :contains_ptr? => false, - :args=>[ {:type=>"unsigned CUSTOM_TYPE", :name=>"abc", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"unsigned", :name=>"xyz", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"CUSTOM_TYPE1 CUSTOM_TYPE2", :name=>"pdq", :ptr? => false, :const? => false, :const_ptr? => false} - ], - :args_string=>"unsigned CUSTOM_TYPE abc, unsigned xyz, CUSTOM_TYPE1 CUSTOM_TYPE2 pdq", - :args_call=>"abc, xyz, pdq" }] + :args => [ {:type => "unsigned CUSTOM_TYPE", :name => "abc", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "unsigned", :name => "xyz", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "CUSTOM_TYPE1 CUSTOM_TYPE2", :name => "pdq", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "unsigned CUSTOM_TYPE abc, unsigned xyz, CUSTOM_TYPE1 CUSTOM_TYPE2 pdq", + :args_call => "abc, xyz, pdq" }] result = @parser.parse("module", source) assert_equal(expected, result[:functions]) end it "extract functions containing a function pointer" do source = "void FunkyTurkey(unsigned int (*func_ptr)(int, char))" - expected = [{ :var_arg=>nil, - :return=>{ :type => "void", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "void cmock_to_return", - :void? => true - }, - :name=>"FunkyTurkey", - :unscoped_name=>"FunkyTurkey", - :namespace=>[], - :class=>nil, - :modifier=>"", - :contains_ptr? => false, - :args=>[ {:type=>"cmock_module_func_ptr1", :name=>"func_ptr", :ptr? => false, :const? => false, :const_ptr? => false} - ], - :args_string=>"cmock_module_func_ptr1 func_ptr", - :args_call=>"func_ptr" }] + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "void", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "void cmock_to_return", + :void? => true + }, + :name => "FunkyTurkey", + :unscoped_name => "FunkyTurkey", + :namespace => [], + :class => nil, + :modifier => "", + :contains_ptr? => false, + :args => [ {:type => "cmock_module_func_ptr1", :name => "func_ptr", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "cmock_module_func_ptr1 func_ptr", + :args_call => "func_ptr" }] typedefs = ["typedef unsigned int(*cmock_module_func_ptr1)(int, char);"] result = @parser.parse("module", source) assert_equal(expected, result[:functions]) @@ -1492,25 +1528,26 @@ it "extract functions using a function pointer with shorthand notation" do source = "void FunkyTurkey(unsigned int func_ptr(int, char))" - expected = [{ :var_arg=>nil, - :return=>{ :type => "void", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "void cmock_to_return", - :void? => true - }, - :name=>"FunkyTurkey", - :unscoped_name=>"FunkyTurkey", - :namespace=>[], - :class=>nil, - :modifier=>"", - :contains_ptr? => false, - :args=>[ {:type=>"cmock_module_func_ptr1", :name=>"func_ptr", :ptr? => false, :const? => false, :const_ptr? => false} - ], - :args_string=>"cmock_module_func_ptr1 func_ptr", - :args_call=>"func_ptr" }] + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "void", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "void cmock_to_return", + :void? => true + }, + :name => "FunkyTurkey", + :unscoped_name => "FunkyTurkey", + :namespace => [], + :class => nil, + :modifier => "", + :contains_ptr? => false, + :args => [ {:type => "cmock_module_func_ptr1", :name => "func_ptr", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "cmock_module_func_ptr1 func_ptr", + :args_call => "func_ptr" }] typedefs = ["typedef unsigned int(*cmock_module_func_ptr1)(int, char);"] result = @parser.parse("module", source) assert_equal(expected, result[:functions]) @@ -1519,25 +1556,26 @@ it "extract functions containing a function pointer with a void" do source = "void FunkyTurkey(void (*func_ptr)(void))" - expected = [{ :var_arg=>nil, - :return=>{ :type => "void", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "void cmock_to_return", - :void? => true - }, - :name=>"FunkyTurkey", - :unscoped_name=>"FunkyTurkey", - :namespace=>[], - :class=>nil, - :modifier=>"", - :contains_ptr? => false, - :args=>[ {:type=>"cmock_module_func_ptr1", :name=>"func_ptr", :ptr? => false, :const? => false, :const_ptr? => false} - ], - :args_string=>"cmock_module_func_ptr1 func_ptr", - :args_call=>"func_ptr" }] + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "void", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "void cmock_to_return", + :void? => true + }, + :name => "FunkyTurkey", + :unscoped_name => "FunkyTurkey", + :namespace => [], + :class => nil, + :modifier => "", + :contains_ptr? => false, + :args => [ {:type => "cmock_module_func_ptr1", :name => "func_ptr", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "cmock_module_func_ptr1 func_ptr", + :args_call => "func_ptr" }] typedefs = ["typedef void(*cmock_module_func_ptr1)(void);"] result = @parser.parse("module", source) assert_equal(expected, result[:functions]) @@ -1546,25 +1584,26 @@ it "extract functions containing a function pointer with an implied void" do source = "void FunkyTurkey(unsigned int (*func_ptr)())" - expected = [{ :var_arg=>nil, - :return=>{ :type => "void", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "void cmock_to_return", - :void? => true - }, - :name=>"FunkyTurkey", - :unscoped_name=>"FunkyTurkey", - :namespace=>[], - :class=>nil, - :modifier=>"", - :contains_ptr? => false, - :args=>[ {:type=>"cmock_module_func_ptr1", :name=>"func_ptr", :ptr? => false, :const? => false, :const_ptr? => false} - ], - :args_string=>"cmock_module_func_ptr1 func_ptr", - :args_call=>"func_ptr" }] + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "void", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "void cmock_to_return", + :void? => true + }, + :name => "FunkyTurkey", + :unscoped_name => "FunkyTurkey", + :namespace => [], + :class => nil, + :modifier => "", + :contains_ptr? => false, + :args => [ {:type => "cmock_module_func_ptr1", :name => "func_ptr", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "cmock_module_func_ptr1 func_ptr", + :args_call => "func_ptr" }] typedefs = ["typedef unsigned int(*cmock_module_func_ptr1)();"] result = @parser.parse("module", source) assert_equal(expected, result[:functions]) @@ -1573,80 +1612,82 @@ it "extract functions containing a constant function pointer and a pointer in the nested arg list" do source = "void FunkyChicken(unsigned int (* const func_ptr)(unsigned long int * , char))" - expected = [{ :var_arg=>nil, - :return=>{ :type => "void", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "void cmock_to_return", - :void? => true - }, - :name=>"FunkyChicken", - :unscoped_name=>"FunkyChicken", - :namespace=>[], - :class=>nil, - :modifier=>"", - :contains_ptr? => false, - :args=>[ {:type=>"cmock_module_func_ptr1", :name=>"func_ptr", :ptr? => false, :const? => true, :const_ptr? => false} - ], - :args_string=>"cmock_module_func_ptr1 const func_ptr", - :args_call=>"func_ptr" }] - typedefs = ["typedef unsigned int(*cmock_module_func_ptr1)(unsigned long int* , char);"] + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "void", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "void cmock_to_return", + :void? => true + }, + :name => "FunkyChicken", + :unscoped_name => "FunkyChicken", + :namespace => [], + :class => nil, + :modifier => "", + :contains_ptr? => false, + :args => [ {:type => "cmock_module_func_ptr1", :name => "func_ptr", :ptr? => false, :const? => true, :const_ptr? => false} + ], + :args_string => "cmock_module_func_ptr1 const func_ptr", + :args_call => "func_ptr" }] + typedefs = ["typedef unsigned int(*cmock_module_func_ptr1)(unsigned long int*, char);"] result = @parser.parse("module", source) assert_equal(expected, result[:functions]) assert_equal(typedefs, result[:typedefs]) end # it "extract functions containing a function pointer taking a vararg" do - # source = "void FunkyParrot(unsigned int (*func_ptr)(int, char, ...))" - # expected = [{ :var_arg=>nil, - # :return=>{ :type => "void", - # :name => 'cmock_to_return', - # :ptr? => false, - # :const? => false, - # :const_ptr? => false, - # :str => "void cmock_to_return", - # :void? => true - # }, - # :name=>"FunkyParrot", - # :unscoped_name=>"FunkyParrot", - # :namespace=>[], - # :class=>nil, - # :modifier=>"", - # :contains_ptr? => false, - # :args=>[ {:type=>"cmock_module_func_ptr1", :name=>"func_ptr", :ptr? => false, :const? => false, :const_ptr? => false} - # ], - # :args_string=>"cmock_module_func_ptr1 func_ptr", - # :args_call=>"func_ptr" }] - # typedefs = ["typedef unsigned int(*cmock_module_func_ptr1)(int, char, ...);"] - # result = @parser.parse("module", source) - # assert_equal(expected, result[:functions]) - # assert_equal(typedefs, result[:typedefs]) + # source = "void FunkyParrot(unsigned int (*func_ptr)(int, char, ...))" + # expected = [{ :var_arg => nil, + # :return => { :type => "void", + # :name => 'cmock_to_return', + # :ptr? => false, + # :const? => false, + # :const_ptr? => false, + # :str => "void cmock_to_return", + # :void? => true + # }, + # :name => "FunkyParrot", + # :unscoped_name => "FunkyParrot", + # :namespace => [], + # :class => nil, + # :modifier => "", + # :contains_ptr? => false, + # :args => [ {:type => "cmock_module_func_ptr1", :name => "func_ptr", :ptr? => false, :const? => false, :const_ptr? => false} + # ], + # :args_string => "cmock_module_func_ptr1 func_ptr", + # :args_call => "func_ptr" }] + # typedefs = ["typedef unsigned int(*cmock_module_func_ptr1)(int, char, ...);"] + # result = @parser.parse("module", source) + # assert_equal(expected, result[:functions]) + # assert_equal(typedefs, result[:typedefs]) # end it "extract functions containing a function pointer with extra parenthesis and two sets" do source = "void FunkyBudgie(int (((* func_ptr1)(int, char))), void (*func_ptr2)(void))" - expected = [{ :var_arg=>nil, - :return=>{ :type => "void", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "void cmock_to_return", - :void? => true - }, - :name=>"FunkyBudgie", - :unscoped_name=>"FunkyBudgie", - :namespace=>[], - :class=>nil, - :modifier=>"", - :contains_ptr? => false, - :args=>[ {:type=>"cmock_module_func_ptr1", :name=>"func_ptr1", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"cmock_module_func_ptr2", :name=>"func_ptr2", :ptr? => false, :const? => false, :const_ptr? => false} - ], - :args_string=>"cmock_module_func_ptr1 func_ptr1, cmock_module_func_ptr2 func_ptr2", - :args_call=>"func_ptr1, func_ptr2" }] + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "void", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "void cmock_to_return", + :void? => true + }, + :name => "FunkyBudgie", + :unscoped_name => "FunkyBudgie", + :namespace => [], + :class => nil, + :modifier => "", + :contains_ptr? => false, + :args => [ {:type => "cmock_module_func_ptr1", :name => "func_ptr1", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "cmock_module_func_ptr2", :name => "func_ptr2", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "cmock_module_func_ptr1 func_ptr1, cmock_module_func_ptr2 func_ptr2", + :args_call => "func_ptr1, func_ptr2" }] typedefs = ["typedef int(*cmock_module_func_ptr1)(int, char);", "typedef void(*cmock_module_func_ptr2)(void);"] result = @parser.parse("module", source) assert_equal(expected, result[:functions]) @@ -1655,27 +1696,28 @@ it "extract functions containing a function pointers, structs and other things" do source = "struct mytype *FunkyRobin(uint16_t num1, uint16_t num2, void (*func_ptr1)(uint16_t num3, struct mytype2 *s));" - expected = [{ :var_arg=>nil, - :return=>{ :type => "struct mytype*", - :name => 'cmock_to_return', - :ptr? => true, - :const? => false, - :const_ptr? => false, - :str => "struct mytype* cmock_to_return", - :void? => false - }, - :name=>"FunkyRobin", - :unscoped_name=>"FunkyRobin", - :namespace=>[], - :class=>nil, - :modifier=>"", - :contains_ptr? => false, - :args=>[ {:type=>"uint16_t", :name=>"num1", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"uint16_t", :name=>"num2", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"cmock_module_func_ptr1", :name=>"func_ptr1", :ptr? => false, :const? => false, :const_ptr? => false} - ], - :args_string=>"uint16_t num1, uint16_t num2, cmock_module_func_ptr1 func_ptr1", - :args_call=>"num1, num2, func_ptr1" }] + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "struct mytype*", + :name => 'cmock_to_return', + :ptr? => true, + :const? => false, + :const_ptr? => false, + :str => "struct mytype* cmock_to_return", + :void? => false + }, + :name => "FunkyRobin", + :unscoped_name => "FunkyRobin", + :namespace => [], + :class => nil, + :modifier => "", + :contains_ptr? => false, + :args => [ {:type => "uint16_t", :name => "num1", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "uint16_t", :name => "num2", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "cmock_module_func_ptr1", :name => "func_ptr1", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "uint16_t num1, uint16_t num2, cmock_module_func_ptr1 func_ptr1", + :args_call => "num1, num2, func_ptr1" }] typedefs = ["typedef void(*cmock_module_func_ptr1)(uint16_t num3, struct mytype2* s);"] result = @parser.parse("module", source) assert_equal(expected, result[:functions]) @@ -1684,25 +1726,26 @@ it "extract functions containing an anonymous function pointer" do source = "void FunkyFowl(unsigned int (* const)(int, char))" - expected = [{ :var_arg=>nil, - :return=>{ :type => "void", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "void cmock_to_return", - :void? => true - }, - :name=>"FunkyFowl", - :unscoped_name=>"FunkyFowl", - :namespace=>[], - :class=>nil, - :modifier=>"", - :contains_ptr? => false, - :args=>[ {:type=>"cmock_module_func_ptr1", :name=>"cmock_arg1", :ptr? => false, :const? => true, :const_ptr? => false} - ], - :args_string=>"cmock_module_func_ptr1 const cmock_arg1", - :args_call=>"cmock_arg1" }] + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "void", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "void cmock_to_return", + :void? => true + }, + :name => "FunkyFowl", + :unscoped_name => "FunkyFowl", + :namespace => [], + :class => nil, + :modifier => "", + :contains_ptr? => false, + :args => [ {:type => "cmock_module_func_ptr1", :name => "cmock_arg1", :ptr? => false, :const? => true, :const_ptr? => false} + ], + :args_string => "cmock_module_func_ptr1 const cmock_arg1", + :args_call => "cmock_arg1" }] typedefs = ["typedef unsigned int(*cmock_module_func_ptr1)(int, char);"] result = @parser.parse("module", source) assert_equal(expected, result[:functions]) @@ -1711,25 +1754,26 @@ it "extract functions returning a function pointer" do source = "unsigned short (*FunkyPidgeon( const char op_code ))( int, long int )" - expected = [{ :var_arg=>nil, - :return=>{ :type => "cmock_module_func_ptr1", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "cmock_module_func_ptr1 cmock_to_return", - :void? => false - }, - :name=>"FunkyPidgeon", - :unscoped_name=>"FunkyPidgeon", - :namespace=>[], - :class=>nil, - :modifier=>"", - :contains_ptr? => false, - :args=>[ {:type=>"char", :name=>"op_code", :ptr? => false, :const? => true, :const_ptr? => false} - ], - :args_string=>"const char op_code", - :args_call=>"op_code" }] + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "cmock_module_func_ptr1", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "cmock_module_func_ptr1 cmock_to_return", + :void? => false + }, + :name => "FunkyPidgeon", + :unscoped_name => "FunkyPidgeon", + :namespace => [], + :class => nil, + :modifier => "", + :contains_ptr? => false, + :args => [ {:type => "char", :name => "op_code", :ptr? => false, :const? => true, :const_ptr? => false} + ], + :args_string => "const char op_code", + :args_call => "op_code" }] typedefs = ["typedef unsigned short(*cmock_module_func_ptr1)( int, long int );"] result = @parser.parse("module", source) assert_equal(expected, result[:functions]) @@ -1738,24 +1782,25 @@ it "extract functions returning a function pointer with implied void" do source = "unsigned short (*FunkyTweetie())()" - expected = [{ :var_arg=>nil, - :return=>{ :type => "cmock_module_func_ptr1", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "cmock_module_func_ptr1 cmock_to_return", - :void? => false - }, - :name=>"FunkyTweetie", - :unscoped_name=>"FunkyTweetie", - :namespace=>[], - :class=>nil, - :modifier=>"", - :contains_ptr? => false, - :args=>[], - :args_string=>"void", - :args_call=>"" }] + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "cmock_module_func_ptr1", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "cmock_module_func_ptr1 cmock_to_return", + :void? => false + }, + :name => "FunkyTweetie", + :unscoped_name => "FunkyTweetie", + :namespace => [], + :class => nil, + :modifier => "", + :contains_ptr? => false, + :args => [], + :args_string => "void", + :args_call => "" }] typedefs = ["typedef unsigned short(*cmock_module_func_ptr1)();"] result = @parser.parse("module", source) assert_equal(expected, result[:functions]) @@ -1764,24 +1809,25 @@ it "extract functions returning a function pointer where everything is a void" do source = "void (* FunkySeaGull(void))(void)" - expected = [{ :var_arg=>nil, - :return=>{ :type => "cmock_module_func_ptr1", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "cmock_module_func_ptr1 cmock_to_return", - :void? => false - }, - :name=>"FunkySeaGull", - :unscoped_name=>"FunkySeaGull", - :namespace=>[], - :class=>nil, - :modifier=>"", - :contains_ptr? => false, - :args=>[], - :args_string=>"void", - :args_call=>"" }] + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "cmock_module_func_ptr1", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "cmock_module_func_ptr1 cmock_to_return", + :void? => false + }, + :name => "FunkySeaGull", + :unscoped_name => "FunkySeaGull", + :namespace => [], + :class => nil, + :modifier => "", + :contains_ptr? => false, + :args => [], + :args_string => "void", + :args_call => "" }] typedefs = ["typedef void(*cmock_module_func_ptr1)(void);"] result = @parser.parse("module", source) assert_equal(expected, result[:functions]) @@ -1790,26 +1836,27 @@ it "extract functions returning a function pointer with some pointer nonsense" do source = "unsigned int * (* FunkyMacaw(double* foo, THING *bar))(unsigned int)" - expected = [{ :var_arg=>nil, - :return=>{ :type => "cmock_module_func_ptr1", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "cmock_module_func_ptr1 cmock_to_return", - :void? => false - }, - :name=>"FunkyMacaw", - :unscoped_name=>"FunkyMacaw", - :namespace=>[], - :class=>nil, - :modifier=>"", - :contains_ptr? => true, - :args=>[ {:type=>"double*", :name=>"foo", :ptr? => true, :const? => false, :const_ptr? => false}, - {:type=>"THING*", :name=>"bar", :ptr? => true, :const? => false, :const_ptr? => false} - ], - :args_string=>"double* foo, THING* bar", - :args_call=>"foo, bar" }] + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "cmock_module_func_ptr1", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "cmock_module_func_ptr1 cmock_to_return", + :void? => false + }, + :name => "FunkyMacaw", + :unscoped_name => "FunkyMacaw", + :namespace => [], + :class => nil, + :modifier => "", + :contains_ptr? => true, + :args => [ {:type => "double*", :name => "foo", :ptr? => true, :const? => false, :const_ptr? => false}, + {:type => "THING*", :name => "bar", :ptr? => true, :const? => false, :const_ptr? => false} + ], + :args_string => "double* foo, THING* bar", + :args_call => "foo, bar" }] typedefs = ["typedef unsigned int *(*cmock_module_func_ptr1)(unsigned int);"] result = @parser.parse("module", source) assert_equal(expected, result[:functions]) @@ -1818,29 +1865,30 @@ it "extract this SQLite3 function with an anonymous function pointer arg (regression test)" do source = "SQLITE_API int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int n, void(*)(void*))" - expected = [{ :var_arg=>nil, - :return=>{ :type => "int", - :name => "cmock_to_return", - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "int cmock_to_return", - :void? => false - }, - :name=>"sqlite3_bind_text", - :unscoped_name=>"sqlite3_bind_text", - :namespace=>[], - :class=>nil, - :modifier=>"SQLITE_API", - :contains_ptr? => true, - :args=>[ {:type=>"sqlite3_stmt*", :name=>"cmock_arg2", :ptr? => true, :const? => false, :const_ptr? => false}, - {:type=>"int", :name=>"cmock_arg3", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"const char*", :name=>"cmock_arg4", :ptr? => false, :const? => true, :const_ptr? => false}, - {:type=>"int", :name=>"n", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"cmock_module_func_ptr1", :name=>"cmock_arg1", :ptr? => false, :const? => false, :const_ptr? => false} - ], - :args_string=>"sqlite3_stmt* cmock_arg2, int cmock_arg3, const char* cmock_arg4, int n, cmock_module_func_ptr1 cmock_arg1", - :args_call=>"cmock_arg2, cmock_arg3, cmock_arg4, n, cmock_arg1" }] + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "int", + :name => "cmock_to_return", + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :name => "sqlite3_bind_text", + :unscoped_name => "sqlite3_bind_text", + :namespace => [], + :class => nil, + :modifier => "SQLITE_API", + :contains_ptr? => true, + :args => [ {:type => "sqlite3_stmt*", :name => "cmock_arg2", :ptr? => true, :const? => false, :const_ptr? => false}, + {:type => "int", :name => "cmock_arg3", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "const char*", :name => "cmock_arg4", :ptr? => false, :const? => true, :const_ptr? => false}, + {:type => "int", :name => "n", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "cmock_module_func_ptr1", :name => "cmock_arg1", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "sqlite3_stmt* cmock_arg2, int cmock_arg3, const char* cmock_arg4, int n, cmock_module_func_ptr1 cmock_arg1", + :args_call => "cmock_arg2, cmock_arg3, cmock_arg4, n, cmock_arg1" }] typedefs = ["typedef void(*cmock_module_func_ptr1)(void*);"] result = @parser.parse("module", source) assert_equal(expected, result[:functions]) @@ -1849,138 +1897,143 @@ it "extract functions with varargs" do source = "int XFiles(int Scully, int Mulder, ...);\n" - expected = [{ :var_arg=>"...", - :return=> { :type => "int", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "int cmock_to_return", - :void? => false - }, - :name=>"XFiles", - :unscoped_name=>"XFiles", - :namespace=>[], - :class=>nil, - :modifier=>"", + expected = [{ :var_arg => "...", + :noreturn => false, + :return => { :type => "int", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :name => "XFiles", + :unscoped_name => "XFiles", + :namespace => [], + :class => nil, + :modifier => "", :contains_ptr? => false, - :args=>[ {:type=>"int", :name=>"Scully", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"int", :name=>"Mulder", :ptr? => false, :const? => false, :const_ptr? => false} - ], - :args_string=>"int Scully, int Mulder", - :args_call=>"Scully, Mulder" - }] + :args => [ {:type => "int", :name => "Scully", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "int", :name => "Mulder", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "int Scully, int Mulder", + :args_call => "Scully, Mulder" + }] assert_equal(expected, @parser.parse("module", source)[:functions]) end it "extract functions with void pointers" do source = "void* MoreSillySongs(void* stuff);\n" - expected = [{ :var_arg=>nil, - :return=> { :type => "void*", - :name => 'cmock_to_return', - :ptr? => true, - :const? => false, - :const_ptr? => false, - :str => "void* cmock_to_return", - :void? => false - }, - :name=>"MoreSillySongs", - :unscoped_name=>"MoreSillySongs", - :namespace=>[], - :class=>nil, - :modifier=>"", + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "void*", + :name => 'cmock_to_return', + :ptr? => true, + :const? => false, + :const_ptr? => false, + :str => "void* cmock_to_return", + :void? => false + }, + :name => "MoreSillySongs", + :unscoped_name => "MoreSillySongs", + :namespace => [], + :class => nil, + :modifier => "", :contains_ptr? => true, - :args=>[ {:type=>"void*", :name=>"stuff", :ptr? => true, :const? => false, :const_ptr? => false} - ], - :args_string=>"void* stuff", - :args_call=>"stuff" - }] + :args => [ {:type => "void*", :name => "stuff", :ptr? => true, :const? => false, :const_ptr? => false} + ], + :args_string => "void* stuff", + :args_call => "stuff" + }] assert_equal(expected, @parser.parse("module", source)[:functions]) end it "extract functions with strippable confusing junk like gcc attributes" do source = "int LaverneAndShirley(int Lenny, int Squiggy) __attribute__((weak)) __attribute__ ((deprecated));\n" - expected = [{ :var_arg=>nil, - :return=> { :type => "int", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "int cmock_to_return", - :void? => false - }, - :name=>"LaverneAndShirley", - :unscoped_name=>"LaverneAndShirley", - :namespace=>[], - :class=>nil, - :modifier=>"", + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "int", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :name => "LaverneAndShirley", + :unscoped_name => "LaverneAndShirley", + :namespace => [], + :class => nil, + :modifier => "", :contains_ptr? => false, - :args=>[ {:type=>"int", :name=>"Lenny", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"int", :name=>"Squiggy", :ptr? => false, :const? => false, :const_ptr? => false} - ], - :args_string=>"int Lenny, int Squiggy", - :args_call=>"Lenny, Squiggy" - }] + :args => [ {:type => "int", :name => "Lenny", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "int", :name => "Squiggy", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "int Lenny, int Squiggy", + :args_call => "Lenny, Squiggy" + }] assert_equal(expected, @parser.parse("module", source)[:functions]) end it "extract functions with strippable confusing junk like gcc attributes with parenthesis" do source = "int TheCosbyShow(int Cliff, int Claire) __attribute__((weak, alias (\"__f\"));\n" - expected = [{ :var_arg=>nil, - :return=> { :type => "int", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "int cmock_to_return", - :void? => false - }, - :name=>"TheCosbyShow", - :unscoped_name=>"TheCosbyShow", - :namespace=>[], - :class=>nil, - :modifier=>"", + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "int", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :name => "TheCosbyShow", + :unscoped_name => "TheCosbyShow", + :namespace => [], + :class => nil, + :modifier => "", :contains_ptr? => false, - :args=>[ {:type=>"int", :name=>"Cliff", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"int", :name=>"Claire", :ptr? => false, :const? => false, :const_ptr? => false} - ], - :args_string=>"int Cliff, int Claire", - :args_call=>"Cliff, Claire" - }] + :args => [ {:type => "int", :name => "Cliff", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "int", :name => "Claire", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "int Cliff, int Claire", + :args_call => "Cliff, Claire" + }] assert_equal(expected, @parser.parse("module", source)[:functions]) end it "divines all permutations of ptr, const, and const_ptr correctly" do truth_table = [ # argument ptr const const_ptr - [ "constNOTconst constNOTconst", false, false, false ], - [ "const constNOTconst constNOTconst", false, true, false ], - [ "constNOTconst const constNOTconst", false, true, false ], - [ "constNOTconst *constNOTconst", true, false, false ], + [ "const constNOTconst *const *constNOTconst", true, true, false ], [ "const constNOTconst *constNOTconst", true, true, false ], + [ "const constNOTconst constNOTconst", false, true, false ], + [ "constNOTconst *const *constNOTconst", true, true, false ], + [ "constNOTconst const *const *constNOTconst", true, true, false ], [ "constNOTconst const *constNOTconst", true, true, false ], - [ "constNOTconst *const constNOTconst", true, false, true ], + [ "constNOTconst const constNOTconst", false, true, false ], + [ "constNOTconst const *const *const constNOTconst", true, true, true ], + [ "const constNOTconst *const *const constNOTconst", true, true, true ], [ "const constNOTconst *const constNOTconst", true, true, true ], + [ "constNOTconst *const *const constNOTconst", true, true, true ], [ "constNOTconst const *const constNOTconst", true, true, true ], - [ "constNOTconst **constNOTconst", true, false, false ], [ "const constNOTconst **constNOTconst", true, false, false ], + [ "constNOTconst **constNOTconst", true, false, false ], + [ "constNOTconst *constNOTconst", true, false, false ], [ "constNOTconst const **constNOTconst", true, false, false ], - [ "constNOTconst *const *constNOTconst", true, true, false ], - [ "const constNOTconst *const *constNOTconst", true, true, false ], - [ "constNOTconst const *const *constNOTconst", true, true, false ], - [ "constNOTconst **const constNOTconst", true, false, true ], + [ "constNOTconst constNOTconst", false, false, false ], [ "const constNOTconst **const constNOTconst", true, false, true ], + [ "constNOTconst **const constNOTconst", true, false, true ], + [ "constNOTconst *const constNOTconst", true, false, true ], [ "constNOTconst const **const constNOTconst", true, false, true ], - [ "constNOTconst *const *const constNOTconst", true, true, true ], - [ "const constNOTconst *const *const constNOTconst", true, true, true ], - [ "constNOTconst const *const *const constNOTconst", true, true, true ] ] truth_table.each do |entry| - assert_equal(@parser.divine_ptr(entry[0]), entry[1]) - assert_equal(@parser.divine_const(entry[0]), entry[2]) - assert_equal(@parser.divine_ptr_and_const(entry[0]), - { ptr?: entry[1], const?: entry[2], const_ptr?: entry[3] }) + lexer = CLexer.new(entry[0]) + tokens = lexer.tokenize + + assert_equal(@parser.guess_ptr_and_const(tokens), + { ptr?: entry[1], const?: entry[2], const_ptr?: entry[3] }) end end @@ -2011,7 +2064,14 @@ ] truth_table.each do |entry| - assert_equal(@parser.divine_ptr(entry[0]), entry[1]) + lexer = CLexer.new(entry[0]) + tokens = lexer.tokenize + if @parser.guess_ptr_and_const(tokens)[:ptr?] != entry[1] + puts "source = #{entry[0].inspect}" + puts "expected = #{entry[1].inspect}" + puts "got @parser.guess_ptr_and_const(tokens)[:ptr?] = #{@parser.guess_ptr_and_const(tokens)[:ptr?]}" + end + assert_equal(@parser.guess_ptr_and_const(tokens)[:ptr?], entry[1]) end end @@ -2245,34 +2305,34 @@ "#endif _NOINCLUDES\n" expected = - "#ifndef _NOINCLUDES\n" + - "#define _NOINCLUDES\n" + - "#include \"unity.h\"\n" + - "#include \"cmock.h\"\n" + - "#include \"YetAnotherHeader.h\"\n" + - "\n" + - "\n" + #The comments are now removed - "#if defined(__GNUC__) && !defined(__ICC) && !defined(__TMS470__)\n" + - "#if __GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ > 6 || (__GNUC_MINOR__ == 6 && __GNUC_PATCHLEVEL__ > 0)))\n" + - "#pragma GCC diagnostic push\n" + - "#endif\n" + - "#if !defined(__clang__)\n" + - "#pragma GCC diagnostic ignored \"-Wpragmas\"\n" + - "#endif\n" + - "#pragma GCC diagnostic ignored \"-Wunknown-pragmas\"\n" + - "#pragma GCC diagnostic ignored \"-Wduplicate-decl-specifier\"\n" + - "#endif\n" + - "\n" + - "int my_function(int a);\n" + - "int staticinlinefunc(struct my_struct *s);\n" + - "static const int my_variable = 5;\n" + - "struct my_struct {\n" + - "int a;\n" + - "int b;\n" + - "int b;\n" + - "char c;\n" + - "};\n" + - "#endif _NOINCLUDES\n" + "#ifndef _NOINCLUDES\n" + + "#define _NOINCLUDES\n" + + "#include \"unity.h\"\n" + + "#include \"cmock.h\"\n" + + "#include \"YetAnotherHeader.h\"\n" + + "\n" + + "\n" + #The comments are now removed + "#if defined(__GNUC__) && !defined(__ICC) && !defined(__TMS470__)\n" + + "#if __GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ > 6 || (__GNUC_MINOR__ == 6 && __GNUC_PATCHLEVEL__ > 0)))\n" + + "#pragma GCC diagnostic push\n" + + "#endif\n" + + "#if !defined(__clang__)\n" + + "#pragma GCC diagnostic ignored \"-Wpragmas\"\n" + + "#endif\n" + + "#pragma GCC diagnostic ignored \"-Wunknown-pragmas\"\n" + + "#pragma GCC diagnostic ignored \"-Wduplicate-decl-specifier\"\n" + + "#endif\n" + + "\n" + + "int my_function(int a);\n" + + "int staticinlinefunc(struct my_struct *s);\n" + + "static const int my_variable = 5;\n" + + "struct my_struct {\n" + + "int a;\n" + + "int b;\n" + + "int b;\n" + + "char c;\n" + + "};\n" + + "#endif _NOINCLUDES\n" assert_equal(expected, @parser.transform_inline_functions(source)) end @@ -2332,66 +2392,67 @@ it "handles parsing multiline functions" do source = "int\nLaverneAndShirley(int Lenny,\n int Squiggy);\n" - expected = [{ :var_arg=>nil, - :return=> { :type => "int", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "int cmock_to_return", - :void? => false - }, - :name=>"LaverneAndShirley", - :unscoped_name=>"LaverneAndShirley", - :namespace=>[], - :class=>nil, - :modifier=>"", + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "int", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :name => "LaverneAndShirley", + :unscoped_name => "LaverneAndShirley", + :namespace => [], + :class => nil, + :modifier => "", :contains_ptr? => false, - :args=>[ {:type=>"int", :name=>"Lenny", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"int", :name=>"Squiggy", :ptr? => false, :const? => false, :const_ptr? => false} - ], - :args_string=>"int Lenny, int Squiggy", - :args_call=>"Lenny, Squiggy" - }] + :args => [ {:type => "int", :name => "Lenny", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "int", :name => "Squiggy", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "int Lenny, int Squiggy", + :args_call => "Lenny, Squiggy" + }] assert_equal(expected, @parser.parse("module", source)[:functions]) end it "imports C++ differently when asked" do source = - [ - "namespace ns1 {\n", - " namespace ns2 {\n", - "\n", - " class cls1 {\n", - " public:\n", - " int f_header_impl(int a, int b){\n", - " return a + b;\n", - " }\n", - "\n", - " static void f_void();\n", - " static int f_ret_simple();\n", - "\n", - " protected:\n", - " static void protected_f_void();\n", - "\n", - " public:\n", - " private:\n", - " static void private_f_void();\n", - " }; // cls1\n", - " } // ns2\n", - "} // ns1\n" - ].join + [ + "namespace ns1 {\n", + " namespace ns2 {\n", + "\n", + " class cls1 {\n", + " public:\n", + " int f_header_impl(int a, int b){\n", + " return a + b;\n", + " }\n", + "\n", + " static void f_void();\n", + " static int f_ret_simple();\n", + "\n", + " protected:\n", + " static void protected_f_void();\n", + "\n", + " public:\n", + " private:\n", + " static void private_f_void();\n", + " }; // cls1\n", + " } // ns2\n", + "} // ns1\n" + ].join expected = - [ - "namespace ns1 { namespace ns2 { class cls1 { public: int f_header_impl", - "static void f_void()", - "static int f_ret_simple()", - "protected: static void protected_f_void()", - "public: private: static void private_f_void()", - "}", - "} }" - ] + [ + "namespace ns1 { namespace ns2 { class cls1 { public: int f_header_impl", + "static void f_void()", + "static int f_ret_simple()", + "protected: static void protected_f_void()", + "public: private: static void private_f_void()", + "}", + "} }" + ] assert_equal(expected, @parser.import_source(source, @test_project, cpp=true)) refute_equal(expected, @parser.import_source(source, @test_project)) @@ -2414,6 +2475,7 @@ def dummy_func :args_call => "", :contains_ptr? => false, :modifier => "", + :noreturn => false, :return => { :type => "void", :name => "cmock_to_return", @@ -2436,10 +2498,11 @@ def voidvoid_func(namespace=[], name="Classic_functional") :args_call => "", :contains_ptr? => false, :modifier => "", + :noreturn => false, :return => { - :type=>"void", - :name=>"cmock_to_return", - :str=>"void cmock_to_return", + :type => "void", + :name => "cmock_to_return", + :str => "void cmock_to_return", :void? => true, :ptr? => false, :const? => false, @@ -2470,7 +2533,7 @@ class Classic { SOURCE expected = [dummy_func, - voidvoid_func(namespace=["ns1"], name="ns1_Classic_functional")] + voidvoid_func(namespace=["ns1"], name="ns1_Classic_functional")] assert_equal(expected, @parser.parse("module", source)[:functions]) end @@ -2488,7 +2551,7 @@ class Classic { SOURCE expected = [dummy_func, - voidvoid_func(namespace=["ns1", "ns2"], name="ns1_ns2_Classic_functional")] + voidvoid_func(namespace=["ns1", "ns2"], name="ns1_ns2_Classic_functional")] assert_equal(expected, @parser.parse("module", source)[:functions]) end @@ -2583,53 +2646,55 @@ class Classy { SOURCE expected = [dummy_func, - voidvoid_func(["ns1"], name="ns1_Classic_functional"), - { :name => "ns1_Classical_functionality", - :unscoped_name => "functionality", - :class => "Classical", - :namespace => ["ns1"], - :var_arg => nil, - :args_string => "int a", - :args => [ - { :ptr? => false, - :const? => false, - :const_ptr? => false, - :name => "a", - :type => "int"}], - :args_call => "a", - :contains_ptr? => false, - :modifier => "", - :return => { - :type=>"int", - :name=>"cmock_to_return", - :str=>"int cmock_to_return", - :void? => false, - :ptr? => false, - :const? => false, - :const_ptr? => false}}, - { :name => "Classy_func", - :unscoped_name => "func", - :class => "Classy", - :namespace => [], - :var_arg => nil, - :args_string => "int* a", - :args => [ - { :ptr? => true, - :const? => false, - :const_ptr? => false, - :name => "a", - :type => "int*"}], - :args_call => "a", - :contains_ptr? => true, - :modifier => "", - :return => { - :type=>"int*", - :name=>"cmock_to_return", - :str=>"int* cmock_to_return", - :void? => false, - :ptr? => true, - :const? => false, - :const_ptr? => false}}] + voidvoid_func(["ns1"], name="ns1_Classic_functional"), + { :name => "ns1_Classical_functionality", + :unscoped_name => "functionality", + :class => "Classical", + :namespace => ["ns1"], + :var_arg => nil, + :args_string => "int a", + :args => [ + { :ptr? => false, + :const? => false, + :const_ptr? => false, + :name => "a", + :type => "int"}], + :args_call => "a", + :contains_ptr? => false, + :modifier => "", + :noreturn => false, + :return => { + :type => "int", + :name => "cmock_to_return", + :str => "int cmock_to_return", + :void? => false, + :ptr? => false, + :const? => false, + :const_ptr? => false}}, + { :name => "Classy_func", + :unscoped_name => "func", + :class => "Classy", + :namespace => [], + :var_arg => nil, + :args_string => "int* a", + :args => [ + { :ptr? => true, + :const? => false, + :const_ptr? => false, + :name => "a", + :type => "int*"}], + :args_call => "a", + :contains_ptr? => true, + :modifier => "", + :noreturn => false, + :return => { + :type => "int*", + :name => "cmock_to_return", + :str => "int* cmock_to_return", + :void? => false, + :ptr? => true, + :const? => false, + :const_ptr? => false}}] assert_equal(expected, @parser.parse("module", source)[:functions]) end @@ -2655,53 +2720,55 @@ class Classy { SOURCE expected = [dummy_func, - voidvoid_func(["ns1"], name="ns1_Classic_functional"), - { :name => "ns1_Classical_functional", - :unscoped_name => "functional", - :class => "Classical", - :namespace => ["ns1"], - :var_arg => nil, - :args_string => "int a", - :args => [ - { :ptr? => false, - :const? => false, - :const_ptr? => false, - :name => "a", - :type => "int"}], - :args_call => "a", - :contains_ptr? => false, - :modifier => "", - :return => { - :type=>"int", - :name=>"cmock_to_return", - :str=>"int cmock_to_return", - :void? => false, - :ptr? => false, - :const? => false, - :const_ptr? => false}}, - { :name => "Classy_functional", - :unscoped_name => "functional", - :class => "Classy", - :namespace => [], - :var_arg => nil, - :args_string => "int* a", - :args => [ - { :ptr? => true, - :const? => false, - :const_ptr? => false, - :name => "a", - :type => "int*"}], - :args_call => "a", - :contains_ptr? => true, - :modifier => "", - :return => { - :type=>"int*", - :name=>"cmock_to_return", - :str=>"int* cmock_to_return", - :void? => false, - :ptr? => true, - :const? => false, - :const_ptr? => false}}] + voidvoid_func(["ns1"], name="ns1_Classic_functional"), + { :name => "ns1_Classical_functional", + :unscoped_name => "functional", + :class => "Classical", + :namespace => ["ns1"], + :var_arg => nil, + :args_string => "int a", + :args => [ + { :ptr? => false, + :const? => false, + :const_ptr? => false, + :name => "a", + :type => "int"}], + :args_call => "a", + :contains_ptr? => false, + :modifier => "", + :noreturn => false, + :return => { + :type => "int", + :name => "cmock_to_return", + :str => "int cmock_to_return", + :void? => false, + :ptr? => false, + :const? => false, + :const_ptr? => false}}, + { :name => "Classy_functional", + :unscoped_name => "functional", + :class => "Classy", + :namespace => [], + :var_arg => nil, + :args_string => "int* a", + :args => [ + { :ptr? => true, + :const? => false, + :const_ptr? => false, + :name => "a", + :type => "int*"}], + :args_call => "a", + :contains_ptr? => true, + :modifier => "", + :noreturn => false, + :return => { + :type => "int*", + :name => "cmock_to_return", + :str => "int* cmock_to_return", + :void? => false, + :ptr? => true, + :const? => false, + :const_ptr? => false}}] assert_equal(expected, @parser.parse("module", source)[:functions]) end @@ -2882,4 +2949,323 @@ class Classy { end + # // PJB // REMOVE BEFORE COMMIT // + + it "builds parser on sound bases" do + + assert(@parser.is_parens([:parens,[[:identifier,"a"]]]),"is_parens identifies parens") + refute(@parser.is_parens([:brackets,[[:identifier,"a"]]]),"is_parens rejects brackets") + refute(@parser.is_parens([:identifier,"Foo"]),"is_parens rejects identifier") + assert_equal([[:identifier,"a"],[:integer_literal,"42"]], + @parser.parens_list([:parens,[[:identifier,"a"],[:integer_literal,"42"]]]), + "parens_list returns list of elements in parens") + + assert(@parser.is_brackets([:brackets,[[:identifier,"a"]]]),"is_brackets identifies brackets") + refute(@parser.is_brackets([:parens,[[:identifier,"a"]]]),"is_brackets rejects parens") + refute(@parser.is_brackets([:identifier,"Foo"]),"is_brackets rejects identifier") + assert_equal([[:identifier,"a"],[:integer_literal,"42"]], + @parser.brackets_list([:brackets,[[:identifier,"a"],[:integer_literal,"42"]]]), + "brackets_list returns list of elements in brackets") + + + # assert(@parser.is_braces([:braces,[[:identifier,"a"]]]),"is_braces identifies braces") + # refute(@parser.is_braces([:brackets,[[:identifier,"a"]]]),"is_braces rejects brackets") + # refute(@parser.is_braces([:identifier,"Foo"]),"is_braces rejects identifier") + # assert_equal([[:identifier,"a"],[:integer_literal,"42"]], + # @parser.braces_list([:braces,[[:identifier,"a"],[:integer_literal,"42"]]]), + # "braces_list returns list of elements in braces") + + assert(@parser.is_identifier([:identifier,"Foo"]),"is_identifier identifies identifier") + assert(@parser.is_identifier([:identifier,"Foo"],"Foo"),"is_identifier identifies identifier with name") + refute(@parser.is_identifier([:identifier,"Foo"],"Bar"),"is_identifier rejects identifier with wrong name") + refute(@parser.is_identifier(:bar,"Bar"),"is_identifier rejects non-identifier") + refute(@parser.is_identifier(:bar),"is_identifier rejects non-identifier") + + assert_equal("Foo",@parser.identifier_name([:identifier,"Foo"]),"identifier_name returns name of identifier") + + assert(@parser.is_c_calling_convention([:identifier,"__stdcall"]),"is_c_calling_convention should identify __stdcall") + refute(@parser.is_c_calling_convention([:identifier,"callfoo"]),"is_c_calling_convention should refute callfoo") + assert(@parser.is_c_calling_convention(:__stdcall),"is_c_calling_convention should accept :__stdcall") + + assert(@parser.is_c_attribute(:const),"is_c_attribute should identify :const") + assert(@parser.is_c_attribute([:identifier,"const"]),"is_c_attribute should identify [:identifier, 'const']") + assert(@parser.is_c_attribute([:identifier,"__ramfunc"]),"is_c_attribute should identify [:identifier, '__ramfunc']") + refute(@parser.is_c_attribute([:identifier,"__attribute__"]),"is_c_attribute should refute [:identifier, '__attribute__']") + refute(@parser.is_c_attribute(:conste),"is_c_attribute should refute :constes") + assert(@parser.is_c_attribute([:identifier,"noreturn"]),"is_c_attribute should identify [:identifier, 'noreturn']") + assert(@parser.is_c_attribute(:noreturn),"is_c_attribute should identify :noreturn") + + assert(@parser.is_gcc_attribute_syntax([:identifier,"__attribute__"],[:parens,[[:parens,[[:identifier,"noreturn"]]]]]), + "is_gcc_attribute_syntax identifies parsed __attribute__((noreturn))") + + assert(@parser.is_attribute([:attribute,nil,"noreturn",nil,:gcc]), + "is_attribute identifies [:attribute,nil,\"noreturn\",nil,:gcc]") + + end + + + it "parses stuffs" do + + source ="int hello (int a, int b) {\n" + + " static int table[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };\n" + + " return table[a] + b;\n" + + "}\n" + + expected = [[:int,[:identifier, "hello"], + [:parens, [:int, [:identifier, "a"], :comma, :int, [:identifier, "b"]]], + [:braces, + [:static, :int, [:identifier, "table"], [:brackets, [[:integer_literal, "10"]]], + :assign, [:braces, [[:integer_literal, "1"],:comma, + [:integer_literal, "2"],:comma, + [:integer_literal, "3"],:comma, + [:integer_literal, "4"],:comma, + [:integer_literal, "5"],:comma, + [:integer_literal, "6"],:comma, + [:integer_literal, "7"],:comma, + [:integer_literal, "8"],:comma, + [:integer_literal, "9"]]], :semicolon, + :return, [:identifier, "table"], [:brackets, [[:identifier, "a"]]], :add_op, [:identifier, "b"], :semicolon]]], + 46] + + lexer = CLexer.new(source) + tokens = lexer.tokenize + assert_equal(expected, @parser.parse_stuffs(tokens,0)) + end + + it "parses gcc attributes" do + + assert_equal([[:attribute,nil,"noreturn",nil,:gcc], + [:attribute,nil,"deprecated",nil,:gcc]], + @parser.parse_gcc_attribute([:identifier,"__attribute__"], + [:parens, [[:parens, [[:identifier, "noreturn"], :comma ,[:identifier, "deprecated"]]]]])) + + assert_equal([[:attribute,nil,"noreturn",nil,:gcc], + [:attribute,nil,"deprecated",nil,:gcc]], + @parser.parse_gcc_attribute([:identifier,"__attribute__"], + [:parens, [[:parens, [[:identifier,"noreturn"]]],:comma,[:parens,[[:identifier,"deprecated"]]]]])) + + assert_equal([[:attribute,nil,"access",[:parens,[[:identifier,"read_write"],:comma,[:integer_literal,"1"]]],:gcc], + [:attribute,nil,"access",[:parens,[[:identifier,"read_only"],:comma,[:integer_literal,"2"]]],:gcc]], + @parser.parse_gcc_attribute([:identifier,"__attribute__"], + [:parens, [[:parens, + [[:identifier,"access"],[:parens,[[:identifier,"read_write"], + :comma,[:integer_literal,"1"]]], + :comma,[:identifier,"access"],[:parens,[[:identifier,"read_only"], + :comma,[:integer_literal,"2"]]]]]]])) + end + + + it "parses cpp attributes" do + + assert_equal([[:attribute,nil,"noreturn",nil,:cpp], + [:attribute,nil,"deprecated",nil,:cpp]], + @parser.parse_cpp_attributes([:brackets, [[:brackets, [[:identifier, "noreturn"], :comma ,[:identifier, "deprecated"]]]]])) + + assert_equal([[:attribute,nil,"access",[:parens,[[:identifier,"read_write"],:comma,[:integer_literal,"1"]]],:cpp], + [:attribute,nil,"access",[:parens,[[:identifier,"read_only"],:comma,[:integer_literal,"2"]]],:cpp]], + @parser.parse_cpp_attributes([:brackets, [[:brackets, + [[:identifier,"access"],[:parens,[[:identifier,"read_write"], + :comma,[:integer_literal,"1"]]], + :comma,[:identifier,"access"],[:parens,[[:identifier,"read_only"], + :comma,[:integer_literal,"2"]]]]]]])) + + assert_equal([[:attribute,"rt","access",[:parens,[[:identifier,"read_write"],:comma,[:integer_literal,"1"]]],:cpp], + [:attribute,"rt","access",[:parens,[[:identifier,"read_only"],:comma,[:integer_literal,"2"]]],:cpp]], + @parser.parse_cpp_attributes([:brackets, [[:brackets, + [[:identifier,"using"],[:identifier,"rt"],:colon, + [:identifier,"access"],[:parens,[[:identifier,"read_write"], + :comma,[:integer_literal,"1"]]],:comma, + [:identifier,"access"],[:parens,[[:identifier,"read_only"], + :comma,[:integer_literal,"2"]]]]]]])) + + assert_equal([[:attribute,"rt","access",[:parens,[[:identifier,"read_write"],:comma,[:integer_literal,"1"]]],:cpp], + [:attribute,"rt","access",[:parens,[[:identifier,"read_only"],:comma,[:integer_literal,"2"]]],:cpp]], + @parser.parse_cpp_attributes([:brackets, [[:brackets, + [[:identifier,"rt"],:colon,:colon,[:identifier,"access"],[:parens,[[:identifier,"read_write"], + :comma,[:integer_literal,"1"]]],:comma, + [:identifier,"rt"],:colon,:colon,[:identifier,"access"],[:parens,[[:identifier,"read_only"], + :comma,[:integer_literal,"2"]]]]]]])) + end + + # it "Note functions do not return" do + # + # # """ + # # :cmock + # # :attributes + # # - '_Noreturn' + # # - 'noreturn' + # # - '[[[[noreturn]]]]' + # # - '__attribute__((noreturn))' + # # - '__attribute__((__noreturn__))' + # # """ + # source ="void foo1() __attribute__((noreturn)); /* GNU C & GNU C++ & Clang */\n" + + # "_Noreturn void foo2(); /* C99 */\n" + + # "noreturn void foo3(); /* C11 */\n" + + # "[[noreturn]] void foo4(); /* C++11 */\n" + # + # expected = ["void foo1()", + # "void foo2()", + # "void foo3()", + # "void foo4()"] + # + # assert_equal(expected, @parser.transform_noreturn(source)) + # end + + it "extract and return function declarations with _Noreturn" do + source = "_Noreturn int Foo(int a, unsigned int b)" + expected = { :var_arg => nil, + :name => "Foo", + :unscoped_name => "Foo", + :class => nil, + :namespace => [], + :noreturn => true, + :return => { :type => "int", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :modifier => "_Noreturn", + :contains_ptr? => false, + :args => [ {:type => "int", :name => "a", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "unsigned int", :name => "b", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "int a, unsigned int b", + :args_call => "a, b" } + assert_equal(expected, @parser.parse_declaration(@test_project, source, [], nil)) + end + + it "extract and return function declarations with noreturn" do + source = "noreturn int Foo(int a, unsigned int b)" + expected = { :var_arg => nil, + :name => "Foo", + :unscoped_name => "Foo", + :class => nil, + :namespace => [], + :noreturn => true, + :return => { :type => "int", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :modifier => "noreturn", + :contains_ptr? => false, + :args => [ {:type => "int", :name => "a", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "unsigned int", :name => "b", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "int a, unsigned int b", + :args_call => "a, b" } + assert_equal(expected, @parser.parse_declaration(@test_project, source, [], nil)) + end + + it "extract and return function declarations with [[noreturn]]" do + source = "[[noreturn]] int Foo(int a, unsigned int b)" + expected = { :var_arg => nil, + :name => "Foo", + :unscoped_name => "Foo", + :class => nil, + :namespace => [], + :noreturn => true, + :return => { :type => "int", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :modifier => "[[noreturn]]", + :contains_ptr? => false, + :args => [ {:type => "int", :name => "a", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "unsigned int", :name => "b", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "int a, unsigned int b", + :args_call => "a, b" } + assert_equal(expected, @parser.parse_declaration(@test_project, source, [], nil)) + end + + it "extract and return function declarations with __attribute__((noreturn))" do + source = "int Foo(int a, unsigned int b) __attribute__((noreturn))" + expected = { :var_arg => nil, + :name => "Foo", + :unscoped_name => "Foo", + :class => nil, + :namespace => [], + :noreturn => true, + :return => { :type => "int", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :modifier => "", + :contains_ptr? => false, + :args => [ {:type => "int", :name => "a", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "unsigned int", :name => "b", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "int a, unsigned int b", + :args_call => "a, b" } + assert_equal(expected, @parser.parse_declaration(@test_project, source, [], nil)) + end + + it "extract and return function declarations with __attribute__ ((noreturn))" do + source = "int Foo(int a, unsigned int b) __attribute__ ((noreturn))" + expected = { :var_arg => nil, + :name => "Foo", + :unscoped_name => "Foo", + :class => nil, + :namespace => [], + :noreturn => true, + :return => { :type => "int", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :modifier => "", + :contains_ptr? => false, + :args => [ {:type => "int", :name => "a", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "unsigned int", :name => "b", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "int a, unsigned int b", + :args_call => "a, b" } + assert_equal(expected, @parser.parse_declaration(@test_project, source, [], nil)) + end + + it "extract and return function declarations with __attribute__ ((__noreturn__))" do + source = "int Foo(int a, unsigned int b) __attribute__ ((__noreturn__))" + expected = { :var_arg => nil, + :name => "Foo", + :unscoped_name => "Foo", + :class => nil, + :namespace => [], + :noreturn => true, + :return => { :type => "int", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :modifier => "", + :contains_ptr? => false, + :args => [ {:type => "int", :name => "a", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "unsigned int", :name => "b", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "int a, unsigned int b", + :args_call => "a, b" } + assert_equal(expected, @parser.parse_declaration(@test_project, source, [], nil)) + end + + end From 2e130ef06bb8fdd3a57f2eb8be52450ec01bea64 Mon Sep 17 00:00:00 2001 From: "Pascal J. Bourguignon" Date: Mon, 15 May 2023 17:39:48 +0200 Subject: [PATCH 08/21] Updated the documentation. --- docs/CMock_Summary.md | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/docs/CMock_Summary.md b/docs/CMock_Summary.md index e63a065c..7c039215 100644 --- a/docs/CMock_Summary.md +++ b/docs/CMock_Summary.md @@ -82,6 +82,7 @@ replied with a version that is older than 2.0.0. Go ahead. We'll wait. Once you have Ruby, you have three options: * Clone the latest [CMock repo on github](https://github.com/ThrowTheSwitch/CMock/) + (This includes updating the submodules: `git submodules update`) * Download the latest [CMock zip from github](https://github.com/ThrowTheSwitch/CMock/) * Install Ceedling (which has it built in!) through your commandline using `gem install ceedling`. @@ -135,7 +136,7 @@ that resembles a pointer or array, it breaks the argument into TWO arguments. The first is the original pointer. The second specify the number of elements it is to verify of that array. If you specify 1, it'll check one object. If 2, it'll assume your pointer is pointing at the first of two elements in an array. -If you specify zero elements and `UNITY_COMPARE_PTRS_ON_ZERO_ARRAY` is defined, +If you specify zero elements and `UNITY_COMPARE_PTRS_ON_ZERO_ARRAY` is defined, then this assertion can also be used to directly compare the pointers to verify that they are pointing to the same memory address. @@ -385,6 +386,38 @@ from the defaults. We've tried to specify what the defaults are below. * **note:** this option will reinsert these attributes onto the mock's calls. If that isn't what you are looking for, check out :strippables. +* `:process_cpp_attributes`: + Allows the parsing and processing of C++ attribute syntax `[[...]]`. + + * defaults: false + +* `:process_gcc_attributes`: + Allows the parsing and processing of GNU gcc attribute syntax + `__attribute__ ((...))`. + When setting it to true, mind removing `__attribute__` from the + `:strippables`` option. + Those attributes are matched both before and after the function name. + + * defaults: false + +* `:noreturn_attributes`: + To allow processing of noreturn attributes, list in + `:noreturn_attributes` the keywords used as noreturn attributes. + (Since such attributes may hide behind macros, you may need to list + the macros too). +> :noreturn_attributes => ['_Noreturn', 'noreturn', '__noreturn__'] + Note: when a keyword is listed in this option, it's available for + both the C syntax (keyword standing alone), the C++ syntax (keyword + in a `[[...]]` syntax, and the GNU gcc syntax (keyword in a + `__attribute__((...))` syntax) + When a noreturn attribute is matched, the returned function + dictionary will contain a :noreturn => true` entry, and the cmock + code generator will then avoid returning from the mocked function, + but instead will use the `TEST_DO_NOT_RETURN()` macro. + + * defaults: [] + + * `:c_calling_conventions`: Similarly, CMock may need to understand which C calling conventions might show up in your codebase. If it encounters something it doesn't From f768138983543f0f1a2e57ed4f37c795be9a2092 Mon Sep 17 00:00:00 2001 From: "Pascal J. Bourguignon" Date: Tue, 16 May 2023 14:02:18 +0200 Subject: [PATCH 09/21] Added noreturn integration test. --- test/system/test_compilation/config.yml | 4 ++++ test/system/test_compilation/noreturn.h | 9 +++++++++ 2 files changed, 13 insertions(+) create mode 100644 test/system/test_compilation/noreturn.h diff --git a/test/system/test_compilation/config.yml b/test/system/test_compilation/config.yml index 787e2e1f..7d5fb86b 100644 --- a/test/system/test_compilation/config.yml +++ b/test/system/test_compilation/config.yml @@ -8,3 +8,7 @@ :treat_as_void: - OSEK_TASK - VOID_TYPE_CRAZINESS + :strippables: [] + :process_gcc_attributes: true + :noreturn_attributes: ['_Noreturn','noreturn'] + diff --git a/test/system/test_compilation/noreturn.h b/test/system/test_compilation/noreturn.h new file mode 100644 index 00000000..f5ab6893 --- /dev/null +++ b/test/system/test_compilation/noreturn.h @@ -0,0 +1,9 @@ +#ifndef noreturn_h +#define noreturn_h +/* #include */ + +_Noreturn void myexec1(const char* program); +void __attribute__((noreturn)) myexec2(const char* program); +void myexec3(const char* program) __attribute__((noreturn)); + +#endif From 570f2a1eb9f1f7794077c21be03f6d68d64e16b0 Mon Sep 17 00:00:00 2001 From: "Pascal J. Bourguignon" Date: Tue, 16 May 2023 14:02:30 +0200 Subject: [PATCH 10/21] Corrections for noreturn integration test. --- lib/cmock_config.rb | 5 +--- lib/cmock_generator.rb | 6 +---- lib/cmock_header_parser.rb | 35 +++++++++++++++------------ test/unit/cmock_header_parser_test.rb | 8 +++--- 4 files changed, 26 insertions(+), 28 deletions(-) diff --git a/lib/cmock_config.rb b/lib/cmock_config.rb index c925420d..7c5891be 100644 --- a/lib/cmock_config.rb +++ b/lib/cmock_config.rb @@ -18,11 +18,8 @@ class CMockConfig :strippables => ['(?:__attribute__\s*\([ (]*.*?[ )]*\)+)'], :process_gcc_attributes => false, # __attribute__((...)) ; also remove it from strippables. :process_cpp_attributes => false, # [[ ... ]] + :noreturn_attributes => [], # simple keyword, before the function name :attributes => %w[__ramfunc __irq __fiq register extern], - :c_noreturn_attributes => [], # simple keyword, before the function name - :gcc_noreturn_attributes => [], # put 'noreturn' for __attribute__((noreturn)), - # # before or after the function name, - # # also remove it from :strippables :c_calling_conventions => %w[__stdcall __cdecl __fastcall], :enforce_strict_ordering => false, :fail_on_unexpected_calls => true, diff --git a/lib/cmock_generator.rb b/lib/cmock_generator.rb index 142b2b25..fd900c43 100644 --- a/lib/cmock_generator.rb +++ b/lib/cmock_generator.rb @@ -90,11 +90,7 @@ def create_skeleton(module_name, parsed_stuff) private if $ThisIsOnlyATest.nil? ############################## def is_noreturn?(function) - if function[:attributes].nil? - return nil; - else - return function[:attributes].include?('noreturn'); - end + function[:noreturn] end def generate_return(function) diff --git a/lib/cmock_header_parser.rb b/lib/cmock_header_parser.rb index 0f560637..55ed1fa5 100644 --- a/lib/cmock_header_parser.rb +++ b/lib/cmock_header_parser.rb @@ -9,15 +9,15 @@ class CMockHeaderParser attr_accessor :funcs, :c_attr_noconst, :c_attributes, :treat_as_void, :treat_externs, :treat_inlines, :inline_function_patterns - attr_reader :c_noreturn_attributes, :c_calling_conventions + attr_reader :noreturn_attributes, :process_gcc_attributes, :process_cpp_attributes, :c_calling_conventions def initialize(cfg) @c_strippables = cfg.strippables @process_gcc_attributes = cfg.process_gcc_attributes @process_cpp_attributes = cfg.process_cpp_attributes + @noreturn_attributes = cfg.noreturn_attributes.uniq @c_attr_noconst = cfg.attributes.uniq - ['const'] @c_attributes = ['const'] + @c_attr_noconst - @c_noreturn_attributes = cfg.c_noreturn_attributes.uniq @c_calling_conventions = cfg.c_calling_conventions.uniq @treat_as_array = cfg.treat_as_array @treat_as_void = (['void'] + cfg.treat_as_void).uniq @@ -256,7 +256,7 @@ def import_source(source, parse_project, cpp = false) # remove assembler pragma sections source.gsub!(/^\s*#\s*pragma\s+asm\s+.*?#\s*pragma\s+endasm/m, '') - if @c_noreturn_attributes.nil? + if @noreturn_attributes.nil? # remove gcc's __attribute__ tags source.gsub!(/__attribute(?:__)?\s*\(\(+.*\)\)+/, '') end @@ -559,7 +559,7 @@ def is_c_calling_convention(stuff) end def is_c_attribute(token) - # whether the token is a C attribute (listed in @c_attributes or in @c_noreturn_attributes). + # whether the token is a C attribute (listed in @c_attributes or in @noreturn_attributes). # note: token may be either a symbol, a string or an :identifier array. if token.is_a?(String) name = token @@ -574,8 +574,8 @@ def is_c_attribute(token) end res = (@c_attributes.index(name) or - ((not @c_noreturn_attributes.nil?) and - (@c_noreturn_attributes.any? { |attr_regexp| name =~ /^#{attr_regexp}$/ }))) + ((not @noreturn_attributes.nil?) and + (@noreturn_attributes.any? { |attr_regexp| name =~ /^#{attr_regexp}$/ }))) res end @@ -620,9 +620,9 @@ def attribute_kind(attribute) def is_noreturn(attribute) if is_identifier(attribute) - @c_noreturn_attributes.include?(identifier_name(attribute)) + @noreturn_attributes.include?(identifier_name(attribute)) elsif is_attribute(attribute) - @c_noreturn_attributes.include?(attribute_qualified_name(attribute)) + @noreturn_attributes.include?(attribute_qualified_name(attribute)) else false end @@ -925,9 +925,8 @@ def parse_type(stuff) type.delete_at(cindex) unless cindex.nil? end - arg_info[:modifier] = unparse(attributes.uniq) arg_info[:type] = unparse(type).gsub(/\s+\*/, '*') # remove space before asterisks - arg_info[:noreturn] = has_noreturn_attribute(attributes) + arg_info[:modifier] = unparse(attributes.uniq) arg_info[:c_calling_convention] = unparse(ccc) [type, attributes, ccc, arg_info] @@ -1255,21 +1254,27 @@ def parse_declaration(parse_project, declaration, namespace = [], classname = ni decl[:name] << '_' unless decl[:name].empty? decl[:name] << decl[:unscoped_name] - decl[:modifier] = parsed[:modifier] + decl[:noreturn] = has_noreturn_attribute(attributes) + if decl[:noreturn] + attributes.delete_if{|attribute| is_noreturn(attribute)} + end + if parsed[:ptr?] if parsed[:const?] - type = [:const] + type + type = [:const] + type unless type[0]==:const attributes.delete_if{|attr| attribute_name(attr) == "const"} - decl[:modifier] = unparse(attributes) end end + # TODO: perhaps we need a specific configuration, or use strippable to select the __attributes__ to remove. + attributes.delete_if{|attribute| is_attribute(attribute) and (attribute_kind(attribute) == :gcc)} + + decl[:modifier] = unparse(attributes.uniq) + if not (parsed[:c_calling_convention].nil? or parsed[:c_calling_convention].empty?) decl[:c_calling_convention] = parsed[:c_calling_convention] end - decl[:noreturn] = has_noreturn_attribute(attributes) - rettype = unparse(type).gsub(/\s+\*/,'*') rettype = 'void' if @local_as_void.include?(rettype.strip) decl[:return] = { :type => rettype, diff --git a/test/unit/cmock_header_parser_test.rb b/test/unit/cmock_header_parser_test.rb index 27f710f1..57ad5be1 100644 --- a/test/unit/cmock_header_parser_test.rb +++ b/test/unit/cmock_header_parser_test.rb @@ -18,9 +18,9 @@ @config.expect :strippables, ["STRIPPABLE"] @config.expect :attributes, ['__ramfunc', 'funky_attrib', 'SQLITE_API','access','rt::access','deprecated'] @config.expect :c_calling_conventions, ['__stdcall'] - @config.expect :c_noreturn_attributes,['_Noreturn', 'noreturn', '__noreturn__'] @config.expect :process_gcc_attributes, true @config.expect :process_cpp_attributes, true + @config.expect :noreturn_attributes,['_Noreturn', 'noreturn', '__noreturn__'] @config.expect :treat_as_void, ['MY_FUNKY_VOID'] @config.expect :treat_as, { "BANJOS" => "INT", "TUBAS" => "HEX16"} @config.expect :treat_as_array, {"IntArray" => "int", "Book" => "Page"} @@ -3127,7 +3127,7 @@ class Classy { :str => "int cmock_to_return", :void? => false }, - :modifier => "_Noreturn", + :modifier => "", :contains_ptr? => false, :args => [ {:type => "int", :name => "a", :ptr? => false, :const? => false, :const_ptr? => false}, {:type => "unsigned int", :name => "b", :ptr? => false, :const? => false, :const_ptr? => false} @@ -3153,7 +3153,7 @@ class Classy { :str => "int cmock_to_return", :void? => false }, - :modifier => "noreturn", + :modifier => "", :contains_ptr? => false, :args => [ {:type => "int", :name => "a", :ptr? => false, :const? => false, :const_ptr? => false}, {:type => "unsigned int", :name => "b", :ptr? => false, :const? => false, :const_ptr? => false} @@ -3179,7 +3179,7 @@ class Classy { :str => "int cmock_to_return", :void? => false }, - :modifier => "[[noreturn]]", + :modifier => "", :contains_ptr? => false, :args => [ {:type => "int", :name => "a", :ptr? => false, :const? => false, :const_ptr? => false}, {:type => "unsigned int", :name => "b", :ptr? => false, :const? => false, :const_ptr? => false} From 4ee5b823ad582407118d0752d4e20031e1263b56 Mon Sep 17 00:00:00 2001 From: "Pascal J. Bourguignon" Date: Thu, 25 May 2023 11:46:35 +0200 Subject: [PATCH 11/21] Added callback_kind to use * or ^ or other for callback types. --- lib/cmock_config.rb | 1 + lib/cmock_generator_plugin_callback.rb | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/cmock_config.rb b/lib/cmock_config.rb index 7c5891be..aec5a8e0 100644 --- a/lib/cmock_config.rb +++ b/lib/cmock_config.rb @@ -35,6 +35,7 @@ class CMockConfig :treat_inlines => :exclude, # the options being :include or :exclude :callback_include_count => true, :callback_after_arg_check => false, + :callback_kind => "*", :includes => nil, :includes_h_pre_orig_header => nil, :includes_h_post_orig_header => nil, diff --git a/lib/cmock_generator_plugin_callback.rb b/lib/cmock_generator_plugin_callback.rb index 6ba8e9bd..781155c5 100644 --- a/lib/cmock_generator_plugin_callback.rb +++ b/lib/cmock_generator_plugin_callback.rb @@ -27,10 +27,11 @@ def instance_structure(function) def mock_function_declarations(function) func_name = function[:name] return_type = function[:return][:type] + kind = @config.callback_kind.nil? ? '*' : @config.callback_kind action = @config.callback_after_arg_check ? 'AddCallback' : 'Stub' style = (@include_count ? 1 : 0) | (function[:args].empty? ? 0 : 2) styles = ['void', 'int cmock_num_calls', function[:args_string], "#{function[:args_string]}, int cmock_num_calls"] - "typedef #{return_type} (* CMOCK_#{func_name}_CALLBACK)(#{styles[style]});\n" \ + "typedef #{return_type} (#{kind} CMOCK_#{func_name}_CALLBACK)(#{styles[style]});\n" \ "void #{func_name}_AddCallback(CMOCK_#{func_name}_CALLBACK Callback);\n" \ "void #{func_name}_Stub(CMOCK_#{func_name}_CALLBACK Callback);\n" \ "#define #{func_name}_StubWithCallback #{func_name}_#{action}\n" From b19ff0356d1cd3dc81c3aa8829a60092a1f6afcc Mon Sep 17 00:00:00 2001 From: Pascal Bourguignon Date: Mon, 26 Jun 2023 14:06:34 +0200 Subject: [PATCH 12/21] Added comment mentionning path of source file to generated files. Related to: #783 cmock of the wrong file is generated. --- lib/cmock.rb | 2 +- lib/cmock_generator.rb | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/cmock.rb b/lib/cmock.rb index 72f86418..a8a0105c 100644 --- a/lib/cmock.rb +++ b/lib/cmock.rb @@ -45,7 +45,7 @@ def generate_mock(src, folder) name = File.basename(src, '.*') ext = File.extname(src) puts "Creating mock for #{name}..." unless @silent - @cm_generator.create_mock(name, @cm_parser.parse(name, File.read(src)), ext, folder) + @cm_generator.create_mock(name, @cm_parser.parse(name, File.read(src)), ext, folder,src) end def generate_skeleton(src) diff --git a/lib/cmock_generator.rb b/lib/cmock_generator.rb index fd900c43..f55d0e1f 100644 --- a/lib/cmock_generator.rb +++ b/lib/cmock_generator.rb @@ -44,7 +44,7 @@ def initialize(config, file_writer, utils, plugins) end end - def create_mock(module_name, parsed_stuff, module_ext = nil, folder = nil) + def create_mock(module_name, parsed_stuff, module_ext = nil, folder = nil, src = nil) # determine the name for our new mock mock_name = @prefix + module_name + @suffix @@ -68,7 +68,8 @@ def create_mock(module_name, parsed_stuff, module_ext = nil, folder = nil) :clean_name => TypeSanitizer.sanitize_c_identifier(mock_name), :folder => mock_folder, :parsed_stuff => parsed_stuff, - :skeleton => false + :skeleton => false, + :source => src } create_mock_subdir(mock_project) @@ -131,6 +132,7 @@ def create_mock_header_file(mock_project) end @file_writer.create_file(mock_project[:mock_name] + mock_project[:module_ext], mock_project[:folder]) do |file, filename| + file << "/* Source File: #{mock_project[:source]} */\n" # if mock_project[:source] create_mock_header_header(file, filename, mock_project) create_mock_header_service_call_declarations(file, mock_project) create_typedefs(file, mock_project) @@ -144,6 +146,7 @@ def create_mock_header_file(mock_project) def create_mock_source_file(mock_project) @file_writer.create_file(mock_project[:mock_name] + '.c', mock_project[:folder]) do |file, filename| + file << "/* Source File: #{mock_project[:source]} */\n" # if mock_project[:source] create_source_header_section(file, filename, mock_project) create_instance_structure(file, mock_project) create_extern_declarations(file) From d9f1d8c9d21f9a152a0cd76d918e7e9cfb3de569 Mon Sep 17 00:00:00 2001 From: "Pascal J. Bourguignon" Date: Mon, 26 Jun 2023 20:15:59 +0200 Subject: [PATCH 13/21] Update docs/CMock_Summary.md Added --recursive to submodules update. Co-authored-by: Crt Mori --- docs/CMock_Summary.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/CMock_Summary.md b/docs/CMock_Summary.md index 7c039215..43dfa2cc 100644 --- a/docs/CMock_Summary.md +++ b/docs/CMock_Summary.md @@ -82,7 +82,7 @@ replied with a version that is older than 2.0.0. Go ahead. We'll wait. Once you have Ruby, you have three options: * Clone the latest [CMock repo on github](https://github.com/ThrowTheSwitch/CMock/) - (This includes updating the submodules: `git submodules update`) + (This includes updating the submodules: `git submodules update --recursive`) * Download the latest [CMock zip from github](https://github.com/ThrowTheSwitch/CMock/) * Install Ceedling (which has it built in!) through your commandline using `gem install ceedling`. From a024b6760f071da72569f4ed84588684a9de22f9 Mon Sep 17 00:00:00 2001 From: "Pascal J. Bourguignon" Date: Tue, 25 Jul 2023 13:02:55 +0200 Subject: [PATCH 14/21] Added tracing of source files. --- lib/cmock.rb | 4 ++-- lib/cmock_header_parser.rb | 41 ++++++++++++++++++++------------------ 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/lib/cmock.rb b/lib/cmock.rb index a8a0105c..ce48603b 100644 --- a/lib/cmock.rb +++ b/lib/cmock.rb @@ -45,13 +45,13 @@ def generate_mock(src, folder) name = File.basename(src, '.*') ext = File.extname(src) puts "Creating mock for #{name}..." unless @silent - @cm_generator.create_mock(name, @cm_parser.parse(name, File.read(src)), ext, folder,src) + @cm_generator.create_mock(name, @cm_parser.parse(src, name, File.read(src)), ext, folder,src) end def generate_skeleton(src) name = File.basename(src, '.*') puts "Creating skeleton for #{name}..." unless @silent - @cm_generator.create_skeleton(name, @cm_parser.parse(name, File.read(src))) + @cm_generator.create_skeleton(name, @cm_parser.parse(src, name, File.read(src))) end end diff --git a/lib/cmock_header_parser.rb b/lib/cmock_header_parser.rb index 55ed1fa5..6a4a17c6 100644 --- a/lib/cmock_header_parser.rb +++ b/lib/cmock_header_parser.rb @@ -10,6 +10,7 @@ class CMockHeaderParser attr_accessor :funcs, :c_attr_noconst, :c_attributes, :treat_as_void, :treat_externs, :treat_inlines, :inline_function_patterns attr_reader :noreturn_attributes, :process_gcc_attributes, :process_cpp_attributes, :c_calling_conventions + attr_reader :parse_project def initialize(cfg) @c_strippables = cfg.strippables @@ -41,11 +42,13 @@ def initialize(cfg) end def raise_parse_error(message) - raise "Failed Parsing Declaration Prototype!" + "\n" + message + # TODO: keep track of line number to be able to insert it in the error message. + raise "#{@parse_project[:source_path]}:1: Failed Parsing Declaration Prototype!" + "\n" + message end - def parse(name, source) - parse_project = { + def parse(src_path, name, source) + @parse_project = { + :source_path => src_path, :module_name => name.gsub(/\W/, ''), :typedefs => [], :functions => [], @@ -54,26 +57,26 @@ def parse(name, source) function_names = [] - all_funcs = parse_functions(import_source(source, parse_project)).map { |item| [item] } - all_funcs += parse_cpp_functions(import_source(source, parse_project, true)) + all_funcs = parse_functions(import_source(source)).map { |item| [item] } + all_funcs += parse_cpp_functions(import_source(source, true)) all_funcs.map do |decl| - func = parse_declaration(parse_project, *decl) + func = parse_declaration(*decl) unless function_names.include? func[:name] - parse_project[:functions] << func + @parse_project[:functions] << func function_names << func[:name] end end - parse_project[:normalized_source] = if @treat_inlines == :include + @parse_project[:normalized_source] = if @treat_inlines == :include transform_inline_functions(source) else '' end { :includes => nil, - :functions => parse_project[:functions], - :typedefs => parse_project[:typedefs], - :normalized_source => parse_project[:normalized_source] } + :functions => @parse_project[:functions], + :typedefs => @parse_project[:typedefs], + :normalized_source => @parse_project[:normalized_source] } end # REMVOVE BEFORE COMMIT # private if $ThisIsOnlyATest.nil? ################ @@ -228,7 +231,7 @@ def transform_inline_functions(source) source end - def import_source(source, parse_project, cpp = false) + def import_source(source, cpp = false) # let's clean up the encoding in case they've done anything weird with the characters we might find source = source.force_encoding('ISO-8859-1').encode('utf-8', :replace => nil) @@ -1033,7 +1036,7 @@ def replace_arrays_by_pointers_in_parameters(parameters) # [:int, [:identifier, "a"]]] - def replace_function_pointers_by_custom_types(parameters, parse_project) + def replace_function_pointers_by_custom_types(parameters) parameters.map do |parameter| plen = parameter.length if 2 Date: Wed, 26 Jul 2023 17:19:27 +0200 Subject: [PATCH 15/21] Added Creating messages. --- lib/cmock.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/cmock.rb b/lib/cmock.rb index ce48603b..3afae70a 100644 --- a/lib/cmock.rb +++ b/lib/cmock.rb @@ -29,12 +29,14 @@ def initialize(options = nil) def setup_mocks(files, folder = nil) [files].flatten.each do |src| + $stderr.puts "Creating mock for #{src}..." unless @silent generate_mock(src, folder) end end def setup_skeletons(files) [files].flatten.each do |src| + $stderr.puts "Creating skeleton for #{src}..." unless @silent generate_skeleton src end end From 2b5742b02ca5008516449a828eb2e6b0f42f483a Mon Sep 17 00:00:00 2001 From: "Pascal J. Bourguignon" Date: Tue, 1 Aug 2023 18:23:06 +0200 Subject: [PATCH 16/21] Added messages. --- lib/cmock.rb | 8 ++------ lib/cmock_generator.rb | 12 ++++++++---- lib/cmock_header_parser.rb | 1 + 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/lib/cmock.rb b/lib/cmock.rb index 3afae70a..c79d6db6 100644 --- a/lib/cmock.rb +++ b/lib/cmock.rb @@ -29,14 +29,12 @@ def initialize(options = nil) def setup_mocks(files, folder = nil) [files].flatten.each do |src| - $stderr.puts "Creating mock for #{src}..." unless @silent generate_mock(src, folder) end end def setup_skeletons(files) [files].flatten.each do |src| - $stderr.puts "Creating skeleton for #{src}..." unless @silent generate_skeleton src end end @@ -46,14 +44,12 @@ def setup_skeletons(files) def generate_mock(src, folder) name = File.basename(src, '.*') ext = File.extname(src) - puts "Creating mock for #{name}..." unless @silent - @cm_generator.create_mock(name, @cm_parser.parse(src, name, File.read(src)), ext, folder,src) + @cm_generator.create_mock(name, @cm_parser.parse(src, name, File.read(src)), ext, folder, src) end def generate_skeleton(src) name = File.basename(src, '.*') - puts "Creating skeleton for #{name}..." unless @silent - @cm_generator.create_skeleton(name, @cm_parser.parse(src, name, File.read(src))) + @cm_generator.create_skeleton(name, @cm_parser.parse(src, name, File.read(src)), src) end end diff --git a/lib/cmock_generator.rb b/lib/cmock_generator.rb index f55d0e1f..6591aa6a 100644 --- a/lib/cmock_generator.rb +++ b/lib/cmock_generator.rb @@ -21,7 +21,7 @@ def initialize(config, file_writer, utils, plugins) @fail_on_unexpected_calls = @config.fail_on_unexpected_calls @exclude_setjmp_h = @config.exclude_setjmp_h @subdir = @config.subdir - + @silent = (@config.verbosity < 2) @includes_h_pre_orig_header = ((@config.includes || []) + (@config.includes_h_pre_orig_header || [])).uniq.map { |h| h =~ / module_name, :module_ext => '.h', @@ -132,7 +136,7 @@ def create_mock_header_file(mock_project) end @file_writer.create_file(mock_project[:mock_name] + mock_project[:module_ext], mock_project[:folder]) do |file, filename| - file << "/* Source File: #{mock_project[:source]} */\n" # if mock_project[:source] + file << "/* Source File: #{mock_project[:source]} */\n" create_mock_header_header(file, filename, mock_project) create_mock_header_service_call_declarations(file, mock_project) create_typedefs(file, mock_project) @@ -146,7 +150,7 @@ def create_mock_header_file(mock_project) def create_mock_source_file(mock_project) @file_writer.create_file(mock_project[:mock_name] + '.c', mock_project[:folder]) do |file, filename| - file << "/* Source File: #{mock_project[:source]} */\n" # if mock_project[:source] + file << "/* Source File: #{mock_project[:source]} */\n" create_source_header_section(file, filename, mock_project) create_instance_structure(file, mock_project) create_extern_declarations(file) diff --git a/lib/cmock_header_parser.rb b/lib/cmock_header_parser.rb index 6a4a17c6..3fd5cddc 100644 --- a/lib/cmock_header_parser.rb +++ b/lib/cmock_header_parser.rb @@ -47,6 +47,7 @@ def raise_parse_error(message) end def parse(src_path, name, source) + $stderr.puts "Parsing #{src_path}" if @verbosity >= 1 @parse_project = { :source_path => src_path, :module_name => name.gsub(/\W/, ''), From 977fb9a6183161a0b1580008273bdc5d5ed3af27 Mon Sep 17 00:00:00 2001 From: "Pascal J. Bourguignon" Date: Tue, 1 Aug 2023 18:25:26 +0200 Subject: [PATCH 17/21] Corrections to fulfill the tests. --- lib/CLexer.rb | 2 +- lib/cmock.rb | 4 +- lib/cmock_header_parser.rb | 17 ++-- test/rakefile_helper.rb | 8 +- test/unit/cmock_generator_main_test.rb | 2 + .../cmock_generator_plugin_callback_test.rb | 31 +++---- test/unit/cmock_header_parser_test.rb | 83 ++++++++++--------- 7 files changed, 83 insertions(+), 64 deletions(-) diff --git a/lib/CLexer.rb b/lib/CLexer.rb index 1cc00bb2..06c5dcd4 100644 --- a/lib/CLexer.rb +++ b/lib/CLexer.rb @@ -123,7 +123,7 @@ def tokenize @input = $' @tokens << OPERATOR_SYMBOLS[operator] else - raise "Unexpected character: #{@input[0]}" + raise "Unexpected character: #{@input[0].inspect}" end end diff --git a/lib/cmock.rb b/lib/cmock.rb index c79d6db6..dc8a2028 100644 --- a/lib/cmock.rb +++ b/lib/cmock.rb @@ -44,12 +44,12 @@ def setup_skeletons(files) def generate_mock(src, folder) name = File.basename(src, '.*') ext = File.extname(src) - @cm_generator.create_mock(name, @cm_parser.parse(src, name, File.read(src)), ext, folder, src) + @cm_generator.create_mock(name, @cm_parser.parse(name, File.read(src), src), ext, folder, src) end def generate_skeleton(src) name = File.basename(src, '.*') - @cm_generator.create_skeleton(name, @cm_parser.parse(src, name, File.read(src)), src) + @cm_generator.create_skeleton(name, @cm_parser.parse(name, File.read(src), src), src) end end diff --git a/lib/cmock_header_parser.rb b/lib/cmock_header_parser.rb index 3fd5cddc..56790328 100644 --- a/lib/cmock_header_parser.rb +++ b/lib/cmock_header_parser.rb @@ -10,7 +10,7 @@ class CMockHeaderParser attr_accessor :funcs, :c_attr_noconst, :c_attributes, :treat_as_void, :treat_externs, :treat_inlines, :inline_function_patterns attr_reader :noreturn_attributes, :process_gcc_attributes, :process_cpp_attributes, :c_calling_conventions - attr_reader :parse_project + attr_accessor :parse_project def initialize(cfg) @c_strippables = cfg.strippables @@ -43,11 +43,15 @@ def initialize(cfg) def raise_parse_error(message) # TODO: keep track of line number to be able to insert it in the error message. - raise "#{@parse_project[:source_path]}:1: Failed Parsing Declaration Prototype!" + "\n" + message + if @parse_project[:source_path].nil? + raise "Failed Parsing Declaration Prototype!" + "\n" + message + else + raise "#{@parse_project[:source_path]}:1: Failed Parsing Declaration Prototype!" + "\n" + message + end end - def parse(src_path, name, source) - $stderr.puts "Parsing #{src_path}" if @verbosity >= 1 + def parse(name, source, src_path = nil) + $stderr.puts "Parsing #{src_path}" if !src_path.nil? and @verbosity >= 1 @parse_project = { :source_path => src_path, :module_name => name.gsub(/\W/, ''), @@ -233,6 +237,7 @@ def transform_inline_functions(source) end def import_source(source, cpp = false) + # let's clean up the encoding in case they've done anything weird with the characters we might find source = source.force_encoding('ISO-8859-1').encode('utf-8', :replace => nil) @@ -287,9 +292,9 @@ def import_source(source, cpp = false) # scan for functions which return function pointers, because they are a pain source.gsub!(/([\w\s\*]+)\(*\(\s*\*([\w\s\*]+)\s*\(([\w\s\*,]*)\)\)\s*\(([\w\s\*,]*)\)\)*/) do |_m| - functype = "cmock_#{parse_project[:module_name]}_func_ptr#{parse_project[:typedefs].size + 1}" + functype = "cmock_#{@parse_project[:module_name]}_func_ptr#{@parse_project[:typedefs].size + 1}" unless cpp # only collect once - parse_project[:typedefs] << "typedef #{Regexp.last_match(1).strip}(*#{functype})(#{Regexp.last_match(4)});" + @parse_project[:typedefs] << "typedef #{Regexp.last_match(1).strip}(*#{functype})(#{Regexp.last_match(4)});" "#{functype} #{Regexp.last_match(2).strip}(#{Regexp.last_match(3)});" end end diff --git a/test/rakefile_helper.rb b/test/rakefile_helper.rb index dd0b6541..283021dd 100644 --- a/test/rakefile_helper.rb +++ b/test/rakefile_helper.rb @@ -21,7 +21,13 @@ module RakefileHelpers def load_configuration(config_file) $cfg_file = config_file - $cfg = YAML.load(File.read('./targets/' + $cfg_file),aliases: true) + yaml_version = YAML::VERSION[0].to_i # Extract major version number of YAML + options = if yaml_version > 3 + { aliases: true } + else + { } + end + $cfg = YAML.load(File.read('./targets/' + $cfg_file), **options) $colour_output = false unless $cfg['colour'] end diff --git a/test/unit/cmock_generator_main_test.rb b/test/unit/cmock_generator_main_test.rb index f7229ba8..fcd0a916 100644 --- a/test/unit/cmock_generator_main_test.rb +++ b/test/unit/cmock_generator_main_test.rb @@ -56,6 +56,7 @@ def mock_implementation(name, args) @config.expect :fail_on_unexpected_calls, true @config.expect :treat_inlines, :exclude @config.expect :exclude_setjmp_h, false + @config.expect :verbosity, 1 @cmock_generator = CMockGenerator.new(@config, @file_writer, @utils, @plugins) @cmock_generator.module_name = @module_name @cmock_generator.module_ext = '.h' @@ -77,6 +78,7 @@ def mock_implementation(name, args) @config.expect :fail_on_unexpected_calls, true @config.expect :treat_inlines, :exclude @config.expect :exclude_setjmp_h, false + @config.expect :verbosity, 1 @cmock_generator_strict = CMockGenerator.new(@config, @file_writer, @utils, @plugins) @test_project = { diff --git a/test/unit/cmock_generator_plugin_callback_test.rb b/test/unit/cmock_generator_plugin_callback_test.rb index 0b5ce1eb..f4568eca 100644 --- a/test/unit/cmock_generator_plugin_callback_test.rb +++ b/test/unit/cmock_generator_plugin_callback_test.rb @@ -12,11 +12,14 @@ before do create_mocks :config, :utils + @config.expect :callback_kind, '*' + @config.expect :callback_kind, '*' @config.expect :callback_include_count, true @config.expect :callback_after_arg_check, false @config.expect :plugins, [:ignore] @cmock_generator_plugin_callback = CMockGeneratorPluginCallback.new(@config, @utils) + end after do @@ -36,7 +39,7 @@ " CMOCK_Oak_CALLBACK Oak_CallbackFunctionPointer;\n" + " int Oak_CallbackCalls;\n" returned = @cmock_generator_plugin_callback.instance_structure(function) - assert_equal(expected, returned) + assert_equal(expected, returned, "Expected:\n#{expected}\nGot:\n#{returned}") end it "add mock function declaration for function without arguments" do @@ -46,7 +49,7 @@ "void Maple_Stub(CMOCK_Maple_CALLBACK Callback);\n", "#define Maple_StubWithCallback Maple_Stub\n" ].join returned = @cmock_generator_plugin_callback.mock_function_declarations(function) - assert_equal(expected, returned) + assert_equal(expected, returned, "Expected:\n#{expected}\nGot:\n#{returned}") end it "add mock function declaration for function without arguments when count is also turned off" do @@ -57,7 +60,7 @@ "#define Maple_StubWithCallback Maple_Stub\n" ].join @cmock_generator_plugin_callback.include_count = false returned = @cmock_generator_plugin_callback.mock_function_declarations(function) - assert_equal(expected, returned) + assert_equal(expected, returned, "Expected:\n#{expected}\nGot:\n#{returned}") end it "add mock function declaration for function with arguments" do @@ -99,7 +102,7 @@ " }\n" ].join returned = @cmock_generator_plugin_callback.mock_implementation(function) - assert_equal(expected, returned) + assert_equal(expected, returned, "Expected:\n#{expected}\nGot:\n#{returned}") end it "add mock function implementation for functions with no arg check and of style 'void func(void)' when count turned off" do @@ -111,7 +114,7 @@ ].join @cmock_generator_plugin_callback.include_count = false returned = @cmock_generator_plugin_callback.mock_implementation(function) - assert_equal(expected, returned) + assert_equal(expected, returned, "Expected:\n#{expected}\nGot:\n#{returned}") end it "add mock function implementation for functions with no arg check and of style 'int func(void)'" do @@ -122,7 +125,7 @@ " }\n" ].join returned = @cmock_generator_plugin_callback.mock_implementation(function) - assert_equal(expected, returned) + assert_equal(expected, returned, "Expected:\n#{expected}\nGot:\n#{returned}") end it "add mock function implementation for functions with no arg check and of style 'void func(int* steak, uint8_t flag)'" do @@ -137,7 +140,7 @@ " }\n" ].join returned = @cmock_generator_plugin_callback.mock_implementation(function) - assert_equal(expected, returned) + assert_equal(expected, returned, "Expected:\n#{expected}\nGot:\n#{returned}") end it "add mock function implementation for functions with no arg check and of style 'void func(int* steak, uint8_t flag)' when count turned off" do @@ -153,7 +156,7 @@ ].join @cmock_generator_plugin_callback.include_count = false returned = @cmock_generator_plugin_callback.mock_implementation(function) - assert_equal(expected, returned) + assert_equal(expected, returned, "Expected:\n#{expected}\nGot:\n#{returned}") end it "add mock function implementation for functions with no arg check and of style 'int16_t func(int* steak, uint8_t flag)'" do @@ -168,7 +171,7 @@ " }\n" ].join returned = @cmock_generator_plugin_callback.mock_implementation(function) - assert_equal(expected, returned) + assert_equal(expected, returned, "Expected:\n#{expected}\nGot:\n#{returned}") end it "add mock function implementation for functions without arg check and of style 'void func(void)' when count turned off" do @@ -183,7 +186,7 @@ ].join @cmock_generator_plugin_callback.include_count = false returned = @cmock_generator_plugin_callback.mock_implementation_precheck(function) - assert_equal(expected, returned) + assert_equal(expected, returned, "Expected:\n#{expected}\nGot:\n#{returned}") end it "add mock function implementation for functions without arg check and of style 'int func(void)'" do @@ -215,7 +218,7 @@ " }\n" ].join returned = @cmock_generator_plugin_callback.mock_implementation_precheck(function) - assert_equal(expected, returned) + assert_equal(expected, returned, "Expected:\n#{expected}\nGot:\n#{returned}") end it "add mock function implementation for functions without arg check and of style 'void func(int* steak, uint8_t flag)' when count turned off" do @@ -234,7 +237,7 @@ ].join @cmock_generator_plugin_callback.include_count = false returned = @cmock_generator_plugin_callback.mock_implementation_precheck(function) - assert_equal(expected, returned) + assert_equal(expected, returned, "Expected:\n#{expected}\nGot:\n#{returned}") end it "add mock function implementation for functions without arg check and of style 'int16_t func(int* steak, uint8_t flag)'" do @@ -252,7 +255,7 @@ " }\n" ].join returned = @cmock_generator_plugin_callback.mock_implementation_precheck(function) - assert_equal(expected, returned) + assert_equal(expected, returned, "Expected:\n#{expected}\nGot:\n#{returned}") end it "add mock interfaces for functions " do @@ -276,6 +279,6 @@ "}\n\n" ].join returned = @cmock_generator_plugin_callback.mock_interfaces(function) - assert_equal(expected, returned) + assert_equal(expected, returned, "Expected:\n#{expected}\nGot:\n#{returned}") end end diff --git a/test/unit/cmock_header_parser_test.rb b/test/unit/cmock_header_parser_test.rb index 57ad5be1..4cd66e52 100644 --- a/test/unit/cmock_header_parser_test.rb +++ b/test/unit/cmock_header_parser_test.rb @@ -31,7 +31,7 @@ @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' - + @parser = CMockHeaderParser.new(@config) @test_project = { @@ -40,6 +40,9 @@ :functions => [], :normalized_source => nil } + + @parser.parse_project = @test_project + end after do @@ -63,7 +66,7 @@ "who" ] - assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip}) + assert_equal(expected, @parser.import_source(source).map!{|s|s.strip}) end it "remove block comments" do @@ -96,7 +99,7 @@ "shown_because_line_above_ended_comment_this_time" ] - assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip}) + assert_equal(expected, @parser.import_source(source).map!{|s|s.strip}) end it "remove strippables from the beginning or end of function declarations" do @@ -116,7 +119,7 @@ "void universal_handler()" ] - assert_equal(expected, @parser.import_source(source, @test_project)) + assert_equal(expected, @parser.import_source(source)) end it "keep gcc's function __attribute__'s" do @@ -136,7 +139,7 @@ "void __attribute__((interrupt))universal_handler()" ] - assert_equal(expected, @parser.import_source(source, @test_project)) + assert_equal(expected, @parser.import_source(source)) end it "remove preprocessor directives" do @@ -147,7 +150,7 @@ expected = [] - assert_equal(expected, @parser.import_source(source, @test_project)) + assert_equal(expected, @parser.import_source(source)) end @@ -162,7 +165,7 @@ expected = ["foo"] - assert_equal(expected, @parser.import_source(source, @test_project)) + assert_equal(expected, @parser.import_source(source)) end @@ -176,7 +179,7 @@ "hoo hah when" ] - assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip}) + assert_equal(expected, @parser.import_source(source).map!{|s|s.strip}) end @@ -189,7 +192,7 @@ expected = ["but I'm here"] - assert_equal(expected, @parser.import_source(source, @test_project)) + assert_equal(expected, @parser.import_source(source)) end @@ -223,7 +226,7 @@ "this should remain!" ] - assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip}) + assert_equal(expected, @parser.import_source(source).map!{|s|s.strip}) end @@ -241,7 +244,7 @@ "} Thinger;\n" + "or me!!\n" - assert_equal(["don't delete me!! or me!!"], @parser.import_source(source, @test_project).map!{|s|s.strip}) + assert_equal(["don't delete me!! or me!!"], @parser.import_source(source).map!{|s|s.strip}) end @@ -259,7 +262,7 @@ "} Whatever;\n" + "me too!!\n" - assert_equal(["I want to live!! me too!!"], @parser.import_source(source, @test_project).map!{|s|s.strip}) + assert_equal(["I want to live!! me too!!"], @parser.import_source(source).map!{|s|s.strip}) end @@ -282,7 +285,7 @@ "I want to live!!\n" assert_equal(["void foo(void)", "struct THINGER foo(void)", "I want to live!!"], - @parser.import_source(source, @test_project).map!{|s|s.strip}) + @parser.import_source(source).map!{|s|s.strip}) end it "remove externed and inline functions" do @@ -301,7 +304,7 @@ "uint32 funcinline(unsigned int)" ] - assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip}) + assert_equal(expected, @parser.import_source(source).map!{|s|s.strip}) end it "remove function definitions but keep function declarations" do @@ -323,7 +326,7 @@ "uint32 func_with_decl_b", #okay. it's not going to be interpretted as another function ] - assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip}) + assert_equal(expected, @parser.import_source(source).map!{|s|s.strip}) end it "remove function definitions with nested braces but keep function declarations" do @@ -365,7 +368,7 @@ "uint32 func_with_decl_c", #okay. it's not going to be interpretted as another function ] - assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip}) + assert_equal(expected, @parser.import_source(source).map!{|s|s.strip}) end it "remove a fully defined inline function" do @@ -463,7 +466,7 @@ ] @parser.treat_externs = :include - assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip}) + assert_equal(expected, @parser.import_source(source).map!{|s|s.strip}) end it "leave inline functions if inline to be included" do @@ -489,7 +492,7 @@ ] @parser.treat_inlines = :include - assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip}) + assert_equal(expected, @parser.import_source(source).map!{|s|s.strip}) end it "leave inline and extern functions if inline and extern to be included" do @@ -518,7 +521,7 @@ @parser.treat_externs = :include @parser.treat_inlines = :include - assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip}) + assert_equal(expected, @parser.import_source(source).map!{|s|s.strip}) end it "Include inline functions that contain user defined inline function formats" do @@ -549,7 +552,7 @@ @parser.treat_inlines = :include @parser.inline_function_patterns = ['static __inline__ __attribute__ \(\(always_inline\)\)', 'static __inline__', '\binline\b'] - assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip}) + assert_equal(expected, @parser.import_source(source).map!{|s|s.strip}) end it "remove defines" do @@ -565,7 +568,7 @@ "void hello(void)", ] - assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip}) + assert_equal(expected, @parser.import_source(source).map!{|s|s.strip}) end @@ -578,7 +581,7 @@ "const int TheMatrix(int Trinity, unsigned int * Neo)", ] - assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip}) + assert_equal(expected, @parser.import_source(source).map!{|s|s.strip}) end @@ -605,7 +608,7 @@ :args => [{:type => "int", :name => "a", :ptr? => false, :const? => false, :const_ptr? => false}], :args_string => "int a", :args_call => "a"} - assert_equal(expected, @parser.parse_declaration(@test_project, source)) + assert_equal(expected, @parser.parse_declaration(source)) end it "handle odd case of typedef'd void as arg" do @@ -629,7 +632,7 @@ :args => [], :args_string => "void", :args_call => "" } - assert_equal(expected, @parser.parse_declaration(@test_project, source)) + assert_equal(expected, @parser.parse_declaration(source)) end it "handle odd case of typedef'd void as arg pointer" do @@ -653,7 +656,7 @@ :args => [{:type => "MY_FUNKY_VOID*", :name => "bluh", :ptr? => true, :const? => false, :const_ptr? => false}], :args_string => "MY_FUNKY_VOID* bluh", :args_call => "bluh" } - assert_equal(expected, @parser.parse_declaration(@test_project, source)) + assert_equal(expected, @parser.parse_declaration(source)) end @@ -666,7 +669,7 @@ "void Foo(int a, float b, char c, char* e)" ] - assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip}) + assert_equal(expected, @parser.import_source(source).map!{|s|s.strip}) end @@ -758,7 +761,7 @@ ], :args_string => "int a, unsigned int b", :args_call => "a, b" } - assert_equal(expected, @parser.parse_declaration(@test_project, source)) + assert_equal(expected, @parser.parse_declaration(source)) end it "extract and return function declarations with no retval" do @@ -786,7 +789,7 @@ ], :args_string => "uint la, int de, bool da", :args_call => "la, de, da" } - assert_equal(expected, @parser.parse_declaration(@test_project, source)) + assert_equal(expected, @parser.parse_declaration(source)) end it "extract and return function declarations with implied voids" do @@ -811,7 +814,7 @@ :args => [ ], :args_string => "void", :args_call => "" } - assert_equal(expected, @parser.parse_declaration(@test_project, source)) + assert_equal(expected, @parser.parse_declaration(source)) end it "extract modifiers properly" do @@ -838,7 +841,7 @@ ], :args_string => "int Trinity, unsigned int* Neo", :args_call => "Trinity, Neo" } - assert_equal(expected, @parser.parse_declaration(@test_project, source)) + assert_equal(expected, @parser.parse_declaration(source)) end it "extract c calling conventions properly" do @@ -866,7 +869,7 @@ ], :args_string => "int Trinity, unsigned int* Neo", :args_call => "Trinity, Neo" } - assert_equal(expected, @parser.parse_declaration(@test_project, source)) + assert_equal(expected, @parser.parse_declaration(source)) end it "extract and return function declarations inside namespace and class" do @@ -892,7 +895,7 @@ ], :args_string => "int a, unsigned int b", :args_call => "a, b" } - assert_equal(expected, @parser.parse_declaration(@test_project, source, ["ns1", "ns2"], "Bar")) + assert_equal(expected, @parser.parse_declaration(source, ["ns1", "ns2"], "Bar")) end it "fully parse multiple prototypes" do @@ -2454,8 +2457,8 @@ "} }" ] - assert_equal(expected, @parser.import_source(source, @test_project, cpp=true)) - refute_equal(expected, @parser.import_source(source, @test_project)) + assert_equal(expected, @parser.import_source(source, cpp=true)) + refute_equal(expected, @parser.import_source(source)) end # only so parse_functions does not raise an error @@ -3134,7 +3137,7 @@ class Classy { ], :args_string => "int a, unsigned int b", :args_call => "a, b" } - assert_equal(expected, @parser.parse_declaration(@test_project, source, [], nil)) + assert_equal(expected, @parser.parse_declaration(source, [], nil)) end it "extract and return function declarations with noreturn" do @@ -3160,7 +3163,7 @@ class Classy { ], :args_string => "int a, unsigned int b", :args_call => "a, b" } - assert_equal(expected, @parser.parse_declaration(@test_project, source, [], nil)) + assert_equal(expected, @parser.parse_declaration(source, [], nil)) end it "extract and return function declarations with [[noreturn]]" do @@ -3186,7 +3189,7 @@ class Classy { ], :args_string => "int a, unsigned int b", :args_call => "a, b" } - assert_equal(expected, @parser.parse_declaration(@test_project, source, [], nil)) + assert_equal(expected, @parser.parse_declaration(source, [], nil)) end it "extract and return function declarations with __attribute__((noreturn))" do @@ -3212,7 +3215,7 @@ class Classy { ], :args_string => "int a, unsigned int b", :args_call => "a, b" } - assert_equal(expected, @parser.parse_declaration(@test_project, source, [], nil)) + assert_equal(expected, @parser.parse_declaration(source, [], nil)) end it "extract and return function declarations with __attribute__ ((noreturn))" do @@ -3238,7 +3241,7 @@ class Classy { ], :args_string => "int a, unsigned int b", :args_call => "a, b" } - assert_equal(expected, @parser.parse_declaration(@test_project, source, [], nil)) + assert_equal(expected, @parser.parse_declaration(source, [], nil)) end it "extract and return function declarations with __attribute__ ((__noreturn__))" do @@ -3264,7 +3267,7 @@ class Classy { ], :args_string => "int a, unsigned int b", :args_call => "a, b" } - assert_equal(expected, @parser.parse_declaration(@test_project, source, [], nil)) + assert_equal(expected, @parser.parse_declaration(source, [], nil)) end From daf41637b48158b2b07eed159185370330953139 Mon Sep 17 00:00:00 2001 From: "Pascal J. Bourguignon" Date: Wed, 2 Aug 2023 10:04:06 +0200 Subject: [PATCH 18/21] Conditionnalized _Noreturn on C11+ --- test/system/test_compilation/noreturn.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/system/test_compilation/noreturn.h b/test/system/test_compilation/noreturn.h index f5ab6893..f1f70949 100644 --- a/test/system/test_compilation/noreturn.h +++ b/test/system/test_compilation/noreturn.h @@ -1,8 +1,11 @@ #ifndef noreturn_h #define noreturn_h -/* #include */ +#if __STDC_VERSION__ >= 201112L +#include _Noreturn void myexec1(const char* program); +#endif + void __attribute__((noreturn)) myexec2(const char* program); void myexec3(const char* program) __attribute__((noreturn)); From 509eacf32954d3755d9b418aff795715079cbabb Mon Sep 17 00:00:00 2001 From: "Pascal J. Bourguignon" Date: Wed, 2 Aug 2023 13:14:42 +0200 Subject: [PATCH 19/21] Corrections hinted by rubocop. --- lib/CLexer.rb | 37 +- lib/cmock_header_parser.rb | 531 +++++++++++++------------- test/unit/cmock_header_parser_test.rb | 58 ++- 3 files changed, 317 insertions(+), 309 deletions(-) diff --git a/lib/CLexer.rb b/lib/CLexer.rb index 06c5dcd4..13beacea 100644 --- a/lib/CLexer.rb +++ b/lib/CLexer.rb @@ -1,9 +1,17 @@ # -# This is a simple lexer for the C programming language. +# This is a simple lexer for the C programming language. # MIT license. (c) 2023 Pascal Bourguignon # class CLexer + # + # CLexer is a simple C lexer. It is used to tokenize a C source file. + # + # Usage: + # lexer = CLexer.new(pre_processed_c_source) + # tokens = lexer.tokenize + # + # The tokenize method returns an array of tokens. KEYWORDS = %w[auto break case char const continue default do double else enum extern float for goto if int long register return short signed @@ -67,14 +75,13 @@ class CLexer '{' => :open_brace, '|' => :logical_or_op, '}' => :close_brace, - '~' => :bitwise_not_op, + '~' => :bitwise_not_op }.freeze - OPERATOR_REGEX = Regexp.new('\A(' + OPERATOR_SYMBOLS.keys.map { |op| Regexp.escape(op) }.join('|') + ')') OPERATOR_SYMS = OPERATOR_SYMBOLS.values.freeze - KEYWORDS_SYMS = KEYWORDS.map{ |n| n.to_sym }.freeze + KEYWORDS_SYMS = KEYWORDS.map(&:to_sym).freeze def initialize(input) @input = input @@ -82,7 +89,7 @@ def initialize(input) end def tokenize - while @input.size > 0 + while @input.size.positive? case @input when /\A[[:space:]]+/m @input = $' @@ -91,7 +98,7 @@ def tokenize when /\A\/\*/ consume_multiline_comment when /\A[_a-zA-Z][_a-zA-Z0-9]*/ - identifier_or_keyword = $& ; + identifier_or_keyword = $& @input = $' if KEYWORDS.include?(identifier_or_keyword) @tokens << identifier_or_keyword.to_sym @@ -99,27 +106,27 @@ def tokenize @tokens << [:identifier, identifier_or_keyword] end when /\A\d+\.\d*([eE][+-]?\d+)?[fFlL]?|\.\d+([eE][+-]?\d+)?[fFlL]?|\d+[eE][+-]?\d+[fFlL]?/ - float_constant = $& ; + float_constant = $& @input = $' @tokens << [:float_literal, float_constant] when /\A\d+/ - integer_constant = $& ; + integer_constant = $& @input = $' @tokens << [:integer_literal, integer_constant] when /\A0[xX][0-9a-fA-F]+/ - hex_constant = $& ; + hex_constant = $& @input = $' @tokens << [:hex_literal, hex_constant] when /\A'((\\.|[^\\'])*)'/ - char_literal = $& ; + char_literal = $& @input = $' @tokens << [:char_literal, char_literal] when /\A"((\\.|[^\\"])*)"/ - string_literal = $& ; + string_literal = $& @input = $' @tokens << [:string_literal, string_literal] when OPERATOR_REGEX - operator = $& ; + operator = $& @input = $' @tokens << OPERATOR_SYMBOLS[operator] else @@ -133,7 +140,7 @@ def tokenize private def consume_multiline_comment - while @input.size > 0 + while @input.size.positive? case @input when /\A\*\// @input = $' @@ -145,8 +152,8 @@ def consume_multiline_comment end end -def example - input = File.read("/home/pbourguignon/src/c-tidbits/pipes/tee.out.c") +def example + input = File.read('tee.c') lexer = CLexer.new(input) tokens = lexer.tokenize puts tokens.inspect diff --git a/lib/cmock_header_parser.rb b/lib/cmock_header_parser.rb index 56790328..fe80e69f 100644 --- a/lib/cmock_header_parser.rb +++ b/lib/cmock_header_parser.rb @@ -16,7 +16,7 @@ def initialize(cfg) @c_strippables = cfg.strippables @process_gcc_attributes = cfg.process_gcc_attributes @process_cpp_attributes = cfg.process_cpp_attributes - @noreturn_attributes = cfg.noreturn_attributes.uniq + @noreturn_attributes = cfg.noreturn_attributes.uniq @c_attr_noconst = cfg.attributes.uniq - ['const'] @c_attributes = ['const'] + @c_attr_noconst @c_calling_conventions = cfg.c_calling_conventions.uniq @@ -25,7 +25,7 @@ def initialize(cfg) attribute_regexp = '((?:\s*__attribute__\s*\(\s*\(.*?\)\s*\))*)' type_and_name_regexp = '([\w\s\*\(\),\[\]]*?\w[\w\s\*\(\),\[\]]*?)' args_regexp = '([\w\s\*\(\),\.\[\]+\-\/]*)' - @function_declaration_parse_base_match = type_and_name_regexp+'\('+ args_regexp + '\)' + attribute_regexp + @function_declaration_parse_base_match = type_and_name_regexp + '\(' + args_regexp + '\)' + attribute_regexp @declaration_parse_matcher = /#{@function_declaration_parse_base_match}$/m @standards = (%w[int short char long unsigned signed] + cfg.treat_as.keys).uniq @array_size_name = cfg.array_size_name @@ -44,14 +44,14 @@ def initialize(cfg) def raise_parse_error(message) # TODO: keep track of line number to be able to insert it in the error message. if @parse_project[:source_path].nil? - raise "Failed Parsing Declaration Prototype!" + "\n" + message + raise 'Failed Parsing Declaration Prototype!' + "\n" + message else raise "#{@parse_project[:source_path]}:1: Failed Parsing Declaration Prototype!" + "\n" + message end end def parse(name, source, src_path = nil) - $stderr.puts "Parsing #{src_path}" if !src_path.nil? and @verbosity >= 1 + $stderr.puts "Parsing #{src_path}" if !src_path.nil? && (@verbosity >= 1) @parse_project = { :source_path => src_path, :module_name => name.gsub(/\W/, ''), @@ -73,10 +73,10 @@ def parse(name, source, src_path = nil) end @parse_project[:normalized_source] = if @treat_inlines == :include - transform_inline_functions(source) - else - '' - end + transform_inline_functions(source) + else + '' + end { :includes => nil, :functions => @parse_project[:functions], @@ -96,9 +96,12 @@ def remove_comments_from_source(source) end def remove_nested_pairs_of_braces(source) - # remove nested pairs of braces because no function declarations will be inside of them (leave outer pair for function definition detection) + # remove nested pairs of braces because no function declarations + # will be inside of them (leave outer pair for function definition + # detection) if RUBY_VERSION.split('.')[0].to_i > 1 - # we assign a string first because (no joke) if Ruby 1.9.3 sees this line as a regex, it will crash. + # we assign a string first because (no joke) if Ruby 1.9.3 sees + # this line as a regex, it will crash. r = '\\{([^\\{\\}]*|\\g<0>)*\\}' source.gsub!(/#{r}/m, '{ }') else @@ -125,7 +128,7 @@ def count_number_of_pairs_of_braces_in_function(source) curr_level -= 1 end - break if is_function_start_found && curr_level == 0 # We reached the end of the inline function body + break if is_function_start_found && curr_level.zero? # We reached the end of the inline function body end if curr_level != 0 @@ -200,10 +203,13 @@ def transform_inline_functions(source) end # 2. Determine if we are dealing with an inline function declaration iso function definition - # If the start of the post-match string is a function-declaration-like string (something ending with semicolon after the function arguments), - # we are dealing with a inline function declaration + # If the start of the post-match string is a + # function-declaration-like string (something ending with + # semicolon after the function arguments), we are dealing with + # a inline function declaration if /\A#{@function_declaration_parse_base_match}\s*;/m =~ inline_function_match.post_match - # Only remove the inline part from the function declaration, leaving the function declaration won't do any harm + # Only remove the inline part from the function declaration, + # leaving the function declaration won't do any harm inspected_source += inline_function_match.pre_match source = inline_function_match.post_match next @@ -214,7 +220,7 @@ def transform_inline_functions(source) if /\A#{@function_declaration_parse_base_match}\s*\{/m =~ inline_function_match.post_match total_pairs_to_remove = count_number_of_pairs_of_braces_in_function(inline_function_match.post_match) - break if total_pairs_to_remove == 0 # Bad source? + break if total_pairs_to_remove.zero? # Bad source? inline_function_stripped = inline_function_match.post_match @@ -226,7 +232,8 @@ def transform_inline_functions(source) next end - # 4. If we get here, it means the regex match, but it is not related to the function (ex. static variable in header) + # 4. If we get here, it means the regex match, but it is not + # related to the function (ex. static variable in header) # Leave this code as it is. inspected_source += inline_function_match.pre_match + inline_function_match[0] source = inline_function_match.post_match @@ -237,20 +244,23 @@ def transform_inline_functions(source) end def import_source(source, cpp = false) - # let's clean up the encoding in case they've done anything weird with the characters we might find source = source.force_encoding('ISO-8859-1').encode('utf-8', :replace => nil) - # void must be void for cmock _ExpectAndReturn calls to process properly, not some weird typedef which equates to void - # to a certain extent, this action assumes we're chewing on pre-processed header files, otherwise we'll most likely just get stuff from @treat_as_void + # void must be void for cmock _ExpectAndReturn calls to process + # properly, not some weird typedef which equates to void to a + # certain extent, this action assumes we're chewing on + # pre-processed header files, otherwise we'll most likely just get + # stuff from @treat_as_void @local_as_void = @treat_as_void void_types = source.scan(/typedef\s+(?:\(\s*)?void(?:\s*\))?\s+([\w]+)\s*;/) if void_types @local_as_void += void_types.flatten.uniq.compact end - # If user wants to mock inline functions, - # remove the (user specific) inline keywords before removing anything else to avoid missing an inline function + # If user wants to mock inline functions, remove the (user + # specific) inline keywords before removing anything else to avoid + # missing an inline function if @treat_inlines == :include @inline_function_patterns.each do |user_format_string| source.gsub!(/#{user_format_string}/, '') # remove user defined inline function patterns @@ -274,8 +284,11 @@ def import_source(source, cpp = false) source.gsub!(/extern\s+\"C\"\s*\{/, '') source.gsub!(/^\s*#.*/, '') - # enums, unions, structs, and typedefs can all contain things (e.g. function pointers) that parse like function prototypes, so yank them - # forward declared structs are removed before struct definitions so they don't mess up real thing later. we leave structs keywords in function prototypes + # enums, unions, structs, and typedefs can all contain things + # (e.g. function pointers) that parse like function prototypes, so + # yank them forward declared structs are removed before struct + # definitions so they don't mess up real thing later. we leave + # structs keywords in function prototypes source.gsub!(/^[\w\s]*struct[^;\{\}\(\)]+;/m, '') # remove forward declared structs source.gsub!(/^[\w\s]*(enum|union|struct|typedef)[\w\s]*\{[^\}]+\}[\w\s\*\,]*;/m, '') # remove struct, union, and enum definitions and typedefs with braces # remove problem keywords @@ -332,7 +345,7 @@ def import_source(source, cpp = false) end src_lines.delete_if(&:empty?) # drop empty lines - src_lines + src_lines end # Rudimentary C++ parser - does not handle all situations - e.g.: @@ -405,15 +418,15 @@ def parse_functions(source) # type : stuffs ; # # stuffs : | stuff stuffs ; - # + # # stuff : token # | :open_paren stuffs :close_paren # | :open_bracket stuffs :close_bracket # | :open_brace stuffs :close_brace # | :lt stuffs :gt' -- angle brackets # ; - # -- Note: we will also scan char_literal and string_literal - # -- because they could appear in constant expressions (eg. enums) + # -- Note: we will also scan char_literal and string_literal + # -- because they could appear in constant expressions (eg. enums) # -- and contain parentheses. # -- Note: angle brackets for templates are very ambiguous, because # -- we may also have '<' tokens in constant expressions (eg. in a enum). @@ -430,7 +443,7 @@ def parse_functions(source) # # # Therefore we will parse in two phases: - # Phase 1: + # Phase 1: # we parse fun_declaration_1 : { stuff } ; # -- this takes care of parentheses, et al. # Phase 2: @@ -440,11 +453,11 @@ def parse_functions(source) # then name identifier, # then the rest is type. - def eos?(src,pos) - src.length<=pos + def eos?(src, pos) + src.length <= pos end - def validate_identifier(token,what) + def validate_identifier(token, what) if token[0] == :identifier token else @@ -452,22 +465,22 @@ def validate_identifier(token,what) end end - def parse_token(src,pos) - if eos?(src,pos) + def parse_token(src, pos) + if eos?(src, pos) raise_parse_error "Expected a token, not end of source at position #{pos}" end - [ src[pos], pos+1 ] + [src[pos], pos + 1] end - def parse_stuff(src,pos) + def parse_stuff(src, pos) # stuff : token - # | '(' stuffs ')' - # | '[' stuffs ']' + # | '(' stuffs ')' + # | '[' stuffs ']' # | '{' stuffs '}' - # | '<' stuffs '>' + # | '<' stuffs '>' # ; stuff = nil - if not eos?(src,pos) + if !eos?(src, pos) case src[pos] when :open_paren then stuff, pos = parse_delimited_stuffs(src, pos, :close_paren) when :open_bracket then stuff, pos = parse_delimited_stuffs(src, pos, :close_bracket) @@ -479,14 +492,14 @@ def parse_stuff(src,pos) [stuff, pos] end - def parse_delimited_stuffs(src,pos,closing) - pos += 1 # eat the opening tokenn + def parse_delimited_stuffs(src, pos, closing) + pos += 1 # eat the opening token stuffs = [] - while not eos?(src, pos) and src[pos] != closing + while !eos?(src, pos) && (src[pos] != closing) item, pos = parse_stuff(src, pos) stuffs << item end - if not eos?(src, pos) + if !eos?(src, pos) pos += 1 # skip closing token end op = case closing @@ -495,41 +508,41 @@ def parse_delimited_stuffs(src,pos,closing) when :close_brace then :braces when :gt then :angle_brackets end - [ [op, stuffs], pos ] + [[op, stuffs], pos] end - def parse_stuffs(src,pos) + def parse_stuffs(src, pos) # stuffs : | stuff stuffs ; stuffs = [] - while not eos?(src, pos) + while !eos?(src, pos) stuff, pos = parse_stuff(src, pos) stuffs << stuff unless stuff.nil? end - [ stuffs, pos ] + [stuffs, pos] end - def is_parens(stuff) - stuff.is_a?(Array) and (stuff.length == 2) and (stuff[0] == :parens) + def parens?(stuff) + stuff.is_a?(Array) and (stuff.length == 2) and (stuff[0] == :parens) end def parens_list(stuff) - stuff[1] if is_parens(stuff) + stuff[1] if parens?(stuff) end - def is_brackets(stuff) - stuff.is_a?(Array) and (stuff.length == 2) and (stuff[0] == :brackets) + def brackets?(stuff) + stuff.is_a?(Array) and (stuff.length == 2) and (stuff[0] == :brackets) end def brackets_list(stuff) - stuff[1] if is_brackets(stuff) + stuff[1] if brackets?(stuff) end - def is_token(token) + def token?(token) token.is_a?(Symbol) and (CLexer::OPERATOR_SYMS.index(token) or CLexer::KEYWORDS_SYMS.index(token)) end - def is_identifier(token,name=nil) - if token.is_a?(Array) and (token.length == 2) + def identifier?(token, name = nil) + if token.is_a?(Array) && (token.length == 2) if name.nil? (token[0] == :identifier) else @@ -541,11 +554,11 @@ def is_identifier(token,name=nil) end def identifier_name(token) - token[1] if token.is_a?(Array) and (token[0] == :identifier) + token[1] if token.is_a?(Array) && (token[0] == :identifier) end def token_name(token) - if is_identifier(token) + if identifier?(token) identifier_name(token) elsif token.is_a?(Symbol) token.to_s @@ -556,106 +569,106 @@ def token_name(token) end end - def is_c_calling_convention(stuff) + def c_calling_convention?(stuff) # whether stuff is a C calling convention (listed in @c_calling_conventions). # note: stuff may be either a symbol, a string or an :identifier array. - res = if stuff.is_a?(Symbol) or is_token(stuff) or is_identifier(stuff) - not @c_calling_conventions.index(token_name(stuff)).nil? + res = if stuff.is_a?(Symbol) || token?(stuff) || identifier?(stuff) + !@c_calling_conventions.index(token_name(stuff)).nil? else false end res end - def is_c_attribute(token) + def c_attribute?(token) # whether the token is a C attribute (listed in @c_attributes or in @noreturn_attributes). # note: token may be either a symbol, a string or an :identifier array. if token.is_a?(String) name = token elsif token.is_a?(Symbol) name = token.to_s - elsif is_token(token) or is_identifier(token) + elsif token?(token) || identifier?(token) name = token_name(token) - elsif is_attribute(token) + elsif attribute?(token) name = attribute_name(token) - else + else return false end - res = (@c_attributes.index(name) or - ((not @noreturn_attributes.nil?) and + res = (@c_attributes.index(name) || + (!@noreturn_attributes.nil? && (@noreturn_attributes.any? { |attr_regexp| name =~ /^#{attr_regexp}$/ }))) res end - def make_attribute(namespace,name,arguments,kind) + def make_attribute(namespace, name, arguments, kind) if name.nil? - raise_parse_error "Attribute name should not be nil! #{namespace.inspect}, #{name.inspect}, #{arguments.inspect}" + raise_parse_error "Attribute name should not be nil! #{namespace.inspect}, #{name.inspect}, #{arguments.inspect}" end - [:attribute,namespace,name,arguments,kind] + [:attribute, namespace, name, arguments, kind] end - def is_attribute(object) - (object.is_a?(Array)) and (object.length == 5) and (object[0] == :attribute) + def attribute?(object) + object.is_a?(Array) && (object.length == 5) && (object[0] == :attribute) end def attribute_namespace(attribute) - raise_parse_error "Not an normalized attribute: #{attribute}" unless is_attribute(attribute) + raise_parse_error "Not an normalized attribute: #{attribute}" unless attribute?(attribute) attribute[1] end def attribute_name(attribute) - raise_parse_error "Not an normalized attribute: #{attribute}" unless is_attribute(attribute) + raise_parse_error "Not an normalized attribute: #{attribute}" unless attribute?(attribute) attribute[2] end def attribute_qualified_name(attribute) if attribute_namespace(attribute) - attribute_namespace(attribute) + "::" + attribute_name(attribute) + attribute_namespace(attribute) + '::' + attribute_name(attribute) else attribute_name(attribute) end end def attribute_arguments(attribute) - raise_parse_error "Not an normalized attribute: #{attribute}" unless is_attribute(attribute) + raise_parse_error "Not an normalized attribute: #{attribute}" unless attribute?(attribute) attribute[3] end def attribute_kind(attribute) - raise_parse_error "Not an normalized attribute: #{attribute}" unless is_attribute(attribute) + raise_parse_error "Not an normalized attribute: #{attribute}" unless attribute?(attribute) attribute[4] end - def is_noreturn(attribute) - if is_identifier(attribute) + def noreturn?(attribute) + if identifier?(attribute) @noreturn_attributes.include?(identifier_name(attribute)) - elsif is_attribute(attribute) + elsif attribute?(attribute) @noreturn_attributes.include?(attribute_qualified_name(attribute)) else false end end - def has_noreturn_attribute(attributes) + def noreturn_attribute?(attributes) attributes.any? do |attribute| - is_noreturn(attribute) + noreturn?(attribute) end end - def is_gcc_attribute_syntax(operator, parameters) + def gcc_attribute_syntax?(operator, parameters) # gcc atributes are all of the syntax __attribute__ (...) # where ... is a list of stuffs. - # so is_gcc_attribute_syntax([:identifier,"__attribute__"],[:parens,stuff_list]) - is_identifier(operator,'__attribute__') and is_parens(parameters) + # so gcc_attribute_syntax?([:identifier,"__attribute__"],[:parens,stuff_list]) + identifier?(operator, '__attribute__') && parens?(parameters) # see parse_gcc_attribute end - def is_processable_gcc_attribute(name) - is_c_attribute(name) + def processable_gcc_attribute?(name) + c_attribute?(name) end - def parse_gcc_attribute(op,stuff) + def parse_gcc_attribute(op, stuff) # gcc atributes are all of the syntax __attribute__ (...) # where ... is a list of stuffs. # Here, attribute = [:attribute, [:parens,stuff_list]] @@ -674,69 +687,70 @@ def parse_gcc_attribute(op,stuff) # --> [[:attribute,[:identifier,"access"],[:parens,[[:identifier,"read_write"],[:integer_literal,"1"]]]], # [:attribute,[:identifier,"access"],[:parens,[[:identifier,"read_only"],[:integer_literal,"2"]]]]] - if not (is_identifier(op,'__attribute__') and is_parens(stuff) and is_parens(parens_list(stuff)[0])) - raise_parse_error "Unexpected attribute syntax #{[op,stuff].inspect}" + unless identifier?(op, '__attribute__') && parens?(stuff) && parens?(parens_list(stuff)[0]) + raise_parse_error "Unexpected attribute syntax #{[op, stuff].inspect}" end normalized = [] - j=0 + j = 0 chunks = parens_list(stuff) - while j '::' ] [ '(' ')' ] ; attributes = [] - if not (is_brackets(stuff) and (1 == brackets_list(stuff).length) and is_brackets(brackets_list(stuff)[0])) + unless brackets?(stuff) && (brackets_list(stuff).length == 1) && brackets?(brackets_list(stuff)[0]) raise_parse_error "Unexpected C++ attribute syntax #{stuff.inspect}" + - "\nis_brackets(stuff) = #{is_brackets(stuff)}" + - "\nbrackets_list(stuff).length = #{is_brackets(stuff) ? brackets_list(stuff).length : nil}" + - "\nis_brackets(brackets_list(stuff)[0]) = #{is_brackets(stuff) and brackets_list(stuff).length>1 ? is_brackets(brackets_list(stuff)[0]) : nil}" + "\nbrackets?(stuff) = #{brackets?(stuff)}" + + "\nbrackets_list(stuff).length = #{brackets?(stuff) ? brackets_list(stuff).length : nil}" + + "\nbrackets?(brackets_list(stuff)[0]) = #{ (brackets?(stuff) && (brackets_list(stuff).length > 1)) ? (brackets?(brackets_list(stuff)[0])) : nil}" end - + stuff = brackets_list(brackets_list(stuff)[0]) # Note: for better support for C++, we'd have to update CLexer for C++ tokens. @@ -758,9 +772,9 @@ def parse_cpp_attributes(stuff) # etc (but C++ lexers must be context-sensitive). default_namespace = nil - start=0 - if 30 + guess[:ptr?] = (starc > 0) else # char* are "strings", not "pointers". - guess[:ptr?] = starc>1 + guess[:ptr?] = (starc > 1) end if first_const.nil? # no const: guess[:const?] = false elsif starc == 0 - # const, no star + # const, no star guess[:const?] = true else # const, some star: - before_last_star = type[0..last_star-1].rindex(:mul_op) - + before_last_star = type[0 .. (last_star - 1)].rindex(:mul_op) + if before_last_star.nil? - # a single star: - guess[:const?] = (first_const0) and (not last_const.nil?) and (last_star < last_const)) + guess[:const_ptr?] = ((starc > 0) && !last_const.nil? && (last_star < last_const)) guess end - def parse_function_signature(src,pos) - - # Phase 1: + def parse_function_signature(src, pos) + # Phase 1: # we parse fun_declaration_1 : { stuff } ; # -- this takes care of parentheses, et al. items, pos = parse_stuffs(src, pos) - raise_parse_error "Unparsed characters from position #{pos}" unless pos == src.length + raise_parse_error "Unparsed characters from position #{pos}" unless pos == src.length # Phase 2: # then match from the end of the list of stuffs, @@ -864,25 +876,25 @@ def parse_function_signature(src,pos) # match from the end of the list of stuffs, # for c_calling_conventions, __attributes__ (...) - i = items.length-1 - while is_c_calling_convention(items[i]) or ((3<=i) and is_gcc_attribute_syntax(items[i-1],items[i])) - if is_c_calling_convention(items[i]) + i = items.length - 1 + while c_calling_convention?(items[i]) || ((3 <= i) && gcc_attribute_syntax?(items[i - 1], items[i])) + if c_calling_convention?(items[i]) ccc << [:c_calling_convention, token_name(items[i])] i -= 1 else - attributes += parse_gcc_attribute(items[i-1],items[i]) + attributes += parse_gcc_attribute(items[i - 1], items[i]) i -= 2 end end # then '(' parameters ')' = '(' stuffs ')' - if is_parens(items[i]) + if parens?(items[i]) parameters = parens_list(items[i]) i -= 1 end # then name identifier, - if not is_identifier(items[i]) + unless identifier?(items[i]) raise_parse_error "Expected an identifier but got #{items[i].inspect} as function name in #{items.inspect}" end name = identifier_name(items[i]) @@ -899,28 +911,27 @@ def parse_type(stuff) # to remove 'const' only when it applies to the pointer itself, not when it # applies to the type pointed to. For non-pointer types, remove any # occurrence of 'const'. - arg_info = guess_ptr_and_const(stuff) - @attributes = (stuff.any?{|item| item == :mul_op}) ? @c_attr_noconst : @c_attributes + @attributes = (stuff.any? { |item| item == :mul_op }) ? @c_attr_noconst : @c_attributes type = [] attributes = [] ccc = [] i = 0 - while i foo_t* foo # foo_t* foo[][][] --> foo_t** foo if parameters == [[]] parameters else parameters.map do |parameter| - if is_parens(parameter) - [:parens] + replace_arrays_by_pointers_in_parameters([parens_list(parameter)]) - elsif parameter.is_a?(Array) - i = parameter.rindex{|item| not is_brackets(item)} - if i.nil? then - # all items are brackets + if parens?(parameter) + [:parens] + replace_arrays_by_pointers_in_parameters([parens_list(parameter)]) + elsif parameter.is_a?(Array) + i = parameter.rindex { |item| !brackets?(item) } + if i.nil? + # all items are brackets raise_parse_error "All items are brackets parameter=#{parameter.inspect}" - elsif i == parameter.length-1 then + elsif i == parameter.length-1 # no item is a brackets - parameter + parameter else - # some brackets, remove them and insert * before the name - # Note: int foo[3][4][5] --> int* foo - parameter[0,i] + [:mul_op] + [parameter[i]] + # some brackets, remove them and insert * before the name + # Note: int foo[3][4][5] --> int* foo + parameter[0, i] + [:mul_op] + [parameter[i]] end.map do |item| # recurse into parens groups: - if is_parens(item) + if parens?(item) [:parens] + replace_arrays_by_pointers_in_parameters([parens_list(item)]) - else - item + else + item end end else @@ -1033,7 +1043,7 @@ def replace_arrays_by_pointers_in_parameters(parameters) # [:brackets, [[:integer_literal, "4"]]]]], # [:parens, [:int, [:identifier, "x"]]] ], # [ :int, [:identifier, "a"] ] ]) -# ==> +# ==> # [[:int, :mul_op, [:identifier, "b"]], # [:int, :mul_op, [:identifier, "c"]], # [:int, @@ -1041,19 +1051,18 @@ def replace_arrays_by_pointers_in_parameters(parameters) # [:parens, [:int, [:identifier, "x"]]]], # [:int, [:identifier, "a"]]] - def replace_function_pointers_by_custom_types(parameters) parameters.map do |parameter| plen = parameter.length - if 20) ? spec[0..ptrindex-1] : [] - funcname = spec[ptrindex+1..-1] + funcdecl = (ptrindex > 0) ? spec[0 .. ptrindex - 1] : [] + funcname = spec[ptrindex + 1 .. -1] constindex = funcname.index(:const) if constindex funcname.delete_at(constindex) @@ -1066,7 +1075,7 @@ def replace_function_pointers_by_custom_types(parameters) raise_parse_error "Invalid syntax for function parameter #{parameter.inspect}" end - elsif is_identifier(parameter[-2]) # ...foo() + elsif identifier?(parameter[-2]) # ...foo() funcdecl = [] funcname = [parameter[-2]] @@ -1076,12 +1085,12 @@ def replace_function_pointers_by_custom_types(parameters) raise_parse_error "Invalid syntax for function parameter #{parameter.inspect}" end - functype = [:identifier,"cmock_#{@parse_project[:module_name]}_func_ptr#{@parse_project[:typedefs].size + 1}"] + functype = [:identifier, "cmock_#{@parse_project[:module_name]}_func_ptr#{@parse_project[:typedefs].size + 1}"] funcret = parameter[0..-3] funcargs = parameter[-1] # add typedef for function pointer - @parse_project[:typedefs] << "typedef #{unparse(funcret)}(#{unparse(funcdecl+[:mul_op]+[functype])})#{unparse(funcargs)};".gsub(/\(\*\s+/,'(*').gsub(/\s+\*/,'*').gsub(/\s+,/,',') + @parse_project[:typedefs] << "typedef #{unparse(funcret)}(#{unparse(funcdecl + [:mul_op] + [functype])})#{unparse(funcargs)};".gsub(/\(\*\s+/,'(*').gsub(/\s+\*/, '*').gsub(/\s+,/, ',') funcname = [[:identifier, "cmock_arg#{@c += 1}"]] if funcname.empty? [functype] + funcconst + funcname else @@ -1090,14 +1099,14 @@ def replace_function_pointers_by_custom_types(parameters) end end - def is_anonymous_parameter(parameter) + def anonymous_parameter?(parameter) parameter = parameter.reject { |token| [:struct, :union, :enum, :const, :mul_op].include?(token) } - if (parameter.length == 0) + if parameter.length == 0 true - elsif (parameter.length == 1) - not (parameter[0] == :ellipsis) + elsif parameter.length == 1 + !(parameter[0] == :ellipsis) else - not is_identifier(parameter[-1]) + !identifier?(parameter[-1]) end end @@ -1105,7 +1114,7 @@ def add_names_to_anonymous_parameters(parameters) parameters.map do |parameter| if parameter.nil? nil - elsif is_anonymous_parameter(parameter) + elsif anonymous_parameter?(parameter) parameter << [:identifier, "cmock_arg#{@c += 1}"] else parameter @@ -1114,23 +1123,23 @@ def add_names_to_anonymous_parameters(parameters) end def parameter_unwrap_superfluous_parentheses(parameter) - pc = parameter.count { |item| is_parens(item) } - if (pc == 1) and is_parens(parameter[-1]) and - (parens_list(parameter[-1]).length == 1) and - is_parens(parens_list(parameter[-1])[0]) - # ... ((...)) --> unwrap ... (...) + pc = parameter.count { |item| parens?(item) } + if (pc == 1) && parens?(parameter[-1]) && + (parens_list(parameter[-1]).length == 1) && + parens?(parens_list(parameter[-1])[0]) + # ... ((...)) --> unwrap ... (...) parameter_unwrap_superfluous_parentheses(parameter[0..-2] + parens_list(parameter[-1])) - elsif (pc == 1) and is_parens(parameter[-1]) and - (parens_list(parameter[-1]).length == 2) and - is_parens(parens_list(parameter[-1])[0]) and - is_parens(parens_list(parameter[-1])[1]) + elsif (pc == 1) && parens?(parameter[-1]) && + (parens_list(parameter[-1]).length == 2) && + parens?(parens_list(parameter[-1])[0]) && + parens?(parens_list(parameter[-1])[1]) # ... ((...)(...)) --> unwrap ... (...)(...) parameter_unwrap_superfluous_parentheses(parameter[0..-2] + [parens_list(parameter[-1])[0]] + [parens_list(parameter[-1])[1]]) - elsif (pc == 2) and is_parens(parameter[-2]) and is_parens(parameter[-1]) and - (parens_list(parameter[-2]).length == 1) and - is_parens(parens_list(parameter[-2])[0]) + elsif (pc == 2) && parens?(parameter[-2])&& parens?(parameter[-1]) && + (parens_list(parameter[-2]).length == 1) && + parens?(parens_list(parameter[-2])[0]) # ... ((...)) (...) --> unwrap ... (...) (...) parameter_unwrap_superfluous_parentheses(parameter[0..-3] + parens_list(parameter[-2]) + parameter[-1]) else @@ -1145,7 +1154,7 @@ def clean_args(parameters) # [ :int, [:identifier, "c"], [:brackets, [:integer_literal, "5"]] ], # [ :int, [:identifier, "a"] ] ] - if parameters.empty? or ((parameters.length == 1) and @local_as_void.include?(unparse(parameters[0]))) + if parameters.empty? || ((parameters.length == 1) && @local_as_void.include?(unparse(parameters[0]))) [:void] else @c = 0 @@ -1163,11 +1172,11 @@ def clean_args(parameters) # magically turn brackets into asterisks, also match for parentheses that come from macros parameters = replace_arrays_by_pointers_in_parameters(parameters) - # scan argument list for function pointers and replace them with custom types + # scan argument list for function pointers and replace them with custom types # scan argument list for function pointers with shorthand notation and replace them with custom types # Note: if I'm not wrong, this new code using tokens handles both cases, with and without funcdecl. # parameters=[[:unsigned, :int, [:parens, [:mul_op, [:identifier, "func_ptr"]]], [:parens, [:int, :comma, :char]]]] - parameters=replace_function_pointers_by_custom_types(parameters) + parameters = replace_function_pointers_by_custom_types(parameters) # automatically name unnamed arguments (those that only had a type) parameters = add_names_to_anonymous_parameters(parameters) @@ -1179,8 +1188,8 @@ def clean_args(parameters) end end - def is_string_type(type) - (type.length>=2) and (type[0]==:char) and (type[1]==:mul_op) + def string_type?(type) + (type.length >= 2) && (type[0] == :char) && (type[1] == :mul_op) end def parse_args(parameters) @@ -1188,15 +1197,15 @@ def parse_args(parameters) # so they're each of the form :void, :ellipsis, or [type... name] args = [] parameters.each do |parameter| - return args if (parameter == :void) or (parameter == [:ellipsis]) + return args if (parameter == :void) || (parameter == [:ellipsis]) - if parameter.nil? or (parameter.length<2) + if parameter.nil? || (parameter.length < 2) raise_parse_error "Invalid parameter #{parameter.inspect} in #{parameters.inspect}" else - type=parameter[0..-2] - name=parameter[-1] + type = parameter[0..-2] + name = parameter[-1] type, _, _, arg_info = parse_type(type) - arg_info[:name]=identifier_name(name) + arg_info[:name] = identifier_name(name) arg_info.delete(:modifier) # don't care about this arg_info.delete(:noreturn) # don't care about this arg_info.delete(:c_calling_convention) # don't care about this @@ -1207,8 +1216,8 @@ def parse_args(parameters) arg_info[:type] = "#{@treat_as_array[arg_info[:type]]}*" arg_info[:ptr?] = true arg_info[:type] = "const #{arg_info[:type]}" if arg_info[:const?] - elsif arg_info[:ptr?] or is_string_type(type) - if arg_info[:const?] + elsif arg_info[:ptr?] || string_type?(type) + if arg_info[:const?] arg_info[:type] = "const #{arg_info[:type]}" end end @@ -1230,7 +1239,7 @@ def parse_args(parameters) args end - + def parse_declaration(declaration, namespace = [], classname = nil) decl = {} decl[:namespace] = namespace @@ -1241,15 +1250,15 @@ def parse_declaration(declaration, namespace = [], classname = nil) # Split declaration into type, name, parameters, attributes, and calling convention - type, name, parameters, p_attributes, p_ccc = parse_function_signature(decl_tokens, 0) + type, name, parameters, p_attributes, _ = parse_function_signature(decl_tokens, 0) # Process function attributes, return type, and name # Some attributes may be written after the parameter list, so we need to # check for them and move them to the front of the declaration. - type, attributes, ccc, parsed = parse_type(type) + type, attributes, _, parsed = parse_type(type) attributes += p_attributes - ccc += p_ccc - + # ccc += p_ccc + # Record original name without scope prefix decl[:unscoped_name] = name @@ -1263,28 +1272,28 @@ def parse_declaration(declaration, namespace = [], classname = nil) decl[:name] << '_' unless decl[:name].empty? decl[:name] << decl[:unscoped_name] - decl[:noreturn] = has_noreturn_attribute(attributes) + decl[:noreturn] = noreturn_attribute?(attributes) if decl[:noreturn] - attributes.delete_if{|attribute| is_noreturn(attribute)} + attributes.delete_if { |attribute| noreturn?(attribute) } end if parsed[:ptr?] - if parsed[:const?] - type = [:const] + type unless type[0]==:const - attributes.delete_if{|attr| attribute_name(attr) == "const"} - end + if parsed[:const?] + type = [:const] + type unless type[0] ==:const + attributes.delete_if { |attr| attribute_name(attr) == 'const' } + end end # TODO: perhaps we need a specific configuration, or use strippable to select the __attributes__ to remove. - attributes.delete_if{|attribute| is_attribute(attribute) and (attribute_kind(attribute) == :gcc)} + attributes.delete_if { |attribute| attribute?(attribute) && (attribute_kind(attribute) == :gcc) } decl[:modifier] = unparse(attributes.uniq) - if not (parsed[:c_calling_convention].nil? or parsed[:c_calling_convention].empty?) - decl[:c_calling_convention] = parsed[:c_calling_convention] + if !(parsed[:c_calling_convention].nil? || parsed[:c_calling_convention].empty?) + decl[:c_calling_convention] = parsed[:c_calling_convention] end - rettype = unparse(type).gsub(/\s+\*/,'*') + rettype = unparse(type).gsub(/\s+\*/, '*') rettype = 'void' if @local_as_void.include?(rettype.strip) decl[:return] = { :type => rettype, :name => 'cmock_to_return', @@ -1308,12 +1317,12 @@ def parse_declaration(declaration, namespace = [], classname = nil) if passign.nil? parameter else - parameter[0..passign-1] + parameter[0 .. passign - 1] end } # check for var args if parameters[-1] == [:ellipsis] - decl[:var_arg] = "..." + decl[:var_arg] = '...' parameters.pop if parameters.empty? parameters = [:void] @@ -1326,7 +1335,7 @@ def parse_declaration(declaration, namespace = [], classname = nil) raise_parse_error "Invalid parameters #{parameters.inspect}" end parameters = clean_args(parameters) - decl[:args_string] = parameters.map{|parameter| unparse(parameter)}.join(', ').gsub(/\s+\*/, '*') + decl[:args_string] = parameters.map { |parameter| unparse(parameter) }.join(', ').gsub(/\s+\*/, '*') decl[:args] = parse_args(parameters) decl[:args_call] = decl[:args].map { |a| a[:name] }.join(', ') decl[:contains_ptr?] = decl[:args].inject(false) { |ptr, arg| arg[:ptr?] ? true : ptr } @@ -1355,11 +1364,11 @@ def prototype_inspect_array_of_hashes(array) array.each { |hash| hashes << prototype_inspect_hash(hash) } case array.size when 0 - return '[]' + '[]' when 1 - return "[#{hashes[0]}]" + "[#{hashes[0]}]" else - return "[\n #{hashes.join("\n ")}\n ]\n" + "[\n #{hashes.join("\n ")}\n ]\n" end end diff --git a/test/unit/cmock_header_parser_test.rb b/test/unit/cmock_header_parser_test.rb index 4cd66e52..822d7cb5 100644 --- a/test/unit/cmock_header_parser_test.rb +++ b/test/unit/cmock_header_parser_test.rb @@ -2956,53 +2956,45 @@ class Classy { it "builds parser on sound bases" do - assert(@parser.is_parens([:parens,[[:identifier,"a"]]]),"is_parens identifies parens") - refute(@parser.is_parens([:brackets,[[:identifier,"a"]]]),"is_parens rejects brackets") - refute(@parser.is_parens([:identifier,"Foo"]),"is_parens rejects identifier") + assert(@parser.parens?([:parens,[[:identifier,"a"]]]),"parens? identifies parens") + refute(@parser.parens?([:brackets,[[:identifier,"a"]]]),"parens? rejects brackets") + refute(@parser.parens?([:identifier,"Foo"]),"parens? rejects identifier") assert_equal([[:identifier,"a"],[:integer_literal,"42"]], @parser.parens_list([:parens,[[:identifier,"a"],[:integer_literal,"42"]]]), "parens_list returns list of elements in parens") - assert(@parser.is_brackets([:brackets,[[:identifier,"a"]]]),"is_brackets identifies brackets") - refute(@parser.is_brackets([:parens,[[:identifier,"a"]]]),"is_brackets rejects parens") - refute(@parser.is_brackets([:identifier,"Foo"]),"is_brackets rejects identifier") + assert(@parser.brackets?([:brackets,[[:identifier,"a"]]]),"brackets? identifies brackets") + refute(@parser.brackets?([:parens,[[:identifier,"a"]]]),"brackets? rejects parens") + refute(@parser.brackets?([:identifier,"Foo"]),"brackets? rejects identifier") assert_equal([[:identifier,"a"],[:integer_literal,"42"]], @parser.brackets_list([:brackets,[[:identifier,"a"],[:integer_literal,"42"]]]), "brackets_list returns list of elements in brackets") - - # assert(@parser.is_braces([:braces,[[:identifier,"a"]]]),"is_braces identifies braces") - # refute(@parser.is_braces([:brackets,[[:identifier,"a"]]]),"is_braces rejects brackets") - # refute(@parser.is_braces([:identifier,"Foo"]),"is_braces rejects identifier") - # assert_equal([[:identifier,"a"],[:integer_literal,"42"]], - # @parser.braces_list([:braces,[[:identifier,"a"],[:integer_literal,"42"]]]), - # "braces_list returns list of elements in braces") - - assert(@parser.is_identifier([:identifier,"Foo"]),"is_identifier identifies identifier") - assert(@parser.is_identifier([:identifier,"Foo"],"Foo"),"is_identifier identifies identifier with name") - refute(@parser.is_identifier([:identifier,"Foo"],"Bar"),"is_identifier rejects identifier with wrong name") - refute(@parser.is_identifier(:bar,"Bar"),"is_identifier rejects non-identifier") - refute(@parser.is_identifier(:bar),"is_identifier rejects non-identifier") + assert(@parser.identifier?([:identifier,"Foo"]),"identifier? identifies identifier") + assert(@parser.identifier?([:identifier,"Foo"],"Foo"),"identifier? identifies identifier with name") + refute(@parser.identifier?([:identifier,"Foo"],"Bar"),"identifier? rejects identifier with wrong name") + refute(@parser.identifier?(:bar,"Bar"),"identifier? rejects non-identifier") + refute(@parser.identifier?(:bar),"identifier? rejects non-identifier") assert_equal("Foo",@parser.identifier_name([:identifier,"Foo"]),"identifier_name returns name of identifier") - assert(@parser.is_c_calling_convention([:identifier,"__stdcall"]),"is_c_calling_convention should identify __stdcall") - refute(@parser.is_c_calling_convention([:identifier,"callfoo"]),"is_c_calling_convention should refute callfoo") - assert(@parser.is_c_calling_convention(:__stdcall),"is_c_calling_convention should accept :__stdcall") + assert(@parser.c_calling_convention?([:identifier,"__stdcall"]),"c_calling_convention? should identify __stdcall") + refute(@parser.c_calling_convention?([:identifier,"callfoo"]),"c_calling_convention? should refute callfoo") + assert(@parser.c_calling_convention?(:__stdcall),"c_calling_convention? should accept :__stdcall") - assert(@parser.is_c_attribute(:const),"is_c_attribute should identify :const") - assert(@parser.is_c_attribute([:identifier,"const"]),"is_c_attribute should identify [:identifier, 'const']") - assert(@parser.is_c_attribute([:identifier,"__ramfunc"]),"is_c_attribute should identify [:identifier, '__ramfunc']") - refute(@parser.is_c_attribute([:identifier,"__attribute__"]),"is_c_attribute should refute [:identifier, '__attribute__']") - refute(@parser.is_c_attribute(:conste),"is_c_attribute should refute :constes") - assert(@parser.is_c_attribute([:identifier,"noreturn"]),"is_c_attribute should identify [:identifier, 'noreturn']") - assert(@parser.is_c_attribute(:noreturn),"is_c_attribute should identify :noreturn") + assert(@parser.c_attribute?(:const),"c_attribute? should identify :const") + assert(@parser.c_attribute?([:identifier,"const"]),"c_attribute? should identify [:identifier, 'const']") + assert(@parser.c_attribute?([:identifier,"__ramfunc"]),"c_attribute? should identify [:identifier, '__ramfunc']") + refute(@parser.c_attribute?([:identifier,"__attribute__"]),"c_attribute? should refute [:identifier, '__attribute__']") + refute(@parser.c_attribute?(:conste),"c_attribute? should refute :constes") + assert(@parser.c_attribute?([:identifier,"noreturn"]),"c_attribute? should identify [:identifier, 'noreturn']") + assert(@parser.c_attribute?(:noreturn),"c_attribute? should identify :noreturn") - assert(@parser.is_gcc_attribute_syntax([:identifier,"__attribute__"],[:parens,[[:parens,[[:identifier,"noreturn"]]]]]), - "is_gcc_attribute_syntax identifies parsed __attribute__((noreturn))") + assert(@parser.gcc_attribute_syntax?([:identifier,"__attribute__"],[:parens,[[:parens,[[:identifier,"noreturn"]]]]]), + "gcc_attribute_syntax? identifies parsed __attribute__((noreturn))") - assert(@parser.is_attribute([:attribute,nil,"noreturn",nil,:gcc]), - "is_attribute identifies [:attribute,nil,\"noreturn\",nil,:gcc]") + assert(@parser.attribute?([:attribute,nil,"noreturn",nil,:gcc]), + "attribute? identifies [:attribute,nil,\"noreturn\",nil,:gcc]") end From 453c915ce91bbb5a2f2a0af5a858aa2ff0341435 Mon Sep 17 00:00:00 2001 From: "Pascal J. Bourguignon" Date: Thu, 10 Aug 2023 12:44:31 +0200 Subject: [PATCH 20/21] Removed including standard libraries from mock sources. --- lib/cmock_file_writer.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/cmock_file_writer.rb b/lib/cmock_file_writer.rb index ee198061..e4e1c82d 100644 --- a/lib/cmock_file_writer.rb +++ b/lib/cmock_file_writer.rb @@ -22,8 +22,7 @@ def create_file(filename, subdir) full_file_name_temp = "#{@config.mock_path}/#{subdir + '/' if subdir}#{filename}.new" full_file_name_done = "#{@config.mock_path}/#{subdir + '/' if subdir}#{filename}" - puts "Creating #{full_file_name_done.inspect}" unless @config.verbosity < 2 - + $stderr.puts "Creating #{full_file_name_done.inspect}" unless (@config.verbosity < 2) File.open(full_file_name_temp, 'w') do |file| yield(file, filename) end From f2b213d7aabae164c59d57fccdf7efe2e5651085 Mon Sep 17 00:00:00 2001 From: "Pascal J. Bourguignon" Date: Fri, 11 Aug 2023 10:29:16 +0200 Subject: [PATCH 21/21] Added message when creating mock and skeleton. --- lib/cmock.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/cmock.rb b/lib/cmock.rb index dc8a2028..5085a4ff 100644 --- a/lib/cmock.rb +++ b/lib/cmock.rb @@ -44,11 +44,13 @@ def setup_skeletons(files) def generate_mock(src, folder) name = File.basename(src, '.*') ext = File.extname(src) + puts "Creating mock for #{name}, from #{src}" unless @silent @cm_generator.create_mock(name, @cm_parser.parse(name, File.read(src), src), ext, folder, src) end def generate_skeleton(src) name = File.basename(src, '.*') + puts "Creating skeleton for #{name}, from #{src}" unless @silent @cm_generator.create_skeleton(name, @cm_parser.parse(name, File.read(src), src), src) end end