Issue #22179 has been reported by sferik (Erik Berlin). ---------------------------------------- Bug #22179: Coverage: redefined methods silently disappear from method Coverage.result after GC https://bugs.ruby-lang.org/issues/22179 * Author: sferik (Erik Berlin) * Status: Open * Backport: 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN ---------------------------------------- When method coverage is enabled, a method that has been redefined vanishes from `Coverage.result` once GC runs. It is not reported as uncovered (count 0), it is missing altogether, so the set of methods reported for a file changes nondeterministically depending on GC timing. ##### Reproduction target.rb: ```ruby def foo 1 end def foo 2 end foo ``` run.rb: ```ruby require "coverage" Coverage.start(methods: true) require_relative "./target" GC.start if ENV["FORCE_GC"] pp Coverage.result.find { |f, _| f.end_with?("target.rb") }.last[:methods] ``` Without GC, both definitions are reported: ``` $ ruby run.rb {[Object, :foo, 5, 0, 7, 3] => 1, [Object, :foo, 1, 0, 3, 3] => 0} ``` With a GC before `Coverage.result`, the first definition is gone: ``` $ FORCE_GC=1 ruby run.rb {[Object, :foo, 5, 0, 7, 3] => 1} ``` The explicit `GC.start` is only for determinism. In real programs any GC that happens to run between the redefinition and `Coverage.result` has the same effect, which is how this was noticed in the wild. I’ve looked into why this is happening and I believe it's because method coverage is not stored per file the way line and branch coverage are, it's reconstructed at result time by walking the heap for method entries (`method_coverage_i` via `rb_objspace_each_objects` in ext/coverage/coverage.c). A shadowed definition that was never called has no entry in `me2counter` and exists only as a garbage method entry after redefinition, so once GC collects it, the heap walk cannot see it and the definition is silently forgotten. This issue was discovered when it was reported as [a bug in SimpleCov](https://github.com/simplecov-ruby/simplecov/issues/1217), of which I am a maintainer. Reproduced on 3.2.11, 3.3.11, 3.4.10, and 4.0.5. -- https://bugs.ruby-lang.org/