Fix generation of invalid ast via parent tracking

This commit is contained in:
Markus Schirp 2013-06-24 22:10:40 +02:00
parent 1559d7ef21
commit 2d0c57ba07
10 changed files with 88 additions and 36 deletions

View file

@ -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 = [

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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