free_mutant/lib/mutant/mutator/node/send.rb
2013-01-03 19:09:43 +01:00

269 lines
6.1 KiB
Ruby

module Mutant
class Mutator
class Node
# Class for mutations where messages are send to objects
class Send < self
handle(Rubinius::AST::Send)
# Test if node corresponds to "self.class"
#
# @param [Rubinius::AST::Node] node
#
# @return [true]
# if node equals to self.class
#
# @return [false]
# otherwise
#
# @api private
#
def self.keyword_name?(node)
node.kind_of?(Rubinius::AST::Send) &&
Mutant::KEYWORDS.include?(node.name) &&
node.receiver.kind_of?(Rubinius::AST::Self)
end
private
# Emit mutations
#
# @return [undefined]
#
# @api private
#
def dispatch
emit_receiver
emit_implicit_self_receiver
emit_receiver_mutations
emit_block_mutations
emit_block_absence_mutation
end
# Emit receiver
#
# @return [undefined]
#
# @api private
#
def emit_receiver
unless to_self?
emit(receiver)
end
end
# Emit block mutations
#
# @return [undefined]
#
# @api private
#
def emit_block_mutations
if node.block
emit_attribute_mutations(:block)
end
end
# Emit receiver mutations
#
# @return [undefined]
#
# @api private
#
def emit_receiver_mutations
util = self.class
unless to_self? or util.keyword_name?(receiver)
emit_attribute_mutations(:receiver)
end
end
# Emit block absence mutation
#
# @return [undefined]
#
# @api private
#
def emit_block_absence_mutation
dup = dup_node
dup.block = nil
emit(dup)
end
# Return receiver AST node
#
# @return [Rubinius::AST::Node]
#
# @api private
#
def receiver
node.receiver
end
# Return name of call
#
# @return [Symbol]
#
# @api private
#
def name
node.name
end
# Check if receiver is self
#
# @return [true]
# if receiver is a Rubinius::AST::Self node
#
# @return [false]
# return false otherwise
#
# @api private
#
def to_self?
receiver.kind_of?(Rubinius::AST::Self)
end
# Emit mutation that replaces explicit send to self with implicit send to self
#
# @example:
#
# # This class does use Foo#a with explicitly specifing the receiver self.
# # But an implicit (privately) call should be used as there is no need for
# # specifing en explicit receiver.
#
# class Foo # Mutation
# def print_a # def print_a
# puts self.a # puts a
# end # end
#
# def a
# :bar
# end
# end
#
# There will not be any exception so the mutant is not killed and such calls where
# implicit receiver should be used will be spotted.
#
# @return [undefined]
#
# @api private
#
def emit_implicit_self_receiver
return unless to_self?
return if self.class.keyword_name?(node)
mutant = dup_node
mutant.privately = true
# TODO: Fix rubinius to allow this as an attr_accessor
mutant.instance_variable_set(:@vcall_style, true)
emit(mutant)
end
class SendWithArguments < self
handle(Rubinius::AST::SendWithArguments)
class BinaryOperatorMethod < Node
private
# Emit mutations
#
# @return [undefined]
#
# @api private
#
def dispatch
emit_left_mutations
emit_right_mutations
end
# Emit left mutations
#
# @return [undefined]
#
# @api private
#
def emit_left_mutations
emit_attribute_mutations(:receiver)
end
# Emit right mutations
#
# @return [undefined]
#
# @api private
#
def emit_right_mutations
right = node.arguments.array.first
Mutator.each(right).each do |mutated|
dup = dup_node
dup.arguments.array[0] = mutated
emit(dup)
end
end
end
private
# Emit mutations
#
# @return [undefined]
#
# @api private
#
def dispatch
super
emit_call_remove_mutation
emit_argument_mutations
end
# Test if message is a binary operator
#
# @return [true]
# if message is a binary operator
#
# @return [false]
# otherwise
#
# @api private
#
def binary_operator?
Mutant::BINARY_METHOD_OPERATORS.include?(node.name)
end
# Emit argument mutations
#
# @api private
#
# @return [undefined]
#
# @api private
#
def emit_argument_mutations
if binary_operator?
run(BinaryOperatorMethod)
return
end
emit_attribute_mutations(:arguments)
end
# Emit transfomr call mutation
#
# @return [undefined]
#
# @api private
#
def emit_call_remove_mutation
array = node.arguments.array
return unless array.length == 1
emit(array.first)
end
end
end
end
end
end