forked from rubocop/rubocop-performance
-
Notifications
You must be signed in to change notification settings - Fork 0
/
redundant_split_regexp_argument.rb
64 lines (55 loc) · 1.83 KB
/
redundant_split_regexp_argument.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# frozen_string_literal: true
module RuboCop
module Cop
module Performance
# This cop identifies places where `split` argument can be replaced from
# a deterministic regexp to a string.
#
# @example
# # bad
# 'a,b,c'.split(/,/)
#
# # good
# 'a,b,c'.split(',')
class RedundantSplitRegexpArgument < Base
extend AutoCorrector
MSG = 'Use string as argument instead of regexp.'
RESTRICT_ON_SEND = %i[split].freeze
DETERMINISTIC_REGEX = /\A(?:#{LITERAL_REGEX})+\Z/.freeze
STR_SPECIAL_CHARS = %w[\n \" \' \\\\ \t \b \f \r].freeze
def_node_matcher :split_call_with_regexp?, <<~PATTERN
{(send !nil? :split $regexp)}
PATTERN
def on_send(node)
return unless (regexp_node = split_call_with_regexp?(node))
return if regexp_node.ignore_case? || regexp_node.content == ' '
return unless determinist_regexp?(regexp_node)
add_offense(regexp_node) do |corrector|
new_argument = replacement(regexp_node)
corrector.replace(regexp_node, "\"#{new_argument}\"")
end
end
private
def determinist_regexp?(regexp_node)
DETERMINISTIC_REGEX.match?(regexp_node.source)
end
def replacement(regexp_node)
regexp_content = regexp_node.content
stack = []
chars = regexp_content.chars.each_with_object([]) do |char, strings|
if stack.empty? && char == '\\'
stack.push(char)
else
strings << "#{stack.pop}#{char}"
end
end
chars.map do |char|
char = char.dup
char.delete!('\\') unless STR_SPECIAL_CHARS.include?(char)
char
end.join
end
end
end
end
end