Skip to content

Commit

Permalink
Support regexp_parser 1.8 and 2.x series
Browse files Browse the repository at this point in the history
Follow #9154,
#9155,
and #9102.

This is a step towards the widespread use of regexp_parser 2.0.

RuboCop core accepts regexp_parser 1.8, but several code is already
incompatible with regexp_parser 1.8. It can cause issue because these
combination of versions.

Therefore, this PR ports code for regexp_parser 1.8 that will never be
maintained from #9102.

Implementation of this patch, code is intentionally duplicated because
it is evaluated only when the class is defined. Also, since obsoleted
code for regexp_parser 1.8 is assumed to never be maintained, the target
to be removed is clear.

To be honest, it's ugly as implementation, but I think it has the least
impact for RuboCop 1.x series users.
  • Loading branch information
koic authored and bbatsov committed Dec 3, 2020
1 parent 1b9df89 commit 11b30cb
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 20 deletions.
32 changes: 24 additions & 8 deletions lib/rubocop/cop/style/redundant_regexp_escape.rb
Expand Up @@ -80,14 +80,30 @@ def delimiter?(node, char)
delimiters.include?(char)
end

def each_escape(node)
node.parsed_tree&.traverse&.reduce(0) do |char_class_depth, (event, expr)|
yield(expr.text[1], expr.ts, !char_class_depth.zero?) if expr.type == :escape

if expr.type == :set
char_class_depth + (event == :enter ? 1 : -1)
else
char_class_depth
if Gem::Version.new(Regexp::Parser::VERSION) >= Gem::Version.new('2.0')
def each_escape(node)
node.parsed_tree&.traverse&.reduce(0) do |char_class_depth, (event, expr)|
yield(expr.text[1], expr.ts, !char_class_depth.zero?) if expr.type == :escape

if expr.type == :set
char_class_depth + (event == :enter ? 1 : -1)
else
char_class_depth
end
end
end
# Please remove this `else` branch when support for regexp_parser 1.8 will be dropped.
# It's for compatibility with regexp_arser 1.8 and will never be maintained.
else
def each_escape(node)
node.parsed_tree&.traverse&.reduce(0) do |char_class_depth, (event, expr)|
yield(expr.text[1], expr.start_index, !char_class_depth.zero?) if expr.type == :escape

if expr.type == :set
char_class_depth + (event == :enter ? 1 : -1)
else
char_class_depth
end
end
end
end
Expand Down
40 changes: 31 additions & 9 deletions lib/rubocop/ext/regexp_node.rb
Expand Up @@ -15,17 +15,39 @@ def ANY.==(_)
# see `ext/regexp_parser`.
attr_reader :parsed_tree

def assign_properties(*)
super
if Gem::Version.new(Regexp::Parser::VERSION) >= Gem::Version.new('2.0')
def assign_properties(*)
super

str = with_interpolations_blanked
@parsed_tree = begin
Regexp::Parser.parse(str, options: options)
rescue StandardError
nil
str = with_interpolations_blanked
@parsed_tree = begin
Regexp::Parser.parse(str, options: options)
rescue StandardError
nil
end
origin = loc.begin.end
@parsed_tree&.each_expression(true) { |e| e.origin = origin }
end
# Please remove this `else` branch when support for regexp_parser 1.8 will be dropped.
# It's for compatibility with regexp_arser 1.8 and will never be maintained.
else
def assign_properties(*)
super

str = with_interpolations_blanked
begin
@parsed_tree = Regexp::Parser.parse(str, options: options)
rescue StandardError
@parsed_tree = nil
else
origin = loc.begin.end
source = @parsed_tree.to_s
@parsed_tree.each_expression(true) do |e|
e.origin = origin
e.source = source
end
end
end
origin = loc.begin.end
@parsed_tree&.each_expression(true) { |e| e.origin = origin }
end

def each_capture(named: ANY)
Expand Down
24 changes: 21 additions & 3 deletions lib/rubocop/ext/regexp_parser.rb
Expand Up @@ -22,9 +22,27 @@ module Expression
module Base
attr_accessor :origin

# Shortcut to `loc.expression`
def expression
@expression ||= origin.adjust(begin_pos: ts, end_pos: ts + full_length)
if Gem::Version.new(Regexp::Parser::VERSION) >= Gem::Version.new('2.0')
# Shortcut to `loc.expression`
def expression
@expression ||= origin.adjust(begin_pos: ts, end_pos: ts + full_length)
end
# Please remove this `else` branch when support for regexp_parser 1.8 will be dropped.
# It's for compatibility with regexp_arser 1.8 and will never be maintained.
else
attr_accessor :source

def start_index
# ts is a byte index; convert it to a character index
@start_index ||= source.byteslice(0, ts).length
end

# Shortcut to `loc.expression`
def expression
@expression ||= begin
origin.adjust(begin_pos: start_index, end_pos: start_index + full_length)
end
end
end

# @returns a location map like `parser` does, with:
Expand Down

0 comments on commit 11b30cb

Please sign in to comment.