mutant 0.5.17 → 0.5.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Changelog.md +6 -0
- data/Rakefile +13 -0
- data/config/flay.yml +1 -1
- data/config/reek.yml +8 -1
- data/lib/mutant.rb +18 -3
- data/lib/mutant/cli.rb +6 -3
- data/lib/mutant/constants.rb +3 -0
- data/lib/mutant/expression.rb +110 -0
- data/lib/mutant/expression/method.rb +92 -0
- data/lib/mutant/expression/namespace.rb +90 -0
- data/lib/mutant/matcher/namespace.rb +3 -4
- data/lib/mutant/meta.rb +31 -0
- data/lib/mutant/meta/example.rb +152 -0
- data/lib/mutant/meta/example/dsl.rb +105 -0
- data/lib/mutant/mutator/node.rb +6 -6
- data/lib/mutant/mutator/node/arguments.rb +1 -1
- data/lib/mutant/mutator/node/begin.rb +1 -1
- data/lib/mutant/mutator/node/const.rb +1 -1
- data/lib/mutant/mutator/node/if.rb +6 -6
- data/lib/mutant/mutator/node/literal/array.rb +2 -2
- data/lib/mutant/mutator/node/literal/hash.rb +2 -2
- data/lib/mutant/mutator/node/literal/range.rb +3 -3
- data/lib/mutant/mutator/node/literal/regex.rb +2 -2
- data/lib/mutant/mutator/node/literal/symbol.rb +1 -1
- data/lib/mutant/mutator/node/op_asgn.rb +1 -3
- data/lib/mutant/mutator/node/or_asgn.rb +31 -0
- data/lib/mutant/mutator/node/send.rb +1 -1
- data/lib/mutant/mutator/node/send/attribute_assignment.rb +1 -1
- data/lib/mutant/reporter/cli/report/mutation.rb +11 -1
- data/lib/mutant/strategy.rb +1 -1
- data/lib/mutant/version.rb +1 -1
- data/meta/and.rb +12 -0
- data/meta/and_asgn.rb +12 -0
- data/meta/array.rb +30 -0
- data/meta/begin.rb +15 -0
- data/meta/binary.rb +12 -0
- data/meta/block.rb +55 -0
- data/meta/block_pass.rb +8 -0
- data/meta/blockarg.rb +10 -0
- data/meta/boolean.rb +15 -0
- data/meta/break.rb +11 -0
- data/meta/case.rb +303 -0
- data/meta/casgn.rb +9 -0
- data/meta/cbase.rb +8 -0
- data/meta/const.rb +9 -0
- data/meta/cvar.rb +7 -0
- data/meta/cvasgn.rb +10 -0
- data/meta/define.rb +118 -0
- data/meta/defined.rb +7 -0
- data/meta/dstr.rb +10 -0
- data/meta/dsym.rb +11 -0
- data/meta/ensure.rb +9 -0
- data/meta/fixnum.rb +19 -0
- data/meta/float.rb +38 -0
- data/meta/gvar.rb +7 -0
- data/meta/gvasgn.rb +10 -0
- data/meta/hash.rb +25 -0
- data/meta/if.rb +57 -0
- data/meta/ivasgn.rb +10 -0
- data/meta/kwbegin.rb +9 -0
- data/meta/lvar.rb +14 -0
- data/meta/lvasgn.rb +10 -0
- data/meta/masgn.rb +7 -0
- data/meta/match_current_line.rb +14 -0
- data/meta/next.rb +11 -0
- data/meta/nil.rb +5 -0
- data/meta/nthref.rb +14 -0
- data/meta/op_assgn.rb +15 -0
- data/meta/or_asgn.rb +22 -0
- data/meta/range.rb +41 -0
- data/meta/redo.rb +5 -0
- data/meta/regex.rb +20 -0
- data/meta/rescue.rb +38 -0
- data/meta/restarg.rb +11 -0
- data/meta/return.rb +15 -0
- data/meta/self.rb +7 -0
- data/meta/send.rb +240 -0
- data/meta/string.rb +7 -0
- data/meta/super.rb +26 -0
- data/meta/symbol.rb +8 -0
- data/meta/unless.rb +15 -0
- data/meta/while.rb +24 -0
- data/meta/yield.rb +10 -0
- data/mutant.gemspec +1 -0
- data/spec/integration/mutant/corpus_spec.rb +29 -25
- data/spec/spec_helper.rb +2 -0
- data/spec/support/mutation_verifier.rb +1 -0
- data/spec/unit/mutant/cli_new_spec.rb +6 -6
- data/spec/unit/mutant/expression/method_spec.rb +50 -0
- data/spec/unit/mutant/expression/namespace/flat_spec.rb +32 -0
- data/spec/unit/mutant/expression/namespace/recursive_spec.rb +37 -0
- data/spec/unit/mutant/matcher/namespace_spec.rb +2 -2
- data/spec/unit/mutant/mutation_spec.rb +1 -1
- data/spec/unit/mutant/mutator/node_spec.rb +14 -0
- metadata +123 -139
- data/lib/mutant/cli/classifier.rb +0 -139
- data/lib/mutant/cli/classifier/method.rb +0 -105
- data/lib/mutant/cli/classifier/namespace.rb +0 -49
- data/spec/unit/mutant/cli/classifier/method_spec.rb +0 -77
- data/spec/unit/mutant/cli/classifier/namespace/flat_spec.rb +0 -58
- data/spec/unit/mutant/cli/classifier/namespace/recursive_spec.rb +0 -58
- data/spec/unit/mutant/cli/classifier_spec.rb +0 -59
- data/spec/unit/mutant/mutator/node/and_asgn_spec.rb +0 -19
- data/spec/unit/mutant/mutator/node/begin_spec.rb +0 -32
- data/spec/unit/mutant/mutator/node/binary_spec.rb +0 -41
- data/spec/unit/mutant/mutator/node/block_pass_spec.rb +0 -15
- data/spec/unit/mutant/mutator/node/block_spec.rb +0 -83
- data/spec/unit/mutant/mutator/node/blockarg_spec.rb +0 -17
- data/spec/unit/mutant/mutator/node/case_spec.rb +0 -329
- data/spec/unit/mutant/mutator/node/cbase_spec.rb +0 -15
- data/spec/unit/mutant/mutator/node/conditional_loop_spec.rb +0 -58
- data/spec/unit/mutant/mutator/node/const_spec.rb +0 -16
- data/spec/unit/mutant/mutator/node/define_spec.rb +0 -171
- data/spec/unit/mutant/mutator/node/defined_spec.rb +0 -14
- data/spec/unit/mutant/mutator/node/dstr_spec.rb +0 -17
- data/spec/unit/mutant/mutator/node/dsym_spec.rb +0 -18
- data/spec/unit/mutant/mutator/node/ensure_spec.rb +0 -16
- data/spec/unit/mutant/mutator/node/if_spec.rb +0 -77
- data/spec/unit/mutant/mutator/node/kwbegin_spec.rb +0 -16
- data/spec/unit/mutant/mutator/node/literal/array_spec.rb +0 -47
- data/spec/unit/mutant/mutator/node/literal/boolean_spec.rb +0 -25
- data/spec/unit/mutant/mutator/node/literal/fixnum_spec.rb +0 -13
- data/spec/unit/mutant/mutator/node/literal/float_spec.rb +0 -53
- data/spec/unit/mutant/mutator/node/literal/hash_spec.rb +0 -33
- data/spec/unit/mutant/mutator/node/literal/nil_spec.rb +0 -10
- data/spec/unit/mutant/mutator/node/literal/range_spec.rb +0 -56
- data/spec/unit/mutant/mutator/node/literal/regex_spec.rb +0 -36
- data/spec/unit/mutant/mutator/node/literal/string_spec.rb +0 -15
- data/spec/unit/mutant/mutator/node/literal/symbol_spec.rb +0 -15
- data/spec/unit/mutant/mutator/node/loop_ctrl_spec.rb +0 -37
- data/spec/unit/mutant/mutator/node/masgn_spec.rb +0 -14
- data/spec/unit/mutant/mutator/node/match_current_line_spec.rb +0 -21
- data/spec/unit/mutant/mutator/node/named_value/access_spec.rb +0 -78
- data/spec/unit/mutant/mutator/node/named_value/constant_assignment_spec.rb +0 -16
- data/spec/unit/mutant/mutator/node/named_value/variable_assignment_spec.rb +0 -61
- data/spec/unit/mutant/mutator/node/nthref_spec.rb +0 -19
- data/spec/unit/mutant/mutator/node/op_assgn_spec.rb +0 -22
- data/spec/unit/mutant/mutator/node/or_asgn_spec.rb +0 -19
- data/spec/unit/mutant/mutator/node/redo_spec.rb +0 -10
- data/spec/unit/mutant/mutator/node/rescue_spec.rb +0 -63
- data/spec/unit/mutant/mutator/node/restarg_spec.rb +0 -18
- data/spec/unit/mutant/mutator/node/return_spec.rb +0 -31
- data/spec/unit/mutant/mutator/node/send_spec.rb +0 -382
- data/spec/unit/mutant/mutator/node/super_spec.rb +0 -46
- data/spec/unit/mutant/mutator/node/yield_spec.rb +0 -17
@@ -36,7 +36,7 @@ module Mutant
|
|
36
36
|
# @api private
|
37
37
|
#
|
38
38
|
def pattern
|
39
|
-
/\A#{Regexp.escape(namespace
|
39
|
+
/\A#{Regexp.escape(namespace)}(?:::)?/
|
40
40
|
end
|
41
41
|
memoize :pattern
|
42
42
|
|
@@ -87,12 +87,11 @@ It raised an error: #{exception.inspect} fix your lib!
|
|
87
87
|
#
|
88
88
|
def emit_scope(scope)
|
89
89
|
name = self.class.scope_name(scope)
|
90
|
-
# FIXME: Fix nokogiri to return a string here
|
91
90
|
unless name.nil? or name.kind_of?(String)
|
92
91
|
$stderr.puts <<-MESSAGE
|
93
92
|
WARNING:
|
94
|
-
#{scope.class}#name did not return a
|
95
|
-
Fix your lib!
|
93
|
+
#{scope.class}#name from: #{scope.inspect} did not return a String or nil.
|
94
|
+
Fix your lib to support normal ruby semantics!
|
96
95
|
MESSAGE
|
97
96
|
return
|
98
97
|
end
|
data/lib/mutant/meta.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
module Mutant
|
2
|
+
# Namespace for mutant metadata
|
3
|
+
module Meta
|
4
|
+
|
5
|
+
require 'mutant/meta/example'
|
6
|
+
require 'mutant/meta/example/dsl'
|
7
|
+
|
8
|
+
# Mutation example
|
9
|
+
class Example
|
10
|
+
|
11
|
+
ALL = []
|
12
|
+
|
13
|
+
# Add example
|
14
|
+
#
|
15
|
+
# @return [undefined]
|
16
|
+
#
|
17
|
+
# @api private
|
18
|
+
#
|
19
|
+
def self.add(&block)
|
20
|
+
ALL << DSL.run(block)
|
21
|
+
end
|
22
|
+
|
23
|
+
Pathname.glob(Pathname.new(__FILE__).parent.parent.parent.join('meta', '**/*.rb'))
|
24
|
+
.sort
|
25
|
+
.each(&method(:require))
|
26
|
+
ALL.freeze
|
27
|
+
|
28
|
+
end # Example
|
29
|
+
|
30
|
+
end # Meta
|
31
|
+
end # Mutant
|
@@ -0,0 +1,152 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Mutant
|
4
|
+
module Meta
|
5
|
+
class Example
|
6
|
+
include Adamantium, Concord::Public.new(:node, :mutations)
|
7
|
+
|
8
|
+
# Return a verification instance
|
9
|
+
#
|
10
|
+
# @return [Verification]
|
11
|
+
#
|
12
|
+
# @api private
|
13
|
+
#
|
14
|
+
def verification
|
15
|
+
Verification.new(self, generated)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Return generated mutations
|
19
|
+
#
|
20
|
+
# @return [Emumerable<Parser::AST::Node>]
|
21
|
+
#
|
22
|
+
# @api private
|
23
|
+
#
|
24
|
+
def generated
|
25
|
+
Mutant::Mutator.each(node).to_a
|
26
|
+
end
|
27
|
+
memoize :generated
|
28
|
+
|
29
|
+
# Example verification
|
30
|
+
class Verification
|
31
|
+
include Adamantium::Flat, Concord.new(:example, :generated)
|
32
|
+
|
33
|
+
# Test if mutation was verified successfully
|
34
|
+
#
|
35
|
+
# @return [Boolean]
|
36
|
+
#
|
37
|
+
# @api private
|
38
|
+
#
|
39
|
+
def success?
|
40
|
+
unparser.success? && missing.empty? && unexpected.empty?
|
41
|
+
end
|
42
|
+
|
43
|
+
# Return error report
|
44
|
+
#
|
45
|
+
# @return [String]
|
46
|
+
#
|
47
|
+
# @api private
|
48
|
+
#
|
49
|
+
def error_report
|
50
|
+
unless unparser.success?
|
51
|
+
return unparser.report
|
52
|
+
end
|
53
|
+
mutation_report
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
# Return unexpected mutationso
|
59
|
+
#
|
60
|
+
# @return [Array<Parser::AST::Node>]
|
61
|
+
#
|
62
|
+
# @api private
|
63
|
+
#
|
64
|
+
def unexpected
|
65
|
+
generated - example.mutations
|
66
|
+
end
|
67
|
+
memoize :unexpected
|
68
|
+
|
69
|
+
# Return mutation report
|
70
|
+
#
|
71
|
+
# @return [String]
|
72
|
+
#
|
73
|
+
# @api private
|
74
|
+
#
|
75
|
+
def mutation_report
|
76
|
+
original_node = example.node
|
77
|
+
[
|
78
|
+
'Original-AST:',
|
79
|
+
original_node.inspect,
|
80
|
+
'Original-Source:',
|
81
|
+
Unparser.unparse(original_node),
|
82
|
+
*missing_report,
|
83
|
+
*unexpected_report
|
84
|
+
].join("\n======\n")
|
85
|
+
end
|
86
|
+
|
87
|
+
# Return missing report
|
88
|
+
#
|
89
|
+
# @return [Array, nil]
|
90
|
+
#
|
91
|
+
# @api private
|
92
|
+
#
|
93
|
+
def missing_report
|
94
|
+
if missing.any?
|
95
|
+
['Missing mutations:', missing.map(&method(:format_mutation)).join("\n-----\n")]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Return unexpected report
|
100
|
+
#
|
101
|
+
# @return [Array, nil]
|
102
|
+
#
|
103
|
+
# @api private
|
104
|
+
#
|
105
|
+
def unexpected_report
|
106
|
+
if unexpected.any?
|
107
|
+
[
|
108
|
+
'Unexpected mutations:',
|
109
|
+
unexpected.map(&method(:format_mutation)).join("\n-----\n")
|
110
|
+
]
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Format mutation
|
115
|
+
#
|
116
|
+
# @return [String]
|
117
|
+
#
|
118
|
+
# @api private
|
119
|
+
#
|
120
|
+
def format_mutation(node)
|
121
|
+
[
|
122
|
+
node.inspect,
|
123
|
+
Unparser.unparse(node)
|
124
|
+
].join("\n")
|
125
|
+
end
|
126
|
+
|
127
|
+
# Return missing mutationso
|
128
|
+
#
|
129
|
+
# @return [Array<Parser::AST::Node>]
|
130
|
+
#
|
131
|
+
# @api private
|
132
|
+
#
|
133
|
+
def missing
|
134
|
+
example.mutations - generated
|
135
|
+
end
|
136
|
+
memoize :missing
|
137
|
+
|
138
|
+
# Return unparser verifier
|
139
|
+
#
|
140
|
+
# @return [Unparser::CLI::Source]
|
141
|
+
#
|
142
|
+
# @api private
|
143
|
+
#
|
144
|
+
def unparser
|
145
|
+
Unparser::CLI::Source::Node.new(Unparser::Preprocessor.run(example.node))
|
146
|
+
end
|
147
|
+
memoize :unparser
|
148
|
+
|
149
|
+
end # Verification
|
150
|
+
end # Example
|
151
|
+
end # Meta
|
152
|
+
end # Mutant
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module Mutant
|
2
|
+
module Meta
|
3
|
+
class Example
|
4
|
+
|
5
|
+
# Example DSL
|
6
|
+
class DSL
|
7
|
+
include NodeHelpers
|
8
|
+
|
9
|
+
# Run DSL on block
|
10
|
+
#
|
11
|
+
# @return [Example]
|
12
|
+
#
|
13
|
+
# @api private
|
14
|
+
#
|
15
|
+
def self.run(block)
|
16
|
+
instance = new
|
17
|
+
instance.instance_eval(&block)
|
18
|
+
instance.example
|
19
|
+
end
|
20
|
+
|
21
|
+
# Initialize DSL context
|
22
|
+
#
|
23
|
+
# @return [undefined]
|
24
|
+
#
|
25
|
+
# @api private
|
26
|
+
#
|
27
|
+
def initialize
|
28
|
+
@source = nil
|
29
|
+
@expected = []
|
30
|
+
end
|
31
|
+
|
32
|
+
# Return example
|
33
|
+
#
|
34
|
+
# @return [Example]
|
35
|
+
#
|
36
|
+
# @raise [RuntimeError]
|
37
|
+
# in case example cannot be build
|
38
|
+
#
|
39
|
+
# @api private
|
40
|
+
#
|
41
|
+
def example
|
42
|
+
raise 'source not defined' unless @source
|
43
|
+
Example.new(@source, @expected)
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
# Set original source
|
49
|
+
#
|
50
|
+
# @param [String,Parser::AST::Node] input
|
51
|
+
#
|
52
|
+
# @return [self]
|
53
|
+
#
|
54
|
+
def source(input)
|
55
|
+
raise 'source already defined' if @source
|
56
|
+
@source = node(input)
|
57
|
+
|
58
|
+
self
|
59
|
+
end
|
60
|
+
|
61
|
+
# Add expected mutation
|
62
|
+
#
|
63
|
+
# @param [String,Parser::AST::Node] input
|
64
|
+
#
|
65
|
+
# @return [self]
|
66
|
+
#
|
67
|
+
def mutation(input)
|
68
|
+
node = node(input)
|
69
|
+
if @expected.include?(node)
|
70
|
+
raise "Node for input: #{input.inspect} is already expected"
|
71
|
+
end
|
72
|
+
@expected << node
|
73
|
+
|
74
|
+
self
|
75
|
+
end
|
76
|
+
|
77
|
+
# Helper method to coerce input to node
|
78
|
+
#
|
79
|
+
# @param [String,Parser::AST::Node] input
|
80
|
+
#
|
81
|
+
# @return [Parser::AST::Node]
|
82
|
+
#
|
83
|
+
# @raise [RuntimeError]
|
84
|
+
# in case input cannot be coerced
|
85
|
+
#
|
86
|
+
# @api private
|
87
|
+
#
|
88
|
+
def node(input)
|
89
|
+
node =
|
90
|
+
case input
|
91
|
+
when String
|
92
|
+
Parser::CurrentRuby.parse(input)
|
93
|
+
when Parser::AST::Node
|
94
|
+
input
|
95
|
+
else
|
96
|
+
raise "Cannot coerce to node: #{source.inspect}"
|
97
|
+
end
|
98
|
+
|
99
|
+
Unparser::Preprocessor.run(node)
|
100
|
+
end
|
101
|
+
|
102
|
+
end # DSL
|
103
|
+
end # Example
|
104
|
+
end # Meta
|
105
|
+
end # Mutant
|
data/lib/mutant/mutator/node.rb
CHANGED
@@ -96,7 +96,7 @@ module Mutant
|
|
96
96
|
#
|
97
97
|
def emit_children_mutations
|
98
98
|
Mutator::Util::Array.each(children, self) do |children|
|
99
|
-
|
99
|
+
emit_type(*children)
|
100
100
|
end
|
101
101
|
end
|
102
102
|
|
@@ -136,7 +136,7 @@ module Mutant
|
|
136
136
|
def delete_child(index)
|
137
137
|
dup_children = children.dup
|
138
138
|
dup_children.delete_at(index)
|
139
|
-
|
139
|
+
emit_type(*dup_children)
|
140
140
|
end
|
141
141
|
|
142
142
|
# Emit updated child
|
@@ -151,7 +151,7 @@ module Mutant
|
|
151
151
|
def emit_child_update(index, node)
|
152
152
|
new_children = children.dup
|
153
153
|
new_children[index] = node
|
154
|
-
|
154
|
+
emit_type(*new_children)
|
155
155
|
end
|
156
156
|
|
157
157
|
# Emit a new AST node with same class as wrapped node
|
@@ -162,7 +162,7 @@ module Mutant
|
|
162
162
|
#
|
163
163
|
# @api private
|
164
164
|
#
|
165
|
-
def
|
165
|
+
def emit_type(*children)
|
166
166
|
emit(new_self(*children))
|
167
167
|
end
|
168
168
|
|
@@ -196,7 +196,7 @@ module Mutant
|
|
196
196
|
#
|
197
197
|
def emit_values(values)
|
198
198
|
values.each do |value|
|
199
|
-
|
199
|
+
emit_type(value)
|
200
200
|
end
|
201
201
|
end
|
202
202
|
|
@@ -221,7 +221,7 @@ module Mutant
|
|
221
221
|
# @api private
|
222
222
|
#
|
223
223
|
def asgn_left?
|
224
|
-
OP_ASSIGN.include?(parent_type) && parent.
|
224
|
+
OP_ASSIGN.include?(parent_type) && parent.node.children.first.equal?(node)
|
225
225
|
end
|
226
226
|
|
227
227
|
end # Node
|
@@ -19,7 +19,7 @@ module Mutant
|
|
19
19
|
#
|
20
20
|
def dispatch
|
21
21
|
emit_nil unless parent_type == :const
|
22
|
-
|
22
|
+
emit_type(nil, *children.drop(1))
|
23
23
|
children.each_with_index do |child, index|
|
24
24
|
mutate_child(index) if child.kind_of?(Parser::AST::Node)
|
25
25
|
end
|
@@ -33,9 +33,9 @@ module Mutant
|
|
33
33
|
#
|
34
34
|
def mutate_condition
|
35
35
|
emit_condition_mutations
|
36
|
-
|
37
|
-
|
38
|
-
|
36
|
+
emit_type(n_not(condition), if_branch, else_branch) unless condition.type == :match_current_line
|
37
|
+
emit_type(N_TRUE, if_branch, else_branch)
|
38
|
+
emit_type(N_FALSE, if_branch, else_branch)
|
39
39
|
end
|
40
40
|
|
41
41
|
# Emit if branch mutations
|
@@ -45,10 +45,10 @@ module Mutant
|
|
45
45
|
# @api private
|
46
46
|
#
|
47
47
|
def mutate_if_branch
|
48
|
-
|
48
|
+
emit_type(condition, else_branch, nil) if else_branch
|
49
49
|
if if_branch
|
50
50
|
emit_if_branch_mutations
|
51
|
-
|
51
|
+
emit_type(condition, if_branch, nil)
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
@@ -61,7 +61,7 @@ module Mutant
|
|
61
61
|
def mutate_else_branch
|
62
62
|
if else_branch
|
63
63
|
emit_else_branch_mutations
|
64
|
-
|
64
|
+
emit_type(condition, nil, else_branch)
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
@@ -19,7 +19,7 @@ module Mutant
|
|
19
19
|
#
|
20
20
|
def dispatch
|
21
21
|
emit_nil
|
22
|
-
|
22
|
+
emit_type
|
23
23
|
mutate_body
|
24
24
|
if children.one?
|
25
25
|
emit(children.first)
|
@@ -36,7 +36,7 @@ module Mutant
|
|
36
36
|
children.each_index do |index|
|
37
37
|
dup_children = children.dup
|
38
38
|
dup_children.delete_at(index)
|
39
|
-
|
39
|
+
emit_type(*dup_children)
|
40
40
|
mutate_child(index)
|
41
41
|
end
|
42
42
|
end
|