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. 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.
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
Prism's Visitor is a stateless dispatch table whose initializer is Object's, so no mutation of the
supercall can be told apart. - #visit_call_node(node) ⇒ Object
- #visit_case_match_node(node) ⇒ Object
-
#visit_case_node(node) ⇒ Object
When there's no explicit
else, Coverage synthesizes one at the case's range. -
#visit_if_node(node) ⇒ Object
if/unless/ postfix / ternary all parse as IfNode (or UnlessNode). - #visit_match_predicate_node(node) ⇒ Object
-
#visit_match_required_node(node) ⇒ Object
One-line pattern matching:
x => patternandx in pattern. -
#visit_program_node(node) ⇒ Object
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.
- #visit_unless_node(node) ⇒ Object
- #visit_until_node(node) ⇒ Object
-
#visit_while_node(node) ⇒ Object
A loop gets a single
:bodyarm and no synthetic else.
Methods included from MethodCollector
#visit_class_node, #visit_def_node, #visit_module_node
Constructor Details
#initialize ⇒ Visitor
Prism's Visitor is a stateless dispatch table whose initializer is
Object's, so no mutation of the super call can be told apart. It
stays for the day that stops being true.
mutant:disable
27 28 29 30 31 32 33 34 35 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 27 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.
21 22 23 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 21 def branches @branches end |
#methods ⇒ Object (readonly)
Returns the value of attribute methods.
21 22 23 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 21 def methods @methods end |
Instance Method Details
#visit_call_node(node) ⇒ Object
72 73 74 75 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 72 def visit_call_node(node) (node) if node.respond_to?(:safe_navigation?) && node. super end |
#visit_case_match_node(node) ⇒ Object
84 85 86 87 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 84 def visit_case_match_node(node) emit_case_like(node, :in) super end |
#visit_case_node(node) ⇒ Object
When there's no explicit else, Coverage synthesizes one at the case's
range.
79 80 81 82 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 79 def visit_case_node(node) emit_case_like(node, :when) super end |
#visit_if_node(node) ⇒ Object
if / unless / postfix / ternary all parse as IfNode (or UnlessNode).
Both carry a then arm 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, and so do we.
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. On 3.2 the dead arm is visited too, branches only.
56 57 58 59 60 61 62 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 56 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
99 100 101 102 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 99 def visit_match_predicate_node(node) emit_oneline_pattern(node, node.pattern) if LEGACY_COVERAGE_LOCATIONS super end |
#visit_match_required_node(node) ⇒ Object
One-line pattern matching: x => pattern and x in pattern. Ruby 3.3's
Coverage reports these as a :case with an :in and an :else arm;
3.4 dropped them entirely, 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.
94 95 96 97 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 94 def visit_match_required_node(node) emit_oneline_pattern(node, node) if LEGACY_COVERAGE_LOCATIONS super end |
#visit_program_node(node) ⇒ Object
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.
40 41 42 43 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 40 def visit_program_node(node) @value_positions = ValuePositions.call(node) if LEGACY_COVERAGE_LOCATIONS super end |
#visit_unless_node(node) ⇒ Object
64 65 66 67 68 69 70 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 64 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
110 111 112 113 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 110 def visit_until_node(node) emit_loop(node, :until) super end |
#visit_while_node(node) ⇒ Object
A loop gets a single :body arm and no synthetic else.
105 106 107 108 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 105 def visit_while_node(node) emit_loop(node, :while) super end |