mutant 0.11.13 → 0.11.16

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/bin/mutant +40 -35
  3. data/lib/mutant/ast/find_metaclass_containing.rb +6 -6
  4. data/lib/mutant/ast/meta/const.rb +1 -1
  5. data/lib/mutant/ast/meta/optarg.rb +1 -1
  6. data/lib/mutant/ast/meta/resbody.rb +1 -1
  7. data/lib/mutant/ast/meta/send.rb +1 -1
  8. data/lib/mutant/ast/meta/symbol.rb +1 -1
  9. data/lib/mutant/ast/meta.rb +1 -1
  10. data/lib/mutant/ast/named_children.rb +1 -1
  11. data/lib/mutant/ast/node_predicates.rb +1 -1
  12. data/lib/mutant/ast/nodes.rb +1 -1
  13. data/lib/mutant/ast/pattern/lexer.rb +1 -1
  14. data/lib/mutant/ast/pattern/parser.rb +1 -1
  15. data/lib/mutant/ast/pattern/source.rb +1 -1
  16. data/lib/mutant/ast/pattern/token.rb +1 -1
  17. data/lib/mutant/ast/pattern.rb +1 -1
  18. data/lib/mutant/ast/regexp/transformer/direct.rb +1 -1
  19. data/lib/mutant/ast/regexp/transformer/named_group.rb +1 -1
  20. data/lib/mutant/ast/regexp/transformer/options_group.rb +1 -1
  21. data/lib/mutant/ast/regexp/transformer/quantifier.rb +1 -1
  22. data/lib/mutant/ast/regexp/transformer/recursive.rb +1 -1
  23. data/lib/mutant/ast/regexp/transformer/root.rb +1 -1
  24. data/lib/mutant/ast/regexp/transformer/text.rb +1 -1
  25. data/lib/mutant/ast/regexp/transformer.rb +1 -1
  26. data/lib/mutant/ast/regexp.rb +1 -1
  27. data/lib/mutant/ast/sexp.rb +1 -1
  28. data/lib/mutant/ast/structure.rb +8 -3
  29. data/lib/mutant/ast/types.rb +1 -1
  30. data/lib/mutant/ast.rb +31 -29
  31. data/lib/mutant/cli/command/environment/run.rb +0 -7
  32. data/lib/mutant/cli/command/environment.rb +4 -5
  33. data/lib/mutant/cli/command.rb +24 -22
  34. data/lib/mutant/cli.rb +2 -4
  35. data/lib/mutant/config.rb +4 -16
  36. data/lib/mutant/matcher/method/instance.rb +3 -2
  37. data/lib/mutant/matcher/method/metaclass.rb +4 -3
  38. data/lib/mutant/matcher/method/singleton.rb +3 -2
  39. data/lib/mutant/matcher/method.rb +32 -23
  40. data/lib/mutant/mutation/config.rb +2 -1
  41. data/lib/mutant/mutator/node/block.rb +5 -3
  42. data/lib/mutant/mutator/node/define.rb +3 -2
  43. data/lib/mutant/parser.rb +0 -7
  44. data/lib/mutant/version.rb +1 -1
  45. data/lib/mutant.rb +2 -3
  46. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7c08faadc47c13b47986719ddba1ca0437797643ff67c4d16869f6e195352fa2
4
- data.tar.gz: 3ea0e03ddff19eb0ae3566e611fbd2171dd0624cbae5c40903c4cbf3ade98ebb
3
+ metadata.gz: ebfdb4dba80abb7c2b06b6b057cebf6db686b598219979ed3d0730b6e5a9ed82
4
+ data.tar.gz: ee03173b094cf29a9d21ad30b383a0b01b8cac7b7ba1132f24f36c25e9e4fee3
5
5
  SHA512:
6
- metadata.gz: c18497e6427a64d05f11dab3a0a177c2bdb99c00a5acb7984bfa76ef7083628afa16ede769a2152b06101dfe0876bd38de02633f8082b38baf1fef7e8c08576b
7
- data.tar.gz: 96348aa482664e934312d7e6fc363b69d94f5158bbb5af7feaef329814769cc645c1474625675e2def3aab0fafc3ab164fe151958a2650019c45084eeb6bfe87
6
+ metadata.gz: 90d9c06d5a2900441b49b3bc0fa039b11bb296e389af41927f711431855a28369a8e31faf0b1cc5ba74ab09c6300c21002ac30c0d6eae6b3d63eac3129a692e8
7
+ data.tar.gz: 725ecfb369b3c28133d898f3405bb5cec8f780027c8e7c05de59c225719924fde51e192186d2f7a2199a57352b056401fa562a67d75409a637d182bbb50d9390
data/bin/mutant CHANGED
@@ -8,43 +8,48 @@ end
8
8
 
