mutant 0.11.28 → 0.11.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/lib/mutant/ast.rb +8 -9
  3. data/lib/mutant/bootstrap.rb +39 -13
  4. data/lib/mutant/cli/command/environment/test.rb +38 -1
  5. data/lib/mutant/cli/command/environment.rb +11 -0
  6. data/lib/mutant/context.rb +31 -61
  7. data/lib/mutant/env.rb +8 -1
  8. data/lib/mutant/expression/method.rb +4 -1
  9. data/lib/mutant/expression/methods.rb +4 -1
  10. data/lib/mutant/expression/namespace.rb +4 -4
  11. data/lib/mutant/hooks.rb +1 -0
  12. data/lib/mutant/integration/null.rb +1 -0
  13. data/lib/mutant/integration.rb +5 -1
  14. data/lib/mutant/matcher/descendants.rb +1 -1
  15. data/lib/mutant/matcher/method/instance.rb +5 -4
  16. data/lib/mutant/matcher/method/metaclass.rb +1 -1
  17. data/lib/mutant/matcher/method/singleton.rb +1 -1
  18. data/lib/mutant/matcher/method.rb +30 -4
  19. data/lib/mutant/matcher/methods.rb +8 -7
  20. data/lib/mutant/matcher/namespace.rb +1 -1
  21. data/lib/mutant/meta/example.rb +12 -2
  22. data/lib/mutant/mutation/runner/sink.rb +7 -3
  23. data/lib/mutant/mutation/runner.rb +2 -3
  24. data/lib/mutant/parallel/connection.rb +178 -0
  25. data/lib/mutant/parallel/pipe.rb +39 -0
  26. data/lib/mutant/parallel/worker.rb +42 -14
  27. data/lib/mutant/parallel.rb +18 -7
  28. data/lib/mutant/reporter/cli/format.rb +19 -2
  29. data/lib/mutant/reporter/cli/printer/test.rb +138 -0
  30. data/lib/mutant/reporter/cli.rb +33 -4
  31. data/lib/mutant/reporter.rb +22 -1
  32. data/lib/mutant/result.rb +53 -2
  33. data/lib/mutant/scope.rb +41 -1
  34. data/lib/mutant/subject/method/instance.rb +3 -2
  35. data/lib/mutant/subject/method/metaclass.rb +1 -1
  36. data/lib/mutant/subject/method/singleton.rb +2 -2
  37. data/lib/mutant/subject/method.rb +1 -1
  38. data/lib/mutant/test/runner/sink.rb +51 -0
  39. data/lib/mutant/test/runner.rb +62 -0
  40. data/lib/mutant/timer.rb +9 -0
  41. data/lib/mutant/version.rb +1 -1
  42. data/lib/mutant/world.rb +2 -0
  43. data/lib/mutant.rb +7 -1
  44. metadata +9 -19
  45. data/lib/mutant/pipe.rb +0 -96
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2580053028745a106b883c1468b4e0c4462d1e5e443f12533ca277149f603239
4
- data.tar.gz: db5d4f28324b0846a9872b3a7b841e8dcdb84ae204f6c15def9f9381db064ea3
3
+ metadata.gz: 3e57360ec50ffdef54f132f4764c940caad83a2d3692ac812b95c236cd8ff52d
4
+ data.tar.gz: cc69f0948238d888f569caeee901dc9197b620a3693474323a0959212d73fc30
5
5
  SHA512:
