Module: SimpleCov::StaticCoverageExtractor

Extended by:
StaticCoverageExtractor
Included in:
StaticCoverageExtractor
Defined in:
lib/simplecov/static_coverage_extractor.rb,
lib/simplecov/static_coverage_extractor/visitor.rb,
lib/simplecov/static_coverage_extractor/prism_compat.rb,
lib/simplecov/static_coverage_extractor/value_position.rb,
lib/simplecov/static_coverage_extractor/method_collector.rb,
lib/simplecov/static_coverage_extractor/condition_folding.rb,
lib/simplecov/static_coverage_extractor/location_conventions.rb

Overview

Static enumeration of the branches and methods Ruby's Coverage library would have reported if a file had been loaded with branches: true / methods: true. Used by SimulateCoverage to backfill data for files added via cover / track_files that were never required during the run, so unloaded files contribute to the branch/method denominators symmetrically with their line coverage (#1059).

The emitted shape mirrors Coverage.result[path] for the same file. Position info comes from Prism's reported source locations; it doesn't always match Coverage's byte-for-byte, but lines are reliable and downstream consumers that key off line numbers see the data they expect.

Prism loads on the first extraction rather than with SimpleCov, so a line-only run never needs it. Where it cannot load at all (JRuby on Windows, whose FFI backend fails to open libprism), extraction answers nil like any other failure.

Defined Under Namespace

Modules: ConditionFolding, LocationConventions, MethodCollector, PrismCompat, ValuePositions Classes: Visitor

Instance Method Summary collapse

Instance Method Details

#branch_start_line(_type, _id, start_line) ⇒ Object

Both keys carry their start line third, after the parts that vary between recordings. Read through a parameter list rather than an index, because every spelling of an index answers the same for a tuple of this fixed shape. Binding an argument has only the one spelling.



81
82
83
# File 'lib/simplecov/static_coverage_extractor.rb', line 81

def branch_start_line(_type, _id, start_line, *)
  start_line
end

#call(source) ⇒ Object

Parse source and return {"branches" => {...}, "methods" => {...}} matching the shape Coverage.result[path] produces. Returns nil when Prism cannot load or parsing fails, which callers treat as "couldn't extract, fall back to empty hashes".



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/simplecov/static_coverage_extractor.rb', line 27

def call(source)
  return nil unless prism_loaded?

  result = Prism.parse(source)
  return nil if result.failure?

  visitor = Visitor.new
  visitor.visit(result.value)
  {"branches" => visitor.branches, "methods" => visitor.methods}
rescue
  # Parser errors beyond the .failure? check, unsupported AST shapes, or
  # anything else: fall back to empty hashes rather than crashing the whole
  # report.
  nil
end

#method_identity(_class_name, name, start_line) ⇒ Object



85
86
87
# File 'lib/simplecov/static_coverage_extractor.rb', line 85

def method_identity(_class_name, name, start_line, *)
  [name, start_line]
end

#prism_loaded? ⇒ Boolean

Memoized, because a failed load leaves nothing in $LOADED_FEATURES and would otherwise be retried for every file.

Returns:

  • (Boolean)


71
72
73
74
75
# File 'lib/simplecov/static_coverage_extractor.rb', line 71

def prism_loaded?
  return @prism_loaded if instance_variable_defined?(:@prism_loaded)

  @prism_loaded = load_prism
end

#real_source_positions(source) ⇒ Object

Summarize a source file's real branch and method positions, for the :eval_generated filter (#1046). Answers:

{
branches: Set[start_line, ...],         # e.g., [3, 12, 20]
methods:  Set[[name, start_line], ...]  # e.g., [[:foo, 7], [:bar, 13]]
}

Branch matching is start_line-only rather than by the full tuple. Static extraction and Coverage can still disagree on a branch's exact column positions, so matching on start_line alone tolerates those differences. Coincidental line-sharing between a real branch and an eval-generated one keeps both, an acceptable false-negative for an opt-in filter.

Returns nil when Prism cannot load or parsing fails, signaling callers to keep every Coverage entry.



59
60
61
62
63
64
65
66
67
# File 'lib/simplecov/static_coverage_extractor.rb', line 59

def real_source_positions(source)
  extracted = call(source)
  return nil unless extracted

  {
    branches: extracted.fetch("branches").keys.to_set { |tuple| branch_start_line(*tuple) },
    methods: extracted.fetch("methods").keys.to_set { |tuple| method_identity(*tuple) }
  }
end