9
9
  require 'mutant'
10
10
 
11
- command = Mutant::CLI.parse(
11
+ Mutant::CLI.parse(
12
12
  arguments: ARGV,
13
13
  world: Mutant::WORLD
14
- )
14
+ ).either(
15
+ ->(message) { Mutant::WORLD.stderr.puts(message); Kernel.exit(false) },
16
+ # rubocop:disable Metrics/BlockLength
17
+ lambda do |command|
18
+ status =
19
+ if command.zombie?
20
+ $stderr.puts('Running mutant zombified!')
21
+ Mutant::Zombifier.call(
22
+ namespace: :Zombie,
23
+ load_path: $LOAD_PATH,
24
+ kernel: Kernel,
25
+ pathname: Pathname,
26
+ require_highjack: Mutant::RequireHighjack
27
+ .public_method(:call)
28
+ .to_proc
29
+ .curry
30
+ .call(Kernel),
31
+ root_require: 'mutant',
32
+ includes: %w[
33
+ adamantium
34
+ anima
35
+ concord
36
+ equalizer
37
+ mprelude
38
+ mutant
39
+ unparser
40
+ variable
41
+ ]
42
+ )
15
43
 
16
- status =
17
- if command.zombie?
18
- $stderr.puts('Running mutant zombified!')
19
- Mutant::Zombifier.call(
20
- namespace: :Zombie,
21
- load_path: $LOAD_PATH,
22
- kernel: Kernel,
23
- pathname: Pathname,
24
- require_highjack: Mutant::RequireHighjack
25
- .public_method(:call)
26
- .to_proc
27
- .curry
28
- .call(Kernel),
29
- root_require: 'mutant',
30
- includes: %w[
31
- adamantium
32
- anima
33
- concord
34
- equalizer
35
- mprelude
36
- mutant
37
- unparser
38
- variable
39
- ]
40
- )
44
+ Zombie::Mutant::CLI.parse(
45
+ arguments: ARGV,
46
+ world: Zombie::Mutant::WORLD
47
+ ).from_right.call
48
+ else
49
+ command.call
50
+ end
41
51
 
42
- Zombie::Mutant::CLI.parse(
43
- arguments: ARGV,
44
- world: Zombie::Mutant::WORLD
45
- ).call
46
- else
47
- command.call
52
+ Kernel.exit(status)
48
53
  end
49
-
50
- Kernel.exit(status)
54
+ # rubocop:enable Metrics/BlockLength
55
+ )
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mutant
4
- module AST
4
+ class AST
5
5
  # Given an AST, finds the sclass that directly(-ish) contains the provided
6
6
  # node.
7
7
  # This won't match arbitrarily complex structures - it only searches the
@@ -10,7 +10,7 @@ module Mutant
10
10
  # Descending into 'begin' nodes is supported because these are generated for
11
11
  # the one-line syntax class << self; def foo; end
12
12
  class FindMetaclassContaining
13
- include NodePredicates, Concord.new(:root, :target), Procto
13
+ include NodePredicates, Concord.new(:ast, :target), Procto
14
14
 
15
15
  SCLASS_BODY_INDEX = 1
16
16
 
@@ -22,11 +22,11 @@ module Mutant
22
22
  #
23
23
  # @api private
24
24
  def call
25
- AST.find_last_path(root) do |current|
26
- next unless n_sclass?(current)
25
+ Structure.for(ast.node.type).each_node(ast.node) do |current|
26
+ return current if n_sclass?(current) && metaclass_of?(current)
27
+ end
27
28
 
28
- metaclass_of?(current)
29
- end.last
29
+ nil
30
30
  end
31
31
 
32
32
  private
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mutant
4
- module AST
4
+ class AST
5
5
  # Node meta information mixin
6
6
  module Meta
7
7
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mutant
4
- module AST
4
+ class AST
5
5
  # Node meta information mixin
6
6
  module Meta
7
7
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mutant
4
- module AST
4
+ class AST
5
5
  # Node meta information mixin
6
6
  module Meta