6
- metadata.gz: 125e846599d5bfbd7ec52d3f7bbcf78f53d2584b7dd447d9313954c10ff4e5176c59fec396833b97f81bcb4bb40d9fdcf1cc0ef138c3a53685af503b0f95678b
7
- data.tar.gz: 2fb0163bfcdc01ed03fb34418a9522af0dab49e4fb8fa91cccf8c9e7cbbdcbd00551e2c36087ca8ee587bda317cc391a0bf7eae564be7834b53eff96873abd55
6
+ metadata.gz: cf4a364cc90b650a6c9dff90b38fb22aaa54d62f98f3b85ae732894a112f89a72b7c6e81b25fdd051b4436e8b55c58203fa4645a1c471cd5745fd665c7f0418b
7
+ data.tar.gz: 27a2195e3cd277816e37970fbbf66ed3d4f5237d9be667345229a9739deebaa45366914c71ce41254b7e0a65ea3e40af6012ddf4f8d654164d5eab904345dcf0
data/lib/mutant/ast.rb CHANGED
@@ -8,12 +8,12 @@ module Mutant
8
8
  )
9
9
 
10
10
  class View
11
- include Adamantium, Anima.new(:node, :path)
11
+ include Adamantium, Anima.new(:node, :stack)
12
12
  end
13
13
 
14
14
  def on_line(line)
15
- line_map.fetch(line, EMPTY_HASH).map do |node, path|
16
- View.new(node: node, path: path)
15
+ line_map.fetch(line, EMPTY_HASH).map do |node, stack|
16
+ View.new(node: node, stack: stack)
17
17
  end
18
18
  end
19
19
 
@@ -22,21 +22,20 @@ module Mutant
22
22
  def line_map
23
23
  line_map = {}
24
24
 
25
- walk_path(node) do |node, path|
25
+ walk_path(node, []) do |node, stack|
26
26
  expression = node.location.expression || next
27
- (line_map[expression.line] ||= []) << [node, path]
27
+ (line_map[expression.line] ||= []) << [node, stack]
28
28
  end
29
29
 
30
30
  line_map
31
31
  end
32
32
  memoize :line_map
33
33
 
34
- def walk_path(node, stack = [node.type], &block)
35
- block.call(node, stack.dup)
34
+ def walk_path(node, stack, &block)
35
+ block.call(node, stack)
36
+ stack = [*stack, node]
36
37
  node.children.grep(::Parser::AST::Node) do |child|
37
- stack.push(child.type)
38
38
  walk_path(child, stack, &block)
39
- stack.pop
40
39
  end
41
40
  end
42
41
  end # AST
@@ -20,7 +20,7 @@ module Mutant
20
20
  '%<scope_class>s#name from: %<scope>s raised an error: %<exception>s'
21
21
 
22
22
  CLASS_NAME_TYPE_MISMATCH_FORMAT =
23
- '%<scope_class>s#name from: %<scope>s returned %<name>s'
23
+ '%<scope_class>s#name from: %<raw_scope>s returned %<name>s'
24
24
 
25
25
  private_constant(*constants(false))
26
26
 
@@ -47,6 +47,32 @@ module Mutant
47
47
  selected_subjects.flat_map(&:mutations)
48
48
  end
49
49
 
50
+ setup_integration(
51
+ env: env,
52
+ mutations: mutations,
53
+ selected_subjects: selected_subjects
54
+ )
55
+ end
56
+ end
57
+ # rubocop:enable Metrics/MethodLength
58
+
59
+ # Run test only bootstrap
60
+ #
61
+ # @param [Env] env
62
+ #
63
+ # @return [Either<String, Env>]
64
+ def self.call_test(env)
65
+ env.record(:bootstrap) do
66
+ setup_integration(
67
+ env: load_hooks(env),
68
+ mutations: [],
69
+ selected_subjects: []
70
+ )
71
+ end
72
+ end
73
+
74
+ def self.setup_integration(env:, mutations:, selected_subjects:)
75
+ env.record(__method__) do
50
76
  Integration.setup(env).fmap do |integration|
