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.

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.



68
69
70
# File 'lib/simplecov/static_coverage_extractor.rb', line 68

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 on parse failure, which callers treat as "couldn't extract, fall back to empty hashes".



24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/simplecov/static_coverage_extractor.rb', line 24

def call(source)
  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



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

def method_identity(_class_name, name, start_line, *)
  [name, start_line]
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 parsing fails, signaling callers to keep every Coverage entry.



54
55
56
57
58
59
60
61
62
# File 'lib/simplecov/static_coverage_extractor.rb', line 54

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