7
7
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mutant
4
- module AST
4
+ class AST
5
5
  # Node meta information mixin
6
6
  module Meta
7
7
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mutant
4
- module AST
4
+ class AST
5
5
  # Node meta information mixin
6
6
  module Meta
7
7
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mutant
4
- module AST
4
+ class AST
5
5
  # Node meta information mixin
6
6
  module Meta
7
7
  end # Meta
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mutant
4
- module AST
4
+ class AST
5
5
 
6
6
  # Helper methods to define named children
7
7
  module NamedChildren
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mutant
4
- module AST
4
+ class AST
5
5
  # Module for node predicates
6
6
  module NodePredicates
7
7
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mutant
4
- module AST
4
+ class AST
5
5
  # Singleton nodes
6
6
  module Nodes
7
7
  extend Sexp
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mutant
4
- module AST
4
+ class AST
5
5
  class Pattern
6
6
  # rubocop:disable Metrics/ClassLength
7
7
  class Lexer
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mutant
4
- module AST
4
+ class AST
5
5
  # rubocop:disable Metrics/ClassLength
6
6
  # rubocop:disable Metrics/MethodLength
7
7
  class Pattern
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mutant
4
- module AST
4
+ class AST
5
5
  class Pattern
6
6
  class Source
7
7
  include Anima.new(:string)
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mutant
4
- module AST
4
+ class AST
5
5
  class Pattern
6
6
  class Token
7
7
  include Anima.new(:type, :value, :location)
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mutant
4
- module AST
4
+ class AST
5
5
  class Pattern
6
6
  include Adamantium
7
7
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mutant
4
- module AST
4
+ class AST
5
5
  module Regexp
6
6
  class Transformer
7
7
  # Transformer for nodes which map directly to other domain
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mutant
4
- module AST
4
+ class AST
5
5
  module Regexp
6
6
  class Transformer
7
7
  # Transformer for named groups
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mutant
4
- module AST
4
+ class AST
5
5
  module Regexp
6
6
  class Transformer
7
7
  # Transformer for option groups
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mutant
4
- module AST
4
+ class AST
5
5
  module Regexp
6
6
  class Transformer
7
7
  # Transformer for regexp quantifiers
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mutant
4
- module AST
4
+ class AST
5
5
  module Regexp
6
6
  class Transformer
7
7
  # Transformer for nodes with children
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mutant
4
- module AST
4
+ class AST
5
5
  module Regexp
6
6
  class Transformer
7
7
  # Transformer for root nodes
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mutant
4
- module AST
4
+ class AST
5
5
  module Regexp
6
6
  class Transformer
7
7
  # Regexp AST transformer for nodes that encode a text value
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mutant
4
- module AST
4
+ class AST
5
5
  module Regexp
6
6
  # Regexp bijective mapper
7
7
  #
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mutant
4
- module AST
4
+ class AST
5
5
  # Regexp source mapper
6
6
  module Regexp
7
7
  # Parse regex string into expression
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mutant
4
- module AST
4
+ class AST
5
5
  # Mixin for node sexp syntax
6
6
  module Sexp
7
7
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mutant
4
- module AST
4
+ class AST
5
5
  # AST Structure metadata
6
6
  # rubocop:disable Metrics/ModuleLength
7
7
  module Structure
@@ -26,7 +26,7 @@ module Mutant
26
26
  end
27
27
 
28
28
  def value(node)
29
- node.children.fetch(index)
29
+ node.children.at(index)
30
30
  end
31
31
  end
32
32
 
@@ -246,7 +246,7 @@ module Mutant
246
246
  type: :class,
247
247
  fixed: Node.fixed(
248
248
  [
249
- [Node::Fixed::Descendant, :name],
249
+ [Node::Fixed::Attribute, :name],
250
250
  [Node::Fixed::Descendant, :superclass],
251
251
  [Node::Fixed::Descendant, :body]
252
252
  ]
@@ -386,6 +386,11 @@ module Mutant
386
386
  fixed: Node.fixed([[Node::Fixed::Attribute, :value]]),
387
387
  variable: nil
388
388
  ),
