mutant 0.8.10 → 0.8.11

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 (247) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -4
  3. data/Changelog.md +8 -0
  4. data/README.md +112 -43
  5. data/Rakefile +2 -16
  6. data/circle.yml +1 -1
  7. data/config/flay.yml +2 -2
  8. data/config/flog.yml +1 -1
  9. data/config/reek.yml +3 -4
  10. data/config/rubocop.yml +53 -16
  11. data/lib/mutant.rb +27 -6
  12. data/lib/mutant/ast/meta/const.rb +2 -0
  13. data/lib/mutant/ast/meta/optarg.rb +2 -0
  14. data/lib/mutant/ast/meta/resbody.rb +2 -0
  15. data/lib/mutant/ast/meta/restarg.rb +2 -0
  16. data/lib/mutant/ast/meta/send.rb +4 -0
  17. data/lib/mutant/ast/meta/symbol.rb +2 -0
  18. data/lib/mutant/ast/named_children.rb +14 -4
  19. data/lib/mutant/ast/nodes.rb +3 -1
  20. data/lib/mutant/ast/regexp.rb +53 -0
  21. data/lib/mutant/ast/regexp/transformer.rb +185 -0
  22. data/lib/mutant/ast/regexp/transformer/alternative.rb +39 -0
  23. data/lib/mutant/ast/regexp/transformer/character_set.rb +46 -0
  24. data/lib/mutant/ast/regexp/transformer/direct.rb +99 -0
  25. data/lib/mutant/ast/regexp/transformer/options_group.rb +66 -0
  26. data/lib/mutant/ast/regexp/transformer/quantifier.rb +112 -0
  27. data/lib/mutant/ast/regexp/transformer/recursive.rb +50 -0
  28. data/lib/mutant/ast/regexp/transformer/root.rb +29 -0
  29. data/lib/mutant/ast/regexp/transformer/text.rb +55 -0
  30. data/lib/mutant/ast/types.rb +92 -5
  31. data/lib/mutant/cli.rb +2 -14
  32. data/lib/mutant/color.rb +1 -1
  33. data/lib/mutant/config.rb +1 -3
  34. data/lib/mutant/expression/methods.rb +1 -1
  35. data/lib/mutant/expression/namespace.rb +2 -2
  36. data/lib/mutant/expression/parser.rb +1 -1
  37. data/lib/mutant/integration.rb +10 -28
  38. data/lib/mutant/isolation.rb +9 -60
  39. data/lib/mutant/isolation/fork.rb +72 -0
  40. data/lib/mutant/isolation/none.rb +25 -0
  41. data/lib/mutant/matcher/config.rb +2 -0
  42. data/lib/mutant/matcher/method/singleton.rb +5 -4
  43. data/lib/mutant/meta.rb +11 -4
  44. data/lib/mutant/meta/example.rb +2 -116
  45. data/lib/mutant/meta/example/dsl.rb +22 -19
  46. data/lib/mutant/meta/example/verification.rb +86 -0
  47. data/lib/mutant/mutator.rb +22 -49
  48. data/lib/mutant/mutator/node.rb +15 -19
  49. data/lib/mutant/mutator/node/and_asgn.rb +1 -1
  50. data/lib/mutant/mutator/node/argument.rb +10 -5
  51. data/lib/mutant/mutator/node/arguments.rb +5 -9
  52. data/lib/mutant/mutator/node/begin.rb +4 -17
  53. data/lib/mutant/mutator/node/block.rb +1 -1
  54. data/lib/mutant/mutator/node/break.rb +1 -1
  55. data/lib/mutant/mutator/node/class.rb +21 -0
  56. data/lib/mutant/mutator/node/conditional_loop.rb +1 -1
  57. data/lib/mutant/mutator/node/define.rb +1 -1
  58. data/lib/mutant/mutator/node/defined.rb +1 -1
  59. data/lib/mutant/mutator/node/dstr.rb +1 -1
  60. data/lib/mutant/mutator/node/dsym.rb +1 -1
  61. data/lib/mutant/mutator/node/generic.rb +3 -3
  62. data/lib/mutant/mutator/node/kwbegin.rb +1 -1
  63. data/lib/mutant/mutator/node/literal.rb +9 -0
  64. data/lib/mutant/mutator/node/literal/boolean.rb +1 -1
  65. data/lib/mutant/mutator/node/literal/fixnum.rb +2 -2
  66. data/lib/mutant/mutator/node/literal/float.rb +4 -6
  67. data/lib/mutant/mutator/node/literal/hash.rb +1 -1
  68. data/lib/mutant/mutator/node/literal/nil.rb +1 -1
  69. data/lib/mutant/mutator/node/literal/range.rb +2 -19
  70. data/lib/mutant/mutator/node/literal/regex.rb +43 -3
  71. data/lib/mutant/mutator/node/literal/string.rb +1 -1
  72. data/lib/mutant/mutator/node/literal/symbol.rb +2 -4
  73. data/lib/mutant/mutator/node/masgn.rb +1 -1
  74. data/lib/mutant/mutator/node/match_current_line.rb +1 -1
  75. data/lib/mutant/mutator/node/mlhs.rb +2 -3
  76. data/lib/mutant/mutator/node/named_value/access.rb +2 -2
  77. data/lib/mutant/mutator/node/named_value/constant_assignment.rb +4 -3
  78. data/lib/mutant/mutator/node/named_value/variable_assignment.rb +4 -4
  79. data/lib/mutant/mutator/node/next.rb +1 -1
  80. data/lib/mutant/mutator/node/nthref.rb +1 -1
  81. data/lib/mutant/mutator/node/or_asgn.rb +1 -1
  82. data/lib/mutant/mutator/node/regexp.rb +44 -0
  83. data/lib/mutant/mutator/node/regopt.rb +31 -0
  84. data/lib/mutant/mutator/node/resbody.rb +1 -1
  85. data/lib/mutant/mutator/node/rescue.rb +1 -3
  86. data/lib/mutant/mutator/node/return.rb +1 -1
  87. data/lib/mutant/mutator/node/send.rb +43 -3
  88. data/lib/mutant/mutator/node/send/attribute_assignment.rb +4 -1
  89. data/lib/mutant/mutator/node/send/conditional.rb +23 -0
  90. data/lib/mutant/mutator/node/send/index.rb +1 -1
  91. data/lib/mutant/mutator/node/splat.rb +1 -1
  92. data/lib/mutant/mutator/node/when.rb +1 -1
  93. data/lib/mutant/mutator/node/yield.rb +1 -1
  94. data/lib/mutant/mutator/util.rb +0 -30
  95. data/lib/mutant/mutator/util/array.rb +4 -16
  96. data/lib/mutant/parallel.rb +1 -1
  97. data/lib/mutant/parallel/worker.rb +1 -1
  98. data/lib/mutant/registry.rb +44 -0
  99. data/lib/mutant/reporter/cli/format.rb +2 -0
  100. data/lib/mutant/reporter/cli/printer.rb +2 -2
  101. data/lib/mutant/reporter/cli/printer/config.rb +0 -1
  102. data/lib/mutant/reporter/cli/printer/env_progress.rb +1 -11
  103. data/lib/mutant/reporter/cli/printer/mutation_progress_result.rb +1 -1
  104. data/lib/mutant/reporter/cli/printer/mutation_result.rb +2 -0
  105. data/lib/mutant/reporter/cli/tput.rb +1 -1
  106. data/lib/mutant/reporter/sequence.rb +3 -0
  107. data/lib/mutant/require_highjack.rb +6 -2
  108. data/lib/mutant/result.rb +1 -1
  109. data/lib/mutant/subject.rb +5 -5
  110. data/lib/mutant/subject/method/instance.rb +1 -2
  111. data/lib/mutant/util.rb +18 -0
  112. data/lib/mutant/version.rb +1 -1
  113. data/lib/mutant/zombifier.rb +5 -3
  114. data/meta/and.rb +1 -1
  115. data/meta/and_asgn.rb +1 -1
  116. data/meta/array.rb +2 -2
  117. data/meta/begin.rb +2 -2
  118. data/meta/block.rb +7 -7
  119. data/meta/block_pass.rb +1 -1
  120. data/meta/blockarg.rb +1 -1
  121. data/meta/break.rb +1 -1
  122. data/meta/case.rb +2 -2
  123. data/meta/casgn.rb +11 -2
  124. data/meta/cbase.rb +1 -1
  125. data/meta/class.rb +10 -0
  126. data/meta/const.rb +9 -1
  127. data/meta/csend.rb +8 -0
  128. data/meta/cvar.rb +1 -1
  129. data/meta/cvasgn.rb +1 -1
  130. data/meta/date.rb +4 -4
  131. data/meta/def.rb +14 -14
  132. data/meta/defined.rb +1 -1
  133. data/meta/dstr.rb +1 -1
  134. data/meta/dsym.rb +1 -1
  135. data/meta/ensure.rb +1 -1
  136. data/meta/false.rb +1 -1
  137. data/meta/float.rb +3 -3
  138. data/meta/gvar.rb +1 -1
  139. data/meta/gvasgn.rb +1 -1
  140. data/meta/hash.rb +1 -1
  141. data/meta/if.rb +17 -3
  142. data/meta/int.rb +1 -1
  143. data/meta/ivar.rb +1 -1
  144. data/meta/ivasgn.rb +14 -1
  145. data/meta/kwarg.rb +8 -0
  146. data/meta/kwbegin.rb +1 -1
  147. data/meta/kwoptarg.rb +11 -0
  148. data/meta/lvar.rb +1 -1
  149. data/meta/lvasgn.rb +1 -1
  150. data/meta/masgn.rb +1 -1
  151. data/meta/match_current_line.rb +2 -2
  152. data/meta/next.rb +1 -1
  153. data/meta/nil.rb +1 -1
  154. data/meta/nthref.rb +5 -5
  155. data/meta/op_assgn.rb +1 -1
  156. data/meta/or.rb +1 -1
  157. data/meta/or_asgn.rb +5 -5
  158. data/meta/range.rb +2 -8
  159. data/meta/redo.rb +1 -1
  160. data/meta/regexp.rb +106 -0
  161. data/meta/regexp/regexp_bol_anchor.rb +13 -0
  162. data/meta/regexp/regexp_bos_anchor.rb +26 -0
  163. data/meta/regexp/regexp_root_expression.rb +13 -0
  164. data/meta/regopt.rb +8 -0
  165. data/meta/rescue.rb +49 -4
  166. data/meta/restarg.rb +6 -9
  167. data/meta/return.rb +2 -2
  168. data/meta/self.rb +1 -1
  169. data/meta/send.rb +228 -55
  170. data/meta/str.rb +1 -1
  171. data/meta/super.rb +3 -3
  172. data/meta/{symbol.rb → sym.rb} +1 -1
  173. data/meta/true.rb +1 -1
  174. data/meta/until.rb +1 -1
  175. data/meta/while.rb +2 -2
  176. data/meta/yield.rb +1 -1
  177. data/mutant-rspec.gemspec +2 -2
  178. data/mutant.gemspec +6 -5
  179. data/spec/integration/mutant/isolation/fork_spec.rb +8 -0
  180. data/spec/integration/mutant/rspec_spec.rb +1 -1
  181. data/spec/integration/mutant/test_mutator_handles_types_spec.rb +1 -2
  182. data/spec/integrations.yml +93 -24
  183. data/spec/spec_helper.rb +12 -7
  184. data/spec/support/compress_helper.rb +1 -1
  185. data/spec/support/corpus.rb +115 -50
  186. data/spec/support/fake_actor.rb +5 -5
  187. data/spec/support/file_system.rb +1 -1
  188. data/spec/support/ice_nine_config.rb +3 -3
  189. data/spec/support/ruby_vm.rb +11 -12
  190. data/spec/support/shared_context.rb +22 -13
  191. data/spec/support/test_app.rb +1 -1
  192. data/spec/support/warning.rb +64 -0
  193. data/spec/support/warnings.yml +4 -0
  194. data/spec/support/xspec.rb +177 -0
  195. data/spec/unit/mutant/actor/env_spec.rb +2 -2
  196. data/spec/unit/mutant/actor/sender_spec.rb +1 -1
  197. data/spec/unit/mutant/ast/meta/send_spec.rb +1 -1
  198. data/spec/unit/mutant/ast/named_children_spec.rb +26 -0
  199. data/spec/unit/mutant/ast/regexp/parse_spec.rb +7 -0
  200. data/spec/unit/mutant/ast/regexp/supported_predicate_spec.rb +14 -0
  201. data/spec/unit/mutant/ast/regexp/transformer/lookup_table/table_spec.rb +19 -0
  202. data/spec/unit/mutant/ast/regexp/transformer/lookup_table_spec.rb +33 -0
  203. data/spec/unit/mutant/ast/regexp/transformer_spec.rb +19 -0
  204. data/spec/unit/mutant/ast/regexp_spec.rb +617 -0
  205. data/spec/unit/mutant/cli_spec.rb +7 -45
  206. data/spec/unit/mutant/context_spec.rb +4 -7
  207. data/spec/unit/mutant/env/{boostrap_spec.rb → bootstrap_spec.rb} +2 -2
  208. data/spec/unit/mutant/env_spec.rb +13 -16
  209. data/spec/unit/mutant/expression/namespace/{flat_spec.rb → exact_spec.rb} +0 -0
  210. data/spec/unit/mutant/integration/rspec_spec.rb +2 -2
  211. data/spec/unit/mutant/integration_spec.rb +14 -0
  212. data/spec/unit/mutant/isolation/fork_spec.rb +155 -0
  213. data/spec/unit/mutant/isolation/none_spec.rb +16 -0
  214. data/spec/unit/mutant/loader_spec.rb +1 -1
  215. data/spec/unit/mutant/matcher/methods/instance_spec.rb +2 -4
  216. data/spec/unit/mutant/meta/example/dsl_spec.rb +106 -0
  217. data/spec/unit/mutant/meta/example/verification_spec.rb +120 -0
  218. data/spec/unit/mutant/meta/example_spec.rb +32 -0
  219. data/spec/unit/mutant/mutator/node_spec.rb +37 -4
  220. data/spec/unit/mutant/mutator_spec.rb +21 -0
  221. data/spec/unit/mutant/{runner → parallel}/driver_spec.rb +0 -0
  222. data/spec/unit/mutant/parallel/master_spec.rb +13 -13
  223. data/spec/unit/mutant/registry_spec.rb +47 -0
  224. data/spec/unit/mutant/reporter/cli/printer/config_spec.rb +0 -4
  225. data/spec/unit/mutant/reporter/cli/printer/env_progress_spec.rb +0 -8
  226. data/spec/unit/mutant/reporter/cli/printer/env_result_spec.rb +0 -2
  227. data/spec/unit/mutant/reporter/cli/printer/status_progressive_spec.rb +0 -8
  228. data/spec/unit/mutant/reporter/cli/printer/status_spec.rb +0 -34
  229. data/spec/unit/mutant/reporter/cli_spec.rb +0 -22
  230. data/spec/unit/mutant/repository/diff_spec.rb +6 -6
  231. data/spec/unit/mutant/require_highjack_spec.rb +38 -14
  232. data/spec/unit/mutant/result/env_spec.rb +1 -4
  233. data/spec/unit/mutant/runner_spec.rb +1 -1
  234. data/spec/unit/mutant/subject/method/instance_spec.rb +1 -1
  235. data/spec/unit/mutant/subject_spec.rb +3 -3
  236. data/spec/unit/mutant/util/one_spec.rb +20 -0
  237. data/spec/unit/mutant/zombifier_spec.rb +18 -18
  238. data/test_app/{Gemfile.rspec3.3 → Gemfile.rspec3.5} +2 -2
  239. metadata +94 -24
  240. data/TODO +0 -21
  241. data/lib/mutant/mutator/node/blockarg.rb +0 -13
  242. data/lib/mutant/mutator/node/restarg.rb +0 -13
  243. data/lib/mutant/mutator/registry.rb +0 -49
  244. data/meta/boolean.rb +0 -13
  245. data/meta/regex.rb +0 -19
  246. data/spec/unit/mutant/isolation_spec.rb +0 -104
  247. data/spec/unit/mutant/mutator/registry_spec.rb +0 -57
@@ -8,7 +8,7 @@ module Mutant
8
8
  handle(:regexp)
9
9
 
10
10
  # No input can ever be matched with this
11
- NULL_REGEXP_SOURCE = 'a\A'.freeze
11
+ NULL_REGEXP_SOURCE = 'nomatch\A'.freeze
12
12
 
13
13
  private
14
14
 
@@ -19,11 +19,12 @@ module Mutant
19
19
  children.last
20
20
  end
21
21
 
22
- # Emit mutants
22
+ # Emit mutations
23
23
  #
24
24
  # @return [undefined]
25
25
  def dispatch
26
- emit_singletons unless parent_node && n_match_current_line?(parent_node)
26
+ mutate_body
27
+ emit_singletons unless parent_node
27
28
  children.each_with_index do |child, index|
28
29
  mutate_child(index) unless n_str?(child)
29
30
  end
@@ -31,6 +32,45 @@ module Mutant
31
32
  emit_type(s(:str, NULL_REGEXP_SOURCE), options)
32
33
  end
33
34
 
35
+ # Mutate regexp body
36
+ #
37
+ # @note will only mutate parts of regexp body if the
38
+ # body is composed of only strings. Regular expressions
39
+ # with interpolation are skipped
40
+ #
41
+ # @return [undefined]
42
+ def mutate_body
43
+ return unless body.all?(&method(:n_str?))
44
+ return unless AST::Regexp.supported?(body_expression)
45
+
46
+ Mutator.mutate(body_ast).each do |mutation|
47
+ source = AST::Regexp.to_expression(mutation).to_s
48
+ emit_type(s(:str, source), options)
49
+ end
50
+ end
51
+
52
+ # AST representation of regexp body
53
+ #
54
+ # @return [Parser::AST::Node]
55
+ def body_ast
56
+ AST::Regexp.to_ast(body_expression)
57
+ end
58
+
59
+ # Expression representation of regexp body
60
+ #
61
+ # @return [Regexp::Expression]
62
+ def body_expression
63
+ AST::Regexp.parse(body.map(&:children).join)
64
+ end
65
+ memoize :body_expression
66
+
67
+ # Children of regexp node which compose regular expression source
68
+ #
69
+ # @return [Array<Parser::AST::Node>]
70
+ def body
71
+ children.slice(0...-1)
72
+ end
73
+
34
74
  end # Regex
35
75
  end # Literal
36
76
  end # Node
@@ -9,7 +9,7 @@ module Mutant
9
9
 
10
10
  private
11
11
 
12
- # Emit mutants
12
+ # Emit mutations
13
13
  #
14
14
  # @return [undefined]
15
15
  def dispatch
@@ -13,14 +13,12 @@ module Mutant
13
13
 
14
14
  private
15
15
 
16
- # Emit mutants
16
+ # Emit mutations
17
17
  #
18
18
  # @return [undefined]
19
19
  def dispatch
20
20
  emit_singletons
21
- Mutator::Util::Symbol.each(value, self) do |value|
22
- emit_type(value)
23
- end
21
+ Util::Symbol.call(value).each(&method(:emit_type))
24
22
  end
25
23
 
26
24
  end # Symbol
@@ -11,7 +11,7 @@ module Mutant
11
11
 
12
12
  private
13
13
 
14
- # Perform dispatch
14
+ # Emit mutations
15
15
  #
16
16
  # @return [undefined]
17
17
  def dispatch
@@ -10,7 +10,7 @@ module Mutant
10
10
 
11
11
  private
12
12
 
13
- # Emit mutants
13
+ # Emit mutations
14
14
  #
15
15
  # @return [undefined]
16
16
  def dispatch
@@ -12,9 +12,8 @@ module Mutant
12
12
  #
13
13
  # @return [undefined]
14
14
  def dispatch
15
- children.each_index do |index|
16
- mutate_child(index)
17
- delete_child(index) unless children.one?
15
+ mutate_single_child do |_child, index|
16
+ delete_child(index)
18
17
  end
19
18
  end
20
19
 
@@ -33,7 +33,7 @@ module Mutant
33
33
  super()
34
34
  end
35
35
 
36
- private
36
+ private
37
37
 
38
38
  # Emit instance variable as attribute send
39
39
  #
@@ -48,7 +48,7 @@ module Mutant
48
48
  def attribute_name
49
49
  name.slice(NAME_RANGE).to_sym
50
50
  end
51
- end
51
+ end # Ivar
52
52
 
53
53
  end # Access
54
54
  end # NamedValue
@@ -12,12 +12,13 @@ module Mutant
12
12
 
13
13
  private
14
14
 
15
- # Perform dispatch
15
+ # Emit mutations
16
16
  #
17
17
  # @return [undefined]
18
18
  def dispatch
19
19
  mutate_name
20
- emit_value_mutations if value
20
+ return unless value # op asgn
21
+ emit_value_mutations
21
22
  emit_remove_const
22
23
  end
23
24
 
@@ -32,7 +33,7 @@ module Mutant
32
33
  #
33
34
  # @return [undefined]
34
35
  def mutate_name
35
- Mutator::Util::Symbol.each(name, self) do |name|
36
+ Util::Symbol.call(name).each do |name|
36
37
  emit_name(name.upcase)
37
38
  end
38
39
  end
@@ -16,20 +16,20 @@ module Mutant
16
16
  }
17
17
 
18
18
  MAP = IceNine.deep_freeze(
19
- Hash[map.map { |type, prefix| [type, [prefix, /^#{Regexp.escape(prefix)}/]] }]
19
+ Hash[map.map { |type, prefix| [type, [prefix, /^#{::Regexp.escape(prefix)}/]] }]
20
20
  )
21
21
 
22
22
  handle(*MAP.keys)
23
23
 
24
24
  private
25
25
 
26
- # Perform dispatch
26
+ # Emit mutations
27
27
  #
28
28
  # @return [undefined]
29
29
  def dispatch
30
30
  emit_singletons
31
31
  mutate_name
32
- emit_value_mutations if value # mlhs!
32
+ emit_value_mutations if value # op asgn!
33
33
  end
34
34
 
35
35
  # Emit name mutations
@@ -38,7 +38,7 @@ module Mutant
38
38
  def mutate_name
39
39
  prefix, regexp = MAP.fetch(node.type)
40
40
  stripped = name.to_s.sub(regexp, EMPTY_STRING)
41
- Mutator::Util::Symbol.each(stripped, self) do |name|
41
+ Util::Symbol.call(stripped).each do |name|
42
42
  emit_name(:"#{prefix}#{name}")
43
43
  end
44
44
  end
@@ -13,7 +13,7 @@ module Mutant
13
13
  #
14
14
  # @return [undefined]
15
15
  def dispatch
16
- super
16
+ super()
17
17
  emit_singletons
18
18
  children.each_index(&method(:delete_child))
19
19
  emit(s(:break, *children))
@@ -10,7 +10,7 @@ module Mutant
10
10
 
11
11
  private
12
12
 
13
- # Perform dispatch
13
+ # Emit mutations
14
14
  #
15
15
  # @return [undefined]
16
16
  def dispatch
@@ -2,7 +2,7 @@ module Mutant
2
2
  class Mutator
3
3
  class Node
4
4
 
5
- # OpAsgn mutator
5
+ # OrAsgn mutator
6
6
  class OrAsgn < self
7
7
 
8
8
  handle(:or_asgn)
@@ -0,0 +1,44 @@
1
+ module Mutant
2
+ class Mutator
3
+ class Node
4
+ module Regexp
5
+ # Generic regexp mutator
6
+ class Generic < Node
7
+ handle(*(AST::Types::REGEXP - %i[regexp_root_expression regexp_bol_anchor]))
8
+
9
+ # Noop dispatch
10
+ #
11
+ # @return [undefined]
12
+ def dispatch
13
+ end
14
+ end # Generic
15
+
16
+ # Mutator for root expression regexp wrapper
17
+ class RootExpression < Node
18
+ handle(:regexp_root_expression)
19
+
20
+ # Emit mutations for children of root node
21
+ #
22
+ # @return [undefined]
23
+ def dispatch
24
+ children.each_index(&method(:mutate_child))
25
+ end
26
+ end # RootExpression
27
+
28
+ # Mutator for beginning of line anchor `^`
29
+ class BeginningOfLineAnchor < Node
30
+ handle(:regexp_bol_anchor)
31
+
32
+ # Emit mutations
33
+ #
34
+ # Replace `^` with `\A`
35
+ #
36
+ # @return [undefined]
37
+ def dispatch
38
+ emit(s(:regexp_bos_anchor))
39
+ end
40
+ end # BeginningOfLineAnchor
41
+ end # Regexp
42
+ end # Node
43
+ end # Mutator
44
+ end # Mutant
@@ -0,0 +1,31 @@
1
+ module Mutant
2
+ class Mutator
3
+ class Node
4
+
5
+ # Regular expression options mutation
6
+ class Regopt < self
7
+
8
+ MUTATED_FLAGS = IceNine.deep_freeze(%i[i])
9
+
10
+ handle(:regopt)
11
+
12
+ private
13
+
14
+ # Emit mutations
15
+ #
16
+ # @return [undefined]
17
+ def dispatch
18
+ emit_type(*mutated_flags)
19
+ end
20
+
21
+ # Altered flags array excluding case insensitive flag
22
+ #
23
+ # @return [Array<Symbol>]
24
+ def mutated_flags
25
+ (children - MUTATED_FLAGS)
26
+ end
27
+
28
+ end # Regopt
29
+ end # Node
30
+ end # Mutator
31
+ end # Mutant
@@ -24,7 +24,7 @@ module Mutant
24
24
  # @return [undefined]
25
25
  def mutate_captures
26
26
  return unless captures
27
- Util::Array::Element.each(captures.children, self) do |matchers|
27
+ Util::Array::Element.call(captures.children).each do |matchers|
28
28
  next if matchers.any?(&method(:n_nil?))
29
29
  emit_captures(s(:array, *matchers))
30
30
  end
@@ -28,10 +28,8 @@ module Mutant
28
28
  # @return [undefined]
29
29
  def mutate_rescue_bodies
30
30
  children_indices(RESCUE_INDICES).each do |index|
31
- rescue_body = children.at(index)
32
- next unless rescue_body
33
31
  mutate_child(index)
34
- resbody_body = AST::Meta::Resbody.new(rescue_body).body
32
+ resbody_body = AST::Meta::Resbody.new(children.fetch(index)).body
35
33
  emit_concat(resbody_body) if resbody_body
36
34
  end
37
35
  end
@@ -10,7 +10,7 @@ module Mutant
10
10
 
11
11
  private
12
12
 
13
- # Emit mutants
13
+ # Emit mutations
14
14
  #
15
15
  # @return [undefined]
16
16
  def dispatch
@@ -18,13 +18,19 @@ module Mutant
18
18
  reverse_each: %i[each],
19
19
  reverse_merge: %i[merge],
20
20
  map: %i[each],
21
+ sample: %i[first last],
22
+ pop: %i[last],
23
+ shift: %i[first],
24
+ first: %i[last],
25
+ last: %i[first],
21
26
  send: %i[public_send __send__],
22
27
  __send__: %i[public_send],
28
+ method: %i[public_method],
23
29
  gsub: %i[sub],
24
30
  eql?: %i[equal?],
25
31
  to_s: %i[to_str],
26
32
  to_i: %i[to_int],
27
- to_a: %i[to_ary],
33
+ to_a: %i[to_ary to_set],
28
34
  to_h: %i[to_hash],
29
35
  at: %i[fetch key?],
30
36
  fetch: %i[key?],
@@ -45,7 +51,7 @@ module Mutant
45
51
 
46
52
  private
47
53
 
48
- # Perform dispatch
54
+ # Emit mutations
49
55
  #
50
56
  # @return [undefined]
51
57
  def dispatch
@@ -82,6 +88,7 @@ module Mutant
82
88
  #
83
89
  # @return [Enumerable<Parser::AST::Node>]
84
90
  alias_method :arguments, :remaining_children
91
+ private :arguments
85
92
 
86
93
  # Perform normal, non special case dispatch
87
94
  #
@@ -102,6 +109,8 @@ module Mutant
102
109
  def emit_selector_specific_mutations
103
110
  emit_const_get_mutation
104
111
  emit_integer_mutation
112
+ emit_dig_mutation
113
+ emit_drop_mutation
105
114
  end
106
115
 
107
116
  # Emit selector mutations specific to top level constants
@@ -116,6 +125,37 @@ module Mutant
116
125
  .each(&method(:emit_selector))
117
126
  end
118
127
 
128
+ # Emit mutation for `#dig`
129
+ #
130
+ # - Mutates `foo.dig(a, b)` to `foo.fetch(a).dig(b)`
131
+ # - Mutates `foo.dig(a)` to `foo.fetch(a)`
132
+ #
133
+ # @return [undefined]
134
+ def emit_dig_mutation
135
+ return if !selector.equal?(:dig) || arguments.none?
136
+
137
+ head, *tail = arguments
138
+
139
+ fetch_mutation = s(:send, receiver, :fetch, head)
140
+
141
+ return emit(fetch_mutation) if tail.empty?
142
+
143
+ emit(s(:send, fetch_mutation, :dig, *tail))
144
+ end
145
+
146
+ # Emit mutation `foo[n..-1]` -> `foo.drop(n)`
147
+ #
148
+ # @return [undefined]
149
+ def emit_drop_mutation
150
+ return if !selector.equal?(:[]) || !arguments.one? || !n_irange?(arguments.first)
151
+
152
+ start, ending = *arguments.first
153
+
154
+ return unless ending.eql?(s(:int, -1))
155
+
156
+ emit(s(:send, receiver, :drop, start))
157
+ end
158
+
119
159
  # Emit mutation from `to_i` to `Integer(...)`
120
160
  #
121
161
  # @return [undefined]
@@ -145,7 +185,7 @@ module Mutant
145
185
  #
146
186
  # @return [undefined]
147
187
  def emit_naked_receiver
148
- emit(receiver) if receiver && !NOT_ASSIGNABLE.include?(receiver.type)
188
+ emit(receiver) if receiver
149
189
  end
150
190
 
151
191
  # Mutate arguments
@@ -5,6 +5,9 @@ module Mutant
5
5
  # Mutator for attribute assignments
6
6
  class AttributeAssignment < self
7
7
 
8
+ ATTRIBUTE_RANGE = (0..-2).freeze
9
+ private_constant(*constants(false))
10
+
8
11
  private
9
12
 
10
13
  # Emit mutations
@@ -28,7 +31,7 @@ module Mutant
28
31
  #
29
32
  # @return [undefined]
30
33
  def emit_attribute_read
31
- emit_type(receiver, selector.to_s[0..-2].to_sym)
34
+ emit_type(receiver, selector[ATTRIBUTE_RANGE].to_sym)
32
35
  end
33
36
 
34
37
  end # AttributeAssignment