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.
Constant Summary
Constants included from ConditionFolding
ConditionFolding::CONTAINER_CONTENTS_NEED_STATIC_LITERALS, ConditionFolding::ELIMINABLE_READ_TYPES, ConditionFolding::FALSY_CONDITION_TYPES, ConditionFolding::FOLDS_SOURCE_FILE, ConditionFolding::PAREN_OPAQUE_TYPES, 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
28 29 30 31 32 33 34 35 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 28 def initialize super @branches = {} @methods = {} @next_id = 0 @class_stack = [] @value_positions = nil end |
Instance Attribute Details
#branches ⇒ Object (readonly)
Returns the value of attribute branches.
22 23 24 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 22 def branches @branches end |
#methods ⇒ Object (readonly)
Returns the value of attribute methods.
22 23 24 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 22 def methods @methods end |
Instance Method Details
#visit_call_node(node) ⇒ Object
70 71 72 73 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 70 def visit_call_node(node) (node) if node.respond_to?(:safe_navigation?) && node. super end |
#visit_case_match_node(node) ⇒ Object
82 83 84 85 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 82 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.
77 78 79 80 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 77 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 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.
54 55 56 57 58 59 60 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 54 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
97 98 99 100 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 97 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.
92 93 94 95 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 92 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
62 63 64 65 66 67 68 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 62 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
108 109 110 111 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 108 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.
103 104 105 106 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 103 def visit_while_node(node) emit_loop(node, :while) super end |