389
+ Node.new(
390
+ type: :forwarded_args,
391
+ fixed: EMPTY_ARRAY,
392
+ variable: nil
393
+ ),
389
394
  Node.new(
390
395
  type: :for,
391
396
  fixed: Node.fixed(
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mutant
4
- module AST
4
+ class AST
5
5
  # Groups of node types
6
6
  module Types # rubocop:disable Metrics/ModuleLength
7
7
  ASSIGNABLE_VARIABLES = Set.new(%i[ivasgn lvasgn cvasgn gvasgn]).freeze
data/lib/mutant/ast.rb CHANGED
@@ -1,41 +1,43 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mutant
4
- # AST helpers
5
- module AST
6
-
7
- # Find last node satisfying predicate (as block)
8
- #
9
- # @return [Parser::AST::Node]
10
- # if satisfying node is found
11
- #
12
- # @yield [Parser::AST::Node]
13
- #
14
- # @yieldreturn [Boolean]
15
- # true in case node satisfies predicate
16
- #
17
- # @return [nil]
18
- # otherwise
19
- def self.find_last_path(node, &predicate)
20
- fail ArgumentError, 'block expected' unless block_given?
21
- path = []
22
- walk(node, [node]) do |candidate, stack|
23
- if predicate.call(candidate)
24
- path = stack.dup
25
- end
4
+ class AST
5
+ include Adamantium, Anima.new(
6
+ :node,
7
+ :comment_associations
8
+ )
9
+
10
+ class View
11
+ include Adamantium, Anima.new(:node, :path)
12
+ end
13
+
14
+ def on_line(line)
15
+ line_map.fetch(line, EMPTY_HASH).map do |node, path|
16
+ View.new(node: node, path: path)
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def line_map
23
+ line_map = {}
24
+
25
+ walk_path(node) do |node, path|
26
+ expression = node.location.expression || next
27
+ (line_map[expression.line] ||= []) << [node, path]
26
28
  end
27
- path
29
+
30
+ line_map
28
31
  end
32
+ memoize :line_map
29
33
 
30
- def self.walk(node, stack, &block)
31
- block.call(node, stack)
34
+ def walk_path(node, stack = [node.type], &block)
35
+ block.call(node, stack.dup)
32
36
  node.children.grep(::Parser::AST::Node) do |child|
33
- stack.push(child)
34
- walk(child, stack, &block)
37
+ stack.push(child.type)
38
+ walk_path(child, stack, &block)
35
39
  stack.pop
36
40
  end
37
41
  end
38
- private_class_method :walk
39
-
40
42
  end # AST
41
43
  end # Mutant
@@ -30,13 +30,6 @@ module Mutant
30
30
  ===============
31
31
  MESSAGE
32
32
 
33
- # Test if command needs to be executed in zombie environment
34
- #
35
- # @return [Bool]
36
- def zombie?
37
- @config.zombie
38
- end
39
-
40
33
  private
41
34
 
42
35
  def action
@@ -19,7 +19,9 @@ module Mutant
19
19
 
20
20
  def initialize(attributes)
21
21
  super(attributes)
22
- @config = Config::DEFAULT
22
+ @config = Config::DEFAULT.with(
23
+ coverage_criteria: Config::CoverageCriteria::EMPTY
24
+ )
23
25
  end
24
26
 
25
27
  def bootstrap
@@ -37,7 +39,7 @@ module Mutant
37
39
  )
38
40
  end
39
41
 
40
- @config = Config.env.merge(file_config).merge(@config).expand_defaults
42
+ @config = Config.env.merge(file_config).merge(@config)
41
43
  end
42
44
 
43
45
  def parse_remaining_arguments(arguments)
@@ -67,9 +69,6 @@ module Mutant
67
69
  # rubocop:disable Metrics/MethodLength
68
70
  def add_environment_options(parser)
69
71
  parser.separator('Environment:')
70
- parser.on('--zombie', 'Run mutant zombified') do
71
- set(zombie: true)
72
- end
73
72
  parser.on('-I', '--include DIRECTORY', 'Add DIRECTORY to $LOAD_PATH') do |directory|
74
73
  add(:includes, directory)
75
74
  end
@@ -4,9 +4,7 @@ module Mutant
4
4
  module CLI
5
5
  # rubocop:disable Metrics/ClassLength
6
6
  class Command
7
- include AbstractType, Anima.new(:world, :main, :parent, :arguments)
8
-
9
- include Equalizer.new(:parent, :arguments)
7
+ include AbstractType, Anima.new(:world, :main, :parent, :zombie)
10
8
 
11
9
  OPTIONS = [].freeze
12
10
  SUBCOMMANDS = [].freeze
@@ -18,21 +16,20 @@ module Mutant
18
16
  define_method(:add_officious) {}
19
17
  end # OptionParser
20
18
 
21
- class FailParse < self
22
- include Concord.new(:world, :message)
23
-
24
- def call
25
- world.stderr.puts(message)
26
- false
27
- end
28
- end
29
-
30
19
  # Parse command
31
20
  #
32
21
  # @return [Command]
33
- def self.parse(**attributes)
34
- new(main: nil, parent: nil, **attributes).__send__(:parse)
22
+ #
23
+ # rubocop:disable Metrics/ParameterLists
24
+ def self.parse(arguments:, parent: nil, world:, zombie: false)
25
+ new(
26
+ main: nil,
27
+ parent: parent,
28
+ world: world,
29
+ zombie: zombie
30
+ ).__send__(:parse, arguments)
35
31
  end
32
+ # rubocop:enable Metrics/ParameterLists
36
33
 
37
34
  # Command name
38
35
  #
@@ -62,12 +59,7 @@ module Mutant
62
59
  [*parent&.full_name, self.class.command_name].join(' ')
63
60
  end
64
61
 
65
- # Test if command needs to be executed in zombie environment
66
- #
67
- # @return [Bool]
68
- def zombie?
69
- false
70
- end
62
+ alias_method :zombie?, :zombie
71
63
 
72
64
  abstract_method :action
73
65
 
@@ -116,7 +108,7 @@ module Mutant
116
108
  end
117
109
  end
118
110
 
119
- def parse
111
+ def parse(arguments)
120
112
  Either
121
113
  .wrap_error(OptionParser::InvalidOption) { parser.order(arguments) }
122
114
  .lmap(&method(:with_help))
@@ -129,6 +121,7 @@ module Mutant
129
121
  parser.separator(nil)
130
122
  end
131
123
 
124
+ # rubocop:disable Metrics/MethodLength
132
125
  def add_global_options(parser)
133
126
  parser.separator("mutant version: #{VERSION}")
134
127
  parser.separator(nil)
@@ -142,6 +135,10 @@ module Mutant
142
135
  parser.on('--version', 'Print mutants version') do
143
136
  capture_main { world.stdout.puts("mutant-#{VERSION}"); true }
144
137
  end
138
+
139
+ parser.on('--zombie', 'Run mutant zombified') do
140
+ @zombie = true
141
+ end
145
142
  end
146
143
 
147
144
  def add_subcommands(parser)
@@ -178,7 +175,12 @@ module Mutant
178
175
  Either::Left.new(with_help('Missing required subcommand!'))
179
176
  else
180
177
  find_command(command_name).bind do |command|
181
- command.parse(**to_h, parent: self, arguments: arguments)
178
+ command.parse(
179
+ arguments: arguments,
180
+ parent: self,
181
+ world: world,
182
+ zombie: zombie
183
+ )
182
184
  end
183
185
  end
184
186
  end
data/lib/mutant/cli.rb CHANGED
@@ -6,10 +6,8 @@ module Mutant
6
6
  # Parse command
7
7
  #
8
8
  # @return [Command]
9
- def self.parse(world:, **attributes)
10
- Command::Root
11
- .parse(world: world, **attributes)
12
- .from_right { |message| Command::FailParse.new(world, message) }
9
+ def self.parse(arguments:, world:)
10
+ Command::Root.parse(arguments: arguments, world: world)
13
11
  end
14
12
  end # CLI
15
13
  end # Mutant
data/lib/mutant/config.rb CHANGED
@@ -21,11 +21,10 @@ module Mutant
21
21
  :matcher,
22
22
  :mutation,
23
23
  :reporter,
24
- :requires,
25
- :zombie
24
+ :requires
26
25
  )