51
77
  env.with(
52
78
  integration: integration,
@@ -57,7 +83,7 @@ module Mutant
57
83
  end
58
84
  end
59
85
  end
60
- # rubocop:enable Metrics/MethodLength
86
+ private_class_method :setup_integration
61
87
 
62
88
  def self.load_hooks(env)
63
89
  env.record(__method__) do
@@ -113,9 +139,9 @@ module Mutant
113
139
  env.record(__method__) do
114
140
  config = env.config
115
141
 
116
- scopes = env.world.object_space.each_object(Module).with_object([]) do |scope, aggregate|
117
- expression = expression(config.reporter, config.expression_parser, scope) || next
118
- aggregate << Scope.new(raw: scope, expression: expression)
142
+ scopes = env.world.object_space.each_object(Module).with_object([]) do |raw_scope, aggregate|
143
+ expression = expression(config.reporter, config.expression_parser, raw_scope) || next
144
+ aggregate << Scope.new(raw: raw_scope, expression: expression)
119
145
  end
120
146
 
121
147
  scopes.sort_by { |scope| scope.expression.syntax }
@@ -123,31 +149,31 @@ module Mutant
123
149
  end
124
150
  private_class_method :matchable_scopes
125
151
 
126
- def self.scope_name(reporter, scope)
127
- scope.name
152
+ def self.scope_name(reporter, raw_scope)
153
+ raw_scope.name
128
154
  rescue => exception
129
155
  semantics_warning(
130
156
  reporter,
131
157
  CLASS_NAME_RAISED_EXCEPTION,
132
158
  exception: exception.inspect,
133
- scope: scope,
134
- scope_class: scope.class
159
+ scope: raw_scope,
160
+ scope_class: raw_scope.class
135
161
  )
136
162
  nil
137
163
  end
138
164
  private_class_method :scope_name
139
165
 
140
166
  # rubocop:disable Metrics/MethodLength
141
- def self.expression(reporter, expression_parser, scope)
142
- name = scope_name(reporter, scope) or return
167
+ def self.expression(reporter, expression_parser, raw_scope)
168
+ name = scope_name(reporter, raw_scope) or return
143
169
 
144
170
  unless name.instance_of?(String)
145
171
  semantics_warning(
146
172
  reporter,
147
173
  CLASS_NAME_TYPE_MISMATCH_FORMAT,
148
174
  name: name,
149
- scope_class: scope.class,
150
- scope: scope
175
+ scope_class: raw_scope.class,
176
+ raw_scope: raw_scope
151
177
  )
152
178
  return
153
179
  end
@@ -8,6 +8,21 @@ module Mutant
8
8
  NAME = 'test'
9
9
  SHORT_DESCRIPTION = 'test subcommands'
10
10
 
11
+ private
12
+
13
+ def parse_remaining_arguments(arguments)
14
+ arguments.each(&method(:add_integration_argument))
15
+ Either::Right.new(self)
16
+ end
17
+
18
+ def bootstrap
19
+ env = Env.empty(world, @config)
20
+
21
+ env
22
+ .record(:config) { Config.load(cli_config: @config, world: world) }
23
+ .bind { |config| Bootstrap.call_test(env.with(config: config)) }
24
+ end
25
+
11
26
  class List < self
12
27
  NAME = 'list'
13
28
  SHORT_DESCRIPTION = 'List tests detected in the environment'
@@ -28,7 +43,29 @@ module Mutant
28
43
  end
29
44
  end
30
45
 
31
- SUBCOMMANDS = [List].freeze
46
+ class Run < self
47
+ NAME = 'run'
48
+ SHORT_DESCRIPTION = 'Run tests'
49
+ SUBCOMMANDS = EMPTY_ARRAY
50
+
51
+ private
52
+
53
+ def action
54
+ bootstrap
55
+ .bind(&Mutant::Test::Runner.public_method(:call))
56
+ .bind(&method(:from_result))
57
+ end
58
+
59
+ def from_result(result)
60
+ if result.success?
61
+ Either::Right.new(nil)
62
+ else
63
+ Either::Left.new('Test failures, exiting nonzero!')
64
+ end
65
+ end
66
+ end
67
+
68
+ SUBCOMMANDS = [List, Run].freeze
32
69
  end # Test
33
70
  end # Environment
34
71
  end # Command
@@ -3,6 +3,7 @@
3
3
  module Mutant
4
4
  module CLI
5
5
  class Command
6
+ # rubocop:disable Metrics/ClassLength
6
7
  class Environment < self
7
8
  NAME = 'environment'
8
9
  SHORT_DESCRIPTION = 'Environment subcommands'
@@ -13,6 +14,7 @@ module Mutant
13
14
  add_runner_options
14
15
  add_integration_options
15
16
  add_matcher_options
17
+ add_reporter_options
16
18
  ].freeze
