Fix specification holes in Mutant::AST::Meta
* Reduce implementation * Fix assumption node specific classes under AST::Meta all have #assignable? method. They are intentionalyl not LSP compatible and do NOT share the class hierarchy for that reason. * Inline APIs with one call side
This commit is contained in:
parent
f066de6778
commit
04a4a44e18
7 changed files with 62 additions and 99 deletions
|
@ -1,3 +1,3 @@
|
|||
---
|
||||
threshold: 18
|
||||
total_score: 1215
|
||||
total_score: 1208
|
||||
|
|
|
@ -3,53 +3,16 @@ module Mutant
|
|||
# Node meta information mixin
|
||||
module Meta
|
||||
|
||||
REGISTRY = {}
|
||||
|
||||
# Return meta for node
|
||||
#
|
||||
# @param [Parser::AST::Node] node
|
||||
#
|
||||
# @return [Meta]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def self.for(node)
|
||||
REGISTRY.fetch(node.type, Generic).new(node)
|
||||
end
|
||||
|
||||
# Mixin to define meta nodes
|
||||
class Node < Module
|
||||
include Concord.new(:type)
|
||||
|
||||
CONCORD = Concord.new(:node)
|
||||
|
||||
# Hook called when module gets included
|
||||
#
|
||||
# @param [Class, Module] host
|
||||
#
|
||||
# @return [undefined]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def included(host)
|
||||
REGISTRY[type] = host
|
||||
host.class_eval do
|
||||
include CONCORD, NamedChildren
|
||||
end
|
||||
end
|
||||
|
||||
end # Node
|
||||
|
||||
# Metadata for resbody nods
|
||||
class Resbody
|
||||
include Node.new(:resbody)
|
||||
include NamedChildren, Concord.new(:node)
|
||||
|
||||
children :captures, :assignment, :body
|
||||
end # Resbody
|
||||
|
||||
# Metadata for send nodes
|
||||
class Send
|
||||
include Node.new(:send)
|
||||
include NamedChildren, Concord.new(:node)
|
||||
|
||||
children :receiver, :selector
|
||||
|
||||
|
@ -81,7 +44,8 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def attribute_assignment?
|
||||
arguments.one? && attribute_assignment_selector?
|
||||
!Types::METHOD_OPERATORS.include?(selector) &&
|
||||
selector.to_s.end_with?(ATTRIBUTE_ASSIGNMENT_SELECTOR_SUFFIX)
|
||||
end
|
||||
|
||||
# Test if AST node is an index assign
|
||||
|
@ -91,7 +55,7 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def index_assignment?
|
||||
arguments.length.equal?(2) && index_assignment_selector?
|
||||
selector.equal?(INDEX_ASSIGNMENT_SELECTOR)
|
||||
end
|
||||
|
||||
# Test for binary operator implemented as method
|
||||
|
@ -101,49 +65,10 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def binary_method_operator?
|
||||
arguments.one? && Types::BINARY_METHOD_OPERATORS.include?(selector)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Test for index assignment operator
|
||||
#
|
||||
# @return [Boolean]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def index_assignment_selector?
|
||||
selector.equal?(INDEX_ASSIGNMENT_SELECTOR)
|
||||
end
|
||||
|
||||
# Test for attribute assignment selector
|
||||
#
|
||||
# @return [Boolean]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def attribute_assignment_selector?
|
||||
!Types::METHOD_OPERATORS.include?(selector) && selector.to_s.end_with?(ATTRIBUTE_ASSIGNMENT_SELECTOR_SUFFIX)
|
||||
Types::BINARY_METHOD_OPERATORS.include?(selector)
|
||||
end
|
||||
|
||||
end # Send
|
||||
|
||||
# Generic node metadata
|
||||
class Generic
|
||||
include Adamantium, Concord.new(:node)
|
||||
|
||||
# Test if AST node is a valid assign target
|
||||
#
|
||||
# @return [Boolean]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def assignment?
|
||||
Types::ASSIGNABLE_VARIABLES.include?(node.type)
|
||||
end
|
||||
|
||||
end # Generic
|
||||
|
||||
end #
|
||||
end # Meta
|
||||
end # AST
|
||||
end # Mutant
|
||||
|
|
|
@ -51,17 +51,6 @@ module Mutant
|
|||
#
|
||||
alias_method :dup_node, :dup_input
|
||||
|
||||
# Return ast meta description
|
||||
#
|
||||
# @return [AST::Meta]
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
def meta
|
||||
AST::Meta.for(node)
|
||||
end
|
||||
memoize :meta
|
||||
|
||||
# Return children
|
||||
#
|
||||
# @return [Array<Parser::AST::Node>]
|
||||
|
|
|
@ -22,7 +22,7 @@ module Mutant
|
|||
emit_right_mutations
|
||||
return if n_ivasgn?(left)
|
||||
emit_left_mutations do |node|
|
||||
AST::Meta.for(node).assignment?
|
||||
AST::Types::ASSIGNABLE_VARIABLES.include?(node.type)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ module Mutant
|
|||
rescue_body = children.at(index)
|
||||
next unless rescue_body
|
||||
mutate_child(index)
|
||||
resbody_body = AST::Meta.for(rescue_body).body
|
||||
resbody_body = AST::Meta::Resbody.new(rescue_body).body
|
||||
emit_concat(resbody_body) if resbody_body
|
||||
end
|
||||
end
|
||||
|
|
|
@ -55,16 +55,23 @@ module Mutant
|
|||
# @api private
|
||||
#
|
||||
def non_index_dispatch
|
||||
case
|
||||
when meta.binary_method_operator?
|
||||
if meta.binary_method_operator?
|
||||
run(Binary)
|
||||
when meta.attribute_assignment?
|
||||
elsif meta.attribute_assignment?
|
||||
run(AttributeAssignment)
|
||||
else
|
||||
normal_dispatch
|
||||
end
|
||||
end
|
||||
|
||||
# Return AST metadata for node
|
||||
#
|
||||
# @return [AST::Meta::Send]
|
||||
def meta
|
||||
AST::Meta::Send.new(node)
|
||||
end
|
||||
memoize :meta
|
||||
|
||||
# Return arguments
|
||||
#
|
||||
# @return [Enumerable<Parser::AST::Node>]
|
||||
|
|
42
spec/unit/mutant/ast/meta/send_spec.rb
Normal file
42
spec/unit/mutant/ast/meta/send_spec.rb
Normal file
|
@ -0,0 +1,42 @@
|
|||
RSpec.describe Mutant::AST::Meta::Send do
|
||||
let(:object) { described_class.new(node) }
|
||||
|
||||
def parse(source)
|
||||
Parser::CurrentRuby.parse(source)
|
||||
end
|
||||
|
||||
let(:method_call) { parse('foo.bar(baz)') }
|
||||
let(:attribute_read) { parse('foo.bar') }
|
||||
let(:index_assignment) { parse('foo[0] = bar') }
|
||||
let(:attribute_assignment) { parse('foo.bar = baz') }
|
||||
let(:binary_method_operator) { parse('foo == bar') }
|
||||
|
||||
class Expectation
|
||||
include Adamantium, Anima.new(:name, :assignment, :attribute_assignment, :index_assignment, :binary_method_operator)
|
||||
|
||||
ALL = [
|
||||
[:method_call, false, false, false, false],
|
||||
[:attribute_read, false, false, false, false],
|
||||
[:index_assignment, true, false, true, false],
|
||||
[:attribute_assignment, true, true, false, false],
|
||||
[:binary_method_operator, false, false, false, true]
|
||||
].map do |values|
|
||||
new(Hash[anima.attribute_names.zip(values)])
|
||||
end.freeze
|
||||
end
|
||||
|
||||
# Rspec should have a build in for this kind of "n-dimensional assertion with context"
|
||||
(Expectation.anima.attribute_names - %i[name]).each do |name|
|
||||
describe "##{name}?" do
|
||||
subject { object.public_send(:"#{name}?") }
|
||||
|
||||
Expectation::ALL.each do |expectation|
|
||||
context "on #{expectation.name}" do
|
||||
let(:node) { public_send(expectation.name) }
|
||||
|
||||
it { should be(expectation.public_send(name)) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Add table
Reference in a new issue