From 22e4a75d67d1e25deed4dce5b801397713dbdefa Mon Sep 17 00:00:00 2001 From: mugitti9 Date: Sat, 29 Aug 2026 13:57:56 +0900 Subject: [PATCH] [Bug #22269] Make coverage_enabled: false effective for compile/compile_file Use it in Coverage.line_stub so compiling the stub does not overwrite already-collected coverage data. --- ext/coverage/lib/coverage.rb | 2 +- iseq.c | 16 ++++++++-------- test/coverage/test_coverage.rb | 29 +++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 9 deletions(-) diff --git a/ext/coverage/lib/coverage.rb b/ext/coverage/lib/coverage.rb index 4bd20e22cbadbe..e0d5a11477f7a4 100644 --- a/ext/coverage/lib/coverage.rb +++ b/ext/coverage/lib/coverage.rb @@ -8,7 +8,7 @@ module Coverage # from a given source code. def self.line_stub(file) lines = File.foreach(file).map { nil } - iseqs = [RubyVM::InstructionSequence.compile_file(file)] + iseqs = [RubyVM::InstructionSequence.compile_file(file, coverage_enabled: false)] until iseqs.empty? iseq = iseqs.pop iseq.trace_points.each {|n, type| lines[n - 1] = 0 if type == :line } diff --git a/iseq.c b/iseq.c index d88ade75866c0e..36df782d1ab96a 100644 --- a/iseq.c +++ b/iseq.c @@ -1497,7 +1497,7 @@ rb_iseq_compile_with_option(VALUE src, VALUE file, VALUE realpath, VALUE line, V rb_exc_raise(GET_EC()->errinfo); } else { - iseq_new_setup_coverage(file, ast_line_count(ast_value)); + if (option.coverage_enabled) iseq_new_setup_coverage(file, ast_line_count(ast_value)); iseq = rb_iseq_new_with_opt(ast_value, name, file, realpath, ln, NULL, 0, ISEQ_TYPE_TOP, &option, Qnil); @@ -1533,7 +1533,7 @@ pm_iseq_compile_with_option(VALUE src, VALUE file, VALUE realpath, VALUE line, V pm_parse_result_init(&result); pm_options_line_set(result.options, NUM2INT(line)); pm_options_scopes_init(result.options, 1); - result.node.coverage_enabled = 1; + result.node.coverage_enabled = option.coverage_enabled; switch (option.frozen_string_literal) { case ISEQ_FROZEN_STRING_LITERAL_UNSET: @@ -1563,7 +1563,7 @@ pm_iseq_compile_with_option(VALUE src, VALUE file, VALUE realpath, VALUE line, V if (error == Qnil) { int error_state; - iseq_new_setup_coverage(file, (int) (pm_parser_line_offsets(result.node.parser)->size - 1)); + if (option.coverage_enabled) iseq_new_setup_coverage(file, (int) (pm_parser_line_offsets(result.node.parser)->size - 1)); iseq = pm_iseq_new_with_opt(&result.node, name, file, realpath, ln, NULL, 0, ISEQ_TYPE_TOP, &option, &error_state); pm_parse_result_free(&result); @@ -1944,6 +1944,8 @@ iseqw_s_compile_file(int argc, VALUE *argv, VALUE self) FilePathValue(file); file = rb_fstring(file); /* rb_io_t->pathv gets frozen anyways */ + make_compile_option(&option, opt); + f = rb_file_open_str(file, "r"); rb_execution_context_t *ec = GET_EC(); @@ -1952,7 +1954,7 @@ iseqw_s_compile_file(int argc, VALUE *argv, VALUE self) parser = rb_parser_new(); rb_parser_set_context(parser, NULL, FALSE); ast_value = rb_parser_load_file(parser, file); - iseq_new_setup_coverage(file, ast_line_count(ast_value)); + if (option.coverage_enabled) iseq_new_setup_coverage(file, ast_line_count(ast_value)); ast = rb_ruby_ast_data_get(ast_value); if (!ast->body.root) exc = GET_EC()->errinfo; @@ -1962,8 +1964,6 @@ iseqw_s_compile_file(int argc, VALUE *argv, VALUE self) rb_exc_raise(exc); } - make_compile_option(&option, opt); - ret = iseqw_new(rb_iseq_new_with_opt(ast_value, rb_fstring_lit("
"), file, rb_realpath_internal(Qnil, file, 1), @@ -2020,7 +2020,7 @@ iseqw_s_compile_file_prism(int argc, VALUE *argv, VALUE self) pm_parse_result_t result; pm_parse_result_init(&result); - result.node.coverage_enabled = 1; + result.node.coverage_enabled = option.coverage_enabled; switch (option.frozen_string_literal) { case ISEQ_FROZEN_STRING_LITERAL_UNSET: @@ -2041,7 +2041,7 @@ iseqw_s_compile_file_prism(int argc, VALUE *argv, VALUE self) if (error == Qnil) { int error_state; - iseq_new_setup_coverage(file, (int) (pm_parser_line_offsets(result.node.parser)->size - 1)); + if (option.coverage_enabled) iseq_new_setup_coverage(file, (int) (pm_parser_line_offsets(result.node.parser)->size - 1)); rb_iseq_t *iseq = pm_iseq_new_with_opt(&result.node, rb_fstring_lit("
"), file, rb_realpath_internal(Qnil, file, 1), diff --git a/test/coverage/test_coverage.rb b/test/coverage/test_coverage.rb index 73e3c0553b8c40..4e384d69007b62 100644 --- a/test/coverage/test_coverage.rb +++ b/test/coverage/test_coverage.rb @@ -1022,6 +1022,35 @@ def test_line_stub } end + def test_line_stub_does_not_clobber_existing_coverage + Dir.mktmpdir {|tmp| + Dir.chdir(tmp) { + File.open("test.rb", "w") do |f| + f.puts <<-EOS + def coverage_test_snapshot + :ok + end + EOS + end + + assert_in_out_err(ARGV, <<-"end;", ["[1, 1, nil]", "[1, 1, nil]", "[1, 2, nil]"], []) + Coverage.start + tmp = Dir.pwd + f = tmp + "/test.rb" + require f + coverage_test_snapshot + cov = Coverage.peek_result[f] + Coverage.line_stub(f) + cov2 = Coverage.peek_result[f] + coverage_test_snapshot + p cov + p cov2 + p Coverage.result[f] + end; + } + } + end + def test_stop_wrong_peephole_optimization result = { :lines => [1, 1, 1, nil]