17
19
 
18
20
  private
@@ -126,7 +128,16 @@ module Mutant
126
128
  set(mutation: @config.mutation.with(timeout: Float(number)))
127
129
  end
128
130
  end
131
+
132
+ def add_reporter_options(parser)
133
+ parser.separator('Reporting:')
134
+
135
+ parser.on('--print-warnings', 'Print warnings') do
136
+ set(reporter: @config.reporter.with(print_warnings: true))
137
+ end
138
+ end
129
139
  end # Run
140
+ # rubocop:enable Metrics/ClassLength
130
141
  end # Command
131
142
  end # CLI
132
143
  end # Mutant
@@ -3,83 +3,53 @@
3
3
  module Mutant
4
4
  # An abstract context where mutations can be applied to.
5
5
  class Context
6
- include Adamantium, Anima.new(:scope, :source_path)
7
- extend AST::Sexp
6
+ include Adamantium, Anima.new(:constant_scope, :scope, :source_path)
8
7
 
9
- NAMESPACE_DELIMITER = '::'
8
+ class ConstantScope
9
+ include AST::Sexp
10
10
 
11
- # Return root node for mutation
12
- #
13
- # @return [Parser::AST::Node]
14
- def root(node)
15
- nesting.reverse.reduce(node) do |current, scope|
16
- self.class.wrap(scope, current)
11
+ class Class < self
12
+ include Anima.new(:const, :descendant)
13
+
14
+ def call(node)
15
+ s(:class, const, nil, descendant.call(node))
16
+ end
17
17
  end
18
- end
19
18
 
20
- # Identification string
21
- #
22
- # @return [String]
23
- def identification
24
- scope.name
25
- end
19
+ class Module < self
20
+ include Anima.new(:const, :descendant)
26
21
 
27
- # Wrap node into ast node
28
- #
29
- # @param [Class, Module] scope
30
- # @param [Parser::AST::Node] node
31
- #
32
- # @return [Parser::AST::Class]
33
- # if scope is of kind Class
34
- #
35
- # @return [Parser::AST::Module]
36
- # if scope is of kind module
37
- def self.wrap(scope, node)
38
- name = s(:const, nil, scope.name.split(NAMESPACE_DELIMITER).last.to_sym)
39
- case scope
40
- when Class
41
- s(:class, name, nil, node)
42
- when Module
43
- s(:module, name, node)
22
+ def call(node)
23
+ s(:module, const, descendant.call(node))
24
+ end
44
25
  end
45
- end
46
26
 
47
- # Nesting of scope
48
- #
49
- # @return [Enumerable<Class,Module>]
50
- def nesting
51
- const = Object
52
- name_nesting.map do |name|
53
- const = const.const_get(name)
27
+ class None < self
28
+ include Equalizer.new
29
+
30
+ def call(node)
31
+ node
32
+ end
54
33
  end
55
34
  end
56
- memoize :nesting
57
35
 
58
- # Unqualified name of scope
59
- #
60
- # @return [String]
61
- def unqualified_name
62
- name_nesting.last
36
+ def match_expressions
37
+ scope.match_expressions
63
38
  end
64
39
 
65
- # Match expressions for scope
40
+ # Return root node for mutation
66
41
  #
