From 8ad27ed1f77c8ea7579eb3a409fb3c57c3e20afa Mon Sep 17 00:00:00 2001 From: "Pascal J. Bourguignon" Date: Thu, 4 May 2023 17:20:53 +0200 Subject: [PATCH 1/7] Ignore backup files. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 57fce96f..e93ff38b 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ Gemfile.lock *.swp examples/make_example/build examples/temp_sensor/build +*~ From 31cf607836d53063226597338a9f79670758c45f Mon Sep 17 00:00:00 2001 From: "Pascal J. Bourguignon" Date: Thu, 4 May 2023 17:24:07 +0200 Subject: [PATCH 2/7] Added generation of noreturn exits. Factorized out the generation of returns with noreturn exits in generate_return and generate_template_return methods. --- lib/cmock_generator.rb | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/lib/cmock_generator.rb b/lib/cmock_generator.rb index c3896dfb..1def32a4 100644 --- a/lib/cmock_generator.rb +++ b/lib/cmock_generator.rb @@ -89,6 +89,34 @@ def create_skeleton(module_name, parsed_stuff) private if $ThisIsOnlyATest.nil? ############################## + def is_noreturn?(function) + if function[:attributes].nil? + return nil; + else + return function[:attributes].include?('noreturn'); + end + end + + def generate_return(function) + if is_noreturn?(function) + return " TEST_DO_NOT_RETURN();\n" + elsif function[:return][:void?] + return " return;\n" + else + return " return cmock_call_instance->ReturnVal;\n" + end + end + + def generate_template_return(function) + if is_noreturn?(function) + return " TEST_DO_NOT_RETURN();\n" + elsif function[:return][:void?] + return " return;\n" + else + return " return (#{(function[:return][:type])})0;\n" + end + end + def create_mock_subdir(mock_project) @file_writer.create_subdir(mock_project[:folder]) end @@ -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 From ed8277801a980dcc434928b24514e6f2618c7939 Mon Sep 17 00:00:00 2001 From: "Pascal J. Bourguignon" Date: Thu, 4 May 2023 17:21:17 +0200 Subject: [PATCH 3/7] Added parsing of noreturn attributes. Added parsing of noreturn attributes in various syntaxes (C syntax, C++ syntax, GNU gcc syntax). To do so safely, added a C lexer, and a quick-and-dirty parenthesis parser (including brackets, braces and angle-brackets for C++). Using the scanned list of tokens, processing parenthesised source code is easier and safer than with regular expressions. --- lib/CLexer.rb | 153 ++ lib/cmock_config.rb | 6 + lib/cmock_header_parser.rb | 1027 ++++++++-- test/rakefile_helper.rb | 2 +- test/unit/cmock_header_parser_test.rb | 2618 ++++++++++++++----------- 5 files changed, 2538 insertions(+), 1268 deletions(-) create mode 100644 lib/CLexer.rb diff --git a/lib/CLexer.rb b/lib/CLexer.rb new file mode 100644 index 00000000..1cc00bb2 --- /dev/null +++ b/lib/CLexer.rb @@ -0,0 +1,153 @@ +# +# This is a simple lexer for the C programming language. +# MIT license. (c) 2023 Pascal Bourguignon +# + +class CLexer + + KEYWORDS = %w[auto break case char const continue default do double else enum + extern float for goto if int long register return short signed + sizeof static struct switch typedef union unsigned void volatile while].freeze + + STAR = :mul_op + ADDRESS = :logical_and_op + + OPERATOR_SYMBOLS = { + + '...' => :ellipsis, + '->*' => :ptr_mem_op, + '>>=' => :right_assign, + '<<=' => :left_assign, + + '==' => :eq, + '!=' => :ne, + '<=' => :le, + '>=' => :ge, + '>>' => :right_op, + '<<' => :left_op, + '+=' => :add_assign, + '-=' => :sub_assign, + '*=' => :mul_assign, + '/=' => :div_assign, + '%=' => :mod_assign, + '&=' => :and_assign, + '^=' => :xor_assign, + '|=' => :or_assign, + '->' => :ptr_op, + '&&' => :and_op, + '||' => :or_op, + '++' => :increment, + '--' => :decrement, + + '<:' => :open_bracket, + ':>' => :close_bracket, + '<%' => :open_brace, + '%>' => :close_brace, + + '!' => :logical_not_op, + '%' => :mod_op, + '&' => :logical_and_op, + '(' => :open_paren, + ')' => :close_paren, + '*' => :mul_op, + '+' => :add_op, + ',' => :comma, + '-' => :sub_op, + '.' => :dot, + '/' => :div_op, + ':' => :colon, + ';' => :semicolon, + '<' => :lt, + '=' => :assign, + '>' => :gt, + '?' => :question, + '[' => :open_bracket, + ']' => :close_bracket, + '^' => :logical_xor_op, + '{' => :open_brace, + '|' => :logical_or_op, + '}' => :close_brace, + '~' => :bitwise_not_op, + + }.freeze + + + OPERATOR_REGEX = Regexp.new('\A(' + OPERATOR_SYMBOLS.keys.map { |op| Regexp.escape(op) }.join('|') + ')') + OPERATOR_SYMS = OPERATOR_SYMBOLS.values.freeze + KEYWORDS_SYMS = KEYWORDS.map{ |n| n.to_sym }.freeze + + def initialize(input) + @input = input + @tokens = [] + end + + def tokenize + while @input.size > 0 + case @input + when /\A[[:space:]]+/m + @input = $' + when /\A\/\/[^\n]*/ + @input = $' + when /\A\/\*/ + consume_multiline_comment + when /\A[_a-zA-Z][_a-zA-Z0-9]*/ + identifier_or_keyword = $& ; + @input = $' + if KEYWORDS.include?(identifier_or_keyword) + @tokens << identifier_or_keyword.to_sym + else + @tokens << [:identifier, identifier_or_keyword] + end + when /\A\d+\.\d*([eE][+-]?\d+)?[fFlL]?|\.\d+([eE][+-]?\d+)?[fFlL]?|\d+[eE][+-]?\d+[fFlL]?/ + float_constant = $& ; + @input = $' + @tokens << [:float_literal, float_constant] + when /\A\d+/ + integer_constant = $& ; + @input = $' + @tokens << [:integer_literal, integer_constant] + when /\A0[xX][0-9a-fA-F]+/ + hex_constant = $& ; + @input = $' + @tokens << [:hex_literal, hex_constant] + when /\A'((\\.|[^\\'])*)'/ + char_literal = $& ; + @input = $' + @tokens << [:char_literal, char_literal] + when /\A"((\\.|[^\\"])*)"/ + string_literal = $& ; + @input = $' + @tokens << [:string_literal, string_literal] + when OPERATOR_REGEX + operator = $& ; + @input = $' + @tokens << OPERATOR_SYMBOLS[operator] + else + raise "Unexpected character: #{@input[0]}" + end + end + + @tokens + end + + private + + def consume_multiline_comment + while @input.size > 0 + case @input + when /\A\*\// + @input = $' + break + when /\A./m + @input = $' + end + end + end +end + +def example + input = File.read("/home/pbourguignon/src/c-tidbits/pipes/tee.out.c") + lexer = CLexer.new(input) + tokens = lexer.tokenize + puts tokens.inspect +end diff --git a/lib/cmock_config.rb b/lib/cmock_config.rb index c7db9451..c925420d 100644 --- a/lib/cmock_config.rb +++ b/lib/cmock_config.rb @@ -16,7 +16,13 @@ class CMockConfig :subdir => nil, :plugins => [], :strippables => ['(?:__attribute__\s*\([ (]*.*?[ )]*\)+)'], + :process_gcc_attributes => false, # __attribute__((...)) ; also remove it from strippables. + :process_cpp_attributes => false, # [[ ... ]] :attributes => %w[__ramfunc __irq __fiq register extern], + :c_noreturn_attributes => [], # simple keyword, before the function name + :gcc_noreturn_attributes => [], # put 'noreturn' for __attribute__((noreturn)), + # # before or after the function name, + # # also remove it from :strippables :c_calling_conventions => %w[__stdcall __cdecl __fastcall], :enforce_strict_ordering => false, :fail_on_unexpected_calls => true, diff --git a/lib/cmock_header_parser.rb b/lib/cmock_header_parser.rb index 15224349..0f560637 100644 --- a/lib/cmock_header_parser.rb +++ b/lib/cmock_header_parser.rb @@ -1,20 +1,30 @@ +# coding: utf-8 # ========================================== # CMock Project - Automatic Mock Generation for C # Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams # [Released under MIT License. Please refer to license.txt for details] # ========================================== +require "#{__dir__}/CLexer" + class CMockHeaderParser attr_accessor :funcs, :c_attr_noconst, :c_attributes, :treat_as_void, :treat_externs, :treat_inlines, :inline_function_patterns + attr_reader :c_noreturn_attributes, :c_calling_conventions def initialize(cfg) @c_strippables = cfg.strippables + @process_gcc_attributes = cfg.process_gcc_attributes + @process_cpp_attributes = cfg.process_cpp_attributes @c_attr_noconst = cfg.attributes.uniq - ['const'] - @c_attributes = ['const'] + c_attr_noconst + @c_attributes = ['const'] + @c_attr_noconst + @c_noreturn_attributes = cfg.c_noreturn_attributes.uniq @c_calling_conventions = cfg.c_calling_conventions.uniq @treat_as_array = cfg.treat_as_array @treat_as_void = (['void'] + cfg.treat_as_void).uniq - @function_declaration_parse_base_match = '([\w\s\*\(\),\[\]]*?\w[\w\s\*\(\),\[\]]*?)\(([\w\s\*\(\),\.\[\]+\-\/]*)\)' + attribute_regexp = '((?:\s*__attribute__\s*\(\s*\(.*?\)\s*\))*)' + type_and_name_regexp = '([\w\s\*\(\),\[\]]*?\w[\w\s\*\(\),\[\]]*?)' + args_regexp = '([\w\s\*\(\),\.\[\]+\-\/]*)' + @function_declaration_parse_base_match = type_and_name_regexp+'\('+ args_regexp + '\)' + attribute_regexp @declaration_parse_matcher = /#{@function_declaration_parse_base_match}$/m @standards = (%w[int short char long unsigned signed] + cfg.treat_as.keys).uniq @array_size_name = cfg.array_size_name @@ -27,6 +37,11 @@ def initialize(cfg) @inline_function_patterns = cfg.inline_function_patterns @c_strippables += ['extern'] if @treat_externs == :include # we'll need to remove the attribute if we're allowing externs @c_strippables += ['inline'] if @treat_inlines == :include # we'll need to remove the attribute if we're allowing inlines + @c = 0 + end + + def raise_parse_error(message) + raise "Failed Parsing Declaration Prototype!" + "\n" + message end def parse(name, source) @@ -61,7 +76,7 @@ def parse(name, source) :normalized_source => parse_project[:normalized_source] } end - private if $ThisIsOnlyATest.nil? ################ + # REMVOVE BEFORE COMMIT # private if $ThisIsOnlyATest.nil? ################ # Remove C/C++ comments from a string # +source+:: String which will have the comments removed @@ -241,8 +256,10 @@ def import_source(source, parse_project, cpp = false) # remove assembler pragma sections source.gsub!(/^\s*#\s*pragma\s+asm\s+.*?#\s*pragma\s+endasm/m, '') - # remove gcc's __attribute__ tags - source.gsub!(/__attribute(?:__)?\s*\(\(+.*\)\)+/, '') + if @c_noreturn_attributes.nil? + # remove gcc's __attribute__ tags + source.gsub!(/__attribute(?:__)?\s*\(\(+.*\)\)+/, '') + end # remove preprocessor statements and extern "C" source.gsub!(/extern\s+\"C\"\s*\{/, '') @@ -306,6 +323,7 @@ def import_source(source, parse_project, cpp = false) end src_lines.delete_if(&:empty?) # drop empty lines + src_lines end # Rudimentary C++ parser - does not handle all situations - e.g.: @@ -371,182 +389,861 @@ def parse_functions(source) funcs end - def parse_type_and_name(arg) - # Split up words and remove known attributes. For pointer types, make sure - # to remove 'const' only when it applies to the pointer itself, not when it - # applies to the type pointed to. For non-pointer types, remove any - # occurrence of 'const'. - arg.gsub!(/(\w)\*/, '\1 *') # pull asterisks away from preceding word - arg.gsub!(/\*(\w)/, '* \1') # pull asterisks away from following word - arg_array = arg.split - arg_info = divine_ptr_and_const(arg) - arg_info[:name] = arg_array[-1] - - attributes = arg.include?('*') ? @c_attr_noconst : @c_attributes - attr_array = [] - type_array = [] - - arg_array[0..-2].each do |word| - if attributes.include?(word) - attr_array << word - elsif @c_calling_conventions.include?(word) - arg_info[:c_calling_convention] = word - else - type_array << word + # This grammar is quite ambiguous: + # + # fun_declaration : type name parameters { attributes | c_calling_convention } ; + # + # type : stuffs ; + # + # stuffs : | stuff stuffs ; + # + # stuff : token + # | :open_paren stuffs :close_paren + # | :open_bracket stuffs :close_bracket + # | :open_brace stuffs :close_brace + # | :lt stuffs :gt' -- angle brackets + # ; + # -- Note: we will also scan char_literal and string_literal + # -- because they could appear in constant expressions (eg. enums) + # -- and contain parentheses. + # -- Note: angle brackets for templates are very ambiguous, because + # -- we may also have '<' tokens in constant expressions (eg. in a enum). + # -- So we'd need a real parser to handle this correctly. + # + # token : identifier | literals_and_other_tokens ; + # + # name : identifier ; + # + # parameters : :open_paren stuffs :close_paren ; + # + # attributes : '__attributes__' :open_paren stuffs :close_paren ; + # -- we won't parse macro calls in attributes because of the ambiguity. + # + # + # Therefore we will parse in two phases: + # Phase 1: + # we parse fun_declaration_1 : { stuff } ; + # -- this takes care of parentheses, et al. + # Phase 2: + # then match from the end of the list of stuffs, + # for c_calling_conventions, __attributes__ (...) + # then '(' parameters ')' = '(' stuffs ')' + # then name identifier, + # then the rest is type. + + def eos?(src,pos) + src.length<=pos + end + + def validate_identifier(token,what) + if token[0] == :identifier + token + else + raise_parse_error "Expected #{what} identifier, got #{token[0]} #{token[1]}" + end + end + + def parse_token(src,pos) + if eos?(src,pos) + raise_parse_error "Expected a token, not end of source at position #{pos}" + end + [ src[pos], pos+1 ] + end + + def parse_stuff(src,pos) + # stuff : token + # | '(' stuffs ')' + # | '[' stuffs ']' + # | '{' stuffs '}' + # | '<' stuffs '>' + # ; + stuff = nil + if not eos?(src,pos) + case src[pos] + when :open_paren then stuff, pos = parse_delimited_stuffs(src, pos, :close_paren) + when :open_bracket then stuff, pos = parse_delimited_stuffs(src, pos, :close_bracket) + when :open_brace then stuff, pos = parse_delimited_stuffs(src, pos, :close_brace) + when :lt then stuff, pos = parse_delimited_stuffs(src, pos, :gt) + else stuff, pos = parse_token(src, pos) end end + [stuff, pos] + end - if arg_info[:const_ptr?] - attr_array << 'const' - type_array.delete_at(type_array.rindex('const')) + def parse_delimited_stuffs(src,pos,closing) + pos += 1 # eat the opening tokenn + stuffs = [] + while not eos?(src, pos) and src[pos] != closing + item, pos = parse_stuff(src, pos) + stuffs << item + end + if not eos?(src, pos) + pos += 1 # skip closing token + end + op = case closing + when :close_paren then :parens + when :close_bracket then :brackets + when :close_brace then :braces + when :gt then :angle_brackets + end + [ [op, stuffs], pos ] + end + + def parse_stuffs(src,pos) + # stuffs : | stuff stuffs ; + stuffs = [] + while not eos?(src, pos) + stuff, pos = parse_stuff(src, pos) + stuffs << stuff unless stuff.nil? end + [ stuffs, pos ] + end - arg_info[:modifier] = attr_array.join(' ') - arg_info[:type] = type_array.join(' ').gsub(/\s+\*/, '*') # remove space before asterisks - arg_info + def is_parens(stuff) + stuff.is_a?(Array) and (stuff.length == 2) and (stuff[0] == :parens) end - def parse_args(arg_list) - args = [] - arg_list.split(',').each do |arg| - arg.strip! - return args if arg =~ /^\s*((\.\.\.)|(void))\s*$/ # we're done if we reach void by itself or ... - - arg_info = parse_type_and_name(arg) - arg_info.delete(:modifier) # don't care about this - arg_info.delete(:c_calling_convention) # don't care about this - - # in C, array arguments implicitly degrade to pointers - # make the translation explicit here to simplify later logic - if @treat_as_array[arg_info[:type]] && !(arg_info[:ptr?]) - arg_info[:type] = "#{@treat_as_array[arg_info[:type]]}*" - arg_info[:type] = "const #{arg_info[:type]}" if arg_info[:const?] - arg_info[:ptr?] = true + def parens_list(stuff) + stuff[1] if is_parens(stuff) + end + + def is_brackets(stuff) + stuff.is_a?(Array) and (stuff.length == 2) and (stuff[0] == :brackets) + end + + def brackets_list(stuff) + stuff[1] if is_brackets(stuff) + end + + def is_token(token) + token.is_a?(Symbol) and (CLexer::OPERATOR_SYMS.index(token) or CLexer::KEYWORDS_SYMS.index(token)) + end + + def is_identifier(token,name=nil) + if token.is_a?(Array) and (token.length == 2) + if name.nil? + (token[0] == :identifier) + else + (token[0] == :identifier) and (token[1] == name) end + else + false + end + end - args << arg_info + def identifier_name(token) + token[1] if token.is_a?(Array) and (token[0] == :identifier) + end + + def token_name(token) + if is_identifier(token) + identifier_name(token) + elsif token.is_a?(Symbol) + token.to_s + elsif token.is_a?(String) + token + else + raise_parse_error "Invalid token #{token.inspect}" end + end - # Try to find array pair in parameters following this pattern : * , <@array_size_type> <@array_size_name> - args.each_with_index do |val, index| - next_index = index + 1 - next unless args.length > next_index + def is_c_calling_convention(stuff) + # whether stuff is a C calling convention (listed in @c_calling_conventions). + # note: stuff may be either a symbol, a string or an :identifier array. + res = if stuff.is_a?(Symbol) or is_token(stuff) or is_identifier(stuff) + not @c_calling_conventions.index(token_name(stuff)).nil? + else + false + end + res + end - if (val[:ptr?] == true) && args[next_index][:name].match(@array_size_name) && @array_size_type.include?(args[next_index][:type]) - val[:array_data?] = true - args[next_index][:array_size?] = true - end + def is_c_attribute(token) + # whether the token is a C attribute (listed in @c_attributes or in @c_noreturn_attributes). + # note: token may be either a symbol, a string or an :identifier array. + if token.is_a?(String) + name = token + elsif token.is_a?(Symbol) + name = token.to_s + elsif is_token(token) or is_identifier(token) + name = token_name(token) + elsif is_attribute(token) + name = attribute_name(token) + else + return false end - args + res = (@c_attributes.index(name) or + ((not @c_noreturn_attributes.nil?) and + (@c_noreturn_attributes.any? { |attr_regexp| name =~ /^#{attr_regexp}$/ }))) + res end - def divine_ptr(arg) - return false unless arg.include? '*' - # treat "const char *" and similar as a string, not a pointer - return false if /(^|\s)(const\s+)?char(\s+const)?\s*\*(?!.*\*)/ =~ arg + def make_attribute(namespace,name,arguments,kind) + if name.nil? + raise_parse_error "Attribute name should not be nil! #{namespace.inspect}, #{name.inspect}, #{arguments.inspect}" + end + [:attribute,namespace,name,arguments,kind] + end - true + def is_attribute(object) + (object.is_a?(Array)) and (object.length == 5) and (object[0] == :attribute) end - def divine_const(arg) - # a non-pointer arg containing "const" is a constant - # an arg containing "const" before the last * is a pointer to a constant - if arg.include?('*') ? (/(^|\s|\*)const(\s(\w|\s)*)?\*(?!.*\*)/ =~ arg) : (/(^|\s)const(\s|$)/ =~ arg) - true + def attribute_namespace(attribute) + raise_parse_error "Not an normalized attribute: #{attribute}" unless is_attribute(attribute) + attribute[1] + end + + def attribute_name(attribute) + raise_parse_error "Not an normalized attribute: #{attribute}" unless is_attribute(attribute) + attribute[2] + end + + def attribute_qualified_name(attribute) + if attribute_namespace(attribute) + attribute_namespace(attribute) + "::" + attribute_name(attribute) + else + attribute_name(attribute) + end + end + + def attribute_arguments(attribute) + raise_parse_error "Not an normalized attribute: #{attribute}" unless is_attribute(attribute) + attribute[3] + end + + def attribute_kind(attribute) + raise_parse_error "Not an normalized attribute: #{attribute}" unless is_attribute(attribute) + attribute[4] + end + + def is_noreturn(attribute) + if is_identifier(attribute) + @c_noreturn_attributes.include?(identifier_name(attribute)) + elsif is_attribute(attribute) + @c_noreturn_attributes.include?(attribute_qualified_name(attribute)) else false end end - def divine_ptr_and_const(arg) - divination = {} + def has_noreturn_attribute(attributes) + attributes.any? do |attribute| + is_noreturn(attribute) + end + end - divination[:ptr?] = divine_ptr(arg) - divination[:const?] = divine_const(arg) + def is_gcc_attribute_syntax(operator, parameters) + # gcc atributes are all of the syntax __attribute__ (...) + # where ... is a list of stuffs. + # so is_gcc_attribute_syntax([:identifier,"__attribute__"],[:parens,stuff_list]) + is_identifier(operator,'__attribute__') and is_parens(parameters) + # see parse_gcc_attribute + end - # an arg containing "const" after the last * is a constant pointer - divination[:const_ptr?] = /\*(?!.*\*)\s*const(\s|$)/ =~ arg ? true : false + def is_processable_gcc_attribute(name) + is_c_attribute(name) + end + + def parse_gcc_attribute(op,stuff) + # gcc atributes are all of the syntax __attribute__ (...) + # where ... is a list of stuffs. + # Here, attribute = [:attribute, [:parens,stuff_list]] + # We want to normalize attribute into a list of atributes: + # + # [:attribute,[:parens,[[:parens,[:identifier,"foo"],:comma,[:identifier,"bar"]]]]] + # --> [[:attribute,[:identifier,"foo"],[:parens,[[:identifier,"bar"]])]]] + # + # [:attribute,[:parens,[[:parens,[[:identifier,"foo"]]],:comma,[:parens,[[:identifier,"bar"]]]]]] + # --> [[:attribute,[:identifier "foo"]],nil],[[:attribute,[:identifier,"bar"]],nil]] + # + # [:attribute, [:parens,[[:parens, + # [[:identifier,"access"],[:parens,[[:identifier,"read_write"],:comma,[:integer_literal,"1"]]],:comma, + # [:identifier,"access"],[:parens,[[:identifier,"read_only"],:comma,[:integer_literal,"2"]]]]]]]]] + # + # --> [[:attribute,[:identifier,"access"],[:parens,[[:identifier,"read_write"],[:integer_literal,"1"]]]], + # [:attribute,[:identifier,"access"],[:parens,[[:identifier,"read_only"],[:integer_literal,"2"]]]]] + + if not (is_identifier(op,'__attribute__') and is_parens(stuff) and is_parens(parens_list(stuff)[0])) + raise_parse_error "Unexpected attribute syntax #{[op,stuff].inspect}" + end + normalized = [] + j=0 + chunks = parens_list(stuff) + while j ':' ] { ',' } ']]' ; + # attribute = [ '::' ] [ '(' ')' ] ; + attributes = [] + + if not (is_brackets(stuff) and (1 == brackets_list(stuff).length) and is_brackets(brackets_list(stuff)[0])) + raise_parse_error "Unexpected C++ attribute syntax #{stuff.inspect}" + + "\nis_brackets(stuff) = #{is_brackets(stuff)}" + + "\nbrackets_list(stuff).length = #{is_brackets(stuff) ? brackets_list(stuff).length : nil}" + + "\nis_brackets(brackets_list(stuff)[0]) = #{is_brackets(stuff) and brackets_list(stuff).length>1 ? is_brackets(brackets_list(stuff)[0]) : nil}" + end + + stuff = brackets_list(brackets_list(stuff)[0]) + + # Note: for better support for C++, we'd have to update CLexer for C++ tokens. + # so using would be :using, and :: would be :double-colon insead of :colon :colon + # etc (but C++ lexers must be context-sensitive). + + default_namespace = nil + start=0 + if 30 else - c = 0 - # magically turn brackets into asterisks, also match for parentheses that come from macros - arg_list.gsub!(/(\w+)(?:\s*\[[^\[\]]*\])+/, '*\1') - # remove space to place asterisks with type (where they belong) - arg_list.gsub!(/\s+\*/, '*') - # pull asterisks away from arg to place asterisks with type (where they belong) - arg_list.gsub!(/\*(\w)/, '* \1') - - # scan argument list for function pointers and replace them with custom types - arg_list.gsub!(/([\w\s\*]+)\(+([\w\s]*)\*[\*\s]*([\w\s]*)\s*\)+\s*\(((?:[\w\s\*]*,?)*)\s*\)*/) do |_m| - functype = "cmock_#{parse_project[:module_name]}_func_ptr#{parse_project[:typedefs].size + 1}" - funcret = Regexp.last_match(1).strip - funcdecl = Regexp.last_match(2).strip - funcname = Regexp.last_match(3).strip - funcargs = Regexp.last_match(4).strip - funconst = '' - if funcname.include? 'const' - funcname.gsub!('const', '').strip! - funconst = 'const ' + # char* are "strings", not "pointers". + guess[:ptr?] = starc>1 + end + + if first_const.nil? + # no const: + guess[:const?] = false + elsif starc == 0 + # const, no star + guess[:const?] = true + else + # const, some star: + before_last_star = type[0..last_star-1].rindex(:mul_op) + + if before_last_star.nil? + # a single star: + guess[:const?] = (first_const0) and (not last_const.nil?) and (last_star < last_const)) + + guess + end + + def parse_function_signature(src,pos) + + # Phase 1: + # we parse fun_declaration_1 : { stuff } ; + # -- this takes care of parentheses, et al. + items, pos = parse_stuffs(src, pos) + raise_parse_error "Unparsed characters from position #{pos}" unless pos == src.length + + # Phase 2: + # then match from the end of the list of stuffs, + # for c_calling_conventions, __attributes__ (...) + # then '(' parameters ')' = '(' stuffs ')' + # then name identifier, + # then the rest is type. + + ccc = [] + attributes = [] + parameters = nil + + # match from the end of the list of stuffs, + # for c_calling_conventions, __attributes__ (...) + i = items.length-1 + while is_c_calling_convention(items[i]) or ((3<=i) and is_gcc_attribute_syntax(items[i-1],items[i])) + if is_c_calling_convention(items[i]) + ccc << [:c_calling_convention, token_name(items[i])] + i -= 1 + else + attributes += parse_gcc_attribute(items[i-1],items[i]) + i -= 2 + end + end + + # then '(' parameters ')' = '(' stuffs ')' + if is_parens(items[i]) + parameters = parens_list(items[i]) + i -= 1 + end + + # then name identifier, + if not is_identifier(items[i]) + raise_parse_error "Expected an identifier but got #{items[i].inspect} as function name in #{items.inspect}" + end + name = identifier_name(items[i]) + i -= 1 + + # then the rest is type. + type = items[0..i] + + [type, name, parameters, attributes, ccc] + end + + def parse_type(stuff) + # Split up words and remove known attributes. For pointer types, make sure + # to remove 'const' only when it applies to the pointer itself, not when it + # applies to the type pointed to. For non-pointer types, remove any + # occurrence of 'const'. + + arg_info = guess_ptr_and_const(stuff) + + @attributes = (stuff.any?{|item| item == :mul_op}) ? @c_attr_noconst : @c_attributes + + type = [] + attributes = [] + ccc = [] + i = 0 + while i" + when :attribute then + case attribute_kind(stuff) + when :gcc then "__attribute__((#{unparse_inner(attribute_qualified_name(stuff))}))" + when :cpp then "[[#{unparse_inner(attribute_qualified_name(stuff))}]]" + when :c then "#{unparse_inner(attribute_qualified_name(stuff))}" end - parse_project[:typedefs] << "typedef #{funcret}(*#{functype})(#{funcargs});" - funcname = "cmock_arg#{c += 1}" if funcname.empty? - "#{functype} #{funconst}#{funcname}" + else stuff.map{|item| unparse_inner(item)}.join(' ') end + elsif stuff.is_a?(String) + stuff + else + raise_parse_error "Unexpected stuff #{stuff.inspect} while unparsing #{@unparsing.inspect}" + end + end + + def unparse(stuff) + @unparsing = stuff + unparse_inner(stuff) + end + + + def replace_arrays_by_pointers_in_parameters(parameters) + # parameter is now a list of parameter declarations each being a lists of tokens. + # + # eg. for (int* b, int c[5], int (*(*farr)[4])(int x),int a=42) we'd have now (=42 has been removed earlier): + # + # [ [ :int, :mul_op, [:identifier, "b"] ], + # [ :int, [:identifier, "c"], [:brackets, [[:integer_literal, "5"]]] ], + # [ :int, [:parens, [:mul_op, [:parens, [:mul_op, [:identifier, "farr"]]], + # [:brackets, [[:integer_literal, "4"]]]]], + # [:parens, [:int, [:identifier, "x"]]]] ], + # [ :int, [:identifier, "a"] ] ] + + # we want to turn instances of: [..., stuff, [:brackets, ... ], ...] into: [..., :mul_op, stuff] + # Note: a single pointer for multidimensionnal arrays: + # foo_t foo[][][] --> foo_t* foo + # foo_t* foo[][][] --> foo_t** foo + if parameters == [[]] + parameters + else + parameters.map do |parameter| + if is_parens(parameter) + [:parens] + replace_arrays_by_pointers_in_parameters([parens_list(parameter)]) + elsif parameter.is_a?(Array) + i = parameter.rindex{|item| not is_brackets(item)} + if i.nil? then + # all items are brackets + raise_parse_error "All items are brackets parameter=#{parameter.inspect}" + elsif i == parameter.length-1 then + # no item is a brackets + parameter + else + # some brackets, remove them and insert * before the name + # Note: int foo[3][4][5] --> int* foo + parameter[0,i] + [:mul_op] + [parameter[i]] + end.map do |item| + # recurse into parens groups: + if is_parens(item) + [:parens] + replace_arrays_by_pointers_in_parameters([parens_list(item)]) + else + item + end + end + else + parameter + end + end + end + end + +# replace_arrays_by_pointers_in_parameters([ [ :int, :mul_op, [:identifier, "b"] ], +# [ :int, [:identifier, "c"], [:brackets, [[:integer_literal, "5"]]] ], +# [ :int, [:parens, [:mul_op, [:parens, [:mul_op, [:identifier, "farr"]]], +# [:brackets, [[:integer_literal, "4"]]]]], +# [:parens, [:int, [:identifier, "x"]]] ], +# [ :int, [:identifier, "a"] ] ]) +# ==> +# [[:int, :mul_op, [:identifier, "b"]], +# [:int, :mul_op, [:identifier, "c"]], +# [:int, +# [:parens, [:mul_op, :mul_op, [:parens, [:mul_op, [:identifier, "farr"]]]]], +# [:parens, [:int, [:identifier, "x"]]]], +# [:int, [:identifier, "a"]]] + + + def replace_function_pointers_by_custom_types(parameters, parse_project) + parameters.map do |parameter| + plen = parameter.length + if 20) ? spec[0..ptrindex-1] : [] + funcname = spec[ptrindex+1..-1] + constindex = funcname.index(:const) + if constindex + funcname.delete_at(constindex) + funcconst = [:const] + else + funcconst = [] + end + + else + raise_parse_error "Invalid syntax for function parameter #{parameter.inspect}" + end + + elsif is_identifier(parameter[-2]) # ...foo() + + funcdecl = [] + funcname = [parameter[-2]] + funcconst = [] - # automatically name unnamed arguments (those that only had a type) - arg_list.split(/\s*,\s*/).map do |arg| - parts = (arg.split - ['struct', 'union', 'enum', 'const', 'const*']) - if (parts.size < 2) || (parts[-1][-1].chr == '*') || @standards.include?(parts[-1]) - "#{arg} cmock_arg#{c += 1}" else - arg + raise_parse_error "Invalid syntax for function parameter #{parameter.inspect}" end - end.join(', ') + + functype = [:identifier,"cmock_#{parse_project[:module_name]}_func_ptr#{parse_project[:typedefs].size + 1}"] + funcret = parameter[0..-3] + funcargs = parameter[-1] + + # add typedef for function pointer + parse_project[:typedefs] << "typedef #{unparse(funcret)}(#{unparse(funcdecl+[:mul_op]+[functype])})#{unparse(funcargs)};".gsub(/\(\*\s+/,'(*').gsub(/\s+\*/,'*').gsub(/\s+,/,',') + funcname = [[:identifier, "cmock_arg#{@c += 1}"]] if funcname.empty? + [functype] + funcconst + funcname + else + parameter + end end end + def is_anonymous_parameter(parameter) + parameter = parameter.reject { |token| [:struct, :union, :enum, :const, :mul_op].include?(token) } + if (parameter.length == 0) + true + elsif (parameter.length == 1) + not (parameter[0] == :ellipsis) + else + not is_identifier(parameter[-1]) + end + end + + def add_names_to_anonymous_parameters(parameters) + parameters.map do |parameter| + if parameter.nil? + nil + elsif is_anonymous_parameter(parameter) + parameter << [:identifier, "cmock_arg#{@c += 1}"] + else + parameter + end + end + end + + def parameter_unwrap_superfluous_parentheses(parameter) + pc = parameter.count { |item| is_parens(item) } + if (pc == 1) and is_parens(parameter[-1]) and + (parens_list(parameter[-1]).length == 1) and + is_parens(parens_list(parameter[-1])[0]) + # ... ((...)) --> unwrap ... (...) + parameter_unwrap_superfluous_parentheses(parameter[0..-2] + parens_list(parameter[-1])) + elsif (pc == 1) and is_parens(parameter[-1]) and + (parens_list(parameter[-1]).length == 2) and + is_parens(parens_list(parameter[-1])[0]) and + is_parens(parens_list(parameter[-1])[1]) + # ... ((...)(...)) --> unwrap ... (...)(...) + parameter_unwrap_superfluous_parentheses(parameter[0..-2] + + [parens_list(parameter[-1])[0]] + + [parens_list(parameter[-1])[1]]) + elsif (pc == 2) and is_parens(parameter[-2]) and is_parens(parameter[-1]) and + (parens_list(parameter[-2]).length == 1) and + is_parens(parens_list(parameter[-2])[0]) + # ... ((...)) (...) --> unwrap ... (...) (...) + parameter_unwrap_superfluous_parentheses(parameter[0..-3] + parens_list(parameter[-2]) + parameter[-1]) + else + parameter + end + end + + def clean_args(parameters, parse_project) + # parameter is now a list of parameter declarations each being a lists of tokens. + # eg. for (int* b, int c[5], int a=42) we'd have now (=42 has been removed earlier): + # [ [ :int, :mul_op, [:identifier, "b"] ], + # [ :int, [:identifier, "c"], [:brackets, [:integer_literal, "5"]] ], + # [ :int, [:identifier, "a"] ] ] + + if parameters.empty? or ((parameters.length == 1) and @local_as_void.include?(unparse(parameters[0]))) + [:void] + else + @c = 0 + + # unwrap superfluous parentheses, eg.: + # + # [:int, [:parens, [[:parens, [[:identity, "foo"],[:parens,[[:int,:comma,:int]]]]]]]] + # --> [:int, [:identity, "foo"], [:parens,[[:int,:comma,:int]]]] + # + # [:int, [:parens, [[:parens, [[:parens, [[:mul_op,[:identity, "foo"]]]], [:parens,[[:int,:comma,:int]]]]]]]] + # --> [:int, [:parens, [[:mul_op,[:identity, "foo"]]]], [:parens,[[:int,:comma,:int]]]] + + parameters = parameters.map { |parameter| parameter_unwrap_superfluous_parentheses(parameter) } + + # magically turn brackets into asterisks, also match for parentheses that come from macros + parameters = replace_arrays_by_pointers_in_parameters(parameters) + + # scan argument list for function pointers and replace them with custom types + # scan argument list for function pointers with shorthand notation and replace them with custom types + # Note: if I'm not wrong, this new code using tokens handles both cases, with and without funcdecl. + # parameters=[[:unsigned, :int, [:parens, [:mul_op, [:identifier, "func_ptr"]]], [:parens, [:int, :comma, :char]]]] + parameters=replace_function_pointers_by_custom_types(parameters, parse_project) + + # automatically name unnamed arguments (those that only had a type) + parameters = add_names_to_anonymous_parameters(parameters) + + if parameters.any?(nil) + raise_parse_error "Invalid parameters #{parameters.inspect}" + end + parameters + end + end + + def is_string_type(type) + (type.length>=2) and (type[0]==:char) and (type[1]==:mul_op) + end + + def parse_args(parameters) + # parameters have been cleaned (clean_args) + # so they're each of the form :void, :ellipsis, or [type... name] + args = [] + parameters.each do |parameter| + return args if (parameter == :void) or (parameter == [:ellipsis]) + + if parameter.nil? or (parameter.length<2) + raise_parse_error "Invalid parameter #{parameter.inspect} in #{parameters.inspect}" + else + type=parameter[0..-2] + name=parameter[-1] + type, _, _, arg_info = parse_type(type) + arg_info[:name]=identifier_name(name) + arg_info.delete(:modifier) # don't care about this + arg_info.delete(:noreturn) # don't care about this + arg_info.delete(:c_calling_convention) # don't care about this + + # in C, array arguments implicitly degrade to pointers + # make the translation explicit here to simplify later logic + if @treat_as_array[arg_info[:type]] && !(arg_info[:ptr?]) + arg_info[:type] = "#{@treat_as_array[arg_info[:type]]}*" + arg_info[:ptr?] = true + arg_info[:type] = "const #{arg_info[:type]}" if arg_info[:const?] + elsif arg_info[:ptr?] or is_string_type(type) + if arg_info[:const?] + arg_info[:type] = "const #{arg_info[:type]}" + end + end + + args << arg_info + end + end + + # Try to find array pair in parameters following this pattern : * , <@array_size_type> <@array_size_name> + args.each_with_index do |val, index| + next_index = index + 1 + next unless args.length > next_index + + if (val[:ptr?] == true) && args[next_index][:name].match(@array_size_name) && @array_size_type.include?(args[next_index][:type]) + val[:array_data?] = true + args[next_index][:array_size?] = true + end + end + + args + end + def parse_declaration(parse_project, declaration, namespace = [], classname = nil) decl = {} decl[:namespace] = namespace decl[:class] = classname - regex_match = @declaration_parse_matcher.match(declaration) - raise "Failed parsing function declaration: '#{declaration}'" if regex_match.nil? + lexer = CLexer.new(declaration) + decl_tokens = lexer.tokenize - # grab argument list - args = regex_match[2].strip + # Split declaration into type, name, parameters, attributes, and calling convention - # process function attributes, return type, and name - parsed = parse_type_and_name(regex_match[1]) + type, name, parameters, p_attributes, p_ccc = parse_function_signature(decl_tokens, 0) + + # Process function attributes, return type, and name + # Some attributes may be written after the parameter list, so we need to + # check for them and move them to the front of the declaration. + type, attributes, ccc, parsed = parse_type(type) + attributes += p_attributes + ccc += p_ccc # Record original name without scope prefix - decl[:unscoped_name] = parsed[:name] + decl[:unscoped_name] = name # Prefix name with namespace scope (if any) and then class decl[:name] = namespace.join('_') @@ -558,12 +1255,22 @@ def parse_declaration(parse_project, declaration, namespace = [], classname = ni decl[:name] << '_' unless decl[:name].empty? decl[:name] << decl[:unscoped_name] - decl[:modifier] = parsed[:modifier] - unless parsed[:c_calling_convention].nil? - decl[:c_calling_convention] = parsed[:c_calling_convention] + decl[:modifier] = parsed[:modifier] + if parsed[:ptr?] + if parsed[:const?] + type = [:const] + type + attributes.delete_if{|attr| attribute_name(attr) == "const"} + decl[:modifier] = unparse(attributes) + end + end + + if not (parsed[:c_calling_convention].nil? or parsed[:c_calling_convention].empty?) + decl[:c_calling_convention] = parsed[:c_calling_convention] end - rettype = parsed[:type] + decl[:noreturn] = has_noreturn_attribute(attributes) + + rettype = unparse(type).gsub(/\s+\*/,'*') rettype = 'void' if @local_as_void.include?(rettype.strip) decl[:return] = { :type => rettype, :name => 'cmock_to_return', @@ -573,34 +1280,51 @@ def parse_declaration(parse_project, declaration, namespace = [], classname = ni :const? => parsed[:const?] || false, :const_ptr? => parsed[:const_ptr?] || false } + parameters = parameters.slice_before { |element| element == :comma }.map { |subarray| subarray.reject { |e| e == :comma } }.to_a + # parameter is now a list of parameter declarations each being a lists of tokens. + # eg. for (int* b, int c[5], int a=42) we'd have now (=42 has been removed earlier): + # [ [ :int, :mul_op, [:identifier, "b"] ], + # [ :int, [:identifier, "c"], [:brackets, [:integer_literal, "5"]] ], + # [ :int, [:identifier, "a"], :assign, [:integer_literal, "42"] ] ] + # remove default argument statements from mock definitions - args.gsub!(/=\s*[a-zA-Z0-9_\.]+\s*/, ' ') + parameters = parameters.map { |parameter| + # type name [ = expression ] + passign = parameter.index(:assign) + if passign.nil? + parameter + else + parameter[0..passign-1] + end } # check for var args - if args =~ /\.\.\./ - decl[:var_arg] = args.match(/[\w\s]*\.\.\./).to_s.strip - args = if args =~ /\,[\w\s]*\.\.\./ - args.gsub!(/\,[\w\s]*\.\.\./, '') - else - 'void' - end + if parameters[-1] == [:ellipsis] + decl[:var_arg] = "..." + parameters.pop + if parameters.empty? + parameters = [:void] + end else decl[:var_arg] = nil end - args = clean_args(args, parse_project) - decl[:args_string] = args - decl[:args] = parse_args(args) + + if parameters.any?(nil) + raise_parse_error "Invalid parameters #{parameters.inspect}" + end + parameters = clean_args(parameters, parse_project) + decl[:args_string] = parameters.map{|parameter| unparse(parameter)}.join(', ').gsub(/\s+\*/, '*') + decl[:args] = parse_args(parameters) decl[:args_call] = decl[:args].map { |a| a[:name] }.join(', ') decl[:contains_ptr?] = decl[:args].inject(false) { |ptr, arg| arg[:ptr?] ? true : ptr } if decl[:return][:type].nil? || decl[:name].nil? || decl[:args].nil? || decl[:return][:type].empty? || decl[:name].empty? - raise "Failed Parsing Declaration Prototype!\n" \ - " declaration: '#{declaration}'\n" \ - " modifier: '#{decl[:modifier]}'\n" \ - " return: #{prototype_inspect_hash(decl[:return])}\n" \ - " function: '#{decl[:name]}'\n" \ - " args: #{prototype_inspect_array_of_hashes(decl[:args])}\n" + raise_parse_error " declaration: '#{declaration}'\n" \ + " modifier: '#{decl[:modifier]}'\n" \ + " noreturn: '#{decl[:noreturn]}'\n" \ + " return: #{prototype_inspect_hash(decl[:return])}\n" \ + " function: '#{decl[:name]}'\n" \ + " args: #{prototype_inspect_array_of_hashes(decl[:args])}\n" end decl @@ -624,4 +1348,5 @@ def prototype_inspect_array_of_hashes(array) return "[\n #{hashes.join("\n ")}\n ]\n" end end + end diff --git a/test/rakefile_helper.rb b/test/rakefile_helper.rb index 3d898905..dd0b6541 100644 --- a/test/rakefile_helper.rb +++ b/test/rakefile_helper.rb @@ -21,7 +21,7 @@ module RakefileHelpers def load_configuration(config_file) $cfg_file = config_file - $cfg = YAML.load(File.read('./targets/' + $cfg_file)) + $cfg = YAML.load(File.read('./targets/' + $cfg_file),aliases: true) $colour_output = false unless $cfg['colour'] end diff --git a/test/unit/cmock_header_parser_test.rb b/test/unit/cmock_header_parser_test.rb index 43832ff8..27f710f1 100644 --- a/test/unit/cmock_header_parser_test.rb +++ b/test/unit/cmock_header_parser_test.rb @@ -1,3 +1,4 @@ +# coding: utf-8 # ========================================== # CMock Project - Automatic Mock Generation for C # Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams @@ -8,14 +9,18 @@ require File.expand_path(File.dirname(__FILE__)) + "/../test_helper" require File.expand_path(File.dirname(__FILE__)) + '/../../lib/cmock_header_parser' +require File.expand_path(File.dirname(__FILE__)) + '/../../lib/CLexer' describe CMockHeaderParser, "Verify CMockHeaderParser Module" do before do create_mocks :config @config.expect :strippables, ["STRIPPABLE"] - @config.expect :attributes, ['__ramfunc', 'funky_attrib', 'SQLITE_API'] + @config.expect :attributes, ['__ramfunc', 'funky_attrib', 'SQLITE_API','access','rt::access','deprecated'] @config.expect :c_calling_conventions, ['__stdcall'] + @config.expect :c_noreturn_attributes,['_Noreturn', 'noreturn', '__noreturn__'] + @config.expect :process_gcc_attributes, true + @config.expect :process_cpp_attributes, true @config.expect :treat_as_void, ['MY_FUNKY_VOID'] @config.expect :treat_as, { "BANJOS" => "INT", "TUBAS" => "HEX16"} @config.expect :treat_as_array, {"IntArray" => "int", "Book" => "Page"} @@ -42,7 +47,7 @@ it "create and initialize variables to defaults appropriately" do assert_nil(@parser.funcs) - assert_equal(['const', '__ramfunc', 'funky_attrib', 'SQLITE_API'], @parser.c_attributes) + assert_equal(['const', '__ramfunc', 'funky_attrib', 'SQLITE_API','access','rt::access','deprecated'], @parser.c_attributes) assert_equal(['void','MY_FUNKY_VOID'], @parser.treat_as_void) end @@ -53,10 +58,10 @@ "who // is you\n" expected = - [ - "abcd", - "who" - ] + [ + "abcd", + "who" + ] assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip}) end @@ -83,13 +88,13 @@ "//*/\n" expected = - [ - "no_comments", - "pre_block", - "shown_because_block_comment_invalid_from_line_comment", - "shown_because_block_comment_invalid_from_shorter_line_comment", - "shown_because_line_above_ended_comment_this_time" - ] + [ + "no_comments", + "pre_block", + "shown_because_block_comment_invalid_from_line_comment", + "shown_because_block_comment_invalid_from_shorter_line_comment", + "shown_because_line_above_ended_comment_this_time" + ] assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip}) end @@ -105,16 +110,16 @@ " void STRIPPABLE universal_handler ();\n" expected = - [ - "void* my_calloc(size_t, size_t)", - "void my_realloc(void*, size_t)", - "void universal_handler()" - ] + [ + "void* my_calloc(size_t, size_t)", + "void my_realloc(void*, size_t)", + "void universal_handler()" + ] assert_equal(expected, @parser.import_source(source, @test_project)) end - it "remove gcc's function __attribute__'s" do + it "keep gcc's function __attribute__'s" do source = "void* my_calloc(size_t, size_t) __attribute__((alloc_size(1,2)));\n" + "void\n" + @@ -125,11 +130,11 @@ " void __attribute__ ((interrupt)) universal_handler ();\n" expected = - [ - "void* my_calloc(size_t, size_t)", - "void my_realloc(void*, size_t)", - "void universal_handler()" - ] + [ # attributes will be removed later, depending on their name. + "void* my_calloc(size_t, size_t)__attribute__((alloc_size(1,2)))", + "void my_realloc(void*, size_t)__attribute__((alloc_size(2)))", + "void __attribute__((interrupt))universal_handler()" + ] assert_equal(expected, @parser.import_source(source, @test_project)) end @@ -167,9 +172,9 @@ "when \\ \n" expected = - [ - "hoo hah when" - ] + [ + "hoo hah when" + ] assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip}) end @@ -212,11 +217,11 @@ "};\n" expected = - [ - "int notatypedef", - "int typedef_isnt_me", - "this should remain!" - ] + [ + "int notatypedef", + "int typedef_isnt_me", + "this should remain!" + ] assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip}) end @@ -291,10 +296,10 @@ "void kinda_ugly_on_the_next_line(unsigned int);\n" expected = - [ - "uint32 extern_name_func(unsigned int)", - "uint32 funcinline(unsigned int)" - ] + [ + "uint32 extern_name_func(unsigned int)", + "uint32 funcinline(unsigned int)" + ] assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip}) end @@ -311,12 +316,12 @@ "}\n" expected = - [ - "uint32 func_with_decl_a(unsigned int)", - "uint32 func_with_decl_a", #okay. it's not going to be interpretted as another function - "uint32 func_with_decl_b(unsigned int)", - "uint32 func_with_decl_b", #okay. it's not going to be interpretted as another function - ] + [ + "uint32 func_with_decl_a(unsigned int)", + "uint32 func_with_decl_a", #okay. it's not going to be interpretted as another function + "uint32 func_with_decl_b(unsigned int)", + "uint32 func_with_decl_b", #okay. it's not going to be interpretted as another function + ] assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip}) end @@ -351,14 +356,14 @@ "}\n" expected = - [ - "uint32 func_with_decl_a(unsigned int)", - "uint32 func_with_decl_a", #okay. it's not going to be interpretted as another function - "uint32 func_with_decl_b(unsigned int)", - "uint32 func_with_decl_b", #okay. it's not going to be interpretted as another function - "uint32 func_with_decl_c(unsigned int)", - "uint32 func_with_decl_c", #okay. it's not going to be interpretted as another function - ] + [ + "uint32 func_with_decl_a(unsigned int)", + "uint32 func_with_decl_a", #okay. it's not going to be interpretted as another function + "uint32 func_with_decl_b(unsigned int)", + "uint32 func_with_decl_b", #okay. it's not going to be interpretted as another function + "uint32 func_with_decl_c(unsigned int)", + "uint32 func_with_decl_c", #okay. it's not going to be interpretted as another function + ] assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip}) end @@ -450,12 +455,12 @@ "void kinda_ugly_on_the_next_line(unsigned int);\n" expected = - [ "extern uint32 foobar(unsigned int)", - "uint32 extern_name_func(unsigned int)", - "uint32 funcinline(unsigned int)", - "extern void bar(unsigned int)", - "extern void kinda_ugly_on_the_next_line(unsigned int)" - ] + [ "extern uint32 foobar(unsigned int)", + "uint32 extern_name_func(unsigned int)", + "uint32 funcinline(unsigned int)", + "extern void bar(unsigned int)", + "extern void kinda_ugly_on_the_next_line(unsigned int)" + ] @parser.treat_externs = :include assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip}) @@ -476,12 +481,12 @@ "}\n" expected = - [ "uint32 extern_name_func(unsigned int)", - "uint32 funcinline(unsigned int)", - "void inlineBar(unsigned int)", - "void staticinlineBar(unsigned int)", - "void bar(unsigned int)" - ] + [ "uint32 extern_name_func(unsigned int)", + "uint32 funcinline(unsigned int)", + "void inlineBar(unsigned int)", + "void staticinlineBar(unsigned int)", + "void bar(unsigned int)" + ] @parser.treat_inlines = :include assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip}) @@ -502,14 +507,14 @@ "}\n" expected = - [ "extern uint32 foobar(unsigned int)", - "uint32 extern_name_func(unsigned int)", - "uint32 funcinline(unsigned int)", - "void inlineBar(unsigned int)", - "extern int extern_bar(void)", - "void staticinlineBar(unsigned int)", - "void bar(unsigned int)" - ] + [ "extern uint32 foobar(unsigned int)", + "uint32 extern_name_func(unsigned int)", + "uint32 funcinline(unsigned int)", + "void inlineBar(unsigned int)", + "extern int extern_bar(void)", + "void staticinlineBar(unsigned int)", + "void bar(unsigned int)" + ] @parser.treat_externs = :include @parser.treat_inlines = :include @@ -535,11 +540,11 @@ expected = [ - "uint32 foo(unsigned int)", - "uint32 bar(unsigned int)", - "void inlineBar(void)", - "int alwaysinlinefunc(int a)", - "void inlinebar(unsigned int)" + "uint32 foo(unsigned int)", + "uint32 bar(unsigned int)", + "void inlineBar(void)", + "int alwaysinlinefunc(int a)", + "void inlinebar(unsigned int)" ] @parser.treat_inlines = :include @@ -556,9 +561,9 @@ "#define get_foo() \\\n ((Thing)foo.bar)" # exercise multiline define expected = - [ - "void hello(void)", - ] + [ + "void hello(void)", + ] assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip}) end @@ -569,9 +574,9 @@ "const int TheMatrix(register int Trinity, unsigned int *restrict Neo)" expected = - [ - "const int TheMatrix(int Trinity, unsigned int * Neo)", - ] + [ + "const int TheMatrix(int Trinity, unsigned int * Neo)", + ] assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip}) end @@ -581,70 +586,73 @@ # since cmock treats void specially, we can't let void be obfuscated it "handle odd case of typedef'd void returned" do source = "MY_FUNKY_VOID FunkyVoidReturned(int a)" - expected = { :var_arg=>nil, - :name=>"FunkyVoidReturned", - :unscoped_name=>"FunkyVoidReturned", - :namespace=>[], - :class=>nil, - :return=>{ :type => "void", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "void cmock_to_return", - :void? => true - }, - :modifier=>"", + expected = { :var_arg => nil, + :name => "FunkyVoidReturned", + :unscoped_name => "FunkyVoidReturned", + :namespace => [], + :class => nil, + :noreturn => false, + :return => { :type => "void", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "void cmock_to_return", + :void? => true + }, + :modifier => "", :contains_ptr? => false, - :args=>[{:type=>"int", :name=>"a", :ptr? => false, :const? => false, :const_ptr? => false}], - :args_string=>"int a", - :args_call=>"a"} + :args => [{:type => "int", :name => "a", :ptr? => false, :const? => false, :const_ptr? => false}], + :args_string => "int a", + :args_call => "a"} assert_equal(expected, @parser.parse_declaration(@test_project, source)) end it "handle odd case of typedef'd void as arg" do source = "int FunkyVoidAsArg(MY_FUNKY_VOID)" - expected = { :var_arg=>nil, - :name=>"FunkyVoidAsArg", - :unscoped_name=>"FunkyVoidAsArg", - :namespace=>[], - :class=>nil, - :return=>{ :type => "int", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "int cmock_to_return", - :void? => false - }, - :modifier=>"", + expected = { :var_arg => nil, + :name => "FunkyVoidAsArg", + :unscoped_name => "FunkyVoidAsArg", + :namespace => [], + :class => nil, + :noreturn => false, + :return => { :type => "int", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :modifier => "", :contains_ptr? => false, - :args=>[], - :args_string=>"void", - :args_call=>"" } + :args => [], + :args_string => "void", + :args_call => "" } assert_equal(expected, @parser.parse_declaration(@test_project, source)) end it "handle odd case of typedef'd void as arg pointer" do source = "char FunkyVoidPointer(MY_FUNKY_VOID* bluh)" - expected = { :var_arg=>nil, - :name=>"FunkyVoidPointer", - :unscoped_name=>"FunkyVoidPointer", - :namespace=>[], - :class=>nil, - :return=>{ :type => "char", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "char cmock_to_return", - :void? => false - }, - :modifier=>"", + expected = { :var_arg => nil, + :name => "FunkyVoidPointer", + :unscoped_name => "FunkyVoidPointer", + :namespace => [], + :class => nil, + :noreturn => false, + :return => { :type => "char", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "char cmock_to_return", + :void? => false + }, + :modifier => "", :contains_ptr? => true, - :args=>[{:type=>"MY_FUNKY_VOID*", :name=>"bluh", :ptr? => true, :const? => false, :const_ptr? => false}], - :args_string=>"MY_FUNKY_VOID* bluh", - :args_call=>"bluh" } + :args => [{:type => "MY_FUNKY_VOID*", :name => "bluh", :ptr? => true, :const? => false, :const_ptr? => false}], + :args_string => "MY_FUNKY_VOID* bluh", + :args_call => "bluh" } assert_equal(expected, @parser.parse_declaration(@test_project, source)) end @@ -654,9 +662,9 @@ "void Foo(int a = 57, float b=37.52, char c= 'd', char* e=\"junk\");\n" expected = - [ - "void Foo(int a, float b, char c, char* e)" - ] + [ + "void Foo(int a, float b, char c, char* e)" + ] assert_equal(expected, @parser.import_source(source, @test_project).map!{|s|s.strip}) end @@ -729,155 +737,161 @@ it "extract and return function declarations with retval and args" do source = "int Foo(int a, unsigned int b)" - expected = { :var_arg=>nil, - :name=>"Foo", - :unscoped_name=>"Foo", - :namespace=>[], - :class=>nil, - :return=>{ :type => "int", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "int cmock_to_return", - :void? => false - }, - :modifier=>"", + expected = { :var_arg => nil, + :name => "Foo", + :unscoped_name => "Foo", + :namespace => [], + :class => nil, + :noreturn => false, + :return => { :type => "int", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :modifier => "", :contains_ptr? => false, - :args=>[ {:type=>"int", :name=>"a", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"unsigned int", :name=>"b", :ptr? => false, :const? => false, :const_ptr? => false} - ], - :args_string=>"int a, unsigned int b", - :args_call=>"a, b" } + :args => [ {:type => "int", :name => "a", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "unsigned int", :name => "b", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "int a, unsigned int b", + :args_call => "a, b" } assert_equal(expected, @parser.parse_declaration(@test_project, source)) end it "extract and return function declarations with no retval" do source = "void FunkyChicken( uint la, int de, bool da)" - expected = { :var_arg=>nil, - :return=>{ :type => "void", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "void cmock_to_return", - :void? => true - }, - :name=>"FunkyChicken", - :unscoped_name=>"FunkyChicken", - :namespace=>[], - :class=>nil, - :modifier=>"", + expected = { :var_arg => nil, + :noreturn => false, + :return => { :type => "void", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "void cmock_to_return", + :void? => true + }, + :name => "FunkyChicken", + :unscoped_name => "FunkyChicken", + :namespace => [], + :class => nil, + :modifier => "", :contains_ptr? => false, - :args=>[ {:type=>"uint", :name=>"la", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"int", :name=>"de", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"bool", :name=>"da", :ptr? => false, :const? => false, :const_ptr? => false} - ], - :args_string=>"uint la, int de, bool da", - :args_call=>"la, de, da" } + :args => [ {:type => "uint", :name => "la", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "int", :name => "de", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "bool", :name => "da", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "uint la, int de, bool da", + :args_call => "la, de, da" } assert_equal(expected, @parser.parse_declaration(@test_project, source)) end it "extract and return function declarations with implied voids" do source = "void tat()" - expected = { :var_arg=>nil, - :return=>{ :type => "void", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "void cmock_to_return", - :void? => true - }, - :name=>"tat", - :unscoped_name=>"tat", - :namespace=>[], - :class=>nil, - :modifier=>"", + expected = { :var_arg => nil, + :noreturn => false, + :return => { :type => "void", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "void cmock_to_return", + :void? => true + }, + :name => "tat", + :unscoped_name => "tat", + :namespace => [], + :class => nil, + :modifier => "", :contains_ptr? => false, - :args=>[ ], - :args_string=>"void", - :args_call=>"" } + :args => [ ], + :args_string => "void", + :args_call => "" } assert_equal(expected, @parser.parse_declaration(@test_project, source)) end it "extract modifiers properly" do source = "const int TheMatrix(int Trinity, unsigned int * Neo)" - expected = { :var_arg=>nil, - :return=>{ :type => "int", - :name => 'cmock_to_return', - :ptr? => false, - :const? => true, - :const_ptr? => false, - :str => "int cmock_to_return", - :void? => false - }, - :name=>"TheMatrix", - :unscoped_name=>"TheMatrix", - :namespace=>[], - :class=>nil, - :modifier=>"const", + expected = { :var_arg => nil, + :noreturn => false, + :return => { :type => "int", + :name => 'cmock_to_return', + :ptr? => false, + :const? => true, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :name => "TheMatrix", + :unscoped_name => "TheMatrix", + :namespace => [], + :class => nil, + :modifier => "const", :contains_ptr? => true, - :args=>[ {:type=>"int", :name=>"Trinity", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"unsigned int*", :name=>"Neo", :ptr? => true, :const? => false, :const_ptr? => false} - ], - :args_string=>"int Trinity, unsigned int* Neo", - :args_call=>"Trinity, Neo" } + :args => [ {:type => "int", :name => "Trinity", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "unsigned int*", :name => "Neo", :ptr? => true, :const? => false, :const_ptr? => false} + ], + :args_string => "int Trinity, unsigned int* Neo", + :args_call => "Trinity, Neo" } assert_equal(expected, @parser.parse_declaration(@test_project, source)) end it "extract c calling conventions properly" do source = "const int __stdcall TheMatrix(int Trinity, unsigned int * Neo)" - expected = { :var_arg=>nil, - :return=>{ :type => "int", - :name => 'cmock_to_return', - :ptr? => false, - :const? => true, - :const_ptr? => false, - :str => "int cmock_to_return", - :void? => false - }, - :name=>"TheMatrix", - :unscoped_name=>"TheMatrix", - :namespace=>[], - :class=>nil, - :modifier=>"const", - :c_calling_convention=>"__stdcall", + expected = { :var_arg => nil, + :noreturn => false, + :return => { :type => "int", + :name => 'cmock_to_return', + :ptr? => false, + :const? => true, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :name => "TheMatrix", + :unscoped_name => "TheMatrix", + :namespace => [], + :class => nil, + :modifier => "const", + :c_calling_convention => "__stdcall", :contains_ptr? => true, - :args=>[ {:type=>"int", :name=>"Trinity", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"unsigned int*", :name=>"Neo", :ptr? => true, :const? => false, :const_ptr? => false} - ], - :args_string=>"int Trinity, unsigned int* Neo", - :args_call=>"Trinity, Neo" } + :args => [ {:type => "int", :name => "Trinity", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "unsigned int*", :name => "Neo", :ptr? => true, :const? => false, :const_ptr? => false} + ], + :args_string => "int Trinity, unsigned int* Neo", + :args_call => "Trinity, Neo" } assert_equal(expected, @parser.parse_declaration(@test_project, source)) end it "extract and return function declarations inside namespace and class" do source = "int Foo(int a, unsigned int b)" - expected = { :var_arg=>nil, - :name=>"ns1_ns2_Bar_Foo", - :unscoped_name=>"Foo", - :class=>"Bar", - :namespace=>["ns1", "ns2"], - :return=>{ :type => "int", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "int cmock_to_return", - :void? => false - }, - :modifier=>"", + expected = { :var_arg => nil, + :name => "ns1_ns2_Bar_Foo", + :unscoped_name => "Foo", + :class => "Bar", + :namespace => ["ns1", "ns2"], + :noreturn => false, + :return => { :type => "int", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :modifier => "", :contains_ptr? => false, - :args=>[ {:type=>"int", :name=>"a", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"unsigned int", :name=>"b", :ptr? => false, :const? => false, :const_ptr? => false} - ], - :args_string=>"int a, unsigned int b", - :args_call=>"a, b" } + :args => [ {:type => "int", :name => "a", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "unsigned int", :name => "b", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "int a, unsigned int b", + :args_call => "a, b" } assert_equal(expected, @parser.parse_declaration(@test_project, source, ["ns1", "ns2"], "Bar")) end @@ -886,46 +900,48 @@ source = "const int TheMatrix(int Trinity, unsigned int * Neo);\n" + "int Morpheus(int, unsigned int*);\n" - expected = [{ :var_arg=>nil, - :return=> { :type => "int", - :name => 'cmock_to_return', - :ptr? => false, - :const? => true, - :const_ptr? => false, - :str => "int cmock_to_return", - :void? => false - }, - :name=>"TheMatrix", - :unscoped_name=>"TheMatrix", - :namespace=>[], - :class=>nil, - :modifier=>"const", + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "int", + :name => 'cmock_to_return', + :ptr? => false, + :const? => true, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :name => "TheMatrix", + :unscoped_name => "TheMatrix", + :namespace => [], + :class => nil, + :modifier => "const", :contains_ptr? => true, - :args=>[ {:type=>"int", :name=>"Trinity", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"unsigned int*", :name=>"Neo", :ptr? => true, :const? => false, :const_ptr? => false} - ], - :args_string=>"int Trinity, unsigned int* Neo", - :args_call=>"Trinity, Neo" }, - { :var_arg=>nil, - :return=> { :type => "int", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "int cmock_to_return", - :void? => false - }, - :name=>"Morpheus", - :unscoped_name=>"Morpheus", - :namespace=>[], - :class=>nil, - :modifier=>"", + :args => [ {:type => "int", :name => "Trinity", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "unsigned int*", :name => "Neo", :ptr? => true, :const? => false, :const_ptr? => false} + ], + :args_string => "int Trinity, unsigned int* Neo", + :args_call => "Trinity, Neo" }, + { :var_arg => nil, + :noreturn => false, + :return => { :type => "int", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :name => "Morpheus", + :unscoped_name => "Morpheus", + :namespace => [], + :class => nil, + :modifier => "", :contains_ptr? => true, - :args=>[ {:type=>"int", :name=>"cmock_arg1", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"unsigned int*", :name=>"cmock_arg2", :ptr? => true, :const? => false, :const_ptr? => false} - ], - :args_string=>"int cmock_arg1, unsigned int* cmock_arg2", - :args_call=>"cmock_arg1, cmock_arg2" + :args => [ {:type => "int", :name => "cmock_arg1", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "unsigned int*", :name => "cmock_arg2", :ptr? => true, :const? => false, :const_ptr? => false} + ], + :args_string => "int cmock_arg1, unsigned int* cmock_arg2", + :args_call => "cmock_arg1, cmock_arg2" }] assert_equal(expected, @parser.parse("module", source)[:functions]) end @@ -935,26 +951,27 @@ source = "const int TheMatrix(int Trinity, unsigned int * Neo);\n" + "const int TheMatrix(int, unsigned int*);\n" - expected = [{ :var_arg=>nil, - :name=>"TheMatrix", - :unscoped_name=>"TheMatrix", - :namespace=>[], - :class=>nil, - :return=> { :type => "int", - :name => 'cmock_to_return', - :ptr? => false, - :const? => true, - :const_ptr? => false, - :str => "int cmock_to_return", - :void? => false - }, - :modifier=>"const", + expected = [{ :var_arg => nil, + :name => "TheMatrix", + :unscoped_name => "TheMatrix", + :namespace => [], + :class => nil, + :noreturn => false, + :return => { :type => "int", + :name => 'cmock_to_return', + :ptr? => false, + :const? => true, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :modifier => "const", :contains_ptr? => true, - :args=>[ {:type=>"int", :name=>"Trinity", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"unsigned int*", :name=>"Neo", :ptr? => true, :const? => false, :const_ptr? => false} - ], - :args_string=>"int Trinity, unsigned int* Neo", - :args_call=>"Trinity, Neo" + :args => [ {:type => "int", :name => "Trinity", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "unsigned int*", :name => "Neo", :ptr? => true, :const? => false, :const_ptr? => false} + ], + :args_string => "int Trinity, unsigned int* Neo", + :args_call => "Trinity, Neo" }] assert_equal(expected, @parser.parse("module", source)[:functions]) end @@ -969,8 +986,9 @@ expected = [{ :var_arg => nil, :name => "PorkRoast", :unscoped_name => "PorkRoast", - :namespace=>[], - :class=>nil, + :namespace => [], + :class => nil, + :noreturn => false, :return => { :type => "const int*", :name => 'cmock_to_return', :ptr? => true, @@ -1001,14 +1019,15 @@ expected = [{ :var_arg => nil, :name => "PorkRoast", :unscoped_name => "PorkRoast", - :namespace=>[], - :class=>nil, - :return => { :type => "int const*", + :namespace => [], + :class => nil, + :noreturn => false, + :return => { :type => "const int*", :name => 'cmock_to_return', :ptr? => true, :const? => true, :const_ptr? => false, - :str => "int const* cmock_to_return", + :str => "const int* cmock_to_return", :void? => false }, :modifier => "", @@ -1027,24 +1046,25 @@ source = "int * const PorkRoast(void);\n" - expected = [{ :var_arg=>nil, - :name=>"PorkRoast", - :unscoped_name=>"PorkRoast", - :namespace=>[], - :class=>nil, - :return=> { :type => "int*", - :name => 'cmock_to_return', - :ptr? => true, - :const? => false, - :const_ptr? => true, - :str => "int* cmock_to_return", - :void? => false - }, - :modifier=>"const", + expected = [{ :var_arg => nil, + :name => "PorkRoast", + :unscoped_name => "PorkRoast", + :namespace => [], + :class => nil, + :noreturn => false, + :return => { :type => "int*", + :name => 'cmock_to_return', + :ptr? => true, + :const? => false, + :const_ptr? => true, + :str => "int* cmock_to_return", + :void? => false + }, + :modifier => "const", :contains_ptr? => false, - :args=>[], - :args_string=>"void", - :args_call=>"" + :args => [], + :args_string => "void", + :args_call => "" }] assert_equal(expected, @parser.parse("module", source)[:functions]) end @@ -1055,9 +1075,10 @@ expected = [{ :name => "foo", :unscoped_name => "foo", - :namespace=>[], - :class=>nil, + :namespace => [], + :class => nil, :modifier => "", + :noreturn => false, :return => { :type => "void", :name => "cmock_to_return", :str => "void cmock_to_return", @@ -1069,14 +1090,14 @@ :var_arg => nil, :args_string => "int const* cmock_arg1, int* const cmock_arg2, const int* cmock_arg3, const int* const cmock_arg4, " + "int const* const cmock_arg5, int* cmock_arg6, int cmock_arg7, const int cmock_arg8", - :args => [{ :type=>"int const*", :name => "cmock_arg1", :ptr? => true, :const? => true, :const_ptr? => false }, - { :type=>"int*", :name => "cmock_arg2", :ptr? => true, :const? => false, :const_ptr? => true }, - { :type=>"const int*", :name => "cmock_arg3", :ptr? => true, :const? => true, :const_ptr? => false }, - { :type=>"const int*", :name => "cmock_arg4", :ptr? => true, :const? => true, :const_ptr? => true }, - { :type=>"int const*", :name => "cmock_arg5", :ptr? => true, :const? => true, :const_ptr? => true }, - { :type=>"int*", :name => "cmock_arg6", :ptr? => true, :const? => false, :const_ptr? => false }, - { :type=>"int", :name => "cmock_arg7", :ptr? => false, :const? => false, :const_ptr? => false }, - { :type=>"int", :name => "cmock_arg8", :ptr? => false, :const? => true, :const_ptr? => false }], + :args => [{ :type => "const int*", :name => "cmock_arg1", :ptr? => true, :const? => true, :const_ptr? => false }, + { :type => "int*", :name => "cmock_arg2", :ptr? => true, :const? => false, :const_ptr? => true }, + { :type => "const int*", :name => "cmock_arg3", :ptr? => true, :const? => true, :const_ptr? => false }, + { :type => "const int*", :name => "cmock_arg4", :ptr? => true, :const? => true, :const_ptr? => true }, + { :type => "const int*", :name => "cmock_arg5", :ptr? => true, :const? => true, :const_ptr? => true }, + { :type => "int*", :name => "cmock_arg6", :ptr? => true, :const? => false, :const_ptr? => false }, + { :type => "int", :name => "cmock_arg7", :ptr? => false, :const? => false, :const_ptr? => false }, + { :type => "int", :name => "cmock_arg8", :ptr? => false, :const? => true, :const_ptr? => false }], :args_call => "cmock_arg1, cmock_arg2, cmock_arg3, cmock_arg4, cmock_arg5, cmock_arg6, cmock_arg7, cmock_arg8", :contains_ptr? => true }] @@ -1090,9 +1111,10 @@ expected = [{ :name => "bar", :unscoped_name => "bar", - :namespace=>[], - :class=>nil, + :namespace => [], + :class => nil, :modifier => "", + :noreturn => false, :return => { :type => "void", :name => "cmock_to_return", :str => "void cmock_to_return", @@ -1104,14 +1126,14 @@ :var_arg => nil, :args_string => "int const* param1, int* const param2, const int* param3, const int* const param4, " + "int const* const param5, int* param6, int param7, const int param8", - :args => [{ :type=>"int const*", :name => "param1", :ptr? => true, :const? => true, :const_ptr? => false }, - { :type=>"int*", :name => "param2", :ptr? => true, :const? => false, :const_ptr? => true }, - { :type=>"const int*", :name => "param3", :ptr? => true, :const? => true, :const_ptr? => false }, - { :type=>"const int*", :name => "param4", :ptr? => true, :const? => true, :const_ptr? => true }, - { :type=>"int const*", :name => "param5", :ptr? => true, :const? => true, :const_ptr? => true }, - { :type=>"int*", :name => "param6", :ptr? => true, :const? => false, :const_ptr? => false }, - { :type=>"int", :name => "param7", :ptr? => false, :const? => false, :const_ptr? => false }, - { :type=>"int", :name => "param8", :ptr? => false, :const? => true, :const_ptr? => false }], + :args => [{ :type => "const int*", :name => "param1", :ptr? => true, :const? => true, :const_ptr? => false }, + { :type => "int*", :name => "param2", :ptr? => true, :const? => false, :const_ptr? => true }, + { :type => "const int*", :name => "param3", :ptr? => true, :const? => true, :const_ptr? => false }, + { :type => "const int*", :name => "param4", :ptr? => true, :const? => true, :const_ptr? => true }, + { :type => "const int*", :name => "param5", :ptr? => true, :const? => true, :const_ptr? => true }, + { :type => "int*", :name => "param6", :ptr? => true, :const? => false, :const_ptr? => false }, + { :type => "int", :name => "param7", :ptr? => false, :const? => false, :const_ptr? => false }, + { :type => "int", :name => "param8", :ptr? => false, :const? => true, :const_ptr? => false }], :args_call => "param1, param2, param3, param4, param5, param6, param7, param8", :contains_ptr? => true }].freeze @@ -1124,9 +1146,10 @@ expected = [{ :name => "AddToBook", :unscoped_name => "AddToBook", - :namespace=>[], - :class=>nil, - :modifier=>"", + :namespace => [], + :class => nil, + :modifier => "", + :noreturn => false, :return => { :type => "Book", :name => "cmock_to_return", :str => "Book cmock_to_return", @@ -1154,11 +1177,12 @@ "FUNKY_VOID_T DrHorrible(int SingAlong);\n" + "int CaptainHammer(CHUNKY_VOID_T);\n" - expected = [{ :var_arg=>nil, - :name=>"DrHorrible", - :unscoped_name=>"DrHorrible", - :namespace=>[], - :class=>nil, + expected = [{ :var_arg => nil, + :name => "DrHorrible", + :unscoped_name => "DrHorrible", + :namespace => [], + :class => nil, + :noreturn => false, :return => { :type => "void", :name => 'cmock_to_return', :ptr? => false, @@ -1167,30 +1191,31 @@ :str => "void cmock_to_return", :void? => true }, - :modifier=>"", + :modifier => "", :contains_ptr? => false, - :args=>[ {:type=>"int", :name=>"SingAlong", :ptr? => false, :const? => false, :const_ptr? => false} ], - :args_string=>"int SingAlong", - :args_call=>"SingAlong" + :args => [ {:type => "int", :name => "SingAlong", :ptr? => false, :const? => false, :const_ptr? => false} ], + :args_string => "int SingAlong", + :args_call => "SingAlong" }, - { :var_arg=>nil, - :return=> { :type => "int", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "int cmock_to_return", - :void? => false - }, - :name=>"CaptainHammer", - :unscoped_name=>"CaptainHammer", - :namespace=>[], - :class=>nil, - :modifier=>"", + { :var_arg => nil, + :noreturn => false, + :return => { :type => "int", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :name => "CaptainHammer", + :unscoped_name => "CaptainHammer", + :namespace => [], + :class => nil, + :modifier => "", :contains_ptr? => false, - :args=>[ ], - :args_string=>"void", - :args_call=>"" + :args => [ ], + :args_string => "void", + :args_call => "" }] assert_equal(expected, @parser.parse("module", source)[:functions]) end @@ -1201,7 +1226,8 @@ "void Penny(struct const _KeepYourHeadUp_ * const BillyBuddy);\n" + "struct TheseArentTheHammer CaptainHammer(void);\n" - expected = [{ :var_arg=>nil, + expected = [{ :var_arg => nil, + :noreturn => false, :return =>{ :type => "int", :name => 'cmock_to_return', :ptr? => false, @@ -1210,193 +1236,200 @@ :str => "int cmock_to_return", :void? => false }, - :name=>"DrHorrible", - :unscoped_name=>"DrHorrible", - :namespace=>[], - :class=>nil, - :modifier=>"", + :name => "DrHorrible", + :unscoped_name => "DrHorrible", + :namespace => [], + :class => nil, + :modifier => "", :contains_ptr? => false, - :args=>[ {:type=>"struct SingAlong", :name=>"Blog", :ptr? => false, :const? => false, :const_ptr? => false} ], - :args_string=>"struct SingAlong Blog", - :args_call=>"Blog" + :args => [ {:type => "struct SingAlong", :name => "Blog", :ptr? => false, :const? => false, :const_ptr? => false} ], + :args_string => "struct SingAlong Blog", + :args_call => "Blog" }, - { :var_arg=>nil, - :return=> { :type => "void", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "void cmock_to_return", - :void? => true - }, - :name=>"Penny", - :unscoped_name=>"Penny", - :namespace=>[], - :class=>nil, - :modifier=>"", + { :var_arg => nil, + :noreturn => false, + :return => { :type => "void", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "void cmock_to_return", + :void? => true + }, + :name => "Penny", + :unscoped_name => "Penny", + :namespace => [], + :class => nil, + :modifier => "", :contains_ptr? => true, - :args=>[ {:type=>"struct const _KeepYourHeadUp_*", :name=>"BillyBuddy", :ptr? => true, :const? => true, :const_ptr? => true} ], - :args_string=>"struct const _KeepYourHeadUp_* const BillyBuddy", - :args_call=>"BillyBuddy" + :args => [ {:type => "const struct _KeepYourHeadUp_*", :name => "BillyBuddy", :ptr? => true, :const? => true, :const_ptr? => true} ], + :args_string => "struct const _KeepYourHeadUp_* const BillyBuddy", + :args_call => "BillyBuddy" }, - { :var_arg=>nil, - :return=> { :type => "struct TheseArentTheHammer", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "struct TheseArentTheHammer cmock_to_return", - :void? => false - }, - :name=>"CaptainHammer", - :unscoped_name=>"CaptainHammer", - :namespace=>[], - :class=>nil, - :modifier=>"", + { :var_arg => nil, + :noreturn => false, + :return => { :type => "struct TheseArentTheHammer", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "struct TheseArentTheHammer cmock_to_return", + :void? => false + }, + :name => "CaptainHammer", + :unscoped_name => "CaptainHammer", + :namespace => [], + :class => nil, + :modifier => "", :contains_ptr? => false, - :args=>[ ], - :args_string=>"void", - :args_call=>"" + :args => [ ], + :args_string => "void", + :args_call => "" }] assert_equal(expected, @parser.parse("module", source)[:functions]) end it "extract functions containing unions with union specifier" do source = "void OrangePeel(union STARS_AND_STRIPES * a, union AFL_CIO b)" - expected = [{ :var_arg=>nil, - :return=>{ :type => "void", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "void cmock_to_return", - :void? => true - }, - :name=>"OrangePeel", - :unscoped_name=>"OrangePeel", - :namespace=>[], - :class=>nil, - :modifier=>"", - :contains_ptr? => true, - :args=>[ {:type=>"union STARS_AND_STRIPES*", :name=>"a", :ptr? => true, :const? => false, :const_ptr? => false}, - {:type=>"union AFL_CIO", :name=>"b", :ptr? => false, :const? => false, :const_ptr? => false} - ], - :args_string=>"union STARS_AND_STRIPES* a, union AFL_CIO b", - :args_call=>"a, b" }] + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "void", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "void cmock_to_return", + :void? => true + }, + :name => "OrangePeel", + :unscoped_name => "OrangePeel", + :namespace => [], + :class => nil, + :modifier => "", + :contains_ptr? => true, + :args => [ {:type => "union STARS_AND_STRIPES*", :name => "a", :ptr? => true, :const? => false, :const_ptr? => false}, + {:type => "union AFL_CIO", :name => "b", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "union STARS_AND_STRIPES* a, union AFL_CIO b", + :args_call => "a, b" }] result = @parser.parse("module", source) assert_equal(expected, result[:functions]) end it "not be thwarted by variables named with primitive types as part of the name" do source = "void ApplePeel(const unsigned int const_param, int int_param, int integer, char character, int* const constant)" - expected = [{ :var_arg=>nil, - :return=>{ :type => "void", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "void cmock_to_return", - :void? => true - }, - :name=>"ApplePeel", - :unscoped_name=>"ApplePeel", - :namespace=>[], - :class=>nil, - :modifier=>"", - :contains_ptr? => true, - :args=>[ {:type=> "unsigned int", :name=>"const_param", :ptr? => false, :const? => true, :const_ptr? => false}, - {:type=>"int", :name=>"int_param", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"int", :name=>"integer", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"char", :name=>"character", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"int*", :name=>"constant", :ptr? => true, :const? => false, :const_ptr? => true} - ], - :args_string=>"const unsigned int const_param, int int_param, int integer, char character, int* const constant", - :args_call=>"const_param, int_param, integer, character, constant" }] + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "void", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "void cmock_to_return", + :void? => true + }, + :name => "ApplePeel", + :unscoped_name => "ApplePeel", + :namespace => [], + :class => nil, + :modifier => "", + :contains_ptr? => true, + :args => [ {:type => "unsigned int", :name => "const_param", :ptr? => false, :const? => true, :const_ptr? => false}, + {:type => "int", :name => "int_param", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "int", :name => "integer", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "char", :name => "character", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "int*", :name => "constant", :ptr? => true, :const? => false, :const_ptr? => true} + ], + :args_string => "const unsigned int const_param, int int_param, int integer, char character, int* const constant", + :args_call => "const_param, int_param, integer, character, constant" }] result = @parser.parse("module", source) assert_equal(expected, result[:functions]) end it "not be thwarted by custom types named similarly to primitive types" do source = "void LemonPeel(integer param, character thing, longint * junk, constant value, int32_t const number)" - expected = [{:var_arg=>nil, - :return=>{ :type => "void", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "void cmock_to_return", - :void? => true - }, - :name=>"LemonPeel", - :unscoped_name=>"LemonPeel", - :namespace=>[], - :class=>nil, - :modifier=>"", + expected = [{:var_arg => nil, + :noreturn => false, + :return => { :type => "void", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "void cmock_to_return", + :void? => true + }, + :name => "LemonPeel", + :unscoped_name => "LemonPeel", + :namespace => [], + :class => nil, + :modifier => "", :contains_ptr? => true, - :args=>[ {:type=>"integer", :name=>"param", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"character", :name=>"thing", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"longint*", :name=>"junk", :ptr? => true, :const? => false, :const_ptr? => false}, - {:type=>"constant", :name=>"value", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"int32_t", :name=>"number", :ptr? => false, :const? => true, :const_ptr? => false} - ], - :args_string=>"integer param, character thing, longint* junk, constant value, int32_t const number", - :args_call=>"param, thing, junk, value, number" }] + :args => [ {:type => "integer", :name => "param", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "character", :name => "thing", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "longint*", :name => "junk", :ptr? => true, :const? => false, :const_ptr? => false}, + {:type => "constant", :name => "value", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "int32_t", :name => "number", :ptr? => false, :const? => true, :const_ptr? => false} + ], + :args_string => "integer param, character thing, longint* junk, constant value, int32_t const number", + :args_call => "param, thing, junk, value, number" }] result = @parser.parse("module", source) assert_equal(expected, result[:functions]) end it "handle some of those chains of C name specifiers naturally" do source = "void CoinOperated(signed char abc, const unsigned long int xyz_123, unsigned int const abc_123, long long arm_of_the_law)" - expected = [{:var_arg=>nil, - :return=>{ :type => "void", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "void cmock_to_return", - :void? => true - }, - :name=>"CoinOperated", - :unscoped_name=>"CoinOperated", - :namespace=>[], - :class=>nil, - :modifier=>"", + expected = [{:var_arg => nil, + :noreturn => false, + :return => { :type => "void", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "void cmock_to_return", + :void? => true + }, + :name => "CoinOperated", + :unscoped_name => "CoinOperated", + :namespace => [], + :class => nil, + :modifier => "", :contains_ptr? => false, - :args=>[ {:type=>"signed char", :name=>"abc", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"unsigned long int", :name=>"xyz_123", :ptr? => false, :const? => true, :const_ptr? => false}, - {:type=>"unsigned int", :name=>"abc_123", :ptr? => false, :const? => true, :const_ptr? => false}, - {:type=>"long long", :name=>"arm_of_the_law", :ptr? => false, :const? => false, :const_ptr? => false} - ], - :args_string=>"signed char abc, const unsigned long int xyz_123, unsigned int const abc_123, long long arm_of_the_law", - :args_call=>"abc, xyz_123, abc_123, arm_of_the_law" }] + :args => [ {:type => "signed char", :name => "abc", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "unsigned long int", :name => "xyz_123", :ptr? => false, :const? => true, :const_ptr? => false}, + {:type => "unsigned int", :name => "abc_123", :ptr? => false, :const? => true, :const_ptr? => false}, + {:type => "long long", :name => "arm_of_the_law", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "signed char abc, const unsigned long int xyz_123, unsigned int const abc_123, long long arm_of_the_law", + :args_call => "abc, xyz_123, abc_123, arm_of_the_law" }] result = @parser.parse("module", source) assert_equal(expected, result[:functions]) end it "handle custom types of various formats" do source = "void CardOperated(CUSTOM_TYPE abc, CUSTOM_TYPE* xyz_123, CUSTOM_TYPE const abcxyz, struct CUSTOM_TYPE const * const abc123)" - expected = [{:var_arg=>nil, - :return=>{ :type => "void", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "void cmock_to_return", - :void? => true - }, - :name=>"CardOperated", - :unscoped_name=>"CardOperated", - :namespace=>[], - :class=>nil, - :modifier=>"", + expected = [{:var_arg => nil, + :noreturn => false, + :return => { :type => "void", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "void cmock_to_return", + :void? => true + }, + :name => "CardOperated", + :unscoped_name => "CardOperated", + :namespace => [], + :class => nil, + :modifier => "", :contains_ptr? => true, - :args=>[ {:type=>"CUSTOM_TYPE", :name=>"abc", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"CUSTOM_TYPE*", :name=>"xyz_123", :ptr? => true, :const? => false, :const_ptr? => false}, - {:type=>"CUSTOM_TYPE", :name=>"abcxyz", :ptr? => false, :const? => true, :const_ptr? => false}, - {:type=>"struct CUSTOM_TYPE const*", :name=>"abc123", :ptr? => true, :const? => true, :const_ptr? => true} - ], - :args_string=>"CUSTOM_TYPE abc, CUSTOM_TYPE* xyz_123, CUSTOM_TYPE const abcxyz, struct CUSTOM_TYPE const* const abc123", - :args_call=>"abc, xyz_123, abcxyz, abc123" }] + :args => [ {:type => "CUSTOM_TYPE", :name => "abc", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "CUSTOM_TYPE*", :name => "xyz_123", :ptr? => true, :const? => false, :const_ptr? => false}, + {:type => "CUSTOM_TYPE", :name => "abcxyz", :ptr? => false, :const? => true, :const_ptr? => false}, + {:type => "const struct CUSTOM_TYPE*", :name => "abc123", :ptr? => true, :const? => true, :const_ptr? => true} + ], + :args_string => "CUSTOM_TYPE abc, CUSTOM_TYPE* xyz_123, CUSTOM_TYPE const abcxyz, struct CUSTOM_TYPE const* const abc123", + :args_call => "abc, xyz_123, abcxyz, abc123" }] result = @parser.parse("module", source) assert_equal(expected, result[:functions]) end @@ -1413,20 +1446,21 @@ { type: 'int**', name: 'thing4', ptr?: true, const?: false, const_ptr?: false }, { type: 'u8*', name: 'thing5', ptr?: true, const?: false, const_ptr?: false } ] - expected = [{:var_arg=>nil, - :return=>{ :type => "void", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "void cmock_to_return", - :void? => true - }, - :name=>"KeyOperated", - :unscoped_name=>"KeyOperated", - :namespace=>[], - :class=>nil, - :modifier=>"", + expected = [{:var_arg => nil, + :noreturn => false, + :return => { :type => "void", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "void cmock_to_return", + :void? => true + }, + :name => "KeyOperated", + :unscoped_name => "KeyOperated", + :namespace => [], + :class => nil, + :modifier => "", :contains_ptr? => true, :args => expected_args, :args_string => 'CUSTOM_TYPE* thing1, int* thing2, ' \ @@ -1438,52 +1472,54 @@ it "give a reasonable guess when dealing with weird combinations of custom types and modifiers" do source = "void Cheese(unsigned CUSTOM_TYPE abc, unsigned xyz, CUSTOM_TYPE1 CUSTOM_TYPE2 pdq)" - expected = [{:var_arg=>nil, - :return=>{ :type => "void", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "void cmock_to_return", - :void? => true - }, - :name=>"Cheese", - :unscoped_name=>"Cheese", - :namespace=>[], - :class=>nil, - :modifier=>"", + expected = [{:var_arg => nil, + :noreturn => false, + :return => { :type => "void", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "void cmock_to_return", + :void? => true + }, + :name => "Cheese", + :unscoped_name => "Cheese", + :namespace => [], + :class => nil, + :modifier => "", :contains_ptr? => false, - :args=>[ {:type=>"unsigned CUSTOM_TYPE", :name=>"abc", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"unsigned", :name=>"xyz", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"CUSTOM_TYPE1 CUSTOM_TYPE2", :name=>"pdq", :ptr? => false, :const? => false, :const_ptr? => false} - ], - :args_string=>"unsigned CUSTOM_TYPE abc, unsigned xyz, CUSTOM_TYPE1 CUSTOM_TYPE2 pdq", - :args_call=>"abc, xyz, pdq" }] + :args => [ {:type => "unsigned CUSTOM_TYPE", :name => "abc", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "unsigned", :name => "xyz", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "CUSTOM_TYPE1 CUSTOM_TYPE2", :name => "pdq", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "unsigned CUSTOM_TYPE abc, unsigned xyz, CUSTOM_TYPE1 CUSTOM_TYPE2 pdq", + :args_call => "abc, xyz, pdq" }] result = @parser.parse("module", source) assert_equal(expected, result[:functions]) end it "extract functions containing a function pointer" do source = "void FunkyTurkey(unsigned int (*func_ptr)(int, char))" - expected = [{ :var_arg=>nil, - :return=>{ :type => "void", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "void cmock_to_return", - :void? => true - }, - :name=>"FunkyTurkey", - :unscoped_name=>"FunkyTurkey", - :namespace=>[], - :class=>nil, - :modifier=>"", - :contains_ptr? => false, - :args=>[ {:type=>"cmock_module_func_ptr1", :name=>"func_ptr", :ptr? => false, :const? => false, :const_ptr? => false} - ], - :args_string=>"cmock_module_func_ptr1 func_ptr", - :args_call=>"func_ptr" }] + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "void", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "void cmock_to_return", + :void? => true + }, + :name => "FunkyTurkey", + :unscoped_name => "FunkyTurkey", + :namespace => [], + :class => nil, + :modifier => "", + :contains_ptr? => false, + :args => [ {:type => "cmock_module_func_ptr1", :name => "func_ptr", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "cmock_module_func_ptr1 func_ptr", + :args_call => "func_ptr" }] typedefs = ["typedef unsigned int(*cmock_module_func_ptr1)(int, char);"] result = @parser.parse("module", source) assert_equal(expected, result[:functions]) @@ -1492,25 +1528,26 @@ it "extract functions using a function pointer with shorthand notation" do source = "void FunkyTurkey(unsigned int func_ptr(int, char))" - expected = [{ :var_arg=>nil, - :return=>{ :type => "void", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "void cmock_to_return", - :void? => true - }, - :name=>"FunkyTurkey", - :unscoped_name=>"FunkyTurkey", - :namespace=>[], - :class=>nil, - :modifier=>"", - :contains_ptr? => false, - :args=>[ {:type=>"cmock_module_func_ptr1", :name=>"func_ptr", :ptr? => false, :const? => false, :const_ptr? => false} - ], - :args_string=>"cmock_module_func_ptr1 func_ptr", - :args_call=>"func_ptr" }] + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "void", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "void cmock_to_return", + :void? => true + }, + :name => "FunkyTurkey", + :unscoped_name => "FunkyTurkey", + :namespace => [], + :class => nil, + :modifier => "", + :contains_ptr? => false, + :args => [ {:type => "cmock_module_func_ptr1", :name => "func_ptr", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "cmock_module_func_ptr1 func_ptr", + :args_call => "func_ptr" }] typedefs = ["typedef unsigned int(*cmock_module_func_ptr1)(int, char);"] result = @parser.parse("module", source) assert_equal(expected, result[:functions]) @@ -1519,25 +1556,26 @@ it "extract functions containing a function pointer with a void" do source = "void FunkyTurkey(void (*func_ptr)(void))" - expected = [{ :var_arg=>nil, - :return=>{ :type => "void", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "void cmock_to_return", - :void? => true - }, - :name=>"FunkyTurkey", - :unscoped_name=>"FunkyTurkey", - :namespace=>[], - :class=>nil, - :modifier=>"", - :contains_ptr? => false, - :args=>[ {:type=>"cmock_module_func_ptr1", :name=>"func_ptr", :ptr? => false, :const? => false, :const_ptr? => false} - ], - :args_string=>"cmock_module_func_ptr1 func_ptr", - :args_call=>"func_ptr" }] + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "void", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "void cmock_to_return", + :void? => true + }, + :name => "FunkyTurkey", + :unscoped_name => "FunkyTurkey", + :namespace => [], + :class => nil, + :modifier => "", + :contains_ptr? => false, + :args => [ {:type => "cmock_module_func_ptr1", :name => "func_ptr", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "cmock_module_func_ptr1 func_ptr", + :args_call => "func_ptr" }] typedefs = ["typedef void(*cmock_module_func_ptr1)(void);"] result = @parser.parse("module", source) assert_equal(expected, result[:functions]) @@ -1546,25 +1584,26 @@ it "extract functions containing a function pointer with an implied void" do source = "void FunkyTurkey(unsigned int (*func_ptr)())" - expected = [{ :var_arg=>nil, - :return=>{ :type => "void", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "void cmock_to_return", - :void? => true - }, - :name=>"FunkyTurkey", - :unscoped_name=>"FunkyTurkey", - :namespace=>[], - :class=>nil, - :modifier=>"", - :contains_ptr? => false, - :args=>[ {:type=>"cmock_module_func_ptr1", :name=>"func_ptr", :ptr? => false, :const? => false, :const_ptr? => false} - ], - :args_string=>"cmock_module_func_ptr1 func_ptr", - :args_call=>"func_ptr" }] + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "void", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "void cmock_to_return", + :void? => true + }, + :name => "FunkyTurkey", + :unscoped_name => "FunkyTurkey", + :namespace => [], + :class => nil, + :modifier => "", + :contains_ptr? => false, + :args => [ {:type => "cmock_module_func_ptr1", :name => "func_ptr", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "cmock_module_func_ptr1 func_ptr", + :args_call => "func_ptr" }] typedefs = ["typedef unsigned int(*cmock_module_func_ptr1)();"] result = @parser.parse("module", source) assert_equal(expected, result[:functions]) @@ -1573,80 +1612,82 @@ it "extract functions containing a constant function pointer and a pointer in the nested arg list" do source = "void FunkyChicken(unsigned int (* const func_ptr)(unsigned long int * , char))" - expected = [{ :var_arg=>nil, - :return=>{ :type => "void", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "void cmock_to_return", - :void? => true - }, - :name=>"FunkyChicken", - :unscoped_name=>"FunkyChicken", - :namespace=>[], - :class=>nil, - :modifier=>"", - :contains_ptr? => false, - :args=>[ {:type=>"cmock_module_func_ptr1", :name=>"func_ptr", :ptr? => false, :const? => true, :const_ptr? => false} - ], - :args_string=>"cmock_module_func_ptr1 const func_ptr", - :args_call=>"func_ptr" }] - typedefs = ["typedef unsigned int(*cmock_module_func_ptr1)(unsigned long int* , char);"] + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "void", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "void cmock_to_return", + :void? => true + }, + :name => "FunkyChicken", + :unscoped_name => "FunkyChicken", + :namespace => [], + :class => nil, + :modifier => "", + :contains_ptr? => false, + :args => [ {:type => "cmock_module_func_ptr1", :name => "func_ptr", :ptr? => false, :const? => true, :const_ptr? => false} + ], + :args_string => "cmock_module_func_ptr1 const func_ptr", + :args_call => "func_ptr" }] + typedefs = ["typedef unsigned int(*cmock_module_func_ptr1)(unsigned long int*, char);"] result = @parser.parse("module", source) assert_equal(expected, result[:functions]) assert_equal(typedefs, result[:typedefs]) end # it "extract functions containing a function pointer taking a vararg" do - # source = "void FunkyParrot(unsigned int (*func_ptr)(int, char, ...))" - # expected = [{ :var_arg=>nil, - # :return=>{ :type => "void", - # :name => 'cmock_to_return', - # :ptr? => false, - # :const? => false, - # :const_ptr? => false, - # :str => "void cmock_to_return", - # :void? => true - # }, - # :name=>"FunkyParrot", - # :unscoped_name=>"FunkyParrot", - # :namespace=>[], - # :class=>nil, - # :modifier=>"", - # :contains_ptr? => false, - # :args=>[ {:type=>"cmock_module_func_ptr1", :name=>"func_ptr", :ptr? => false, :const? => false, :const_ptr? => false} - # ], - # :args_string=>"cmock_module_func_ptr1 func_ptr", - # :args_call=>"func_ptr" }] - # typedefs = ["typedef unsigned int(*cmock_module_func_ptr1)(int, char, ...);"] - # result = @parser.parse("module", source) - # assert_equal(expected, result[:functions]) - # assert_equal(typedefs, result[:typedefs]) + # source = "void FunkyParrot(unsigned int (*func_ptr)(int, char, ...))" + # expected = [{ :var_arg => nil, + # :return => { :type => "void", + # :name => 'cmock_to_return', + # :ptr? => false, + # :const? => false, + # :const_ptr? => false, + # :str => "void cmock_to_return", + # :void? => true + # }, + # :name => "FunkyParrot", + # :unscoped_name => "FunkyParrot", + # :namespace => [], + # :class => nil, + # :modifier => "", + # :contains_ptr? => false, + # :args => [ {:type => "cmock_module_func_ptr1", :name => "func_ptr", :ptr? => false, :const? => false, :const_ptr? => false} + # ], + # :args_string => "cmock_module_func_ptr1 func_ptr", + # :args_call => "func_ptr" }] + # typedefs = ["typedef unsigned int(*cmock_module_func_ptr1)(int, char, ...);"] + # result = @parser.parse("module", source) + # assert_equal(expected, result[:functions]) + # assert_equal(typedefs, result[:typedefs]) # end it "extract functions containing a function pointer with extra parenthesis and two sets" do source = "void FunkyBudgie(int (((* func_ptr1)(int, char))), void (*func_ptr2)(void))" - expected = [{ :var_arg=>nil, - :return=>{ :type => "void", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "void cmock_to_return", - :void? => true - }, - :name=>"FunkyBudgie", - :unscoped_name=>"FunkyBudgie", - :namespace=>[], - :class=>nil, - :modifier=>"", - :contains_ptr? => false, - :args=>[ {:type=>"cmock_module_func_ptr1", :name=>"func_ptr1", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"cmock_module_func_ptr2", :name=>"func_ptr2", :ptr? => false, :const? => false, :const_ptr? => false} - ], - :args_string=>"cmock_module_func_ptr1 func_ptr1, cmock_module_func_ptr2 func_ptr2", - :args_call=>"func_ptr1, func_ptr2" }] + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "void", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "void cmock_to_return", + :void? => true + }, + :name => "FunkyBudgie", + :unscoped_name => "FunkyBudgie", + :namespace => [], + :class => nil, + :modifier => "", + :contains_ptr? => false, + :args => [ {:type => "cmock_module_func_ptr1", :name => "func_ptr1", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "cmock_module_func_ptr2", :name => "func_ptr2", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "cmock_module_func_ptr1 func_ptr1, cmock_module_func_ptr2 func_ptr2", + :args_call => "func_ptr1, func_ptr2" }] typedefs = ["typedef int(*cmock_module_func_ptr1)(int, char);", "typedef void(*cmock_module_func_ptr2)(void);"] result = @parser.parse("module", source) assert_equal(expected, result[:functions]) @@ -1655,27 +1696,28 @@ it "extract functions containing a function pointers, structs and other things" do source = "struct mytype *FunkyRobin(uint16_t num1, uint16_t num2, void (*func_ptr1)(uint16_t num3, struct mytype2 *s));" - expected = [{ :var_arg=>nil, - :return=>{ :type => "struct mytype*", - :name => 'cmock_to_return', - :ptr? => true, - :const? => false, - :const_ptr? => false, - :str => "struct mytype* cmock_to_return", - :void? => false - }, - :name=>"FunkyRobin", - :unscoped_name=>"FunkyRobin", - :namespace=>[], - :class=>nil, - :modifier=>"", - :contains_ptr? => false, - :args=>[ {:type=>"uint16_t", :name=>"num1", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"uint16_t", :name=>"num2", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"cmock_module_func_ptr1", :name=>"func_ptr1", :ptr? => false, :const? => false, :const_ptr? => false} - ], - :args_string=>"uint16_t num1, uint16_t num2, cmock_module_func_ptr1 func_ptr1", - :args_call=>"num1, num2, func_ptr1" }] + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "struct mytype*", + :name => 'cmock_to_return', + :ptr? => true, + :const? => false, + :const_ptr? => false, + :str => "struct mytype* cmock_to_return", + :void? => false + }, + :name => "FunkyRobin", + :unscoped_name => "FunkyRobin", + :namespace => [], + :class => nil, + :modifier => "", + :contains_ptr? => false, + :args => [ {:type => "uint16_t", :name => "num1", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "uint16_t", :name => "num2", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "cmock_module_func_ptr1", :name => "func_ptr1", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "uint16_t num1, uint16_t num2, cmock_module_func_ptr1 func_ptr1", + :args_call => "num1, num2, func_ptr1" }] typedefs = ["typedef void(*cmock_module_func_ptr1)(uint16_t num3, struct mytype2* s);"] result = @parser.parse("module", source) assert_equal(expected, result[:functions]) @@ -1684,25 +1726,26 @@ it "extract functions containing an anonymous function pointer" do source = "void FunkyFowl(unsigned int (* const)(int, char))" - expected = [{ :var_arg=>nil, - :return=>{ :type => "void", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "void cmock_to_return", - :void? => true - }, - :name=>"FunkyFowl", - :unscoped_name=>"FunkyFowl", - :namespace=>[], - :class=>nil, - :modifier=>"", - :contains_ptr? => false, - :args=>[ {:type=>"cmock_module_func_ptr1", :name=>"cmock_arg1", :ptr? => false, :const? => true, :const_ptr? => false} - ], - :args_string=>"cmock_module_func_ptr1 const cmock_arg1", - :args_call=>"cmock_arg1" }] + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "void", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "void cmock_to_return", + :void? => true + }, + :name => "FunkyFowl", + :unscoped_name => "FunkyFowl", + :namespace => [], + :class => nil, + :modifier => "", + :contains_ptr? => false, + :args => [ {:type => "cmock_module_func_ptr1", :name => "cmock_arg1", :ptr? => false, :const? => true, :const_ptr? => false} + ], + :args_string => "cmock_module_func_ptr1 const cmock_arg1", + :args_call => "cmock_arg1" }] typedefs = ["typedef unsigned int(*cmock_module_func_ptr1)(int, char);"] result = @parser.parse("module", source) assert_equal(expected, result[:functions]) @@ -1711,25 +1754,26 @@ it "extract functions returning a function pointer" do source = "unsigned short (*FunkyPidgeon( const char op_code ))( int, long int )" - expected = [{ :var_arg=>nil, - :return=>{ :type => "cmock_module_func_ptr1", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "cmock_module_func_ptr1 cmock_to_return", - :void? => false - }, - :name=>"FunkyPidgeon", - :unscoped_name=>"FunkyPidgeon", - :namespace=>[], - :class=>nil, - :modifier=>"", - :contains_ptr? => false, - :args=>[ {:type=>"char", :name=>"op_code", :ptr? => false, :const? => true, :const_ptr? => false} - ], - :args_string=>"const char op_code", - :args_call=>"op_code" }] + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "cmock_module_func_ptr1", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "cmock_module_func_ptr1 cmock_to_return", + :void? => false + }, + :name => "FunkyPidgeon", + :unscoped_name => "FunkyPidgeon", + :namespace => [], + :class => nil, + :modifier => "", + :contains_ptr? => false, + :args => [ {:type => "char", :name => "op_code", :ptr? => false, :const? => true, :const_ptr? => false} + ], + :args_string => "const char op_code", + :args_call => "op_code" }] typedefs = ["typedef unsigned short(*cmock_module_func_ptr1)( int, long int );"] result = @parser.parse("module", source) assert_equal(expected, result[:functions]) @@ -1738,24 +1782,25 @@ it "extract functions returning a function pointer with implied void" do source = "unsigned short (*FunkyTweetie())()" - expected = [{ :var_arg=>nil, - :return=>{ :type => "cmock_module_func_ptr1", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "cmock_module_func_ptr1 cmock_to_return", - :void? => false - }, - :name=>"FunkyTweetie", - :unscoped_name=>"FunkyTweetie", - :namespace=>[], - :class=>nil, - :modifier=>"", - :contains_ptr? => false, - :args=>[], - :args_string=>"void", - :args_call=>"" }] + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "cmock_module_func_ptr1", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "cmock_module_func_ptr1 cmock_to_return", + :void? => false + }, + :name => "FunkyTweetie", + :unscoped_name => "FunkyTweetie", + :namespace => [], + :class => nil, + :modifier => "", + :contains_ptr? => false, + :args => [], + :args_string => "void", + :args_call => "" }] typedefs = ["typedef unsigned short(*cmock_module_func_ptr1)();"] result = @parser.parse("module", source) assert_equal(expected, result[:functions]) @@ -1764,24 +1809,25 @@ it "extract functions returning a function pointer where everything is a void" do source = "void (* FunkySeaGull(void))(void)" - expected = [{ :var_arg=>nil, - :return=>{ :type => "cmock_module_func_ptr1", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "cmock_module_func_ptr1 cmock_to_return", - :void? => false - }, - :name=>"FunkySeaGull", - :unscoped_name=>"FunkySeaGull", - :namespace=>[], - :class=>nil, - :modifier=>"", - :contains_ptr? => false, - :args=>[], - :args_string=>"void", - :args_call=>"" }] + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "cmock_module_func_ptr1", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "cmock_module_func_ptr1 cmock_to_return", + :void? => false + }, + :name => "FunkySeaGull", + :unscoped_name => "FunkySeaGull", + :namespace => [], + :class => nil, + :modifier => "", + :contains_ptr? => false, + :args => [], + :args_string => "void", + :args_call => "" }] typedefs = ["typedef void(*cmock_module_func_ptr1)(void);"] result = @parser.parse("module", source) assert_equal(expected, result[:functions]) @@ -1790,26 +1836,27 @@ it "extract functions returning a function pointer with some pointer nonsense" do source = "unsigned int * (* FunkyMacaw(double* foo, THING *bar))(unsigned int)" - expected = [{ :var_arg=>nil, - :return=>{ :type => "cmock_module_func_ptr1", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "cmock_module_func_ptr1 cmock_to_return", - :void? => false - }, - :name=>"FunkyMacaw", - :unscoped_name=>"FunkyMacaw", - :namespace=>[], - :class=>nil, - :modifier=>"", - :contains_ptr? => true, - :args=>[ {:type=>"double*", :name=>"foo", :ptr? => true, :const? => false, :const_ptr? => false}, - {:type=>"THING*", :name=>"bar", :ptr? => true, :const? => false, :const_ptr? => false} - ], - :args_string=>"double* foo, THING* bar", - :args_call=>"foo, bar" }] + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "cmock_module_func_ptr1", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "cmock_module_func_ptr1 cmock_to_return", + :void? => false + }, + :name => "FunkyMacaw", + :unscoped_name => "FunkyMacaw", + :namespace => [], + :class => nil, + :modifier => "", + :contains_ptr? => true, + :args => [ {:type => "double*", :name => "foo", :ptr? => true, :const? => false, :const_ptr? => false}, + {:type => "THING*", :name => "bar", :ptr? => true, :const? => false, :const_ptr? => false} + ], + :args_string => "double* foo, THING* bar", + :args_call => "foo, bar" }] typedefs = ["typedef unsigned int *(*cmock_module_func_ptr1)(unsigned int);"] result = @parser.parse("module", source) assert_equal(expected, result[:functions]) @@ -1818,29 +1865,30 @@ it "extract this SQLite3 function with an anonymous function pointer arg (regression test)" do source = "SQLITE_API int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int n, void(*)(void*))" - expected = [{ :var_arg=>nil, - :return=>{ :type => "int", - :name => "cmock_to_return", - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "int cmock_to_return", - :void? => false - }, - :name=>"sqlite3_bind_text", - :unscoped_name=>"sqlite3_bind_text", - :namespace=>[], - :class=>nil, - :modifier=>"SQLITE_API", - :contains_ptr? => true, - :args=>[ {:type=>"sqlite3_stmt*", :name=>"cmock_arg2", :ptr? => true, :const? => false, :const_ptr? => false}, - {:type=>"int", :name=>"cmock_arg3", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"const char*", :name=>"cmock_arg4", :ptr? => false, :const? => true, :const_ptr? => false}, - {:type=>"int", :name=>"n", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"cmock_module_func_ptr1", :name=>"cmock_arg1", :ptr? => false, :const? => false, :const_ptr? => false} - ], - :args_string=>"sqlite3_stmt* cmock_arg2, int cmock_arg3, const char* cmock_arg4, int n, cmock_module_func_ptr1 cmock_arg1", - :args_call=>"cmock_arg2, cmock_arg3, cmock_arg4, n, cmock_arg1" }] + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "int", + :name => "cmock_to_return", + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :name => "sqlite3_bind_text", + :unscoped_name => "sqlite3_bind_text", + :namespace => [], + :class => nil, + :modifier => "SQLITE_API", + :contains_ptr? => true, + :args => [ {:type => "sqlite3_stmt*", :name => "cmock_arg2", :ptr? => true, :const? => false, :const_ptr? => false}, + {:type => "int", :name => "cmock_arg3", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "const char*", :name => "cmock_arg4", :ptr? => false, :const? => true, :const_ptr? => false}, + {:type => "int", :name => "n", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "cmock_module_func_ptr1", :name => "cmock_arg1", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "sqlite3_stmt* cmock_arg2, int cmock_arg3, const char* cmock_arg4, int n, cmock_module_func_ptr1 cmock_arg1", + :args_call => "cmock_arg2, cmock_arg3, cmock_arg4, n, cmock_arg1" }] typedefs = ["typedef void(*cmock_module_func_ptr1)(void*);"] result = @parser.parse("module", source) assert_equal(expected, result[:functions]) @@ -1849,138 +1897,143 @@ it "extract functions with varargs" do source = "int XFiles(int Scully, int Mulder, ...);\n" - expected = [{ :var_arg=>"...", - :return=> { :type => "int", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "int cmock_to_return", - :void? => false - }, - :name=>"XFiles", - :unscoped_name=>"XFiles", - :namespace=>[], - :class=>nil, - :modifier=>"", + expected = [{ :var_arg => "...", + :noreturn => false, + :return => { :type => "int", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :name => "XFiles", + :unscoped_name => "XFiles", + :namespace => [], + :class => nil, + :modifier => "", :contains_ptr? => false, - :args=>[ {:type=>"int", :name=>"Scully", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"int", :name=>"Mulder", :ptr? => false, :const? => false, :const_ptr? => false} - ], - :args_string=>"int Scully, int Mulder", - :args_call=>"Scully, Mulder" - }] + :args => [ {:type => "int", :name => "Scully", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "int", :name => "Mulder", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "int Scully, int Mulder", + :args_call => "Scully, Mulder" + }] assert_equal(expected, @parser.parse("module", source)[:functions]) end it "extract functions with void pointers" do source = "void* MoreSillySongs(void* stuff);\n" - expected = [{ :var_arg=>nil, - :return=> { :type => "void*", - :name => 'cmock_to_return', - :ptr? => true, - :const? => false, - :const_ptr? => false, - :str => "void* cmock_to_return", - :void? => false - }, - :name=>"MoreSillySongs", - :unscoped_name=>"MoreSillySongs", - :namespace=>[], - :class=>nil, - :modifier=>"", + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "void*", + :name => 'cmock_to_return', + :ptr? => true, + :const? => false, + :const_ptr? => false, + :str => "void* cmock_to_return", + :void? => false + }, + :name => "MoreSillySongs", + :unscoped_name => "MoreSillySongs", + :namespace => [], + :class => nil, + :modifier => "", :contains_ptr? => true, - :args=>[ {:type=>"void*", :name=>"stuff", :ptr? => true, :const? => false, :const_ptr? => false} - ], - :args_string=>"void* stuff", - :args_call=>"stuff" - }] + :args => [ {:type => "void*", :name => "stuff", :ptr? => true, :const? => false, :const_ptr? => false} + ], + :args_string => "void* stuff", + :args_call => "stuff" + }] assert_equal(expected, @parser.parse("module", source)[:functions]) end it "extract functions with strippable confusing junk like gcc attributes" do source = "int LaverneAndShirley(int Lenny, int Squiggy) __attribute__((weak)) __attribute__ ((deprecated));\n" - expected = [{ :var_arg=>nil, - :return=> { :type => "int", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "int cmock_to_return", - :void? => false - }, - :name=>"LaverneAndShirley", - :unscoped_name=>"LaverneAndShirley", - :namespace=>[], - :class=>nil, - :modifier=>"", + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "int", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :name => "LaverneAndShirley", + :unscoped_name => "LaverneAndShirley", + :namespace => [], + :class => nil, + :modifier => "", :contains_ptr? => false, - :args=>[ {:type=>"int", :name=>"Lenny", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"int", :name=>"Squiggy", :ptr? => false, :const? => false, :const_ptr? => false} - ], - :args_string=>"int Lenny, int Squiggy", - :args_call=>"Lenny, Squiggy" - }] + :args => [ {:type => "int", :name => "Lenny", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "int", :name => "Squiggy", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "int Lenny, int Squiggy", + :args_call => "Lenny, Squiggy" + }] assert_equal(expected, @parser.parse("module", source)[:functions]) end it "extract functions with strippable confusing junk like gcc attributes with parenthesis" do source = "int TheCosbyShow(int Cliff, int Claire) __attribute__((weak, alias (\"__f\"));\n" - expected = [{ :var_arg=>nil, - :return=> { :type => "int", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "int cmock_to_return", - :void? => false - }, - :name=>"TheCosbyShow", - :unscoped_name=>"TheCosbyShow", - :namespace=>[], - :class=>nil, - :modifier=>"", + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "int", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :name => "TheCosbyShow", + :unscoped_name => "TheCosbyShow", + :namespace => [], + :class => nil, + :modifier => "", :contains_ptr? => false, - :args=>[ {:type=>"int", :name=>"Cliff", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"int", :name=>"Claire", :ptr? => false, :const? => false, :const_ptr? => false} - ], - :args_string=>"int Cliff, int Claire", - :args_call=>"Cliff, Claire" - }] + :args => [ {:type => "int", :name => "Cliff", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "int", :name => "Claire", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "int Cliff, int Claire", + :args_call => "Cliff, Claire" + }] assert_equal(expected, @parser.parse("module", source)[:functions]) end it "divines all permutations of ptr, const, and const_ptr correctly" do truth_table = [ # argument ptr const const_ptr - [ "constNOTconst constNOTconst", false, false, false ], - [ "const constNOTconst constNOTconst", false, true, false ], - [ "constNOTconst const constNOTconst", false, true, false ], - [ "constNOTconst *constNOTconst", true, false, false ], + [ "const constNOTconst *const *constNOTconst", true, true, false ], [ "const constNOTconst *constNOTconst", true, true, false ], + [ "const constNOTconst constNOTconst", false, true, false ], + [ "constNOTconst *const *constNOTconst", true, true, false ], + [ "constNOTconst const *const *constNOTconst", true, true, false ], [ "constNOTconst const *constNOTconst", true, true, false ], - [ "constNOTconst *const constNOTconst", true, false, true ], + [ "constNOTconst const constNOTconst", false, true, false ], + [ "constNOTconst const *const *const constNOTconst", true, true, true ], + [ "const constNOTconst *const *const constNOTconst", true, true, true ], [ "const constNOTconst *const constNOTconst", true, true, true ], + [ "constNOTconst *const *const constNOTconst", true, true, true ], [ "constNOTconst const *const constNOTconst", true, true, true ], - [ "constNOTconst **constNOTconst", true, false, false ], [ "const constNOTconst **constNOTconst", true, false, false ], + [ "constNOTconst **constNOTconst", true, false, false ], + [ "constNOTconst *constNOTconst", true, false, false ], [ "constNOTconst const **constNOTconst", true, false, false ], - [ "constNOTconst *const *constNOTconst", true, true, false ], - [ "const constNOTconst *const *constNOTconst", true, true, false ], - [ "constNOTconst const *const *constNOTconst", true, true, false ], - [ "constNOTconst **const constNOTconst", true, false, true ], + [ "constNOTconst constNOTconst", false, false, false ], [ "const constNOTconst **const constNOTconst", true, false, true ], + [ "constNOTconst **const constNOTconst", true, false, true ], + [ "constNOTconst *const constNOTconst", true, false, true ], [ "constNOTconst const **const constNOTconst", true, false, true ], - [ "constNOTconst *const *const constNOTconst", true, true, true ], - [ "const constNOTconst *const *const constNOTconst", true, true, true ], - [ "constNOTconst const *const *const constNOTconst", true, true, true ] ] truth_table.each do |entry| - assert_equal(@parser.divine_ptr(entry[0]), entry[1]) - assert_equal(@parser.divine_const(entry[0]), entry[2]) - assert_equal(@parser.divine_ptr_and_const(entry[0]), - { ptr?: entry[1], const?: entry[2], const_ptr?: entry[3] }) + lexer = CLexer.new(entry[0]) + tokens = lexer.tokenize + + assert_equal(@parser.guess_ptr_and_const(tokens), + { ptr?: entry[1], const?: entry[2], const_ptr?: entry[3] }) end end @@ -2011,7 +2064,14 @@ ] truth_table.each do |entry| - assert_equal(@parser.divine_ptr(entry[0]), entry[1]) + lexer = CLexer.new(entry[0]) + tokens = lexer.tokenize + if @parser.guess_ptr_and_const(tokens)[:ptr?] != entry[1] + puts "source = #{entry[0].inspect}" + puts "expected = #{entry[1].inspect}" + puts "got @parser.guess_ptr_and_const(tokens)[:ptr?] = #{@parser.guess_ptr_and_const(tokens)[:ptr?]}" + end + assert_equal(@parser.guess_ptr_and_const(tokens)[:ptr?], entry[1]) end end @@ -2245,34 +2305,34 @@ "#endif _NOINCLUDES\n" expected = - "#ifndef _NOINCLUDES\n" + - "#define _NOINCLUDES\n" + - "#include \"unity.h\"\n" + - "#include \"cmock.h\"\n" + - "#include \"YetAnotherHeader.h\"\n" + - "\n" + - "\n" + #The comments are now removed - "#if defined(__GNUC__) && !defined(__ICC) && !defined(__TMS470__)\n" + - "#if __GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ > 6 || (__GNUC_MINOR__ == 6 && __GNUC_PATCHLEVEL__ > 0)))\n" + - "#pragma GCC diagnostic push\n" + - "#endif\n" + - "#if !defined(__clang__)\n" + - "#pragma GCC diagnostic ignored \"-Wpragmas\"\n" + - "#endif\n" + - "#pragma GCC diagnostic ignored \"-Wunknown-pragmas\"\n" + - "#pragma GCC diagnostic ignored \"-Wduplicate-decl-specifier\"\n" + - "#endif\n" + - "\n" + - "int my_function(int a);\n" + - "int staticinlinefunc(struct my_struct *s);\n" + - "static const int my_variable = 5;\n" + - "struct my_struct {\n" + - "int a;\n" + - "int b;\n" + - "int b;\n" + - "char c;\n" + - "};\n" + - "#endif _NOINCLUDES\n" + "#ifndef _NOINCLUDES\n" + + "#define _NOINCLUDES\n" + + "#include \"unity.h\"\n" + + "#include \"cmock.h\"\n" + + "#include \"YetAnotherHeader.h\"\n" + + "\n" + + "\n" + #The comments are now removed + "#if defined(__GNUC__) && !defined(__ICC) && !defined(__TMS470__)\n" + + "#if __GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ > 6 || (__GNUC_MINOR__ == 6 && __GNUC_PATCHLEVEL__ > 0)))\n" + + "#pragma GCC diagnostic push\n" + + "#endif\n" + + "#if !defined(__clang__)\n" + + "#pragma GCC diagnostic ignored \"-Wpragmas\"\n" + + "#endif\n" + + "#pragma GCC diagnostic ignored \"-Wunknown-pragmas\"\n" + + "#pragma GCC diagnostic ignored \"-Wduplicate-decl-specifier\"\n" + + "#endif\n" + + "\n" + + "int my_function(int a);\n" + + "int staticinlinefunc(struct my_struct *s);\n" + + "static const int my_variable = 5;\n" + + "struct my_struct {\n" + + "int a;\n" + + "int b;\n" + + "int b;\n" + + "char c;\n" + + "};\n" + + "#endif _NOINCLUDES\n" assert_equal(expected, @parser.transform_inline_functions(source)) end @@ -2332,66 +2392,67 @@ it "handles parsing multiline functions" do source = "int\nLaverneAndShirley(int Lenny,\n int Squiggy);\n" - expected = [{ :var_arg=>nil, - :return=> { :type => "int", - :name => 'cmock_to_return', - :ptr? => false, - :const? => false, - :const_ptr? => false, - :str => "int cmock_to_return", - :void? => false - }, - :name=>"LaverneAndShirley", - :unscoped_name=>"LaverneAndShirley", - :namespace=>[], - :class=>nil, - :modifier=>"", + expected = [{ :var_arg => nil, + :noreturn => false, + :return => { :type => "int", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :name => "LaverneAndShirley", + :unscoped_name => "LaverneAndShirley", + :namespace => [], + :class => nil, + :modifier => "", :contains_ptr? => false, - :args=>[ {:type=>"int", :name=>"Lenny", :ptr? => false, :const? => false, :const_ptr? => false}, - {:type=>"int", :name=>"Squiggy", :ptr? => false, :const? => false, :const_ptr? => false} - ], - :args_string=>"int Lenny, int Squiggy", - :args_call=>"Lenny, Squiggy" - }] + :args => [ {:type => "int", :name => "Lenny", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "int", :name => "Squiggy", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "int Lenny, int Squiggy", + :args_call => "Lenny, Squiggy" + }] assert_equal(expected, @parser.parse("module", source)[:functions]) end it "imports C++ differently when asked" do source = - [ - "namespace ns1 {\n", - " namespace ns2 {\n", - "\n", - " class cls1 {\n", - " public:\n", - " int f_header_impl(int a, int b){\n", - " return a + b;\n", - " }\n", - "\n", - " static void f_void();\n", - " static int f_ret_simple();\n", - "\n", - " protected:\n", - " static void protected_f_void();\n", - "\n", - " public:\n", - " private:\n", - " static void private_f_void();\n", - " }; // cls1\n", - " } // ns2\n", - "} // ns1\n" - ].join + [ + "namespace ns1 {\n", + " namespace ns2 {\n", + "\n", + " class cls1 {\n", + " public:\n", + " int f_header_impl(int a, int b){\n", + " return a + b;\n", + " }\n", + "\n", + " static void f_void();\n", + " static int f_ret_simple();\n", + "\n", + " protected:\n", + " static void protected_f_void();\n", + "\n", + " public:\n", + " private:\n", + " static void private_f_void();\n", + " }; // cls1\n", + " } // ns2\n", + "} // ns1\n" + ].join expected = - [ - "namespace ns1 { namespace ns2 { class cls1 { public: int f_header_impl", - "static void f_void()", - "static int f_ret_simple()", - "protected: static void protected_f_void()", - "public: private: static void private_f_void()", - "}", - "} }" - ] + [ + "namespace ns1 { namespace ns2 { class cls1 { public: int f_header_impl", + "static void f_void()", + "static int f_ret_simple()", + "protected: static void protected_f_void()", + "public: private: static void private_f_void()", + "}", + "} }" + ] assert_equal(expected, @parser.import_source(source, @test_project, cpp=true)) refute_equal(expected, @parser.import_source(source, @test_project)) @@ -2414,6 +2475,7 @@ def dummy_func :args_call => "", :contains_ptr? => false, :modifier => "", + :noreturn => false, :return => { :type => "void", :name => "cmock_to_return", @@ -2436,10 +2498,11 @@ def voidvoid_func(namespace=[], name="Classic_functional") :args_call => "", :contains_ptr? => false, :modifier => "", + :noreturn => false, :return => { - :type=>"void", - :name=>"cmock_to_return", - :str=>"void cmock_to_return", + :type => "void", + :name => "cmock_to_return", + :str => "void cmock_to_return", :void? => true, :ptr? => false, :const? => false, @@ -2470,7 +2533,7 @@ class Classic { SOURCE expected = [dummy_func, - voidvoid_func(namespace=["ns1"], name="ns1_Classic_functional")] + voidvoid_func(namespace=["ns1"], name="ns1_Classic_functional")] assert_equal(expected, @parser.parse("module", source)[:functions]) end @@ -2488,7 +2551,7 @@ class Classic { SOURCE expected = [dummy_func, - voidvoid_func(namespace=["ns1", "ns2"], name="ns1_ns2_Classic_functional")] + voidvoid_func(namespace=["ns1", "ns2"], name="ns1_ns2_Classic_functional")] assert_equal(expected, @parser.parse("module", source)[:functions]) end @@ -2583,53 +2646,55 @@ class Classy { SOURCE expected = [dummy_func, - voidvoid_func(["ns1"], name="ns1_Classic_functional"), - { :name => "ns1_Classical_functionality", - :unscoped_name => "functionality", - :class => "Classical", - :namespace => ["ns1"], - :var_arg => nil, - :args_string => "int a", - :args => [ - { :ptr? => false, - :const? => false, - :const_ptr? => false, - :name => "a", - :type => "int"}], - :args_call => "a", - :contains_ptr? => false, - :modifier => "", - :return => { - :type=>"int", - :name=>"cmock_to_return", - :str=>"int cmock_to_return", - :void? => false, - :ptr? => false, - :const? => false, - :const_ptr? => false}}, - { :name => "Classy_func", - :unscoped_name => "func", - :class => "Classy", - :namespace => [], - :var_arg => nil, - :args_string => "int* a", - :args => [ - { :ptr? => true, - :const? => false, - :const_ptr? => false, - :name => "a", - :type => "int*"}], - :args_call => "a", - :contains_ptr? => true, - :modifier => "", - :return => { - :type=>"int*", - :name=>"cmock_to_return", - :str=>"int* cmock_to_return", - :void? => false, - :ptr? => true, - :const? => false, - :const_ptr? => false}}] + voidvoid_func(["ns1"], name="ns1_Classic_functional"), + { :name => "ns1_Classical_functionality", + :unscoped_name => "functionality", + :class => "Classical", + :namespace => ["ns1"], + :var_arg => nil, + :args_string => "int a", + :args => [ + { :ptr? => false, + :const? => false, + :const_ptr? => false, + :name => "a", + :type => "int"}], + :args_call => "a", + :contains_ptr? => false, + :modifier => "", + :noreturn => false, + :return => { + :type => "int", + :name => "cmock_to_return", + :str => "int cmock_to_return", + :void? => false, + :ptr? => false, + :const? => false, + :const_ptr? => false}}, + { :name => "Classy_func", + :unscoped_name => "func", + :class => "Classy", + :namespace => [], + :var_arg => nil, + :args_string => "int* a", + :args => [ + { :ptr? => true, + :const? => false, + :const_ptr? => false, + :name => "a", + :type => "int*"}], + :args_call => "a", + :contains_ptr? => true, + :modifier => "", + :noreturn => false, + :return => { + :type => "int*", + :name => "cmock_to_return", + :str => "int* cmock_to_return", + :void? => false, + :ptr? => true, + :const? => false, + :const_ptr? => false}}] assert_equal(expected, @parser.parse("module", source)[:functions]) end @@ -2655,53 +2720,55 @@ class Classy { SOURCE expected = [dummy_func, - voidvoid_func(["ns1"], name="ns1_Classic_functional"), - { :name => "ns1_Classical_functional", - :unscoped_name => "functional", - :class => "Classical", - :namespace => ["ns1"], - :var_arg => nil, - :args_string => "int a", - :args => [ - { :ptr? => false, - :const? => false, - :const_ptr? => false, - :name => "a", - :type => "int"}], - :args_call => "a", - :contains_ptr? => false, - :modifier => "", - :return => { - :type=>"int", - :name=>"cmock_to_return", - :str=>"int cmock_to_return", - :void? => false, - :ptr? => false, - :const? => false, - :const_ptr? => false}}, - { :name => "Classy_functional", - :unscoped_name => "functional", - :class => "Classy", - :namespace => [], - :var_arg => nil, - :args_string => "int* a", - :args => [ - { :ptr? => true, - :const? => false, - :const_ptr? => false, - :name => "a", - :type => "int*"}], - :args_call => "a", - :contains_ptr? => true, - :modifier => "", - :return => { - :type=>"int*", - :name=>"cmock_to_return", - :str=>"int* cmock_to_return", - :void? => false, - :ptr? => true, - :const? => false, - :const_ptr? => false}}] + voidvoid_func(["ns1"], name="ns1_Classic_functional"), + { :name => "ns1_Classical_functional", + :unscoped_name => "functional", + :class => "Classical", + :namespace => ["ns1"], + :var_arg => nil, + :args_string => "int a", + :args => [ + { :ptr? => false, + :const? => false, + :const_ptr? => false, + :name => "a", + :type => "int"}], + :args_call => "a", + :contains_ptr? => false, + :modifier => "", + :noreturn => false, + :return => { + :type => "int", + :name => "cmock_to_return", + :str => "int cmock_to_return", + :void? => false, + :ptr? => false, + :const? => false, + :const_ptr? => false}}, + { :name => "Classy_functional", + :unscoped_name => "functional", + :class => "Classy", + :namespace => [], + :var_arg => nil, + :args_string => "int* a", + :args => [ + { :ptr? => true, + :const? => false, + :const_ptr? => false, + :name => "a", + :type => "int*"}], + :args_call => "a", + :contains_ptr? => true, + :modifier => "", + :noreturn => false, + :return => { + :type => "int*", + :name => "cmock_to_return", + :str => "int* cmock_to_return", + :void? => false, + :ptr? => true, + :const? => false, + :const_ptr? => false}}] assert_equal(expected, @parser.parse("module", source)[:functions]) end @@ -2882,4 +2949,323 @@ class Classy { end + # // PJB // REMOVE BEFORE COMMIT // + + it "builds parser on sound bases" do + + assert(@parser.is_parens([:parens,[[:identifier,"a"]]]),"is_parens identifies parens") + refute(@parser.is_parens([:brackets,[[:identifier,"a"]]]),"is_parens rejects brackets") + refute(@parser.is_parens([:identifier,"Foo"]),"is_parens rejects identifier") + assert_equal([[:identifier,"a"],[:integer_literal,"42"]], + @parser.parens_list([:parens,[[:identifier,"a"],[:integer_literal,"42"]]]), + "parens_list returns list of elements in parens") + + assert(@parser.is_brackets([:brackets,[[:identifier,"a"]]]),"is_brackets identifies brackets") + refute(@parser.is_brackets([:parens,[[:identifier,"a"]]]),"is_brackets rejects parens") + refute(@parser.is_brackets([:identifier,"Foo"]),"is_brackets rejects identifier") + assert_equal([[:identifier,"a"],[:integer_literal,"42"]], + @parser.brackets_list([:brackets,[[:identifier,"a"],[:integer_literal,"42"]]]), + "brackets_list returns list of elements in brackets") + + + # assert(@parser.is_braces([:braces,[[:identifier,"a"]]]),"is_braces identifies braces") + # refute(@parser.is_braces([:brackets,[[:identifier,"a"]]]),"is_braces rejects brackets") + # refute(@parser.is_braces([:identifier,"Foo"]),"is_braces rejects identifier") + # assert_equal([[:identifier,"a"],[:integer_literal,"42"]], + # @parser.braces_list([:braces,[[:identifier,"a"],[:integer_literal,"42"]]]), + # "braces_list returns list of elements in braces") + + assert(@parser.is_identifier([:identifier,"Foo"]),"is_identifier identifies identifier") + assert(@parser.is_identifier([:identifier,"Foo"],"Foo"),"is_identifier identifies identifier with name") + refute(@parser.is_identifier([:identifier,"Foo"],"Bar"),"is_identifier rejects identifier with wrong name") + refute(@parser.is_identifier(:bar,"Bar"),"is_identifier rejects non-identifier") + refute(@parser.is_identifier(:bar),"is_identifier rejects non-identifier") + + assert_equal("Foo",@parser.identifier_name([:identifier,"Foo"]),"identifier_name returns name of identifier") + + assert(@parser.is_c_calling_convention([:identifier,"__stdcall"]),"is_c_calling_convention should identify __stdcall") + refute(@parser.is_c_calling_convention([:identifier,"callfoo"]),"is_c_calling_convention should refute callfoo") + assert(@parser.is_c_calling_convention(:__stdcall),"is_c_calling_convention should accept :__stdcall") + + assert(@parser.is_c_attribute(:const),"is_c_attribute should identify :const") + assert(@parser.is_c_attribute([:identifier,"const"]),"is_c_attribute should identify [:identifier, 'const']") + assert(@parser.is_c_attribute([:identifier,"__ramfunc"]),"is_c_attribute should identify [:identifier, '__ramfunc']") + refute(@parser.is_c_attribute([:identifier,"__attribute__"]),"is_c_attribute should refute [:identifier, '__attribute__']") + refute(@parser.is_c_attribute(:conste),"is_c_attribute should refute :constes") + assert(@parser.is_c_attribute([:identifier,"noreturn"]),"is_c_attribute should identify [:identifier, 'noreturn']") + assert(@parser.is_c_attribute(:noreturn),"is_c_attribute should identify :noreturn") + + assert(@parser.is_gcc_attribute_syntax([:identifier,"__attribute__"],[:parens,[[:parens,[[:identifier,"noreturn"]]]]]), + "is_gcc_attribute_syntax identifies parsed __attribute__((noreturn))") + + assert(@parser.is_attribute([:attribute,nil,"noreturn",nil,:gcc]), + "is_attribute identifies [:attribute,nil,\"noreturn\",nil,:gcc]") + + end + + + it "parses stuffs" do + + source ="int hello (int a, int b) {\n" + + " static int table[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };\n" + + " return table[a] + b;\n" + + "}\n" + + expected = [[:int,[:identifier, "hello"], + [:parens, [:int, [:identifier, "a"], :comma, :int, [:identifier, "b"]]], + [:braces, + [:static, :int, [:identifier, "table"], [:brackets, [[:integer_literal, "10"]]], + :assign, [:braces, [[:integer_literal, "1"],:comma, + [:integer_literal, "2"],:comma, + [:integer_literal, "3"],:comma, + [:integer_literal, "4"],:comma, + [:integer_literal, "5"],:comma, + [:integer_literal, "6"],:comma, + [:integer_literal, "7"],:comma, + [:integer_literal, "8"],:comma, + [:integer_literal, "9"]]], :semicolon, + :return, [:identifier, "table"], [:brackets, [[:identifier, "a"]]], :add_op, [:identifier, "b"], :semicolon]]], + 46] + + lexer = CLexer.new(source) + tokens = lexer.tokenize + assert_equal(expected, @parser.parse_stuffs(tokens,0)) + end + + it "parses gcc attributes" do + + assert_equal([[:attribute,nil,"noreturn",nil,:gcc], + [:attribute,nil,"deprecated",nil,:gcc]], + @parser.parse_gcc_attribute([:identifier,"__attribute__"], + [:parens, [[:parens, [[:identifier, "noreturn"], :comma ,[:identifier, "deprecated"]]]]])) + + assert_equal([[:attribute,nil,"noreturn",nil,:gcc], + [:attribute,nil,"deprecated",nil,:gcc]], + @parser.parse_gcc_attribute([:identifier,"__attribute__"], + [:parens, [[:parens, [[:identifier,"noreturn"]]],:comma,[:parens,[[:identifier,"deprecated"]]]]])) + + assert_equal([[:attribute,nil,"access",[:parens,[[:identifier,"read_write"],:comma,[:integer_literal,"1"]]],:gcc], + [:attribute,nil,"access",[:parens,[[:identifier,"read_only"],:comma,[:integer_literal,"2"]]],:gcc]], + @parser.parse_gcc_attribute([:identifier,"__attribute__"], + [:parens, [[:parens, + [[:identifier,"access"],[:parens,[[:identifier,"read_write"], + :comma,[:integer_literal,"1"]]], + :comma,[:identifier,"access"],[:parens,[[:identifier,"read_only"], + :comma,[:integer_literal,"2"]]]]]]])) + end + + + it "parses cpp attributes" do + + assert_equal([[:attribute,nil,"noreturn",nil,:cpp], + [:attribute,nil,"deprecated",nil,:cpp]], + @parser.parse_cpp_attributes([:brackets, [[:brackets, [[:identifier, "noreturn"], :comma ,[:identifier, "deprecated"]]]]])) + + assert_equal([[:attribute,nil,"access",[:parens,[[:identifier,"read_write"],:comma,[:integer_literal,"1"]]],:cpp], + [:attribute,nil,"access",[:parens,[[:identifier,"read_only"],:comma,[:integer_literal,"2"]]],:cpp]], + @parser.parse_cpp_attributes([:brackets, [[:brackets, + [[:identifier,"access"],[:parens,[[:identifier,"read_write"], + :comma,[:integer_literal,"1"]]], + :comma,[:identifier,"access"],[:parens,[[:identifier,"read_only"], + :comma,[:integer_literal,"2"]]]]]]])) + + assert_equal([[:attribute,"rt","access",[:parens,[[:identifier,"read_write"],:comma,[:integer_literal,"1"]]],:cpp], + [:attribute,"rt","access",[:parens,[[:identifier,"read_only"],:comma,[:integer_literal,"2"]]],:cpp]], + @parser.parse_cpp_attributes([:brackets, [[:brackets, + [[:identifier,"using"],[:identifier,"rt"],:colon, + [:identifier,"access"],[:parens,[[:identifier,"read_write"], + :comma,[:integer_literal,"1"]]],:comma, + [:identifier,"access"],[:parens,[[:identifier,"read_only"], + :comma,[:integer_literal,"2"]]]]]]])) + + assert_equal([[:attribute,"rt","access",[:parens,[[:identifier,"read_write"],:comma,[:integer_literal,"1"]]],:cpp], + [:attribute,"rt","access",[:parens,[[:identifier,"read_only"],:comma,[:integer_literal,"2"]]],:cpp]], + @parser.parse_cpp_attributes([:brackets, [[:brackets, + [[:identifier,"rt"],:colon,:colon,[:identifier,"access"],[:parens,[[:identifier,"read_write"], + :comma,[:integer_literal,"1"]]],:comma, + [:identifier,"rt"],:colon,:colon,[:identifier,"access"],[:parens,[[:identifier,"read_only"], + :comma,[:integer_literal,"2"]]]]]]])) + end + + # it "Note functions do not return" do + # + # # """ + # # :cmock + # # :attributes + # # - '_Noreturn' + # # - 'noreturn' + # # - '[[[[noreturn]]]]' + # # - '__attribute__((noreturn))' + # # - '__attribute__((__noreturn__))' + # # """ + # source ="void foo1() __attribute__((noreturn)); /* GNU C & GNU C++ & Clang */\n" + + # "_Noreturn void foo2(); /* C99 */\n" + + # "noreturn void foo3(); /* C11 */\n" + + # "[[noreturn]] void foo4(); /* C++11 */\n" + # + # expected = ["void foo1()", + # "void foo2()", + # "void foo3()", + # "void foo4()"] + # + # assert_equal(expected, @parser.transform_noreturn(source)) + # end + + it "extract and return function declarations with _Noreturn" do + source = "_Noreturn int Foo(int a, unsigned int b)" + expected = { :var_arg => nil, + :name => "Foo", + :unscoped_name => "Foo", + :class => nil, + :namespace => [], + :noreturn => true, + :return => { :type => "int", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :modifier => "_Noreturn", + :contains_ptr? => false, + :args => [ {:type => "int", :name => "a", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "unsigned int", :name => "b", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "int a, unsigned int b", + :args_call => "a, b" } + assert_equal(expected, @parser.parse_declaration(@test_project, source, [], nil)) + end + + it "extract and return function declarations with noreturn" do + source = "noreturn int Foo(int a, unsigned int b)" + expected = { :var_arg => nil, + :name => "Foo", + :unscoped_name => "Foo", + :class => nil, + :namespace => [], + :noreturn => true, + :return => { :type => "int", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :modifier => "noreturn", + :contains_ptr? => false, + :args => [ {:type => "int", :name => "a", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "unsigned int", :name => "b", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "int a, unsigned int b", + :args_call => "a, b" } + assert_equal(expected, @parser.parse_declaration(@test_project, source, [], nil)) + end + + it "extract and return function declarations with [[noreturn]]" do + source = "[[noreturn]] int Foo(int a, unsigned int b)" + expected = { :var_arg => nil, + :name => "Foo", + :unscoped_name => "Foo", + :class => nil, + :namespace => [], + :noreturn => true, + :return => { :type => "int", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :modifier => "[[noreturn]]", + :contains_ptr? => false, + :args => [ {:type => "int", :name => "a", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "unsigned int", :name => "b", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "int a, unsigned int b", + :args_call => "a, b" } + assert_equal(expected, @parser.parse_declaration(@test_project, source, [], nil)) + end + + it "extract and return function declarations with __attribute__((noreturn))" do + source = "int Foo(int a, unsigned int b) __attribute__((noreturn))" + expected = { :var_arg => nil, + :name => "Foo", + :unscoped_name => "Foo", + :class => nil, + :namespace => [], + :noreturn => true, + :return => { :type => "int", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :modifier => "", + :contains_ptr? => false, + :args => [ {:type => "int", :name => "a", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "unsigned int", :name => "b", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "int a, unsigned int b", + :args_call => "a, b" } + assert_equal(expected, @parser.parse_declaration(@test_project, source, [], nil)) + end + + it "extract and return function declarations with __attribute__ ((noreturn))" do + source = "int Foo(int a, unsigned int b) __attribute__ ((noreturn))" + expected = { :var_arg => nil, + :name => "Foo", + :unscoped_name => "Foo", + :class => nil, + :namespace => [], + :noreturn => true, + :return => { :type => "int", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :modifier => "", + :contains_ptr? => false, + :args => [ {:type => "int", :name => "a", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "unsigned int", :name => "b", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "int a, unsigned int b", + :args_call => "a, b" } + assert_equal(expected, @parser.parse_declaration(@test_project, source, [], nil)) + end + + it "extract and return function declarations with __attribute__ ((__noreturn__))" do + source = "int Foo(int a, unsigned int b) __attribute__ ((__noreturn__))" + expected = { :var_arg => nil, + :name => "Foo", + :unscoped_name => "Foo", + :class => nil, + :namespace => [], + :noreturn => true, + :return => { :type => "int", + :name => 'cmock_to_return', + :ptr? => false, + :const? => false, + :const_ptr? => false, + :str => "int cmock_to_return", + :void? => false + }, + :modifier => "", + :contains_ptr? => false, + :args => [ {:type => "int", :name => "a", :ptr? => false, :const? => false, :const_ptr? => false}, + {:type => "unsigned int", :name => "b", :ptr? => false, :const? => false, :const_ptr? => false} + ], + :args_string => "int a, unsigned int b", + :args_call => "a, b" } + assert_equal(expected, @parser.parse_declaration(@test_project, source, [], nil)) + end + + end From 82f67c27e0c0e046207304ed333abbc2a1f56007 Mon Sep 17 00:00:00 2001 From: "Pascal J. Bourguignon" Date: Mon, 15 May 2023 17:39:48 +0200 Subject: [PATCH 4/7] Updated the documentation. --- docs/CMock_Summary.md | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/docs/CMock_Summary.md b/docs/CMock_Summary.md index e63a065c..7c039215 100644 --- a/docs/CMock_Summary.md +++ b/docs/CMock_Summary.md @@ -82,6 +82,7 @@ replied with a version that is older than 2.0.0. Go ahead. We'll wait. Once you have Ruby, you have three options: * Clone the latest [CMock repo on github](https://github.com/ThrowTheSwitch/CMock/) + (This includes updating the submodules: `git submodules update`) * Download the latest [CMock zip from github](https://github.com/ThrowTheSwitch/CMock/) * Install Ceedling (which has it built in!) through your commandline using `gem install ceedling`. @@ -135,7 +136,7 @@ that resembles a pointer or array, it breaks the argument into TWO arguments. The first is the original pointer. The second specify the number of elements it is to verify of that array. If you specify 1, it'll check one object. If 2, it'll assume your pointer is pointing at the first of two elements in an array. -If you specify zero elements and `UNITY_COMPARE_PTRS_ON_ZERO_ARRAY` is defined, +If you specify zero elements and `UNITY_COMPARE_PTRS_ON_ZERO_ARRAY` is defined, then this assertion can also be used to directly compare the pointers to verify that they are pointing to the same memory address. @@ -385,6 +386,38 @@ from the defaults. We've tried to specify what the defaults are below. * **note:** this option will reinsert these attributes onto the mock's calls. If that isn't what you are looking for, check out :strippables. +* `:process_cpp_attributes`: + Allows the parsing and processing of C++ attribute syntax `[[...]]`. + + * defaults: false + +* `:process_gcc_attributes`: + Allows the parsing and processing of GNU gcc attribute syntax + `__attribute__ ((...))`. + When setting it to true, mind removing `__attribute__` from the + `:strippables`` option. + Those attributes are matched both before and after the function name. + + * defaults: false + +* `:noreturn_attributes`: + To allow processing of noreturn attributes, list in + `:noreturn_attributes` the keywords used as noreturn attributes. + (Since such attributes may hide behind macros, you may need to list + the macros too). +> :noreturn_attributes => ['_Noreturn', 'noreturn', '__noreturn__'] + Note: when a keyword is listed in this option, it's available for + both the C syntax (keyword standing alone), the C++ syntax (keyword + in a `[[...]]` syntax, and the GNU gcc syntax (keyword in a + `__attribute__((...))` syntax) + When a noreturn attribute is matched, the returned function + dictionary will contain a :noreturn => true` entry, and the cmock + code generator will then avoid returning from the mocked function, + but instead will use the `TEST_DO_NOT_RETURN()` macro. + + * defaults: [] + + * `:c_calling_conventions`: Similarly, CMock may need to understand which C calling conventions might show up in your codebase. If it encounters something it doesn't From 0ac2fcd8de2872fc1be841653e884d71fe5f64c3 Mon Sep 17 00:00:00 2001 From: "Pascal J. Bourguignon" Date: Tue, 16 May 2023 14:02:18 +0200 Subject: [PATCH 5/7] Added noreturn integration test. --- test/system/test_compilation/config.yml | 4 ++++ test/system/test_compilation/noreturn.h | 9 +++++++++ 2 files changed, 13 insertions(+) create mode 100644 test/system/test_compilation/noreturn.h diff --git a/test/system/test_compilation/config.yml b/test/system/test_compilation/config.yml index 787e2e1f..7d5fb86b 100644 --- a/test/system/test_compilation/config.yml +++ b/test/system/test_compilation/config.yml @@ -8,3 +8,7 @@ :treat_as_void: - OSEK_TASK - VOID_TYPE_CRAZINESS + :strippables: [] + :process_gcc_attributes: true + :noreturn_attributes: ['_Noreturn','noreturn'] + diff --git a/test/system/test_compilation/noreturn.h b/test/system/test_compilation/noreturn.h new file mode 100644 index 00000000..f5ab6893 --- /dev/null +++ b/test/system/test_compilation/noreturn.h @@ -0,0 +1,9 @@ +#ifndef noreturn_h +#define noreturn_h +/* #include */ + +_Noreturn void myexec1(const char* program); +void __attribute__((noreturn)) myexec2(const char* program); +void myexec3(const char* program) __attribute__((noreturn)); + +#endif From ff1dd9c67de64e070567c5356a6de4953cc498a0 Mon Sep 17 00:00:00 2001 From: "Pascal J. Bourguignon" Date: Tue, 16 May 2023 14:02:30 +0200 Subject: [PATCH 6/7] Corrections for noreturn integration test. --- lib/cmock_config.rb | 5 +--- lib/cmock_generator.rb | 6 +---- lib/cmock_header_parser.rb | 35 +++++++++++++++------------ test/unit/cmock_header_parser_test.rb | 8 +++--- 4 files changed, 26 insertions(+), 28 deletions(-) diff --git a/lib/cmock_config.rb b/lib/cmock_config.rb index c925420d..7c5891be 100644 --- a/lib/cmock_config.rb +++ b/lib/cmock_config.rb @@ -18,11 +18,8 @@ class CMockConfig :strippables => ['(?:__attribute__\s*\([ (]*.*?[ )]*\)+)'], :process_gcc_attributes => false, # __attribute__((...)) ; also remove it from strippables. :process_cpp_attributes => false, # [[ ... ]] + :noreturn_attributes => [], # simple keyword, before the function name :attributes => %w[__ramfunc __irq __fiq register extern], - :c_noreturn_attributes => [], # simple keyword, before the function name - :gcc_noreturn_attributes => [], # put 'noreturn' for __attribute__((noreturn)), - # # before or after the function name, - # # also remove it from :strippables :c_calling_conventions => %w[__stdcall __cdecl __fastcall], :enforce_strict_ordering => false, :fail_on_unexpected_calls => true, diff --git a/lib/cmock_generator.rb b/lib/cmock_generator.rb index 1def32a4..a98cfad1 100644 --- a/lib/cmock_generator.rb +++ b/lib/cmock_generator.rb @@ -90,11 +90,7 @@ def create_skeleton(module_name, parsed_stuff) private if $ThisIsOnlyATest.nil? ############################## def is_noreturn?(function) - if function[:attributes].nil? - return nil; - else - return function[:attributes].include?('noreturn'); - end + function[:noreturn] end def generate_return(function) diff --git a/lib/cmock_header_parser.rb b/lib/cmock_header_parser.rb index 0f560637..55ed1fa5 100644 --- a/lib/cmock_header_parser.rb +++ b/lib/cmock_header_parser.rb @@ -9,15 +9,15 @@ class CMockHeaderParser attr_accessor :funcs, :c_attr_noconst, :c_attributes, :treat_as_void, :treat_externs, :treat_inlines, :inline_function_patterns - attr_reader :c_noreturn_attributes, :c_calling_conventions + attr_reader :noreturn_attributes, :process_gcc_attributes, :process_cpp_attributes, :c_calling_conventions def initialize(cfg) @c_strippables = cfg.strippables @process_gcc_attributes = cfg.process_gcc_attributes @process_cpp_attributes = cfg.process_cpp_attributes + @noreturn_attributes = cfg.noreturn_attributes.uniq @c_attr_noconst = cfg.attributes.uniq - ['const'] @c_attributes = ['const'] + @c_attr_noconst - @c_noreturn_attributes = cfg.c_noreturn_attributes.uniq @c_calling_conventions = cfg.c_calling_conventions.uniq @treat_as_array = cfg.treat_as_array @treat_as_void = (['void'] + cfg.treat_as_void).uniq @@ -256,7 +256,7 @@ def import_source(source, parse_project, cpp = false) # remove assembler pragma sections source.gsub!(/^\s*#\s*pragma\s+asm\s+.*?#\s*pragma\s+endasm/m, '') - if @c_noreturn_attributes.nil? + if @noreturn_attributes.nil? # remove gcc's __attribute__ tags source.gsub!(/__attribute(?:__)?\s*\(\(+.*\)\)+/, '') end @@ -559,7 +559,7 @@ def is_c_calling_convention(stuff) end def is_c_attribute(token) - # whether the token is a C attribute (listed in @c_attributes or in @c_noreturn_attributes). + # whether the token is a C attribute (listed in @c_attributes or in @noreturn_attributes). # note: token may be either a symbol, a string or an :identifier array. if token.is_a?(String) name = token @@ -574,8 +574,8 @@ def is_c_attribute(token) end res = (@c_attributes.index(name) or - ((not @c_noreturn_attributes.nil?) and - (@c_noreturn_attributes.any? { |attr_regexp| name =~ /^#{attr_regexp}$/ }))) + ((not @noreturn_attributes.nil?) and + (@noreturn_attributes.any? { |attr_regexp| name =~ /^#{attr_regexp}$/ }))) res end @@ -620,9 +620,9 @@ def attribute_kind(attribute) def is_noreturn(attribute) if is_identifier(attribute) - @c_noreturn_attributes.include?(identifier_name(attribute)) + @noreturn_attributes.include?(identifier_name(attribute)) elsif is_attribute(attribute) - @c_noreturn_attributes.include?(attribute_qualified_name(attribute)) + @noreturn_attributes.include?(attribute_qualified_name(attribute)) else false end @@ -925,9 +925,8 @@ def parse_type(stuff) type.delete_at(cindex) unless cindex.nil? end - arg_info[:modifier] = unparse(attributes.uniq) arg_info[:type] = unparse(type).gsub(/\s+\*/, '*') # remove space before asterisks - arg_info[:noreturn] = has_noreturn_attribute(attributes) + arg_info[:modifier] = unparse(attributes.uniq) arg_info[:c_calling_convention] = unparse(ccc) [type, attributes, ccc, arg_info] @@ -1255,21 +1254,27 @@ def parse_declaration(parse_project, declaration, namespace = [], classname = ni decl[:name] << '_' unless decl[:name].empty? decl[:name] << decl[:unscoped_name] - decl[:modifier] = parsed[:modifier] + decl[:noreturn] = has_noreturn_attribute(attributes) + if decl[:noreturn] + attributes.delete_if{|attribute| is_noreturn(attribute)} + end + if parsed[:ptr?] if parsed[:const?] - type = [:const] + type + type = [:const] + type unless type[0]==:const attributes.delete_if{|attr| attribute_name(attr) == "const"} - decl[:modifier] = unparse(attributes) end end + # TODO: perhaps we need a specific configuration, or use strippable to select the __attributes__ to remove. + attributes.delete_if{|attribute| is_attribute(attribute) and (attribute_kind(attribute) == :gcc)} + + decl[:modifier] = unparse(attributes.uniq) + if not (parsed[:c_calling_convention].nil? or parsed[:c_calling_convention].empty?) decl[:c_calling_convention] = parsed[:c_calling_convention] end - decl[:noreturn] = has_noreturn_attribute(attributes) - rettype = unparse(type).gsub(/\s+\*/,'*') rettype = 'void' if @local_as_void.include?(rettype.strip) decl[:return] = { :type => rettype, diff --git a/test/unit/cmock_header_parser_test.rb b/test/unit/cmock_header_parser_test.rb index 27f710f1..57ad5be1 100644 --- a/test/unit/cmock_header_parser_test.rb +++ b/test/unit/cmock_header_parser_test.rb @@ -18,9 +18,9 @@ @config.expect :strippables, ["STRIPPABLE"] @config.expect :attributes, ['__ramfunc', 'funky_attrib', 'SQLITE_API','access','rt::access','deprecated'] @config.expect :c_calling_conventions, ['__stdcall'] - @config.expect :c_noreturn_attributes,['_Noreturn', 'noreturn', '__noreturn__'] @config.expect :process_gcc_attributes, true @config.expect :process_cpp_attributes, true + @config.expect :noreturn_attributes,['_Noreturn', 'noreturn', '__noreturn__'] @config.expect :treat_as_void, ['MY_FUNKY_VOID'] @config.expect :treat_as, { "BANJOS" => "INT", "TUBAS" => "HEX16"} @config.expect :treat_as_array, {"IntArray" => "int", "Book" => "Page"} @@ -3127,7 +3127,7 @@ class Classy { :str => "int cmock_to_return", :void? => false }, - :modifier => "_Noreturn", + :modifier => "", :contains_ptr? => false, :args => [ {:type => "int", :name => "a", :ptr? => false, :const? => false, :const_ptr? => false}, {:type => "unsigned int", :name => "b", :ptr? => false, :const? => false, :const_ptr? => false} @@ -3153,7 +3153,7 @@ class Classy { :str => "int cmock_to_return", :void? => false }, - :modifier => "noreturn", + :modifier => "", :contains_ptr? => false, :args => [ {:type => "int", :name => "a", :ptr? => false, :const? => false, :const_ptr? => false}, {:type => "unsigned int", :name => "b", :ptr? => false, :const? => false, :const_ptr? => false} @@ -3179,7 +3179,7 @@ class Classy { :str => "int cmock_to_return", :void? => false }, - :modifier => "[[noreturn]]", + :modifier => "", :contains_ptr? => false, :args => [ {:type => "int", :name => "a", :ptr? => false, :const? => false, :const_ptr? => false}, {:type => "unsigned int", :name => "b", :ptr? => false, :const? => false, :const_ptr? => false} From 0f71c57955a6172434b6a40423bf8375d6c3c2e8 Mon Sep 17 00:00:00 2001 From: "Pascal J. Bourguignon" Date: Thu, 25 May 2023 11:46:35 +0200 Subject: [PATCH 7/7] Added callback_kind to use * or ^ or other for callback types. --- lib/cmock_config.rb | 1 + lib/cmock_generator_plugin_callback.rb | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/cmock_config.rb b/lib/cmock_config.rb index 7c5891be..aec5a8e0 100644 --- a/lib/cmock_config.rb +++ b/lib/cmock_config.rb @@ -35,6 +35,7 @@ class CMockConfig :treat_inlines => :exclude, # the options being :include or :exclude :callback_include_count => true, :callback_after_arg_check => false, + :callback_kind => "*", :includes => nil, :includes_h_pre_orig_header => nil, :includes_h_post_orig_header => nil, diff --git a/lib/cmock_generator_plugin_callback.rb b/lib/cmock_generator_plugin_callback.rb index 6ba8e9bd..781155c5 100644 --- a/lib/cmock_generator_plugin_callback.rb +++ b/lib/cmock_generator_plugin_callback.rb @@ -27,10 +27,11 @@ def instance_structure(function) def mock_function_declarations(function) func_name = function[:name] return_type = function[:return][:type] + kind = @config.callback_kind.nil? ? '*' : @config.callback_kind action = @config.callback_after_arg_check ? 'AddCallback' : 'Stub' style = (@include_count ? 1 : 0) | (function[:args].empty? ? 0 : 2) styles = ['void', 'int cmock_num_calls', function[:args_string], "#{function[:args_string]}, int cmock_num_calls"] - "typedef #{return_type} (* CMOCK_#{func_name}_CALLBACK)(#{styles[style]});\n" \ + "typedef #{return_type} (#{kind} CMOCK_#{func_name}_CALLBACK)(#{styles[style]});\n" \ "void #{func_name}_AddCallback(CMOCK_#{func_name}_CALLBACK Callback);\n" \ "void #{func_name}_Stub(CMOCK_#{func_name}_CALLBACK Callback);\n" \ "#define #{func_name}_StubWithCallback #{func_name}_#{action}\n"