You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add :eval_generated filter for ignore_branches and ignore_methods
Rails delegate (and other module_eval / class_eval / instance_eval
macros that pass __FILE__ and __LINE__) inject method and branch entries
into a file's coverage data even when the static source has nothing at
that line. Ruby's Coverage library attributes the eval'd code to the
caller's position, so a single delegate call shows up as a missed def
and a missed if branch wherever the macro fires. The previous workaround
was to disable eval coverage entirely, which loses the legitimate
signal.
Adds the :eval_generated token to SimpleCov.ignore_branches and adds a
new SimpleCov.ignore_methods with the same token. Both filters walk the
static source through SimpleCov::StaticCoverageExtractor (Prism) and
drop any Coverage tuple whose start line lacks a real def keyword (for
methods) or branch construct (for branches). Line presence is the
matcher, so a real branch or def that shares a line with an
eval-generated entry survives. Prism ships with Ruby 3.3+, and on older
Rubies gem install prism enables the filter, otherwise the setting is a
no-op.
The filters are opt-in and stored regardless of which coverage criteria
are enabled at call time, matching the existing ignore_branches
lifecycle. Unknown tokens raise SimpleCov::ConfigurationError to catch
typos. README and CHANGELOG updated, with specs covering the DSL, the
new StaticCoverageExtractor.real_source_positions helper, and SourceFile
integration.
Resolves#1046.
Copy file name to clipboardExpand all lines: CHANGELOG.md
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -51,6 +51,7 @@ Unreleased
51
51
*`SimpleCov::Result.new` is roughly 7× faster for already-string-keyed input (the `SimpleCov.collate` hot path). The previous implementation deep-cloned each file's coverage data with `JSON.parse(JSON.dump(coverage))` per source file — a useful normalization for live `Coverage.result` symbol keys, but pure overhead for resultsets loaded from disk that already have string keys. `Result` now stringifies the outer hash keys with `transform_keys` only when needed; the inner branch/method-key shape is already handled by `SourceFile#restore_ruby_data_structure`. See #916.
52
52
53
53
## Bugfixes
54
+
* Added `:eval_generated` tokens to `SimpleCov.ignore_branches` and the new `SimpleCov.ignore_methods` so projects using macros like Rails' `delegate` (or any pattern that calls `module_eval(body, __FILE__, __LINE__)`) can drop the synthetic branch and method entries those macros inject. Ruby's `Coverage` attributes eval'd code to the caller's `__FILE__` / `__LINE__`, so a `delegate :foo, to: :bar` line surfaces as if it had a `def foo` and an `if` branch right there. Detection uses Prism to walk the static source and treats any Coverage entry whose start_line lacks a real `def` keyword (for methods) or branch construct (for branches) as eval-generated. Opt in with `ignore_methods :eval_generated` and / or `ignore_branches :eval_generated`. Prism ships with Ruby 3.3+; on older Rubies `gem install prism` enables the filter, otherwise the setting is a no-op. See #1046.
54
55
* Files added via `cover` / `track_files` that were never `require`'d during the run now contribute branch and method entries to the report, not just lines. Previously `SimulateCoverage` left those fields as empty hashes (because parsing source ourselves felt risky), which made unloaded files invisible to the branch and method denominators while their lines DID count — so a `cover "{app,lib}/**/*.rb"` glob over files without specs silently inflated branch% relative to line% (the OP's reproduction was via SonarQube, which surfaces the asymmetry more visibly than the SimpleCov HTML report). Branches and methods are now enumerated statically via `SimpleCov::StaticCoverageExtractor`, which uses Prism to walk the AST and emits Coverage-shaped tuples without loading the file. The shape matches what Ruby's own `Coverage` library reports for the same source: `:if` / `:case` / `:while` / `:until` constructs plus their `:then` / `:else` / `:when` / `:in` / `:body` arms, with the synthetic `:else` for case-without-explicit-else that the `ignore_branches :implicit_else` setting (see Enhancements) targets. Prism is bundled with Ruby 3.3+; on older Rubies `gem install prism` enables the fix, otherwise SimulateCoverage falls back to the previous "empty hashes" behavior. See #1059.
55
56
* HTML report: two groups whose names share an alphanumeric suffix but differ only in a leading non-letter (e.g. `">100LOC"` / `"<10LOC"`, or any pair using different special characters) no longer render into the same DOM container. The JS that built HTML ids from group names stripped every non-letter prefix and then every remaining non-alphanumeric char, so both names sanitized to `"LOC"` and the second group silently replaced the first in the rendered tabs. The new encoding (`"g-" + each-non-id-char-as-hex`) preserves uniqueness across all input shapes. See #1038.
56
57
*`SimpleCov::Result` now warns when it drops source files because their absolute paths aren't on the local filesystem, instead of silently producing an empty `0 / 0 (100.00%)` report. The most common trigger is `SimpleCov.collate` invoked from a machine or working directory different from where the individual resultsets were generated — when *every* entry is missing the warning explicitly names that case and points at the issue; when only some are missing the warning is quieter and lists up to five paths with a `(+N more)` suffix. See #980.
0 commit comments