67
- # @return [Enumerable<Expression>]
68
- def match_expressions
69
- name_nesting.each_index.reverse_each.map do |index|
70
- Expression::Namespace::Recursive.new(
71
- scope_name: name_nesting.take(index.succ).join(NAMESPACE_DELIMITER)
72
- )
73
- end
42
+ # @return [Parser::AST::Node]
43
+ def root(node)
44
+ constant_scope.call(node)
74
45
  end
75
- memoize :match_expressions
76
46
 
77
- private
78
-
79
- def name_nesting
80
- scope.name.split(NAMESPACE_DELIMITER)
47
+ # Identification string
48
+ #
49
+ # @return [String]
50
+ def identification
51
+ scope.raw.name
81
52
  end
82
- memoize :name_nesting
83
53
 
84
54
  end # Context
85
55
  end # Mutant
data/lib/mutant/env.rb CHANGED
@@ -66,10 +66,18 @@ module Mutant
66
66
  )
67
67
  end
68
68
 
69
+ def run_test_index(test_index)
70
+ integration.call([integration.all_tests.fetch(test_index)])
71
+ end
72
+
69
73
  def emit_mutation_worker_process_start(index:)
70
74
  hooks.run(:mutation_worker_process_start, index: index)
71
75
  end
72
76
 
77
+ def emit_test_worker_process_start(index:)
78
+ hooks.run(:test_worker_process_start, index: index)
79
+ end
80
+
73
81
  # The test selections
74
82
  #
75
83
  # @return Hash{Mutation => Enumerable<Test>}
@@ -175,7 +183,6 @@ module Mutant
175
183
  def timer
176
184
  world.timer
177
185
  end
178
-
179
186
  end # Env
180
187
  # rubocop:enable Metrics/ClassLength
181
188
  end # Mutant
@@ -78,7 +78,10 @@ module Mutant
78
78
  private
79
79
 
80
80
  def scope
81
- Object.const_get(scope_name)
81
+ Scope.new(
82
+ raw: Object.const_get(scope_name),
83
+ expression: Namespace::Exact.new(scope_name: scope_name)
84
+ )
82
85
  end
83
86
 
84
87
  end # Method
@@ -57,7 +57,10 @@ module Mutant
57
57
  private
58
58
 
59
59
  def scope
60
- Object.const_get(scope_name)
60
+ Scope.new(
61
+ expression: Namespace::Exact.new(scope_name: scope_name),
62
+ raw: Object.const_get(scope_name)
63
+ )
61
64
  end
62
65
 
63
66
  end # Methods
@@ -66,10 +66,10 @@ module Mutant
66
66
  #
67
67
  # @return [Matcher]
68
68
  def matcher
69
- scope = find_scope
69
+ raw_scope = find_raw_scope
70
70
 
71
- if scope
72
- Matcher::Scope.new(scope: scope)
71
+ if raw_scope
72
+ Matcher::Scope.new(scope: Scope.new(expression: self, raw: raw_scope))
73
73
  else
74
74
  Matcher::Null.new
75
75
  end
@@ -83,7 +83,7 @@ module Mutant
83
83
 
84
84
  private
85
85
 
86
- def find_scope
86
+ def find_raw_scope
87
87
  Object.const_get(scope_name)
88
88
  rescue NameError # rubocop:disable Lint/SuppressedException
89
89
  end
data/lib/mutant/hooks.rb CHANGED
@@ -10,6 +10,7 @@ module Mutant
10
10
  mutation_insert_post
11
11
  mutation_insert_pre
12
12
  mutation_worker_process_start
13
+ test_worker_process_start
13
14
  ].product([EMPTY_ARRAY]).to_h.transform_values(&:freeze).freeze
14
15
 
15
16
  MESSAGE = 'Unknown hook %s'
@@ -18,6 +18,7 @@ module Mutant
18
18
  # @return [Result::Test]
19
19
  def call(_tests)
20
20
  Result::Test.new(
21
+ output: '',
21
22
  passed: true,
22
23
  runtime: 0.0
23
24
  )
