Class: Mutineer::Mutators::RegexLiteral

Inherits:
Base
  • Object
show all
Defined in:
lib/mutineer/mutators/regex_literal.rb

Overview

Regular-expression literal mutator (Tier-2).

Conservative, clearly-safe textual swaps inside a plain regex literal: drop a leading ^ anchor, drop a trailing $ anchor, and swap the + and * quantifiers. Interpolated regexes use a different node type and are never visited. Escaped tokens (preceded by a backslash) are left alone.

Instance Method Summary collapse

Methods inherited from Base

#heredoc?, #mutations_for

Instance Method Details

#emit(start_offset, end_offset, replacement) ⇒ void (private)

This method returns an undefined value.

Emits a regex mutation.

Parameters:

  • start_offset (Integer) —

    byte start.

  • end_offset (Integer) —

    byte end.

  • replacement (String) —

    replacement text.



62
63
64
65
66
67
68
69
# File 'lib/mutineer/mutators/regex_literal.rb', line 62

def emit(start_offset, end_offset, replacement)
  @mutations << Mutation.new(
    start_offset: start_offset,
    end_offset: end_offset,
    replacement: replacement,
    operator: :regex
  )
end

#scan(content, base) ⇒ void (private)

This method returns an undefined value.

Scans the pattern bytes and emits anchor/quantifier mutations.

Parameters:

  • content (String) —

    regex pattern between the slashes.

  • base (Integer) —

    byte offset of the pattern start in source.



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/mutineer/mutators/regex_literal.rb', line 32

def scan(content, base)
  emit(base, base + 1, "") if content.start_with?("^")
  emit(base + content.bytesize - 1, base + content.bytesize, "") if trailing_anchor?(content)

  escaped = false
  offset = base
  content.each_char do |ch|
    unless escaped
      emit(offset, offset + 1, "*") if ch == "+"
      emit(offset, offset + 1, "+") if ch == "*"
    end
    escaped = ch == "\\" && !escaped
    offset += ch.bytesize
  end
end

#trailing_anchor?(content) ⇒ Boolean (private)

Whether the pattern ends with an unescaped $ anchor.

Parameters:

  • content (String) —

    regex pattern.

Returns:

  • (Boolean) —

    true when a trailing anchor is droppable.



52
53
54
# File 'lib/mutineer/mutators/regex_literal.rb', line 52

def trailing_anchor?(content)
  content.end_with?("$") && !content.end_with?("\\$")
end

#visit_regular_expression_node(node) ⇒ void

This method returns an undefined value.

Visits regular-expression literals.

Parameters:

  • node (Prism::RegularExpressionNode) —

    node to inspect.



18
19
20
21
22
23
# File 'lib/mutineer/mutators/regex_literal.rb', line 18

def visit_regular_expression_node(node)
  loc = node.content_loc
  content = @source.byteslice(loc.start_offset...loc.end_offset)
  scan(content, loc.start_offset)
  super
end