diff --git a/lib/mutant/ast/regexp.rb b/lib/mutant/ast/regexp.rb
index eee61cfc..39a8aa67 100644
--- a/lib/mutant/ast/regexp.rb
+++ b/lib/mutant/ast/regexp.rb
@@ -8,10 +8,15 @@ module Mutant
#
# @param regexp [String]
#
- # @return [Regexp::Expression]
+ # @return [Regexp::Expression, nil]
+ #
+ # rubocop:disable Lint/HandleExceptions
def self.parse(regexp)
::Regexp::Parser.parse(regexp)
+ # regexp_parser is more strict than MRI
+ rescue ::Regexp::Scanner::PrematureEndError
end
+ # rubocop:enable Lint/HandleExceptions
# Convert expression into ast node
#
diff --git a/lib/mutant/mutator/node/literal/regex.rb b/lib/mutant/mutator/node/literal/regex.rb
index 39d779ea..5d0e4e9a 100644
--- a/lib/mutant/mutator/node/literal/regex.rb
+++ b/lib/mutant/mutator/node/literal/regex.rb
@@ -42,7 +42,7 @@ module Mutant
#
# @return [undefined]
def mutate_body
- return unless body.all?(&method(:n_str?))
+ return unless body.all?(&method(:n_str?)) && body_ast
Mutator.mutate(body_ast).each do |mutation|
source = AST::Regexp.to_expression(mutation).to_s
@@ -52,14 +52,14 @@ module Mutant
# AST representation of regexp body
#
- # @return [Parser::AST::Node]
+ # @return [Parser::AST::Node, nil]
def body_ast
- AST::Regexp.to_ast(body_expression)
+ body_expression and AST::Regexp.to_ast(body_expression)
end
# Expression representation of regexp body
#
- # @return [Regexp::Expression]
+ # @return [Regexp::Expression, nil]
def body_expression
AST::Regexp.parse(body.map(&:children).join)
end
diff --git a/meta/regexp.rb b/meta/regexp.rb
index 8952a5a1..ca127f03 100644
--- a/meta/regexp.rb
+++ b/meta/regexp.rb
@@ -76,6 +76,15 @@ Mutant::Meta::Example.add :regexp do
mutation '/(?(1)(foo)(?:bar))/'
end
+# Case where MRI would accept an expression but regexp_parser not.
+Mutant::Meta::Example.add :regexp do
+ source '/u{/'
+
+ singleton_mutations
+ mutation '//'
+ mutation '/nomatch\A/'
+end
+
Pathname
.glob(Pathname.new(__dir__).join('regexp', '*.rb'))
.sort
diff --git a/spec/integrations.yml b/spec/integrations.yml
index 1f5dac3d..0b260fca 100644
--- a/spec/integrations.yml
+++ b/spec/integrations.yml
@@ -7,15 +7,7 @@
ruby_glob_pattern: '**/*_spec.rb'
mutation_coverage: false
mutation_generation: true
- expected_errors:
- "#":
- - language/regexp/escapes_spec.rb
- '#':
- - language/regexp/escapes_spec.rb
- "#":
- - language/regexp/interpolation_spec.rb
- '#':
- - language/regexp/escapes_spec.rb
+ expected_errors: {}
exclude:
- core/string/casecmp_spec.rb
- core/symbol/casecmp_spec.rb
diff --git a/spec/unit/mutant/ast/regexp/parse_spec.rb b/spec/unit/mutant/ast/regexp/parse_spec.rb
index 3094b64e..b0062cd4 100644
--- a/spec/unit/mutant/ast/regexp/parse_spec.rb
+++ b/spec/unit/mutant/ast/regexp/parse_spec.rb
@@ -1,7 +1,19 @@
# frozen_string_literal: true
RSpec.describe Mutant::AST::Regexp, '.parse' do
- it 'parses using minor ruby version' do
- expect(described_class.parse(/foo/).to_re).to eql(/foo/)
+ def apply(input)
+ described_class.parse(input)
+ end
+
+ context 'on regexp regexp_parser does accept' do
+ it 'parses using minor ruby version' do
+ expect(apply(/foo/).to_re).to eql(/foo/)
+ end
+ end
+
+ context 'on regexp regexp_parser does not accept' do
+ it 'returns nil' do
+ expect(apply(/u{/)).to be(nil)
+ end
end
end