@@ -4,7 +4,11 @@ module Mutant
4
4
 
5
5
  # Abstract base class mutant test framework integrations
6
6
  class Integration
7
- include AbstractType, Adamantium, Anima.new(:arguments, :expression_parser, :world)
7
+ include AbstractType, Adamantium, Anima.new(
8
+ :arguments,
9
+ :expression_parser,
10
+ :world
11
+ )
8
12
 
9
13
  LOAD_MESSAGE = <<~'MESSAGE'
10
14
  Unable to load integration mutant-%<integration_name>s:
@@ -10,7 +10,7 @@ module Mutant
10
10
  const = env.world.try_const_get(const_name) or return EMPTY_ARRAY
11
11
 
12
12
  Chain.new(
13
- matchers: matched_scopes(env, const).map { |scope| Scope.new(scope: scope.raw) }
13
+ matchers: matched_scopes(env, const).map { |scope| Scope.new(scope: scope) }
14
14
  ).call(env)
15
15
  end
16
16
 
@@ -8,7 +8,7 @@ module Mutant
8
8
 
9
9
  # Dispatching builder, detects memoizable case
10
10
  #
11
- # @param [Class, Module] scope
11
+ # @param [Scope] scope
12
12
  # @param [UnboundMethod] method
13
13
  #
14
14
  # @return [Matcher::Method::Instance]
@@ -31,7 +31,7 @@ module Mutant
31
31
  # rubocop:enable Metrics/MethodLength
32
32
 
33
33
  def self.memoized_method?(scope, method_name)
34
- scope < Adamantium && scope.memoized?(method_name)
34
+ scope.raw < Adamantium && scope.raw.memoized?(method_name)
35
35
  end
36
36
  private_class_method :memoized_method?
37
37
 
@@ -48,9 +48,9 @@ module Mutant
48
48
  end
49
49
 
50
50
  def visibility
51
- if scope.private_instance_methods.include?(method_name)
51
+ if scope.raw.private_instance_methods.include?(method_name)
52
52
  :private
53
- elsif scope.protected_instance_methods.include?(method_name)
53
+ elsif scope.raw.protected_instance_methods.include?(method_name)
54
54
  :protected
55
55
  else
56
56
  :public
@@ -65,6 +65,7 @@ module Mutant
65
65
 
66
66
  def source_location
67
67
  scope
68
+ .raw
68
69
  .unmemoized_instance_method(method_name)
69
70
  .source_location
70
71
  end
@@ -62,7 +62,7 @@ module Mutant
62
62
 
63
63
  def sclass_const_name?(node)
64
64
  name = node.children.fetch(CONST_NAME_INDEX)
65
- name.to_s.eql?(context.unqualified_name)
65
+ name.to_s.eql?(scope.unqualified_name)
66
66
  end
67
67
 
68
68
  end # Evaluator
@@ -46,7 +46,7 @@ module Mutant
46
46
 
47
47
  def receiver_name?(node)
48
48
  name = node.children.fetch(NAME_INDEX)
49
- name.to_s.eql?(context.unqualified_name)
49
+ name.to_s.eql?(scope.unqualified_name)
50
50
  end
51
51
 
52
52
  end # Evaluator
@@ -14,6 +14,11 @@ module Mutant
14
14
  CLOSURE_WARNING_FORMAT =
15
15
  '%s is dynamically defined in a closure, unable to emit subject'
16
16
 
17
+ CONSTANT_SCOPES = {
18
+ class: Context::ConstantScope::Class,
19
+ module: Context::ConstantScope::Module
20
+ }.freeze
21
+
17
22
  # Matched subjects
18
23
  #
19
24
  # @param [Env] env
@@ -28,6 +33,8 @@ module Mutant
28
33
  # Present to avoid passing the env argument around in case the
29
34
  # logic would be implemented directly on the Matcher::Method
30
35
  # instance
36
+ #
37
+ # rubocop:disable Metrics/ClassLength
31
38
  class Evaluator
