-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
delete_suffix.rb
94 lines (83 loc) · 2.9 KB
/
delete_suffix.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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# frozen_string_literal: true
module RuboCop
module Cop
module Performance
# In Ruby 2.5, `String#delete_suffix` has been added.
#
# This cop identifies places where `gsub(/suffix\z/, '')` and `sub(/suffix\z/, '')`
# can be replaced by `delete_suffix('suffix')`.
#
# This cop has `SafeMultiline` configuration option that `true` by default because
# `suffix$` is unsafe as it will behave incompatible with `delete_suffix?`
# for receiver is multiline string.
#
# The `delete_suffix('suffix')` method is faster than `gsub(/suffix\z/, '')`.
#
# @safety
# This cop is unsafe because `Pathname` has `sub` but not `delete_suffix`.
#
# @example
#
# # bad
# str.gsub(/suffix\z/, '')
# str.gsub!(/suffix\z/, '')
#
# str.sub(/suffix\z/, '')
# str.sub!(/suffix\z/, '')
#
# # good
# str.delete_suffix('suffix')
# str.delete_suffix!('suffix')
#
# @example SafeMultiline: true (default)
#
# # good
# str.gsub(/suffix$/, '')
# str.gsub!(/suffix$/, '')
# str.sub(/suffix$/, '')
# str.sub!(/suffix$/, '')
#
# @example SafeMultiline: false
#
# # bad
# str.gsub(/suffix$/, '')
# str.gsub!(/suffix$/, '')
# str.sub(/suffix$/, '')
# str.sub!(/suffix$/, '')
#
class DeleteSuffix < Base
include RegexpMetacharacter
extend AutoCorrector
extend TargetRubyVersion
minimum_target_ruby_version 2.5
MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
RESTRICT_ON_SEND = %i[gsub gsub! sub sub!].freeze
PREFERRED_METHODS = {
gsub: :delete_suffix,
gsub!: :delete_suffix!,
sub: :delete_suffix,
sub!: :delete_suffix!
}.freeze
def_node_matcher :delete_suffix_candidate?, <<~PATTERN
(call $!nil? ${:gsub :gsub! :sub :sub!} (regexp (str $#literal_at_end?) (regopt)) (str $_))
PATTERN
# rubocop:disable Metrics/AbcSize
def on_send(node)
return unless (receiver, bad_method, regexp_str, replace_string = delete_suffix_candidate?(node))
return unless replace_string.empty?
good_method = PREFERRED_METHODS[bad_method]
message = format(MSG, current: bad_method, prefer: good_method)
add_offense(node.loc.selector, message: message) do |corrector|
regexp_str = drop_end_metacharacter(regexp_str)
regexp_str = interpret_string_escapes(regexp_str)
string_literal = to_string_literal(regexp_str)
new_code = "#{receiver.source}#{node.loc.dot.source}#{good_method}(#{string_literal})"
corrector.replace(node, new_code)
end
end
# rubocop:enable Metrics/AbcSize
alias on_csend on_send
end
end
end
end