Class: SimpleCov::StaticCoverageExtractor::Visitor
- Inherits:
-
Prism::Visitor
- Object
- Prism::Visitor
- SimpleCov::StaticCoverageExtractor::Visitor
- Includes:
- ConditionFolding, LocationConventions, MethodCollector
- Defined in:
- lib/simplecov/static_coverage_extractor/visitor.rb
Overview
Prism visitor that accumulates branch and method tuples in the
shape Ruby's Coverage reports. Tuple ids are sequential across
the file like Coverage's, but the numbering order can differ
(e.g. case/when and chained &. are visited in a different
order than Coverage numbers them). That's fine: the combiners
intern on source span and the report output drops ids, so nothing
downstream compares them. Only defined when Prism is loadable;
StaticCoverageExtractor.available? is the runtime gate.
Constant Summary
Constants included from ConditionFolding
ConditionFolding::CONTAINER_CONTENTS_NEED_STATIC_LITERALS, ConditionFolding::DEAD_ARM_BRANCHES_SURVIVE, ConditionFolding::ELIMINABLE_READ_TYPES, ConditionFolding::FALSY_CONDITION_TYPES, ConditionFolding::FOLDS_SOURCE_FILE, ConditionFolding::PARENS_ALWAYS_TRANSPARENT, ConditionFolding::PAREN_OPAQUE_TYPES, ConditionFolding::PRISM_ERA_ELIMINABLE_READS, ConditionFolding::STATIC_CONDITION_TYPES, ConditionFolding::STATIC_LITERAL_LEAF_TYPES
Constants included from LocationConventions
LocationConventions::LEGACY_COVERAGE_LOCATIONS
Instance Attribute Summary collapse
-
#branches ⇒ Object
readonly
Returns the value of attribute branches.
-
#methods ⇒ Object
readonly
Returns the value of attribute methods.
Instance Method Summary collapse
-
#initialize ⇒ Visitor
constructor
A new instance of Visitor.
- #visit_call_node(node) ⇒ Object
- #visit_case_match_node(node) ⇒ Object
-
#visit_case_node(node) ⇒ Object
case/whenandcase/in(pattern matching) parse as CaseNode and CaseMatchNode respectively. -
#visit_if_node(node) ⇒ Object
if/unless/ postfix-if / postfix-unless / ternary all parse as IfNode (or UnlessNode). - #visit_match_predicate_node(node) ⇒ Object
-
#visit_match_required_node(node) ⇒ Object
One-line pattern matching:
x => pattern(MatchRequiredNode) andx in pattern(MatchPredicateNode). -
#visit_program_node(node) ⇒ Object
Entry point for a parsed file.
- #visit_unless_node(node) ⇒ Object
- #visit_until_node(node) ⇒ Object
-
#visit_while_node(node) ⇒ Object
while/untilloops get a single:bodyarm.
Methods included from MethodCollector
#visit_class_node, #visit_def_node, #visit_module_node
Constructor Details
#initialize ⇒ Visitor
Returns a new instance of Visitor.
32 33 34 35 36 37 38 39 40 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 32 def initialize super @branches = {} @methods = {} @next_id = 0 @class_stack = [] @value_positions = nil @suppress_methods = false end |
Instance Attribute Details
#branches ⇒ Object (readonly)
Returns the value of attribute branches.
30 31 32 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 30 def branches @branches end |
#methods ⇒ Object (readonly)
Returns the value of attribute methods.
30 31 32 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 30 def methods @methods end |
Instance Method Details
#visit_call_node(node) ⇒ Object
84 85 86 87 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 84 def visit_call_node(node) (node) if node.respond_to?(:safe_navigation?) && node. super end |
#visit_case_match_node(node) ⇒ Object
97 98 99 100 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 97 def visit_case_match_node(node) emit_case_like(node, :in) super end |
#visit_case_node(node) ⇒ Object
case/when and case/in (pattern matching) parse as CaseNode
and CaseMatchNode respectively. When there's no explicit else,
Coverage synthesizes one at the case's range.
92 93 94 95 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 92 def visit_case_node(node) emit_case_like(node, :when) super end |
#visit_if_node(node) ⇒ Object
if / unless / postfix-if / postfix-unless / ternary all parse
as IfNode (or UnlessNode). Both carry a then arm (the
statements body) and an optional subsequent (an ElseNode for
else, another IfNode for elsif). When the subsequent is
missing, Coverage synthesizes a :else arm attributed to the
whole condition's range — we do the same.
A folded condition emits no tuple, and on modern Rubies only its
live arm is descended into: the compiler eliminates the dead
arm's entire subtree, so a branch or method nested there would be
a phantom no loaded run can produce. A falsy if's elsif chain
survives as a plain if, which is what visiting the subsequent
IfNode emits. On 3.2 the dead arm is visited too, branches only
(see visit_folded_arms).
68 69 70 71 72 73 74 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 68 def visit_if_node(node) verdict = folded_condition(node.predicate) return visit_folded_arms(verdict, node.statements, PrismCompat.subsequent(node)) if verdict emit_if_like(node, :if) super end |
#visit_match_predicate_node(node) ⇒ Object
114 115 116 117 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 114 def visit_match_predicate_node(node) emit_oneline_pattern(node, node.pattern.location) if LEGACY_COVERAGE_LOCATIONS super end |
#visit_match_required_node(node) ⇒ Object
One-line pattern matching: x => pattern (MatchRequiredNode) and
x in pattern (MatchPredicateNode). Ruby 3.3's Coverage reports
these as a :case with an :in and an :else arm; 3.4 dropped
them entirely (no branch), so this is legacy-only. The two forms
differ only in where Coverage anchors the synthesized :else:
=> uses the whole expression, in uses just the pattern.
simplecov:disable branch — legacy-only arms; unreachable on the modern dogfood Ruby
109 110 111 112 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 109 def visit_match_required_node(node) emit_oneline_pattern(node, node.location) if LEGACY_COVERAGE_LOCATIONS super end |
#visit_program_node(node) ⇒ Object
Entry point for a parsed file. On legacy Rubies the location of an empty branch arm depends on whether its construct is in value (tail) position, so precompute that once for the whole tree before emitting anything. Modern Rubies don't need it (see LocationConventions), so the pass is skipped there.
47 48 49 50 51 52 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 47 def visit_program_node(node) # simplecov:disable branch — legacy-only arm; unreachable on the modern dogfood Ruby @value_positions = ValuePositions.call(node) if LEGACY_COVERAGE_LOCATIONS # simplecov:enable branch super end |
#visit_unless_node(node) ⇒ Object
76 77 78 79 80 81 82 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 76 def visit_unless_node(node) verdict = folded_condition(node.predicate) return visit_folded_arms(verdict, PrismCompat.else_clause(node), node.statements) if verdict emit_if_like(node, :unless) super end |
#visit_until_node(node) ⇒ Object
127 128 129 130 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 127 def visit_until_node(node) emit_loop(node, :until) super end |
#visit_while_node(node) ⇒ Object
while / until loops get a single :body arm. No synthetic
else (the loop either runs the body or doesn't).
122 123 124 125 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 122 def visit_while_node(node) emit_loop(node, :while) super end |