Skip to content

Commit

Permalink
Upgrade to unparser ~> 0.5.2
Browse files Browse the repository at this point in the history
* Remove all hacks for unparser normalization
* Adjust verification to enforce the better unparser constraints
* Add 2.7 syntax support
  • Loading branch information
mbj committed Oct 16, 2020
1 parent e823048 commit 2b896a0
Show file tree
Hide file tree
Showing 33 changed files with 394 additions and 284 deletions.
4 changes: 4 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# v0.9.14 2020-10-16

* Add 2.7 syntax support. [#1062](https://github.com/mbj/mutant/pull/1062).

# v0.9.13 2020-10-07

* Improve isolation error reporting [#1055](https://github.com/mbj/mutant/pull/1055).
Expand Down
6 changes: 3 additions & 3 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
mutant (0.9.13)
mutant (0.9.14)
abstract_type (~> 0.0.7)
adamantium (~> 0.2.0)
anima (~> 0.3.1)
Expand All @@ -14,7 +14,7 @@ PATH
mprelude (~> 0.1.0)
parser (~> 2.7.1)
procto (~> 0.0.2)
unparser (~> 0.4.8)
unparser (~> 0.5.2)
variable (~> 0.0.1)

GEM
Expand Down Expand Up @@ -78,7 +78,7 @@ GEM
ruby-progressbar (1.10.1)
thread_safe (0.3.6)
unicode-display_width (1.6.1)
unparser (0.4.9)
unparser (0.5.2)
abstract_type (~> 0.0.7)
adamantium (~> 0.2.0)
anima (~> 0.3.1)
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ Supported indicates if a specific Ruby version / Implementation is actively supp
| -------------- | -------------- | ------- | ------------------ | ------------------ | ------------------ |
| cRUBY/MRI | 2.5 | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| cRUBY/MRI | 2.6 | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| cRUBY/MRI | 2.7 | :heavy_check_mark: | :soon: | :soon: | :heavy_check_mark: |
| cRUBY/MRI | 2.7 | :heavy_check_mark: | :heavy_check_mark: | :soon: | :heavy_check_mark: |
| jruby | TBD | :email: | :email: | :email: | :email: |
| mruby | TBD | :email: | :email: | :email: | :email: |
| cRUBY/MRI | < 2.5 | :no_entry: | :no_entry: | :no_entry: | :no_entry: |
Expand Down
3 changes: 1 addition & 2 deletions lib/mutant.rb
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,7 @@ module Mutant
require 'mutant/mutator/node/begin'
require 'mutant/mutator/node/binary'
require 'mutant/mutator/node/const'
require 'mutant/mutator/node/dstr'
require 'mutant/mutator/node/dsym'
require 'mutant/mutator/node/dynamic_literal'
require 'mutant/mutator/node/kwbegin'
require 'mutant/mutator/node/named_value/access'
require 'mutant/mutator/node/named_value/constant_assignment'
Expand Down
14 changes: 13 additions & 1 deletion lib/mutant/meta/example.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,19 @@
module Mutant
module Meta
class Example
include Adamantium, Anima.new(:file, :original_source, :node, :types, :expected)
include Adamantium
include Anima.new(
:expected,
:file,
:lvars,
:node,
:original_source,
:types
)

class Expected
include Anima.new(:original_source, :node)
end

# Verification instance for example
#
Expand Down
46 changes: 25 additions & 21 deletions lib/mutant/meta/example/dsl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,11 @@ def self.call(file, types, block)
#
# @return [undefined]
def initialize(file, types)
@expected = []
@file = file
@types = types
@lvars = []
@source = nil
@expected = []
@types = types
end

# Example captured by DSL
Expand All @@ -42,37 +43,37 @@ def example
Example.new(
expected: @expected,
file: @file,
lvars: @lvars,
node: @node,
original_source: @source,
types: @types
)
end

# Declare a local variable
#
# @param [Symbol]
def declare_lvar(name)
@lvars << name
end

private

# rubocop:disable Metrics/MethodLength
def source(input)
fail 'source already defined' if @source

case input
when String
@source = input
@node = Unparser::Preprocessor.run(Unparser.parse(input))
when ::Parser::AST::Node
@source = Unparser.unparse(input)
@node = input
else
fail "Unsupported input: #{input.inspect}"
end
@source = input
@node = node(input)
end
# rubocop:enable Metrics/MethodLength

def mutation(input)
node = node(input)
if @expected.include?(node)
expected = Expected.new(original_source: input, node: node(input))

if @expected.include?(expected)
fail "Mutation for input: #{input.inspect} is already expected"
end
@expected << node

@expected << expected
end

def singleton_mutations
Expand All @@ -83,14 +84,17 @@ def singleton_mutations
def node(input)
case input
when String
Unparser::Preprocessor.run(Unparser.parse(input))
when ::Parser::AST::Node
input
parser.parse(Unparser.buffer(input))
else
fail "Cannot coerce to node: #{input.inspect}"
fail "Unsupported input: #{input.inspect}"
end
end

def parser
Unparser.parser.tap do |parser|
@lvars.each(&parser.static_env.public_method(:declare))
end
end
end # DSL
end # Example
end # Meta
Expand Down
96 changes: 69 additions & 27 deletions lib/mutant/meta/example/verification.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,52 +11,94 @@ class Verification
#
# @return [Boolean]
def success?
[missing, unexpected, no_diffs, invalid_syntax].all?(&:empty?)
[
original_verification,
invalid,
missing,
no_diffs,
unexpected
].all?(&:empty?)
end
memoize :success?

# Error report
#
# @return [String]
def error_report
fail 'no error report on successful validation' if success?

YAML.dump(
'file' => example.file,
'original_ast' => example.node.inspect,
'original_source' => example.original_source,
'missing' => format_mutations(missing),
'unexpected' => format_mutations(unexpected),
'invalid_syntax' => format_mutations(invalid_syntax),
'no_diff' => no_diff_report
)
reports.join("\n")
end
memoize :error_report

private

def reports
reports = [example.file]
reports.concat(original)
reports.concat(original_verification)
reports.concat(make_report('Missing mutations:', missing))
reports.concat(make_report('Unexpected mutations:', unexpected))
reports.concat(make_report('No-Diff mutations:', no_diffs))
reports.concat(invalid)
end

def make_report(label, mutations)
if mutations.any?
[label, mutations.map(&method(:report_mutation))]
else
[]
end
end

def report_mutation(mutation)
[
mutation.node.inspect,
mutation.source
]
end

def original
[
'Original:',
example.node,
example.original_source
]
end

def original_verification
validation = Unparser::Validation.from_string(example.original_source)
if validation.success?
[]
else
[
prefix('[original]', validation.report)
]
end
end

def prefix(prefix, string)
string.each_line.map do |line|
"#{prefix} #{line}"
end.join
end

def invalid
mutations.each_with_object([]) do |mutation, aggregate|
validation = Unparser::Validation.from_node(mutation.node)
aggregate << prefix('[invalid-mutation]', validation.report) unless validation.success?
end
end
memoize :invalid

def unexpected
mutations.reject do |mutation|
example.expected.include?(mutation.node)
example.expected.any? { |expected| expected.node.eql?(mutation.node) }
end
end
memoize :unexpected

def missing
(example.expected - mutations.map(&:node)).map do |node|
Mutation::Evil.new(self, node)
(example.expected.map(&:node) - mutations.map(&:node)).map do |node|
Mutation::Evil.new(nil, node)
end
end
memoize :missing

def invalid_syntax
mutations.reject do |mutation|
::Parser::CurrentRuby.parse(mutation.source)
rescue ::Parser::SyntaxError # rubocop:disable Lint/SuppressedException
end
end
memoize :invalid_syntax

def no_diffs
mutations.select { |mutation| mutation.source.eql?(example.original_source_generated) }
end
Expand Down
4 changes: 2 additions & 2 deletions lib/mutant/mutator/node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def emit_self
end

def emit_nil
emit(N_NIL) unless left_assignment?
emit(N_NIL) unless left_op_assignment?
end

def parent_node
Expand All @@ -86,7 +86,7 @@ def parent_type
parent_node&.type
end

def left_assignment?
def left_op_assignment?
AST::Types::OP_ASSIGN.include?(parent_type) && parent.node.children.first.equal?(node)
end

Expand Down
22 changes: 0 additions & 22 deletions lib/mutant/mutator/node/dsym.rb

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,19 @@
module Mutant
class Mutator
class Node
# Mutator for dynamic literals
class DynamicLiteral < self

# Dstr mutator
class Dstr < Generic

handle(:dstr)
handle(:dstr, :dsym)

private

def dispatch
super()
emit_singletons

children.each_index do |index|
mutate_child(index, &method(:n_begin?))
end
end

end # Dstr
Expand Down
8 changes: 4 additions & 4 deletions lib/mutant/mutator/node/index.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def dispatch
end

def emit_send_forms
return if left_assignment?
return if left_op_assignment?

SEND_REPLACEMENTS.each do |selector|
emit(s(:send, receiver, selector, *indices))
Expand All @@ -43,7 +43,7 @@ def emit_drop_mutation

def mutate_indices
children_indices(index_range).each do |index|
emit_propagation(children.fetch(index)) unless left_assignment?
emit_propagation(children.fetch(index)) unless left_op_assignment?
delete_child(index)
mutate_child(index)
end
Expand Down Expand Up @@ -77,7 +77,7 @@ class Assign < self
def dispatch
super()

return if left_assignment?
return if left_op_assignment?

emit_index_read
emit(children.last)
Expand All @@ -89,7 +89,7 @@ def emit_index_read
end

def index_range
if left_assignment?
if left_op_assignment?
NO_VALUE_RANGE
else
REGULAR_RANGE
Expand Down
Loading

0 comments on commit 2b896a0

Please sign in to comment.