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
27 28 29 30 31 32 33 34 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 27 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.
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
69 70 71 72 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 69 def visit_call_node(node) (node) if node.respond_to?(:safe_navigation?) && node. super end |
#visit_case_match_node(node) ⇒ Object
81 82 83 84 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 81 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.
76 77 78 79 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 76 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.
53 54 55 56 57 58 59 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 53 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
96 97 98 99 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 96 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.
91 92 93 94 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 91 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.
39 40 41 42 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 39 def visit_program_node(node) @value_positions = ValuePositions.call(node) if LEGACY_COVERAGE_LOCATIONS super end |
#visit_unless_node(node) ⇒ Object
61 62 63 64 65 66 67 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 61 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
107 108 109 110 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 107 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.
102 103 104 105 |
# File 'lib/simplecov/static_coverage_extractor/visitor.rb', line 102 def visit_while_node(node) emit_loop(node, :while) super end |