Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# unreleased

* Fix a crash on blocks using the Ruby 3.4 `it` parameter. Mutating a block
without parameters whose body holds an `it` block, such as
`foo { bar { it } }`, raised `KeyError: key not found: :itblock`. `it` blocks
also gain the mutation to `nil`, as numbered parameter blocks have:
`items.map { it.price }` now mutates to `nil` too.

* Leave the log of each killed mutation out of the session file. It holds the
output of the tests that killed the mutation, which the session subcommands
never show, and on a suite whose failures print large values, such as the
Expand Down
1 change: 1 addition & 0 deletions ruby/lib/mutant.rb
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ module Mutant
require 'mutant/mutator/node/procarg_zero'
require 'mutant/mutator/node/kwargs'
require 'mutant/mutator/node/numblock'
require 'mutant/mutator/node/itblock' if RUBY_VERSION > '3.4.0'
require 'mutant/mutator/node/const_pattern'
require 'mutant/mutator/regexp'
require 'mutant/loader'
Expand Down
11 changes: 11 additions & 0 deletions ruby/lib/mutant/ast/structure.rb
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,17 @@ def variable_descendants(node)
),
variable: nil
),
Node.new(
type: :itblock,
fixed: Node.fixed(
[
[Node::Fixed::Descendant, :receiver],
[Node::Fixed::Attribute, :parameter],
[Node::Fixed::Descendant, :body]
]
),
variable: nil
),
Node.new(
type: :ivar,
fixed: Node.fixed([[Node::Fixed::Attribute, :name]]),
Expand Down
36 changes: 36 additions & 0 deletions ruby/lib/mutant/mutator/node/itblock.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# frozen_string_literal: true

module Mutant
class Mutator
class Node
class Itblock < self

handle(:itblock)

children :receiver, :parameter, :body

private

def dispatch
emit_nil
emit_receiver_mutations(&method(:n_send?))
emit_body_mutations(&method(:references_it?))
end

def references_it?(node)
return true if node.eql?(s(:lvar, :it))

children = owns_it?(node) ? [node.children.first] : node.children

children.any? do |child|
child.instance_of?(::Parser::AST::Node) && references_it?(child)
end
end

def owns_it?(node)
n_block?(node) || n_itblock?(node) || n_def?(node) || n_defs?(node)
end
end # Itblock
end # Node
end # Mutator
end # Mutant
13 changes: 13 additions & 0 deletions ruby/meta/block.rb
Original file line number Diff line number Diff line change
Expand Up @@ -251,3 +251,16 @@
mutation 'obj&.foo { |x| x }'
mutation 'obj&.foo { |x| self.bar }'
end

if RUBY_VERSION > '3.4.0'
Mutant::Meta::Example.add :block do
source 'foo { bar { it } }'

singleton_mutations
mutation 'foo'
mutation 'foo {}'
mutation 'foo { raise }'
mutation 'foo { nil }'
mutation 'bar { it }'
end
end
81 changes: 81 additions & 0 deletions ruby/meta/itblock.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# frozen_string_literal: true

if RUBY_VERSION > '3.4.0'
Mutant::Meta::Example.add :itblock do
source 'foo(false) { it.to_s }'

singleton_mutations
mutation 'foo(true) { it.to_s }'
mutation 'foo { it.to_s }'
mutation 'foo(false) { it }'
mutation 'foo(false) { it.to_str }'
end

Mutant::Meta::Example.add :itblock do
source 'foo { it.bar(1) { it } }'

singleton_mutations
mutation 'foo { it.bar { it } }'
mutation 'foo { it.bar(nil) { it } }'
mutation 'foo { it.bar(0) { it } }'
mutation 'foo { it.bar(2) { it } }'
mutation 'foo { it.bar(167) { it } }'
end

Mutant::Meta::Example.add :itblock do
source 'foo { it.bar { |it| it } }'

singleton_mutations
mutation 'foo { it.bar }'
mutation 'foo { it }'
mutation 'foo { it.bar { |it| } }'
mutation 'foo { it.bar { |it| raise } }'
mutation 'foo { it.bar { |it| nil } }'
end

Mutant::Meta::Example.add :itblock do
source 'foo { it; def bar(it) = it }'

singleton_mutations
mutation 'foo { it }'
mutation 'foo { it; def bar(it); end }'
mutation 'foo { it; def bar(it); raise; end }'
mutation 'foo { it; def bar(it); super; end }'
mutation 'foo { it; def bar(it); nil; end }'
end

Mutant::Meta::Example.add :itblock do
source 'foo { it; def self.bar(it) = it }'

singleton_mutations
mutation 'foo { it }'
mutation 'foo { it; def self.bar(it); end }'
mutation 'foo { it; def self.bar(it); raise; end }'
mutation 'foo { it; def self.bar(it); super; end }'
mutation 'foo { it; def self.bar(it); nil; end }'
end

Mutant::Meta::Example.add :itblock do
source 'foo { def it.bar = 1 }'

singleton_mutations
mutation 'foo { def it.bar; end }'
mutation 'foo { def it.bar; raise; end }'
mutation 'foo { def it.bar; super; end }'
mutation 'foo { def it.bar; nil; end }'
mutation 'foo { def it.bar; 0; end }'
mutation 'foo { def it.bar; 2; end }'
mutation 'foo { def it.bar; 167; end }'
end

Mutant::Meta::Example.add :itblock do
source 'foo { bar(it, 1) }'

singleton_mutations
mutation 'foo { bar(it) }'
mutation 'foo { bar(it, nil) }'
mutation 'foo { bar(it, 0) }'
mutation 'foo { bar(it, 2) }'
mutation 'foo { bar(it, 167) }'
end
end
Loading