27
26
 
28
- %i[fail_fast zombie].each do |name|
27
+ %i[fail_fast].each do |name|
29
28
  define_method(:"#{name}?") { public_send(name) }
30
29
  end
31
30
 
@@ -42,7 +41,7 @@ module Mutant
42
41
  MUTATION_TIMEOUT_DEPRECATION = <<~'MESSAGE'
43
42
  Deprecated configuration toplevel key `mutation_timeout` found.
44
43
 
45
- This key will be removed in the next mayor version.
44
+ This key will be removed in the next major version.
46
45
  Instead place your mutation timeout configuration under the `mutation` key
47
46
  like this:
48
47
 
@@ -74,8 +73,7 @@ module Mutant
74
73
  jobs: other.jobs || jobs,
75
74
  matcher: matcher.merge(other.matcher),
76
75
  mutation: mutation.merge(other.mutation),
77
- requires: requires + other.requires,
78
- zombie: zombie || other.zombie
76
+ requires: requires + other.requires
79
77
  )
80
78
  end
81
79
  # rubocop:enable Metrics/AbcSize
@@ -100,16 +98,6 @@ module Mutant
100
98
  end
101
99
  end
102
100
 
103
- # Expand config with defaults
104
- #
105
- # @return [Config]
106
- def expand_defaults
107
- with(
108
- coverage_criteria: CoverageCriteria::DEFAULT.merge(coverage_criteria),
109
- jobs: jobs || 1
110
- )
111
- end
112
-
113
101
  def self.load_contents(env, path)