32
39
  include(
33
40
  AbstractType,
@@ -57,7 +64,7 @@ module Mutant
57
64
  def match_view
58
65
  return EMPTY_ARRAY if matched_view.nil?
59
66
 
60
- if matched_view.path.any?(&:block.public_method(:equal?))
67
+ if matched_view.stack.any? { |node| node.type.equal?(:block) }
61
68
  env.warn(CLOSURE_WARNING_FORMAT % target_method)
62
69
 
63
70
  return EMPTY_ARRAY
@@ -80,7 +87,26 @@ module Mutant
80
87
  end
81
88
 
82
89
  def context
83
- Context.new(scope: scope, source_path: source_path)
90
+ Context.new(constant_scope: constant_scope, scope: scope, source_path: source_path)
91
+ end
92
+
93
+ # rubocop:disable Metrics/MethodLength
94
+ def constant_scope
95
+ matched_view
96
+ .stack
97
+ .reverse
98
+ .reduce(Context::ConstantScope::None.new) do |descendant, node|
99
+ klass = CONSTANT_SCOPES[node.type]
100
+
101
+ if klass
102
+ klass.new(
103
+ const: node.children.fetch(0),
104
+ descendant: descendant
105
+ )
106
+ else
107
+ descendant
108
+ end
109
+ end
84
110
  end
85
111
 
86
112
  def ast
@@ -151,9 +177,9 @@ module Mutant
151
177
  # end
152
178
  #
153
179
  # Change to this once 3.0 is EOL.
154
- if scope.private_methods.include?(method_name)
180
+ if scope.raw.private_methods.include?(method_name)
155
181
  :private
156
- elsif scope.protected_methods.include?(method_name)
182
+ elsif scope.raw.protected_methods.include?(method_name)
157
183
  :protected
158
184
  else
159
185
  :public
@@ -57,11 +57,11 @@ module Mutant
57
57
  private
58
58
 
59
59
  def access(_env, method_name)
60
- scope.method(method_name)
60
+ scope.raw.method(method_name)
61
61
  end
62
62
 
63
63
  def candidate_scope
64
- scope.singleton_class
64
+ scope.raw.singleton_class
65
65
  end
66
66
 
67
67
  end # Singleton
@@ -73,11 +73,11 @@ module Mutant
73
73
  private
74
74
 
75
75
  def access(_env, method_name)
76
- scope.method(method_name)
76
+ scope.raw.method(method_name)
77
77
  end
78
78
 
79
79
  def candidate_scope
80
- scope.singleton_class
80
+ scope.raw.singleton_class
81
81
  end
82
82
  end # Metaclass
83
83
 
@@ -105,18 +105,19 @@ module Mutant
105
105
  private
106
106
 
107
107
  # rubocop:disable Lint/RescueException
108
+ # mutant:disable - unstable source locations under < ruby-3.2
108
109
  def access(env, method_name)
109
- scope.instance_method(method_name)
110
+ candidate_scope.instance_method(method_name)
110
111
  rescue Exception => exception
111
112
  env.warn(
112
- MESSAGE % { scope: scope, method_name: method_name, exception: exception }
113
+ MESSAGE % { scope: scope, method_name: method_name, exception: exception.inspect }
113
114
  )
114
115
  nil
115
116
  end
116
117
  # rubocop:enable Lint/RescueException
117
118
 
118
119
  def candidate_scope
119
- scope
120
+ scope.raw
120
121
  end
121
122
 
122
123
  end # Instance
@@ -13,7 +13,7 @@ module Mutant
13
13
  # @return [Enumerable<Subject>]
14
14
  def call(env)
15
15
  Chain.new(
16
- matchers: matched_scopes(env).map { |scope| Scope.new(scope: scope.raw) }
16
+ matchers: matched_scopes(env).map { |scope| Scope.new(scope: scope) }
17
17
  ).call(env)
18
18
  end
19
19