From 0af0e20d15d7dd2c35724d433354ae70513af060 Mon Sep 17 00:00:00 2001 From: laurens Date: Tue, 12 Nov 2019 15:12:49 +0100 Subject: [PATCH] Refactor transform_inline_functions - Just looking for static|inline in the gsub is a bit too aggressive. Instead, look for the "static inline" and parse it: - Everything before the match should just be copied, we don't want to touch anything but the inline functions. - Remove the implementation of the inline function (this is enclosed in square brackets) and replace it with ";" to complete the transformation to normal/non-inline function. - Copy everything after the inline function implementation Repeat the above step until we can't find "static inline" anymore - To remove the inline implementation, we count the number of square-bracket 'levels' in the inline function and remove every pair. This ensures that constructs like: inline void func(void) { if (...) { //NOP } else { //NOP } } will not end up leaving the 'else' keyword unremoved: inline void func(void); else If we count the number of levels, we don't end up in this scenario since we remove 3 'pairs', the if-one, the else-one and finally the main body. --- lib/cmock_header_parser.rb | 67 +++++++++++++++++++++------ test/unit/cmock_header_parser_test.rb | 67 ++++++++++++++++++++++++--- 2 files changed, 112 insertions(+), 22 deletions(-) diff --git a/lib/cmock_header_parser.rb b/lib/cmock_header_parser.rb index cc6bcfa..6939c53 100644 --- a/lib/cmock_header_parser.rb +++ b/lib/cmock_header_parser.rb @@ -74,27 +74,64 @@ class CMockHeaderParser end def transform_inline_functions(source) + # Format to look for inline functions. + # This is a combination of "static" and "inline" keywords ("static inline", "inline static", "inline", "static") + # There are several possibilities: + # - sometimes they appear together, sometimes individually, + # - The keywords can appear before or after the return type (this is a compiler warning but people do weird stuff), + # so we check for word boundaries when searching for them + # - We first remove "static inline" combinations and boil down to + inline_function_regex_formats = [ + /(static\s+inline|inline\s+static)\s*/, # Last part (\s*) is just to remove whitespaces (only to prettify the output) + /(\bstatic\b|\binline\b)\s*/, # Last part (\s*) is just to remove whitespaces (only to prettify the output) + ] + # let's clean up the encoding in case they've done anything weird with the characters we might find source = source.force_encoding("ISO-8859-1").encode("utf-8", :replace => nil) - source.gsub!(/(static|inline)+.*\{.*\w*\}/m) do |m| - m.gsub!(/(static|inline)/, '') # remove static and inline keywords - m = remove_nested_pairs_of_braces(m) + # - Just looking for static|inline in the gsub is a bit too aggressive (functions that are named like this, ...), so we try to be a bit smarter + # Instead, look for "static inline" and parse it: + # - Everything before the match should just be copied, we don't want + # to touch anything but the inline functions. + # - Remove the implementation of the inline function (this is enclosed + # in square brackets) and replace it with ";" to complete the + # transformation to normal/non-inline function. + # To ensure proper removal of the function body, we count the number of square-bracket pairs + # and remove the pairs one-by-one. + # - Copy everything after the inline function implementation and start the parsing of the next inline function - # Functions having "{ }" at this point are/were inline functions, - # Disguise them as normal functions with the ";" - m.gsub!(/\s*\{\s\}/, ";") + inline_function_regex_formats.each do |format| + loop do + inline_function_match = source.match(/#{format}/) # Search for inline function declaration + break if nil == inline_function_match - # Cleanup the function declarations - # Not strictly necessary, it will compile just fine, but it can help during debugging - m_lines = m.split(/\s*;\s*/).uniq - m_lines.each do |m_line| - m_line.gsub!(/^\s+/, '') # remove extra white space from beginning of line - m_line.gsub!(/\s+/, ' ') # remove remaining extra white space - m_line.gsub!(/\n/, '') # remove newlines + # Get number of square brackets in function, we already now there is atleast 1 pair (main function body) + curr = 0 + started = false + total = 0 + + inline_function_match.post_match.each_char do |c| + if ("{" == c) + curr += 1 + total +=1 + started = true + elsif ("}" == c) + curr -=1 + end + + break if started && curr == 0 # We reached the end of the inline function body + end + + inline_function_stripped = inline_function_match.post_match + until total == 1 + inline_function_stripped.sub!(/\{[^\{\}]*\}/, "") # Remove code in between square brackets + total -= 1 + end + + inline_function_stripped.sub!(/(\s*\{[^\{\}]*|[^\{\}]*\})+/, ";") # Remove inline implementation + + source = inline_function_match.pre_match + inline_function_stripped # Make new source with the inline function removed and move on to the next end - - m_lines.join(";\n") + ";" # Join the lines and add the last semicolon manually end return source diff --git a/test/unit/cmock_header_parser_test.rb b/test/unit/cmock_header_parser_test.rb index b2cd653..9ef83a2 100644 --- a/test/unit/cmock_header_parser_test.rb +++ b/test/unit/cmock_header_parser_test.rb @@ -1842,18 +1842,59 @@ describe CMockHeaderParser, "Verify CMockHeaderParser Module" do "};\n" + "int my_function(int a);\n" + "int my_better_function(struct my_struct *s);\n" + - "static inline int get_member_a(struct my_struct *s)\n" + + "static inline int staticinlinebar(struct my_struct *s)\n" + # This is a function with a lot of indentation levels, we should be able to handle it + "{\n" + + "\t{\n" + + "\t\t{\n" + + "\t\t\treturn s->a;\n" + + "\t\t}\n" + + "\t}\n" + + "}\n" + + "static inline int staticinlinefunc(struct my_struct *s)\n" + "{\n" + " return s->a;\n" + "}\n" + - "inline static int my_func_0(int a)\n" + - "{\n" + + "int bar(struct my_struct *s);\n" + # A normal function to screw with our parser + "inline static int inlinestaticfunc(int a) {\n" + " return a + 42;\n" + "}\n" + - "inline int my_func_1(struct my_struct *s)\n" + + "inline int StaticInlineFunc(struct my_struct *s)\n" + "{\n" + " return get_member_a(s) + 42;\n" + "}\n" + + "int inline StaticInlineBar(struct my_struct *s)\n" + + "{\n" + + " return get_member_a(s) + 42;\n" + + "}\n" + + "struct staticinlinestruct {\n" + # Add a structure declaration between the inline functions, just to make sure we don't touch it! + "int a;\n" + + "};\n" + + "\n" + + "struct staticinlinestruct fubarstruct(struct my_struct *s);\n" + # Another normal function to screw with our parser + "static inline struct staticinlinestruct inlinefubarfunction(struct my_struct *s)\n" + + "{\n" + + " return (struct staticinlinestruct)*s;\n" + + "}\n" + + "int fubar(struct my_struct *s);\n" + # Another normal function to screw with our parser + "inline int stuff(int num)" + + "{" + + " int reg = 0x12;" + + " if (num > 0)" + + " {" + + " reg |= (0x0Eu);" + + " }" + + " else" + + " {" + + " reg |= (0x07u);" + + " }" + + " return reg;" + + "}" + + "\n" + + "int inline static dummy_func_2(int a, char b, float c) {" + # This is a sneaky one, inline static is placed AFTER the return value + " c += 3.14;" + + " b -= 32;" + + " return a + (int)(b) + (int)c;" + + "}" + "#endif _NOINCLUDES\n" expected = @@ -1883,9 +1924,21 @@ describe CMockHeaderParser, "Verify CMockHeaderParser Module" do "};\n" + "int my_function(int a);\n" + "int my_better_function(struct my_struct *s);\n" + - "int get_member_a(struct my_struct *s);\n" + - "int my_func_0(int a);\n" + - "int my_func_1(struct my_struct *s);\n" + + "int staticinlinebar(struct my_struct *s);\n" + + "int staticinlinefunc(struct my_struct *s);\n" + + "int bar(struct my_struct *s);\n" + + "int inlinestaticfunc(int a);\n" + + "int StaticInlineFunc(struct my_struct *s);\n" + + "int StaticInlineBar(struct my_struct *s);\n" + + "struct staticinlinestruct {\n" + + "int a;\n" + + "};\n" + + "\n" + + "struct staticinlinestruct fubarstruct(struct my_struct *s);\n" + + "struct staticinlinestruct inlinefubarfunction(struct my_struct *s);\n" + + "int fubar(struct my_struct *s);\n" + + "int stuff(int num);\n" + + "int dummy_func_2(int a, char b, float c);" + "#endif _NOINCLUDES\n" assert_equal(expected, @parser.transform_inline_functions(source))