114
102
  Transform::Named
115
103
  .new(
@@ -30,8 +30,9 @@ module Mutant
30
30
 
31
31
  # Instance method specific evaluator
32
32
  class Evaluator < Evaluator
33
- SUBJECT_CLASS = Subject::Method::Instance
34
- NAME_INDEX = 0
33
+ MATCH_NODE_TYPE = :def
34
+ NAME_INDEX = 0
35
+ SUBJECT_CLASS = Subject::Method::Instance
35
36
 
36
37
  private
37
38
 
@@ -22,10 +22,11 @@ module Mutant
22
22
  # Metaclass method evaluator
23
23
  class Evaluator < Evaluator
24
24
  # Terminology note: the "receiver" is the `self` in `class << self`
25
- SUBJECT_CLASS = Subject::Method::Metaclass
26
- NAME_INDEX = 0
27
25
  CONST_NAME_INDEX = 1
26
+ MATCH_NODE_TYPE = :def
27
+ NAME_INDEX = 0
28
28
  SCLASS_RECEIVER_INDEX = 0
29
+ SUBJECT_CLASS = Subject::Method::Metaclass
29
30
  RECEIVER_WARNING = 'Can only match :def inside :sclass on ' \
30
31
  ':self or :const, got :sclass on %p ' \
31
32
  'unable to match'
@@ -45,7 +46,7 @@ module Mutant
45
46
  end
46
47
 
47
48
  def metaclass_containing(node)
48
- AST::FindMetaclassContaining.call(ast.node, node)
49
+ AST::FindMetaclassContaining.call(ast, node)
49
50
  end
50
51
 
51
52
  def line?(node)
@@ -18,10 +18,11 @@ module Mutant
18
18
 
19
19
  # Singleton method evaluator
20
20
  class Evaluator < Evaluator
21
- SUBJECT_CLASS = Subject::Method::Singleton
22
- RECEIVER_INDEX = 0
21
+ MATCH_NODE_TYPE = :defs
23
22
  NAME_INDEX = 1
23
+ RECEIVER_INDEX = 0
24
24
  RECEIVER_WARNING = 'Can only match :defs on :self or :const got %p unable to match'
25
+ SUBJECT_CLASS = Subject::Method::Singleton
25
26
 
26
27
  private
27
28
 
@@ -41,23 +41,38 @@ module Mutant
41
41
  #
42
42
  # @return [Enumerable<Subject>]
43
43
  def call
44
- return EMPTY_ARRAY if skip?
44
+ location = source_location
45
+
46
+ if location.nil? || !location.first.end_with?('.rb')
47
+ env.warn(SOURCE_LOCATION_WARNING_FORMAT % target_method)
45
48
 
46
- [subject].compact
49
+ return EMPTY_ARRAY
50
+ end
51
+
52
+ match_view
47
53
  end
48
54
 
49
55
  private
50
56
 
51
- def skip?
52
- location = source_location
53
-
54
- file = location&.first
57
+ def match_view
58
+ return EMPTY_ARRAY if matched_view.nil?
55
59
 
56
- if location.nil? || !file.end_with?('.rb')
57
- env.warn(SOURCE_LOCATION_WARNING_FORMAT % target_method)
58
- elsif matched_node_path.any?(&method(:n_block?))
60
+ if matched_view.path.any?(&:block.public_method(:equal?))
59
61
  env.warn(CLOSURE_WARNING_FORMAT % target_method)
62
+
63
+ return EMPTY_ARRAY
60
64
  end
65
+
66
+ [subject]
67
+ end
68
+
69
+ def subject
70
+ self.class::SUBJECT_CLASS.new(
71
+ config: subject_config(matched_view.node),
72
+ context: context,
73
+ node: matched_view.node,
74
+ visibility: visibility
75
+ )
61
76
  end
62
77
 
63
78
  def method_name
@@ -95,17 +110,6 @@ module Mutant
95
110
  T::Private::Methods.signature_for_method(target_method)
96
111
  end
97
112
 
98
- def subject
99
- node = matched_node_path.last || return
100
-
101
- self.class::SUBJECT_CLASS.new(
102
- config: subject_config(node),
103
- context: context,
104
- node: node,
105
- visibility: visibility
106
- )
107
- end
108
-
109
113
  def subject_config(node)
110
114
  Subject::Config.parse(
111
115
  comments: ast.comment_associations.fetch(node, []),
@@ -113,10 +117,15 @@ module Mutant
113
117
  )
114
118
  end
115
119
 
116
- def matched_node_path
117
- AST.find_last_path(ast.node, &method(:match?))
120
+ def matched_view
121
+ return if source_location.nil?
122
+
123
+ ast
124
+ .on_line(source_line)
125
+ .select { |view| view.node.type.eql?(self.class::MATCH_NODE_TYPE) && match?(view.node) }
126
+ .last
118
127
  end
119
- memoize :matched_node_path
128
+ memoize :matched_view
120
129
 
121
130
  def visibility
122
131
  # This can be cleaned up once we are on >ruby-3.0
@@ -28,7 +28,8 @@ module Mutant
28
28
 
29
29
  def merge(other)
30
30
  with(
31
- timeout: other.timeout || timeout
31
+ ignore_patterns: other.ignore_patterns,
32
+ timeout: other.timeout || timeout
32
33
  )
33
34
  end
34
35
  end # Config
@@ -38,9 +38,11 @@ module Mutant
38
38
  end
39
39
 
40
40
  def body_has_control?
41
- AST.find_last_path(body) do |node|
42
- n_break?(node) || n_next?(node)
43
- end.any?
41
+ AST::Structure.for(body.type).each_node(body) do |node|
42
+ return true if n_break?(node) || n_next?(node)
43
+ end
44
+
45
+ false
44
46
  end
45
47
 
46
48
  def mutate_body_receiver
@@ -14,9 +14,10 @@ module Mutant
14
14
  emit_body(N_RAISE)
15
15
  emit_body(N_ZSUPER)
16
16
 
17
- return unless body && !ignore?(body)
17
+ return if !body || ignore?(body)
18
+
19
+ emit_body(nil) unless n_begin?(body) && body.children.any?(&method(:ignore?))
18
20
 
19
- emit_body(nil) unless body.children.any?(&method(:ignore?))
20
21
  emit_body_mutations
21
22
  end
22
23
 
data/lib/mutant/parser.rb CHANGED
@@ -5,13 +5,6 @@ module Mutant
5
5
  class Parser
6
6
  include Adamantium, Equalizer.new
7
7
 
8
- class AST
9
- include Anima.new(
10
- :node,
11
- :comment_associations
12
- )
13
- end
14
-
15
8
  # Initialize object
16
9
  #
17
10
  # @return [undefined]
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Mutant
4
4
  # Current mutant version
5
- VERSION = '0.11.13'
5
+ VERSION = '0.11.16'
6
6
  end # Mutant
data/lib/mutant.rb CHANGED
@@ -273,7 +273,7 @@ module Mutant
273
273
  # Reopen class to initialize constant to avoid dep circle
274
274
  class Config
275
275
  DEFAULT = new(
276
- coverage_criteria: Config::CoverageCriteria::EMPTY,
276
+ coverage_criteria: Config::CoverageCriteria::DEFAULT,
277
277
  expression_parser: Expression::Parser.new([
278
278
  Expression::Descendants,
279
279
  Expression::Method,
@@ -291,8 +291,7 @@ module Mutant
291
291
  matcher: Matcher::Config::DEFAULT,
292
292
  mutation: Mutation::Config::DEFAULT,
293
293
  reporter: Reporter::CLI.build(WORLD.stdout),
294
- requires: EMPTY_ARRAY,
295
- zombie: false
294
+ requires: EMPTY_ARRAY
296
295
  )
297
296
  end # Config
298
297
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mutant
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.13
4
+ version: 0.11.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - Markus Schirp
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-07-06 00:00:00.000000000 Z
11
+ date: 2022-09-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: diff-lcs