Fix generation of invalid ast via parent tracking
This commit is contained in:
parent
1559d7ef21
commit
2d0c57ba07
10 changed files with 88 additions and 36 deletions
|
@ -6,7 +6,7 @@ module Mutant
|
|||
'!' => '_bang'
|
||||
}.freeze
|
||||
|
||||
BINARY_METHOD_OPERATOR_EXPANSIONS = {
|
||||
OPERATOR_EXPANSIONS = {
|
||||
:<=> => :spaceship_operator,
|
||||
:=== => :case_equality_operator,
|
||||
:[]= => :element_writer,
|
||||
|
@ -29,19 +29,20 @@ module Mutant
|
|||
:< => :less_than_operator,
|
||||
:> => :greater_than_operator,
|
||||
:+ => :addition_operator,
|
||||
:- => :substraction_operator
|
||||
}.freeze
|
||||
|
||||
UNARY_METHOD_OPERATOR_EXPANSIONS = {
|
||||
:- => :substraction_operator,
|
||||
:~@ => :unary_match_operator,
|
||||
:+@ => :unary_addition_operator,
|
||||
:-@ => :unary_substraction_operator,
|
||||
:'!' => :negation_operator
|
||||
}.freeze
|
||||
|
||||
BINARY_METHOD_OPERATORS = BINARY_METHOD_OPERATOR_EXPANSIONS.keys.to_set.freeze
|
||||
INDEX_OPERATORS = [ :[], :[]= ].freeze
|
||||
|
||||
OPERATOR_EXPANSIONS = BINARY_METHOD_OPERATOR_EXPANSIONS.merge(UNARY_METHOD_OPERATOR_EXPANSIONS).freeze
|
||||
UNARY_METHOD_OPERATORS = [
|
||||
:~@, :+@, :-@, :'!'
|
||||
].freeze
|
||||
|
||||
BINARY_METHOD_OPERATORS = (OPERATOR_EXPANSIONS.keys - (INDEX_OPERATORS + UNARY_METHOD_OPERATORS)).to_set.freeze
|
||||
|
||||
# Hopefully all types parser does generate
|
||||
NODE_TYPES = [
|
||||
|
|
|
@ -12,9 +12,9 @@ module Mutant
|
|||
#
|
||||
# @api private
|
||||
#
|
||||
def self.each(node, &block)
|
||||
return to_enum(__method__, node) unless block_given?
|
||||
Registry.lookup(node).new(node, block)
|
||||
def self.each(node, parent=nil, &block)
|
||||
return to_enum(__method__, node, parent) unless block_given?
|
||||
Registry.lookup(node).new(node, parent, block)
|
||||
|
||||
self
|
||||
end
|
||||
|
@ -52,19 +52,28 @@ module Mutant
|
|||
#
|
||||
attr_reader :input
|
||||
|
||||
# Return input
|
||||
#
|
||||
# @return [Object]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
attr_reader :parent
|
||||
|
||||
private
|
||||
|
||||
# Initialize object
|
||||
#
|
||||
# @param [Object] input
|
||||
# @param [Object] parent
|
||||
# @param [#call(node)] block
|
||||
#
|
||||
# @return [undefined]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def initialize(input, block)
|
||||
@input, @block = IceNine.deep_freeze(input), block
|
||||
def initialize(input, parent, block)
|
||||
@input, @parent, @block = input, parent, block
|
||||
@seen = Set.new
|
||||
guard(input)
|
||||
dispatch
|
||||
|
@ -173,7 +182,7 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def run(mutator)
|
||||
mutator.new(input, method(:emit))
|
||||
mutator.new(input, self, method(:emit))
|
||||
end
|
||||
|
||||
# Shortcut to create a new unfrozen duplicate of input
|
||||
|
|
|
@ -99,7 +99,7 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def emit_children_mutations
|
||||
Mutator::Util::Array.each(children) do |children|
|
||||
Mutator::Util::Array.each(children, self) do |children|
|
||||
emit_self(*children)
|
||||
end
|
||||
end
|
||||
|
@ -125,7 +125,7 @@ module Mutant
|
|||
def mutate_child(index, mutator = Mutator)
|
||||
children = node.children
|
||||
child = children[index]
|
||||
mutator.each(child) do |mutation|
|
||||
mutator.each(child, self) do |mutation|
|
||||
emit_child_update(index, mutation)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -30,7 +30,7 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def emit_name_mutation
|
||||
Mutator::Util::Symbol.each(name) do |name|
|
||||
Mutator::Util::Symbol.each(name, self) do |name|
|
||||
emit_name(name)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -39,7 +39,7 @@ module Mutant
|
|||
#
|
||||
def mutate_name
|
||||
prefix = MAP.fetch(node.type)
|
||||
Mutator::Util::Symbol.each(name) do |name|
|
||||
Mutator::Util::Symbol.each(name, self) do |name|
|
||||
emit_name("#{prefix}#{name}")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -16,7 +16,7 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def dispatch
|
||||
Util::Array.each(children) do |children|
|
||||
Util::Array.each(children, self) do |children|
|
||||
if children.length > 1
|
||||
emit_self(*children)
|
||||
end
|
||||
|
@ -24,7 +24,21 @@ module Mutant
|
|||
children.each do |child|
|
||||
emit(child)
|
||||
end
|
||||
emit(nil)
|
||||
emit(nil) unless parent_send?
|
||||
end
|
||||
|
||||
# Test if parent input is a send
|
||||
#
|
||||
# @return [true]
|
||||
# if parent input is a send node
|
||||
#
|
||||
# @return [false]
|
||||
# otherwise
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def parent_send?
|
||||
parent && parent.input.type == :send
|
||||
end
|
||||
|
||||
end # Block
|
||||
|
|
|
@ -6,6 +6,7 @@ module Mutant
|
|||
# Run ulitity mutator
|
||||
#
|
||||
# @param [Object] object
|
||||
# @param [Object] parent
|
||||
#
|
||||
# @return [Enumerator<Object>]
|
||||
# if no block given
|
||||
|
@ -15,10 +16,10 @@ module Mutant
|
|||
#
|
||||
# @api private
|
||||
#
|
||||
def self.each(object, &block)
|
||||
return to_enum(__method__, object) unless block_given?
|
||||
def self.each(object, parent, &block)
|
||||
return to_enum(__method__, object, parent) unless block_given?
|
||||
|
||||
new(object, block)
|
||||
new(object, parent, block)
|
||||
|
||||
self
|
||||
end
|
||||
|
|
|
@ -15,9 +15,10 @@ describe Mutant::Mutator, '#emit_new' do
|
|||
end
|
||||
end
|
||||
|
||||
let(:object) { class_under_test.new(input, block) }
|
||||
let(:block) { Block.new }
|
||||
let(:input) { :input }
|
||||
let(:object) { class_under_test.new(input, parent, block) }
|
||||
let(:block) { Block.new }
|
||||
let(:input) { :input }
|
||||
let(:parent) { :parent }
|
||||
|
||||
let(:class_under_test) do
|
||||
Class.new(described_class) do
|
||||
|
|
|
@ -15,9 +15,10 @@ describe Mutant::Mutator, '#emit' do
|
|||
end
|
||||
end
|
||||
|
||||
let(:object) { class_under_test.new(input, block) }
|
||||
let(:block) { Block.new }
|
||||
let(:input) { :nput }
|
||||
let(:object) { class_under_test.new(input, parent, block) }
|
||||
let(:block) { Block.new }
|
||||
let(:input) { :input }
|
||||
let(:parent) { :parent }
|
||||
|
||||
let(:class_under_test) do
|
||||
Class.new(described_class) do
|
||||
|
|
|
@ -168,21 +168,46 @@ describe Mutant::Mutator, 'send' do
|
|||
end
|
||||
|
||||
context 'binary operator methods' do
|
||||
Mutant::BINARY_METHOD_OPERATORS.each do |operator|
|
||||
let(:source) { "true #{operator} false" }
|
||||
context 'nested' do
|
||||
let(:source) { '(left - right) / foo' }
|
||||
|
||||
let(:mutations) do
|
||||
mutations = []
|
||||
mutations << "false #{operator} false"
|
||||
mutations << "nil #{operator} false"
|
||||
mutations << "true #{operator} true"
|
||||
mutations << "true #{operator} nil"
|
||||
mutations << 'true'
|
||||
mutations << 'false'
|
||||
mutations << 'foo'
|
||||
mutations << 'left - right'
|
||||
end
|
||||
|
||||
it_should_behave_like 'a mutator'
|
||||
end
|
||||
|
||||
Mutant::BINARY_METHOD_OPERATORS.each do |operator|
|
||||
context 'on literal scalar arguments' do
|
||||
let(:source) { "true #{operator} false" }
|
||||
|
||||
let(:mutations) do
|
||||
mutations = []
|
||||
mutations << "false #{operator} false"
|
||||
mutations << "nil #{operator} false"
|
||||
mutations << "true #{operator} true"
|
||||
mutations << "true #{operator} nil"
|
||||
mutations << 'true'
|
||||
mutations << 'false'
|
||||
end
|
||||
|
||||
it_should_behave_like 'a mutator'
|
||||
end
|
||||
|
||||
context 'on non literal scalar arguments' do
|
||||
let(:source) { "left #{operator} right" }
|
||||
let(:mutations) do
|
||||
mutations = []
|
||||
mutations << 'left'
|
||||
mutations << 'right'
|
||||
end
|
||||
|
||||
it_should_behave_like 'a mutator'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue