mutant 0.8.10 → 0.8.11

Sign up to get free protection for your applications and to get access to all the features.
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