forked from rubocop/rubocop-performance
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstart_with.rb
79 lines (72 loc) · 2.51 KB
/
start_with.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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# frozen_string_literal: true
module RuboCop
module Cop
module Performance
# Identifies unnecessary use of a regex where `String#start_with?` would suffice.
#
# This cop has `SafeMultiline` configuration option that `true` by default because
# `^start` is unsafe as it will behave incompatible with `start_with?`
# for receiver is multiline string.
#
# @safety
# This will change to a new method call which isn't guaranteed to be on the
# object. Switching these methods has to be done with knowledge of the types
# of the variables which rubocop doesn't have.
#
# @example
# # bad
# 'abc'.match?(/\Aab/)
# /\Aab/.match?('abc')
# 'abc' =~ /\Aab/
# /\Aab/ =~ 'abc'
# 'abc'.match(/\Aab/)
# /\Aab/.match('abc')
#
# # good
# 'abc'.start_with?('ab')
#
# @example SafeMultiline: true (default)
#
# # good
# 'abc'.match?(/^ab/)
# /^ab/.match?('abc')
# 'abc' =~ /^ab/
# /^ab/ =~ 'abc'
# 'abc'.match(/^ab/)
# /^ab/.match('abc')
#
# @example SafeMultiline: false
#
# # bad
# 'abc'.match?(/^ab/)
# /^ab/.match?('abc')
# 'abc' =~ /^ab/
# /^ab/ =~ 'abc'
# 'abc'.match(/^ab/)
# /^ab/.match('abc')
#
class StartWith < Base
include RegexpMetacharacter
extend AutoCorrector
MSG = 'Use `String#start_with?` instead of a regex match anchored to the beginning of the string.'
RESTRICT_ON_SEND = %i[match =~ match?].freeze
def_node_matcher :redundant_regex?, <<~PATTERN
{(send $!nil? {:match :=~ :match?} (regexp (str $#literal_at_start?) (regopt)))
(send (regexp (str $#literal_at_start?) (regopt)) {:match :match?} $_)
(match-with-lvasgn (regexp (str $#literal_at_start?) (regopt)) $_)}
PATTERN
def on_send(node)
return unless (receiver, regex_str = redundant_regex?(node))
add_offense(node) do |corrector|
receiver, regex_str = regex_str, receiver if receiver.is_a?(String)
regex_str = drop_start_metacharacter(regex_str)
regex_str = interpret_string_escapes(regex_str)
new_source = "#{receiver.source}.start_with?(#{to_string_literal(regex_str)})"
corrector.replace(node.source_range, new_source)
end
end
alias on_match_with_lvasgn on_send
end
end
end
end