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 +*~ diff --git a/docs/CMock_Summary.md b/docs/CMock_Summary.md index e63a065c..43dfa2cc 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 --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`. @@ -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 diff --git a/lib/CLexer.rb b/lib/CLexer.rb new file mode 100644 index 00000000..13beacea --- /dev/null +++ b/lib/CLexer.rb @@ -0,0 +1,160 @@ +# +# 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 + 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(&:to_sym).freeze + + def initialize(input) + @input = input + @tokens = [] + end + + def tokenize + while @input.size.positive? + 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].inspect}" + end + end + + @tokens + end + + private + + def consume_multiline_comment + while @input.size.positive? + case @input + when /\A\*\// + @input = $' + break + when /\A./m + @input = $' + end + end + end +end + +def example + input = File.read('tee.c') + lexer = CLexer.new(input) + tokens = lexer.tokenize + puts tokens.inspect +end diff --git a/lib/cmock.rb b/lib/cmock.rb index 72f86418..dc8a2028 100644 --- a/lib/cmock.rb +++ b/lib/cmock.rb @@ -44,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(name, File.read(src)), ext, folder) + @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}..." unless @silent - @cm_generator.create_skeleton(name, @cm_parser.parse(name, File.read(src))) + @cm_generator.create_skeleton(name, @cm_parser.parse(name, File.read(src), src), src) end end diff --git a/lib/cmock_config.rb b/lib/cmock_config.rb index c7db9451..aec5a8e0 100644 --- a/lib/cmock_config.rb +++ b/lib/cmock_config.rb @@ -16,6 +16,9 @@ class CMockConfig :subdir => nil, :plugins => [], :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_calling_conventions => %w[__stdcall __cdecl __fastcall], :enforce_strict_ordering => false, @@ -32,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_file_writer.rb b/lib/cmock_file_writer.rb index f30c44b2..ee198061 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}" + 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..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 =~ / TypeSanitizer.sanitize_c_identifier(mock_name), :folder => mock_folder, :parsed_stuff => parsed_stuff, - :skeleton => false + :skeleton => false, + :source => src } create_mock_subdir(mock_project) @@ -76,7 +79,9 @@ def create_mock(module_name, parsed_stuff, module_ext = nil, folder = nil) create_mock_source_file(mock_project) end - def create_skeleton(module_name, parsed_stuff) + def create_skeleton(module_name, parsed_stuff, src = nil) + $stderr.puts "Creating skeleton for #{src ? src : module_name}..." unless @silent + mock_project = { :module_name => module_name, :module_ext => '.h', @@ -89,6 +94,30 @@ def create_skeleton(module_name, parsed_stuff) private if $ThisIsOnlyATest.nil? ############################## + def is_noreturn?(function) + function[:noreturn] + 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 @@ -100,11 +129,14 @@ 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 @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" create_mock_header_header(file, filename, mock_project) create_mock_header_service_call_declarations(file, mock_project) create_typedefs(file, mock_project) @@ -118,6 +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" create_source_header_section(file, filename, mock_project) create_instance_structure(file, mock_project) create_extern_declarations(file) @@ -209,11 +242,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 +311,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 @@ -342,7 +370,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 @@ -374,7 +402,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 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" 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/lib/cmock_header_parser.rb b/lib/cmock_header_parser.rb index 15224349..fe80e69f 100644 --- a/lib/cmock_header_parser.rb +++ b/lib/cmock_header_parser.rb @@ -1,20 +1,31 @@ +# 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 :noreturn_attributes, :process_gcc_attributes, :process_cpp_attributes, :c_calling_conventions + attr_accessor :parse_project 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_attributes = ['const'] + @c_attr_noconst @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,10 +38,22 @@ 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) + # 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 + else + raise "#{@parse_project[:source_path]}:1: Failed Parsing Declaration Prototype!" + "\n" + message + end end - def parse(name, source) - parse_project = { + def parse(name, source, src_path = nil) + $stderr.puts "Parsing #{src_path}" if !src_path.nil? && (@verbosity >= 1) + @parse_project = { + :source_path => src_path, :module_name => name.gsub(/\W/, ''), :typedefs => [], :functions => [], @@ -39,29 +62,29 @@ 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 - transform_inline_functions(source) - else - '' - end + @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 - 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 @@ -73,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 @@ -102,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 @@ -177,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 @@ -191,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 @@ -203,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 @@ -213,20 +243,24 @@ 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) - # 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 @@ -241,15 +275,20 @@ 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 @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*\{/, '') 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 @@ -266,9 +305,9 @@ def import_source(source, parse_project, 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 @@ -306,6 +345,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 +411,856 @@ def parse_functions(source) funcs end - def parse_type_and_name(arg) + # 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 !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 + + def parse_delimited_stuffs(src, pos, closing) + pos += 1 # eat the opening token + stuffs = [] + while !eos?(src, pos) && (src[pos] != closing) + item, pos = parse_stuff(src, pos) + stuffs << item + end + if !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 !eos?(src, pos) + stuff, pos = parse_stuff(src, pos) + stuffs << stuff unless stuff.nil? + end + [stuffs, pos] + end + + def parens?(stuff) + stuff.is_a?(Array) and (stuff.length == 2) and (stuff[0] == :parens) + end + + def parens_list(stuff) + stuff[1] if parens?(stuff) + end + + def brackets?(stuff) + stuff.is_a?(Array) and (stuff.length == 2) and (stuff[0] == :brackets) + end + + def brackets_list(stuff) + stuff[1] if brackets?(stuff) + end + + def token?(token) + token.is_a?(Symbol) and (CLexer::OPERATOR_SYMS.index(token) or CLexer::KEYWORDS_SYMS.index(token)) + end + + def identifier?(token, name = nil) + if token.is_a?(Array) && (token.length == 2) + if name.nil? + (token[0] == :identifier) + else + (token[0] == :identifier) and (token[1] == name) + end + else + false + end + end + + def identifier_name(token) + token[1] if token.is_a?(Array) && (token[0] == :identifier) + end + + def token_name(token) + if 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 + + 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) || token?(stuff) || identifier?(stuff) + !@c_calling_conventions.index(token_name(stuff)).nil? + else + false + end + res + end + + 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 token?(token) || identifier?(token) + name = token_name(token) + elsif attribute?(token) + name = attribute_name(token) + else + return false + end + + 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) + 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 + + 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 attribute?(attribute) + attribute[1] + end + + def attribute_name(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) + else + attribute_name(attribute) + end + end + + def attribute_arguments(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 attribute?(attribute) + attribute[4] + end + + def noreturn?(attribute) + if identifier?(attribute) + @noreturn_attributes.include?(identifier_name(attribute)) + elsif attribute?(attribute) + @noreturn_attributes.include?(attribute_qualified_name(attribute)) + else + false + end + end + + def noreturn_attribute?(attributes) + attributes.any? do |attribute| + noreturn?(attribute) + end + end + + def gcc_attribute_syntax?(operator, parameters) + # gcc atributes are all of the syntax __attribute__ (...) + # where ... is a list of stuffs. + # so gcc_attribute_syntax?([:identifier,"__attribute__"],[:parens,stuff_list]) + identifier?(operator, '__attribute__') && parens?(parameters) + # see parse_gcc_attribute + end + + def processable_gcc_attribute?(name) + 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"]]]]] + + unless identifier?(op, '__attribute__') && parens?(stuff) && parens?(parens_list(stuff)[0]) + raise_parse_error "Unexpected attribute syntax #{[op, stuff].inspect}" + end + normalized = [] + j = 0 + chunks = parens_list(stuff) + while j < chunks.length + chunk = chunks[j] + j += 1 + if chunk == :comma + next + end + items = parens_list(chunk) + i = 0 + name = nil + while i < items.length + thing = items[i] + i += 1 + if name.nil? + if thing == :comma + # wait for next + elsif identifier?(thing) + name = thing + elsif thing.is_a?(Symbol) + name = thing + else + raise_parse_error "Unexpected attribute syntax #{attribute.inspect}" + end + else + if thing == :comma + if processable_gcc_attribute?(token_name(name)) + normalized << make_attribute(nil, token_name(name), nil, :gcc) + end + name = nil + elsif parens?(thing) + if processable_gcc_attribute?(token_name(name)) + normalized << make_attribute(nil, token_name(name), thing, :gcc) + end + name = nil + if (i < items.length) && (items[i] == :comma) + i += 1 + end + else + raise_parse_error "Unexpected attribute syntax #{attribute.inspect}" + end + end + end + unless name.nil? + if processable_gcc_attribute?(token_name(name)) + normalized << make_attribute(nil, token_name(name), nil, :gcc) + end + name = nil + end + end + normalized + end + + def cpp_attribute_syntax?(stuff) + brackets?(stuff) and (brackets_list(stuff).length == 1) and + brackets?(brackets_list(stuff)[0]) + end + + def processable_cpp_attribute?(name) + c_attribute?(name) + end + + def parse_cpp_attributes(stuff) + # stuff = '[[' [ 'using' ':' ] { ',' } ']]' ; + # attribute = [ '::' ] [ '(' ')' ] ; + attributes = [] + + unless brackets?(stuff) && (brackets_list(stuff).length == 1) && brackets?(brackets_list(stuff)[0]) + raise_parse_error "Unexpected C++ attribute syntax #{stuff.inspect}" + + "\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. + # 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 (3 < stuff.length) && (identifier?(stuff[0]) || token?(stuff[0])) && (token_name(stuff[0]) == 'using') + if identifier?(stuff[1]) && (:colon == stuff[2]) + default_namespace = identifier_name(stuff[1]) + start = 3 + else + raise_parse_error "Invalid using syntax in attributes #{stuff.inspect}" + end + end + + i = start + while i < stuff.length + namespace = default_namespace + name = nil + arguments = nil + + if identifier?(stuff[i]) + if (i + 2 < stuff.length) && (:colon == stuff[i + 1]) && (:colon == stuff[i + 2]) + namespace = identifier_name(stuff[i]) + i += 3 + end + end + if (i < stuff.length) && identifier?(stuff[i]) + name = identifier_name(stuff[i]) + i += 1 + if (i < stuff.length) && parens?(stuff[i]) + # we don't further parse the arguments, this may be done lazily if needed. + arguments = stuff[i] + i += 1 + end + if processable_cpp_attribute?((namespace ? namespace + '::' : '' ) + name) + attributes << make_attribute(namespace, name, arguments, :cpp) + end + if (i < stuff.length) && (:comma == stuff[i]) + i += 1 + end + elsif (i < stuff.length) + raise_parse_error "Unexpected token #{stuff[i].inspect} in C++11 attribute expression #{stuff.inspect}, expected an attribute identifier." + end + end + attributes + end + + def guess_ptr_and_const(type) + # type is a stuffs list + guess = {} + starc = type.count(:mul_op) + first_const = type.index(:const) + last_const = type.rindex(:const) + char = type.index(:char) + last_star = type.rindex(:mul_op) + + if char.nil? + guess[:ptr?] = (starc > 0) + else + # 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_const < last_star) + else + const = type[before_last_star .. last_star].index(:const) + guess[:const?] = !const.nil? + end + end + + # an arg containing "const" after the last * is a constant pointer + guess[:const_ptr?] = ((starc > 0) && !last_const.nil? && (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 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]) + i -= 2 + end + end + + # then '(' parameters ')' = '(' stuffs ')' + if parens?(items[i]) + parameters = parens_list(items[i]) + i -= 1 + end + + # then name identifier, + 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]) + 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.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 + 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 < stuff.length + if (i + 1 < stuff.length) && gcc_attribute_syntax?(stuff[i], stuff[i + 1]) # __attribute__ ( ... ) + if @process_gcc_attributes + attributes += parse_gcc_attribute(stuff[i], stuff[i + 1]) + end + i += 1 + elsif cpp_attribute_syntax?(stuff[i]) # [[ ... ]] + if @process_cpp_attributes + attributes += parse_cpp_attributes(stuff[i]) + end + elsif c_attribute?(stuff[i]) + attributes << make_attribute(nil, token_name(stuff[i]), nil, :c) + elsif c_calling_convention?(stuff[i]) + ccc << [:c_calling_convention, token_name(stuff[i])] else - type_array << word + type << stuff[i] end + i += 1 end if arg_info[:const_ptr?] - attr_array << 'const' - type_array.delete_at(type_array.rindex('const')) + attributes << make_attribute(nil, 'const', nil, :c) + cindex = type.rindex(:const) + type.delete_at(cindex) unless cindex.nil? end - arg_info[:modifier] = attr_array.join(' ') - arg_info[:type] = type_array.join(' ').gsub(/\s+\*/, '*') # remove space before asterisks - arg_info + arg_info[:type] = unparse(type).gsub(/\s+\*/, '*') # remove space before asterisks + arg_info[:modifier] = unparse(attributes.uniq) + arg_info[:c_calling_convention] = unparse(ccc) + + [type, attributes, ccc, arg_info] 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 unparse_inner(stuff) + if CLexer::OPERATOR_SYMS.include?(stuff) + CLexer::OPERATOR_SYMBOLS.key(stuff) + elsif CLexer::KEYWORDS_SYMS.include?(stuff) + stuff.to_s + elsif stuff.nil? + '' + elsif stuff.is_a?(Array) + case stuff[0] + when :identifier, :string_literal, :char_literal, + :integer_literal, :float_literal, :hex_literal then stuff[1] + when :c_calling_convention then "#{unparse_inner(stuff[1])}" + when :parens then "(#{unparse_inner(stuff[1])})" + when :brackets then "[#{unparse_inner(stuff[1])}]" + when :braces then "{#{unparse_inner(stuff[1])}}" + when :angle_brackets then "<#{unparse_inner(stuff[1])}>" + 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 + else stuff.map { |item| unparse_inner(item) }.join(' ') end - - args << arg_info + elsif stuff.is_a?(String) + stuff + else + raise_parse_error "Unexpected stuff #{stuff.inspect} while unparsing #{@unparsing.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 unparse(stuff) + @unparsing = stuff + unparse_inner(stuff) + 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 + 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 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 + # 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 parens?(item) + [:parens] + replace_arrays_by_pointers_in_parameters([parens_list(item)]) + else + item + end + end + else + parameter + end end end - - args 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 +# 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) + parameters.map do |parameter| + plen = parameter.length + if (2 < plen) && parens?(parameter[-1]) # ...x() + if parens?(parameter[-2]) # ...()() + + spec = parens_list(parameter[-2]) + ptrindex = spec.index(:mul_op) + if ptrindex # ...(...*...)() + + funcdecl = (ptrindex > 0) ? 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 identifier?(parameter[-2]) # ...foo() + + funcdecl = [] + funcname = [parameter[-2]] + funcconst = [] - true + else + 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}"] + 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 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) + def anonymous_parameter?(parameter) + parameter = parameter.reject { |token| [:struct, :union, :enum, :const, :mul_op].include?(token) } + if parameter.length == 0 true + elsif parameter.length == 1 + !(parameter[0] == :ellipsis) else - false + !identifier?(parameter[-1]) end end - def divine_ptr_and_const(arg) - divination = {} - - divination[:ptr?] = divine_ptr(arg) - divination[:const?] = divine_const(arg) - - # an arg containing "const" after the last * is a constant pointer - divination[:const_ptr?] = /\*(?!.*\*)\s*const(\s|$)/ =~ arg ? true : false + def add_names_to_anonymous_parameters(parameters) + parameters.map do |parameter| + if parameter.nil? + nil + elsif anonymous_parameter?(parameter) + parameter << [:identifier, "cmock_arg#{@c += 1}"] + else + parameter + end + end + end - divination + def parameter_unwrap_superfluous_parentheses(parameter) + 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) && 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) && 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 + parameter + end end - def clean_args(arg_list, parse_project) - if @local_as_void.include?(arg_list.strip) || arg_list.empty? - 'void' + def clean_args(parameters) + # 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? || ((parameters.length == 1) && @local_as_void.include?(unparse(parameters[0]))) + [:void] else - c = 0 + @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 - 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') + parameters = replace_arrays_by_pointers_in_parameters(parameters) # 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 ' - end - if funcdecl != '' - funcdecl += ' ' - end - parse_project[:typedefs] << "typedef #{funcret}(#{funcdecl}*#{functype})(#{funcargs});" - funcname = "cmock_arg#{c += 1}" if funcname.empty? - "#{functype} #{funconst}#{funcname}" + # 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) + + # 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 - # scan argument list for function pointers with shorthand notation and replace them with custom types - arg_list.gsub!(/([\w\s\*]+)\s+(\w+)\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 - funcname = Regexp.last_match(2).strip - funcargs = Regexp.last_match(3).strip - funconst = '' - if funcname.include? 'const' - funcname.gsub!('const', '').strip! - funconst = 'const ' + def string_type?(type) + (type.length >= 2) && (type[0] == :char) && (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) || (parameter == [:ellipsis]) + + 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, _, _, 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?] || string_type?(type) + if arg_info[:const?] + arg_info[:type] = "const #{arg_info[:type]}" + end end - parse_project[:typedefs] << "typedef #{funcret}(*#{functype})(#{funcargs});" - funcname = "cmock_arg#{c += 1}" if funcname.empty? - "#{functype} #{funconst}#{funcname}" + + args << arg_info end + end - # 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 - end - end.join(', ') + # 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) + def parse_declaration(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, _ = 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, _, 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 +1272,28 @@ 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[:noreturn] = noreturn_attribute?(attributes) + if decl[:noreturn] + 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 + end + + # TODO: perhaps we need a specific configuration, or use strippable to select the __attributes__ to remove. + attributes.delete_if { |attribute| attribute?(attribute) && (attribute_kind(attribute) == :gcc) } + + decl[:modifier] = unparse(attributes.uniq) + + if !(parsed[:c_calling_convention].nil? || parsed[:c_calling_convention].empty?) decl[:c_calling_convention] = parsed[:c_calling_convention] end - rettype = parsed[:type] + rettype = unparse(type).gsub(/\s+\*/, '*') rettype = 'void' if @local_as_void.include?(rettype.strip) decl[:return] = { :type => rettype, :name => 'cmock_to_return', @@ -573,34 +1303,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) + 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 @@ -617,11 +1364,12 @@ 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 + 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/rakefile_helper.rb b/test/rakefile_helper.rb index 3d898905..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)) + 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/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..f1f70949 --- /dev/null +++ b/test/system/test_compilation/noreturn.h @@ -0,0 +1,12 @@ +#ifndef noreturn_h +#define noreturn_h + +#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)); + +#endif diff --git a/test/unit/cmock_generator_main_test.rb b/test/unit/cmock_generator_main_test.rb index 1a66c0a5..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 = { @@ -343,9 +345,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 +485,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 +502,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_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_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" + diff --git a/test/unit/cmock_header_parser_test.rb b/test/unit/cmock_header_parser_test.rb index 43832ff8..822d7cb5 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 :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"} @@ -26,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 = { @@ -35,6 +40,9 @@ :functions => [], :normalized_source => nil } + + @parser.parse_project = @test_project + end after do @@ -42,7 +50,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,12 +61,12 @@ "who // is you\n" expected = - [ - "abcd", - "who" - ] + [ + "abcd", + "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 @@ -83,15 +91,15 @@ "//*/\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}) + assert_equal(expected, @parser.import_source(source).map!{|s|s.strip}) end it "remove strippables from the beginning or end of function declarations" do @@ -105,16 +113,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)) + assert_equal(expected, @parser.import_source(source)) 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,13 +133,13 @@ " 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)) + assert_equal(expected, @parser.import_source(source)) end it "remove preprocessor directives" do @@ -142,7 +150,7 @@ expected = [] - assert_equal(expected, @parser.import_source(source, @test_project)) + assert_equal(expected, @parser.import_source(source)) end @@ -157,7 +165,7 @@ expected = ["foo"] - assert_equal(expected, @parser.import_source(source, @test_project)) + assert_equal(expected, @parser.import_source(source)) end @@ -167,11 +175,11 @@ "when \\ \n" expected = - [ - "hoo hah when" - ] + [ + "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 @@ -184,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 @@ -212,13 +220,13 @@ "};\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}) + assert_equal(expected, @parser.import_source(source).map!{|s|s.strip}) end @@ -236,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 @@ -254,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 @@ -277,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 @@ -291,12 +299,12 @@ "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}) + assert_equal(expected, @parser.import_source(source).map!{|s|s.strip}) end it "remove function definitions but keep function declarations" do @@ -311,14 +319,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_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}) + assert_equal(expected, @parser.import_source(source).map!{|s|s.strip}) end it "remove function definitions with nested braces but keep function declarations" do @@ -351,16 +359,16 @@ "}\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}) + assert_equal(expected, @parser.import_source(source).map!{|s|s.strip}) end it "remove a fully defined inline function" do @@ -450,15 +458,15 @@ "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}) + assert_equal(expected, @parser.import_source(source).map!{|s|s.strip}) end it "leave inline functions if inline to be included" do @@ -476,15 +484,15 @@ "}\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}) + 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 @@ -502,18 +510,18 @@ "}\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 - 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 @@ -535,16 +543,16 @@ 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 @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 @@ -556,11 +564,11 @@ "#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}) + assert_equal(expected, @parser.import_source(source).map!{|s|s.strip}) end @@ -569,11 +577,11 @@ "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}) + assert_equal(expected, @parser.import_source(source).map!{|s|s.strip}) end @@ -581,71 +589,74 @@ # 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"} - assert_equal(expected, @parser.parse_declaration(@test_project, source)) + :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(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=>"" } - assert_equal(expected, @parser.parse_declaration(@test_project, source)) + :args => [], + :args_string => "void", + :args_call => "" } + assert_equal(expected, @parser.parse_declaration(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" } - assert_equal(expected, @parser.parse_declaration(@test_project, source)) + :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(source)) end @@ -654,11 +665,11 @@ "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}) + assert_equal(expected, @parser.import_source(source).map!{|s|s.strip}) end @@ -729,165 +740,116 @@ 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" } - assert_equal(expected, @parser.parse_declaration(@test_project, source)) + :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(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" } - assert_equal(expected, @parser.parse_declaration(@test_project, source)) + :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(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=>"" } - assert_equal(expected, @parser.parse_declaration(@test_project, source)) + :args => [ ], + :args_string => "void", + :args_call => "" } + assert_equal(expected, @parser.parse_declaration(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" } - assert_equal(expected, @parser.parse_declaration(@test_project, source)) + :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(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", - :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" } - 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=>"", - :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, ["ns1", "ns2"], "Bar")) - end - - it "fully parse multiple prototypes" do - - source = "const int TheMatrix(int Trinity, unsigned int * Neo);\n" + - "int Morpheus(int, unsigned int*);\n" - - expected = [{ :var_arg=>nil, - :return=> { :type => "int", + expected = { :var_arg => nil, + :noreturn => false, + :return => { :type => "int", :name => 'cmock_to_return', :ptr? => false, :const? => true, @@ -895,19 +857,30 @@ :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 => "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" } + assert_equal(expected, @parser.parse_declaration(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"], + :noreturn => false, + :return => { :type => "int", :name => 'cmock_to_return', :ptr? => false, :const? => false, @@ -915,17 +888,63 @@ :str => "int cmock_to_return", :void? => false }, - :name=>"Morpheus", - :unscoped_name=>"Morpheus", - :namespace=>[], - :class=>nil, - :modifier=>"", + :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(source, ["ns1", "ns2"], "Bar")) + end + + it "fully parse multiple prototypes" do + + source = "const int TheMatrix(int Trinity, unsigned int * Neo);\n" + + "int Morpheus(int, unsigned int*);\n" + + 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, + :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 +954,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 +989,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 +1022,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 +1049,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 +1078,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 +1093,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 +1114,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 +1129,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 +1149,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 +1180,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 +1194,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 +1229,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 +1239,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 +1449,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 +1475,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 +1531,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 +1559,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 +1587,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 +1615,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 +1699,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 +1729,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 +1757,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 +1785,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 +1812,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 +1839,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 +1868,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 +1900,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 +2067,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 +2308,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,69 +2395,70 @@ 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)) + 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 @@ -2414,6 +2478,7 @@ def dummy_func :args_call => "", :contains_ptr? => false, :modifier => "", + :noreturn => false, :return => { :type => "void", :name => "cmock_to_return", @@ -2436,10 +2501,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 +2536,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 +2554,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 +2649,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 +2723,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 +2952,315 @@ class Classy { end + # // PJB // REMOVE BEFORE COMMIT // + + it "builds parser on sound bases" do + + 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.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.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.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.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.gcc_attribute_syntax?([:identifier,"__attribute__"],[:parens,[[:parens,[[:identifier,"noreturn"]]]]]), + "gcc_attribute_syntax? identifies parsed __attribute__((noreturn))") + + assert(@parser.attribute?([:attribute,nil,"noreturn",nil,:gcc]), + "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 => "", + :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(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 => "", + :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(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 => "", + :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(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(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(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(source, [], nil)) + end + + end