diff --git a/lib/cmock_header_parser.rb b/lib/cmock_header_parser.rb index 3a1d670..c4d5aa9 100644 --- a/lib/cmock_header_parser.rb +++ b/lib/cmock_header_parser.rb @@ -64,6 +64,48 @@ class CMockHeaderParser private if $ThisIsOnlyATest.nil? ################ + # Remove code disabled by basic preprocesser tags like #if 0, #if 1, etc. + def remove_disabled_code_from_source(source) + result = [] + emit = true + # Stack entries: [restore_emit, else_emit, seen_else] + # restore_emit: value to restore `emit` to after matching #endif + # else_emit: value to set `emit` to after #else + # seen_else: whether #else at this level has already been seen + stack = [] + + source.each_line do |line| + stripped = line.strip + if stripped =~ /^#\s*if\s+0\s*$/ + stack.push([emit, emit, false]) + emit = false + elsif stripped =~ /^#\s*if\s+1\s*$/ + stack.push([emit, false, false]) + # emit unchanged: keep the if-branch + elsif stripped =~ /^#\s*if/ + stack.push([emit, emit, false]) + # emit unchanged: unknown condition, keep both branches + elsif stripped =~ /^#\s*else\b/ + unless stack.empty? + entry = stack.last + unless entry[2] + stack[-1] = [entry[0], entry[1], true] + emit = entry[1] + end + end + elsif stripped =~ /^#\s*endif\b/ + unless stack.empty? + entry = stack.pop + emit = entry[0] + end + elsif emit + result << line + end + end + + result.join + end + # Remove C/C++ comments from a string # +source+:: String which will have the comments removed def remove_comments_from_source(source) @@ -248,6 +290,10 @@ class CMockHeaderParser # remove preprocessor statements and extern "C" source.gsub!(/extern\s+"C"\s*\{/, '') + + # handle basic literal preprocessor conditionals before stripping all directives + source = remove_disabled_code_from_source(source) + source.gsub!(/^\s*#.*/, '') # enums, unions, structs, and typedefs can all contain things (e.g. function pointers) that parse like function prototypes, so yank them diff --git a/test/unit/cmock_header_parser_test.rb b/test/unit/cmock_header_parser_test.rb index b62dac5..2ef7922 100644 --- a/test/unit/cmock_header_parser_test.rb +++ b/test/unit/cmock_header_parser_test.rb @@ -146,6 +146,118 @@ describe CMockHeaderParser, "Verify CMockHeaderParser Module" do assert_equal(expected, @parser.import_source(source, @test_project)) end + it "remove code inside #if 0 blocks" do + source = + "void before(void);\n" + + "#if 0\n" + + "void hidden(void);\n" + + "#endif\n" + + "void after(void);\n" + + expected = ["void before(void)", "void after(void)"] + + assert_equal(expected, @parser.import_source(source, @test_project).map! { |s| s.strip }) + end + + it "keep code inside #if 1 blocks" do + source = + "void before(void);\n" + + "#if 1\n" + + "void visible(void);\n" + + "#endif\n" + + "void after(void);\n" + + expected = ["void before(void)", "void visible(void)", "void after(void)"] + + assert_equal(expected, @parser.import_source(source, @test_project).map! { |s| s.strip }) + end + + it "keep else branch of #if 0 blocks" do + source = + "void before(void);\n" + + "#if 0\n" + + "void hidden(void);\n" + + "#else\n" + + "void visible(void);\n" + + "#endif\n" + + "void after(void);\n" + + expected = ["void before(void)", "void visible(void)", "void after(void)"] + + assert_equal(expected, @parser.import_source(source, @test_project).map! { |s| s.strip }) + end + + it "remove else branch of #if 1 blocks" do + source = + "void before(void);\n" + + "#if 1\n" + + "void visible(void);\n" + + "#else\n" + + "void hidden(void);\n" + + "#endif\n" + + "void after(void);\n" + + expected = ["void before(void)", "void visible(void)", "void after(void)"] + + assert_equal(expected, @parser.import_source(source, @test_project).map! { |s| s.strip }) + end + + it "handle nested #if 1 blocks inside #if 0" do + source = + "void before(void);\n" + + "#if 0\n" + + "void hidden1(void);\n" + + "#if 1\n" + + "void hidden2(void);\n" + + "#endif\n" + + "void hidden3(void);\n" + + "#endif\n" + + "void after(void);\n" + + expected = ["void before(void)", "void after(void)"] + + assert_equal(expected, @parser.import_source(source, @test_project).map! { |s| s.strip }) + end + + it "handle nested #if 0 blocks inside #if 1" do + source = + "void before(void);\n" + + "#if 1\n" + + "void visible1(void);\n" + + "#if 0\n" + + "void hidden1(void);\n" + + "#endif\n" + + "void visible2(void);\n" + + "#endif\n" + + "void after(void);\n" + + expected = ["void before(void)", "void visible1(void)", "void visible2(void)", "void after(void)"] + + assert_equal(expected, @parser.import_source(source, @test_project).map! { |s| s.strip }) + end + + it "handle back to back #if 1 blocks and #if 0 blocks" do + source = + "void before(void);\n" + + "#if 0\n" + + "void hidden1(void);\n" + + "#endif\n" + + "#if 1\n" + + "void visible1(void);\n" + + "#endif\n" + + "#if 0\n" + + "void hidden2(void);\n" + + "#endif\n" + + "#if 1\n" + + "void visible2(void);\n" + + "#endif\n" + + "void after(void);\n" + + expected = ["void before(void)", "void visible1(void)", "void visible2(void)", "void after(void)"] + + assert_equal(expected, @parser.import_source(source, @test_project).map! { |s| s.strip }) + end + it "remove assembler pragma sections" do source =