rubocop 0.67.2 → 0.68.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/config/default.yml +86 -233
- data/exe/rubocop +0 -12
- data/lib/rubocop.rb +13 -30
- data/lib/rubocop/ast/builder.rb +4 -0
- data/lib/rubocop/ast/node/alias_node.rb +24 -0
- data/lib/rubocop/ast/node/class_node.rb +31 -0
- data/lib/rubocop/ast/node/module_node.rb +24 -0
- data/lib/rubocop/ast/node/range_node.rb +7 -0
- data/lib/rubocop/ast/node/resbody_node.rb +12 -0
- data/lib/rubocop/ast/node/self_class_node.rb +24 -0
- data/lib/rubocop/cli.rb +40 -4
- data/lib/rubocop/config.rb +9 -7
- data/lib/rubocop/config_loader.rb +48 -7
- data/lib/rubocop/config_loader_resolver.rb +5 -4
- data/lib/rubocop/cop/commissioner.rb +24 -0
- data/lib/rubocop/cop/correctors/unused_arg_corrector.rb +18 -6
- data/lib/rubocop/cop/internal_affairs/node_destructuring.rb +12 -14
- data/lib/rubocop/cop/layout/access_modifier_indentation.rb +9 -20
- data/lib/rubocop/cop/layout/align_arguments.rb +93 -0
- data/lib/rubocop/cop/layout/align_parameters.rb +57 -33
- data/lib/rubocop/cop/layout/class_structure.rb +5 -5
- data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +6 -8
- data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +3 -6
- data/lib/rubocop/cop/layout/empty_lines_around_module_body.rb +1 -2
- data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +1 -0
- data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +292 -0
- data/lib/rubocop/cop/layout/{first_parameter_indentation.rb → indent_first_argument.rb} +11 -12
- data/lib/rubocop/cop/layout/{indent_array.rb → indent_first_array_element.rb} +2 -2
- data/lib/rubocop/cop/layout/{indent_hash.rb → indent_first_hash_element.rb} +2 -2
- data/lib/rubocop/cop/layout/indent_first_parameter.rb +96 -0
- data/lib/rubocop/cop/layout/indentation_width.rb +4 -16
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +2 -4
- data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +1 -16
- data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +1 -2
- data/lib/rubocop/cop/lint/duplicate_methods.rb +6 -8
- data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +4 -8
- data/lib/rubocop/cop/lint/heredoc_method_call_position.rb +157 -0
- data/lib/rubocop/cop/lint/inherit_exception.rb +3 -4
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +18 -1
- data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +3 -5
- data/lib/rubocop/cop/lint/underscore_prefixed_variable_name.rb +25 -5
- data/lib/rubocop/cop/lint/useless_assignment.rb +2 -6
- data/lib/rubocop/cop/lint/useless_setter_call.rb +1 -2
- data/lib/rubocop/cop/message_annotator.rb +1 -0
- data/lib/rubocop/cop/metrics/line_length.rb +139 -28
- data/lib/rubocop/cop/metrics/perceived_complexity.rb +3 -4
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +190 -0
- data/lib/rubocop/cop/mixin/{array_hash_indentation.rb → multiline_element_indentation.rb} +3 -2
- data/lib/rubocop/cop/mixin/too_many_lines.rb +3 -7
- data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +33 -4
- data/lib/rubocop/cop/rails/active_record_override.rb +23 -8
- data/lib/rubocop/cop/rails/delegate.rb +5 -8
- data/lib/rubocop/cop/rails/environment_comparison.rb +5 -3
- data/lib/rubocop/cop/rails/find_each.rb +1 -1
- data/lib/rubocop/cop/rails/redundant_allow_nil.rb +3 -3
- data/lib/rubocop/cop/rails/reflection_class_name.rb +1 -1
- data/lib/rubocop/cop/rails/skips_model_validations.rb +6 -7
- data/lib/rubocop/cop/rails/time_zone.rb +3 -10
- data/lib/rubocop/cop/rails/validation.rb +3 -0
- data/lib/rubocop/cop/registry.rb +3 -3
- data/lib/rubocop/cop/style/alias.rb +13 -7
- data/lib/rubocop/cop/style/block_delimiters.rb +20 -0
- data/lib/rubocop/cop/style/class_and_module_children.rb +19 -21
- data/lib/rubocop/cop/style/class_methods.rb +16 -24
- data/lib/rubocop/cop/style/conditional_assignment.rb +20 -49
- data/lib/rubocop/cop/style/documentation.rb +3 -7
- data/lib/rubocop/cop/style/format_string.rb +18 -21
- data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
- data/lib/rubocop/cop/style/inverse_methods.rb +4 -0
- data/lib/rubocop/cop/style/lambda.rb +12 -8
- data/lib/rubocop/cop/style/mixin_grouping.rb +8 -10
- data/lib/rubocop/cop/style/module_function.rb +2 -3
- data/lib/rubocop/cop/style/next.rb +10 -14
- data/lib/rubocop/cop/style/one_line_conditional.rb +5 -3
- data/lib/rubocop/cop/style/optional_arguments.rb +1 -4
- data/lib/rubocop/cop/style/random_with_offset.rb +44 -47
- data/lib/rubocop/cop/style/redundant_return.rb +6 -14
- data/lib/rubocop/cop/style/redundant_sort_by.rb +1 -1
- data/lib/rubocop/cop/style/safe_navigation.rb +3 -0
- data/lib/rubocop/cop/style/struct_inheritance.rb +2 -3
- data/lib/rubocop/cop/style/symbol_proc.rb +20 -40
- data/lib/rubocop/cop/style/unless_else.rb +1 -2
- data/lib/rubocop/cop/style/yoda_condition.rb +8 -7
- data/lib/rubocop/cop/util.rb +2 -4
- data/lib/rubocop/file_finder.rb +5 -10
- data/lib/rubocop/formatter/disabled_config_formatter.rb +5 -0
- data/lib/rubocop/node_pattern.rb +304 -170
- data/lib/rubocop/options.rb +4 -1
- data/lib/rubocop/rspec/shared_contexts.rb +3 -0
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop/yaml_duplication_checker.rb +1 -1
- metadata +26 -50
- data/lib/rubocop/cop/performance/caller.rb +0 -69
- data/lib/rubocop/cop/performance/case_when_splat.rb +0 -177
- data/lib/rubocop/cop/performance/casecmp.rb +0 -108
- data/lib/rubocop/cop/performance/chain_array_allocation.rb +0 -78
- data/lib/rubocop/cop/performance/compare_with_block.rb +0 -122
- data/lib/rubocop/cop/performance/count.rb +0 -102
- data/lib/rubocop/cop/performance/detect.rb +0 -110
- data/lib/rubocop/cop/performance/double_start_end_with.rb +0 -94
- data/lib/rubocop/cop/performance/end_with.rb +0 -56
- data/lib/rubocop/cop/performance/fixed_size.rb +0 -97
- data/lib/rubocop/cop/performance/flat_map.rb +0 -78
- data/lib/rubocop/cop/performance/inefficient_hash_search.rb +0 -99
- data/lib/rubocop/cop/performance/open_struct.rb +0 -46
- data/lib/rubocop/cop/performance/range_include.rb +0 -50
- data/lib/rubocop/cop/performance/redundant_block_call.rb +0 -93
- data/lib/rubocop/cop/performance/redundant_match.rb +0 -56
- data/lib/rubocop/cop/performance/redundant_merge.rb +0 -183
- data/lib/rubocop/cop/performance/regexp_match.rb +0 -265
- data/lib/rubocop/cop/performance/reverse_each.rb +0 -42
- data/lib/rubocop/cop/performance/size.rb +0 -77
- data/lib/rubocop/cop/performance/start_with.rb +0 -59
- data/lib/rubocop/cop/performance/string_replacement.rb +0 -173
- data/lib/rubocop/cop/performance/times_map.rb +0 -71
- data/lib/rubocop/cop/performance/unfreeze_string.rb +0 -50
- data/lib/rubocop/cop/performance/uri_default_parser.rb +0 -47
@@ -1,42 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RuboCop
|
4
|
-
module Cop
|
5
|
-
module Performance
|
6
|
-
# This cop is used to identify usages of `reverse.each` and
|
7
|
-
# change them to use `reverse_each` instead.
|
8
|
-
#
|
9
|
-
# @example
|
10
|
-
# # bad
|
11
|
-
# [].reverse.each
|
12
|
-
#
|
13
|
-
# # good
|
14
|
-
# [].reverse_each
|
15
|
-
class ReverseEach < Cop
|
16
|
-
include RangeHelp
|
17
|
-
|
18
|
-
MSG = 'Use `reverse_each` instead of `reverse.each`.'.freeze
|
19
|
-
UNDERSCORE = '_'.freeze
|
20
|
-
|
21
|
-
def_node_matcher :reverse_each?, <<-MATCHER
|
22
|
-
(send $(send _ :reverse) :each)
|
23
|
-
MATCHER
|
24
|
-
|
25
|
-
def on_send(node)
|
26
|
-
reverse_each?(node) do |receiver|
|
27
|
-
location_of_reverse = receiver.loc.selector.begin_pos
|
28
|
-
end_location = node.loc.selector.end_pos
|
29
|
-
|
30
|
-
range = range_between(location_of_reverse, end_location)
|
31
|
-
|
32
|
-
add_offense(node, location: range)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def autocorrect(node)
|
37
|
-
->(corrector) { corrector.replace(node.loc.dot, UNDERSCORE) }
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
@@ -1,77 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RuboCop
|
4
|
-
module Cop
|
5
|
-
module Performance
|
6
|
-
# This cop is used to identify usages of `count` on an
|
7
|
-
# `Array` and `Hash` and change them to `size`.
|
8
|
-
#
|
9
|
-
# @example
|
10
|
-
# # bad
|
11
|
-
# [1, 2, 3].count
|
12
|
-
#
|
13
|
-
# # bad
|
14
|
-
# {a: 1, b: 2, c: 3}.count
|
15
|
-
#
|
16
|
-
# # good
|
17
|
-
# [1, 2, 3].size
|
18
|
-
#
|
19
|
-
# # good
|
20
|
-
# {a: 1, b: 2, c: 3}.size
|
21
|
-
#
|
22
|
-
# # good
|
23
|
-
# [1, 2, 3].count { |e| e > 2 }
|
24
|
-
# TODO: Add advanced detection of variables that could
|
25
|
-
# have been assigned to an array or a hash.
|
26
|
-
class Size < Cop
|
27
|
-
MSG = 'Use `size` instead of `count`.'.freeze
|
28
|
-
|
29
|
-
def on_send(node)
|
30
|
-
return unless eligible_node?(node)
|
31
|
-
|
32
|
-
add_offense(node, location: :selector)
|
33
|
-
end
|
34
|
-
|
35
|
-
def autocorrect(node)
|
36
|
-
->(corrector) { corrector.replace(node.loc.selector, 'size') }
|
37
|
-
end
|
38
|
-
|
39
|
-
private
|
40
|
-
|
41
|
-
def eligible_node?(node)
|
42
|
-
return false unless node.method?(:count) && !node.arguments?
|
43
|
-
|
44
|
-
eligible_receiver?(node.receiver) && !allowed_parent?(node.parent)
|
45
|
-
end
|
46
|
-
|
47
|
-
def eligible_receiver?(node)
|
48
|
-
return false unless node
|
49
|
-
|
50
|
-
array?(node) || hash?(node)
|
51
|
-
end
|
52
|
-
|
53
|
-
def allowed_parent?(node)
|
54
|
-
node && node.block_type?
|
55
|
-
end
|
56
|
-
|
57
|
-
def array?(node)
|
58
|
-
return true if node.array_type?
|
59
|
-
return false unless node.send_type?
|
60
|
-
|
61
|
-
_, constant = *node.receiver
|
62
|
-
|
63
|
-
constant == :Array || node.method_name == :to_a
|
64
|
-
end
|
65
|
-
|
66
|
-
def hash?(node)
|
67
|
-
return true if node.hash_type?
|
68
|
-
return false unless node.send_type?
|
69
|
-
|
70
|
-
_, constant = *node.receiver
|
71
|
-
|
72
|
-
constant == :Hash || node.method_name == :to_h
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
@@ -1,59 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RuboCop
|
4
|
-
module Cop
|
5
|
-
module Performance
|
6
|
-
# This cop identifies unnecessary use of a regex where
|
7
|
-
# `String#start_with?` would suffice.
|
8
|
-
#
|
9
|
-
# @example
|
10
|
-
# # bad
|
11
|
-
# 'abc'.match?(/\Aab/)
|
12
|
-
# 'abc' =~ /\Aab/
|
13
|
-
# 'abc'.match(/\Aab/)
|
14
|
-
#
|
15
|
-
# # good
|
16
|
-
# 'abc'.start_with?('ab')
|
17
|
-
class StartWith < Cop
|
18
|
-
MSG = 'Use `String#start_with?` instead of a regex match anchored to ' \
|
19
|
-
'the beginning of the string.'.freeze
|
20
|
-
SINGLE_QUOTE = "'".freeze
|
21
|
-
|
22
|
-
def_node_matcher :redundant_regex?, <<-PATTERN
|
23
|
-
{(send $!nil? {:match :=~ :match?} (regexp (str $#literal_at_start?) (regopt)))
|
24
|
-
(send (regexp (str $#literal_at_start?) (regopt)) {:match :=~} $_)}
|
25
|
-
PATTERN
|
26
|
-
|
27
|
-
def literal_at_start?(regex_str)
|
28
|
-
# is this regexp 'literal' in the sense of only matching literal
|
29
|
-
# chars, rather than using metachars like `.` and `*` and so on?
|
30
|
-
# also, is it anchored at the start of the string?
|
31
|
-
# (tricky: \s, \d, and so on are metacharacters, but other characters
|
32
|
-
# escaped with a slash are just literals. LITERAL_REGEX takes all
|
33
|
-
# that into account.)
|
34
|
-
regex_str =~ /\A\\A(?:#{LITERAL_REGEX})+\z/
|
35
|
-
end
|
36
|
-
|
37
|
-
def on_send(node)
|
38
|
-
return unless redundant_regex?(node)
|
39
|
-
|
40
|
-
add_offense(node)
|
41
|
-
end
|
42
|
-
|
43
|
-
def autocorrect(node)
|
44
|
-
redundant_regex?(node) do |receiver, regex_str|
|
45
|
-
receiver, regex_str = regex_str, receiver if receiver.is_a?(String)
|
46
|
-
regex_str = regex_str[2..-1] # drop \A anchor
|
47
|
-
regex_str = interpret_string_escapes(regex_str)
|
48
|
-
|
49
|
-
lambda do |corrector|
|
50
|
-
new_source = receiver.source + '.start_with?(' +
|
51
|
-
to_string_literal(regex_str) + ')'
|
52
|
-
corrector.replace(node.source_range, new_source)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
@@ -1,173 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RuboCop
|
4
|
-
module Cop
|
5
|
-
module Performance
|
6
|
-
# This cop identifies places where `gsub` can be replaced by
|
7
|
-
# `tr` or `delete`.
|
8
|
-
#
|
9
|
-
# @example
|
10
|
-
# # bad
|
11
|
-
# 'abc'.gsub('b', 'd')
|
12
|
-
# 'abc'.gsub('a', '')
|
13
|
-
# 'abc'.gsub(/a/, 'd')
|
14
|
-
# 'abc'.gsub!('a', 'd')
|
15
|
-
#
|
16
|
-
# # good
|
17
|
-
# 'abc'.gsub(/.*/, 'a')
|
18
|
-
# 'abc'.gsub(/a+/, 'd')
|
19
|
-
# 'abc'.tr('b', 'd')
|
20
|
-
# 'a b c'.delete(' ')
|
21
|
-
class StringReplacement < Cop
|
22
|
-
include RangeHelp
|
23
|
-
|
24
|
-
MSG = 'Use `%<prefer>s` instead of `%<current>s`.'.freeze
|
25
|
-
DETERMINISTIC_REGEX = /\A(?:#{LITERAL_REGEX})+\Z/.freeze
|
26
|
-
DELETE = 'delete'.freeze
|
27
|
-
TR = 'tr'.freeze
|
28
|
-
BANG = '!'.freeze
|
29
|
-
SINGLE_QUOTE = "'".freeze
|
30
|
-
|
31
|
-
def_node_matcher :string_replacement?, <<-PATTERN
|
32
|
-
(send _ {:gsub :gsub!}
|
33
|
-
${regexp str (send (const nil? :Regexp) {:new :compile} _)}
|
34
|
-
$str)
|
35
|
-
PATTERN
|
36
|
-
|
37
|
-
def on_send(node)
|
38
|
-
string_replacement?(node) do |first_param, second_param|
|
39
|
-
return if accept_second_param?(second_param)
|
40
|
-
return if accept_first_param?(first_param)
|
41
|
-
|
42
|
-
offense(node, first_param, second_param)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
def autocorrect(node)
|
47
|
-
_string, _method, first_param, second_param = *node
|
48
|
-
first_source, = first_source(first_param)
|
49
|
-
second_source, = *second_param
|
50
|
-
|
51
|
-
unless first_param.str_type?
|
52
|
-
first_source = interpret_string_escapes(first_source)
|
53
|
-
end
|
54
|
-
|
55
|
-
replacement_method =
|
56
|
-
replacement_method(node, first_source, second_source)
|
57
|
-
|
58
|
-
replace_method(node, first_source, second_source, first_param,
|
59
|
-
replacement_method)
|
60
|
-
end
|
61
|
-
|
62
|
-
def replace_method(node, first, second, first_param, replacement)
|
63
|
-
lambda do |corrector|
|
64
|
-
corrector.replace(node.loc.selector, replacement)
|
65
|
-
unless first_param.str_type?
|
66
|
-
corrector.replace(first_param.source_range,
|
67
|
-
to_string_literal(first))
|
68
|
-
end
|
69
|
-
|
70
|
-
if second.empty? && first.length == 1
|
71
|
-
remove_second_param(corrector, node, first_param)
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
private
|
77
|
-
|
78
|
-
def accept_second_param?(second_param)
|
79
|
-
second_source, = *second_param
|
80
|
-
second_source.length > 1
|
81
|
-
end
|
82
|
-
|
83
|
-
def accept_first_param?(first_param)
|
84
|
-
first_source, options = first_source(first_param)
|
85
|
-
return true if first_source.nil?
|
86
|
-
|
87
|
-
unless first_param.str_type?
|
88
|
-
return true if options
|
89
|
-
return true unless first_source =~ DETERMINISTIC_REGEX
|
90
|
-
|
91
|
-
# This must be done after checking DETERMINISTIC_REGEX
|
92
|
-
# Otherwise things like \s will trip us up
|
93
|
-
first_source = interpret_string_escapes(first_source)
|
94
|
-
end
|
95
|
-
|
96
|
-
first_source.length != 1
|
97
|
-
end
|
98
|
-
|
99
|
-
def offense(node, first_param, second_param)
|
100
|
-
first_source, = first_source(first_param)
|
101
|
-
unless first_param.str_type?
|
102
|
-
first_source = interpret_string_escapes(first_source)
|
103
|
-
end
|
104
|
-
second_source, = *second_param
|
105
|
-
message = message(node, first_source, second_source)
|
106
|
-
|
107
|
-
add_offense(node, location: range(node), message: message)
|
108
|
-
end
|
109
|
-
|
110
|
-
def first_source(first_param)
|
111
|
-
case first_param.type
|
112
|
-
when :regexp
|
113
|
-
source_from_regex_literal(first_param)
|
114
|
-
when :send
|
115
|
-
source_from_regex_constructor(first_param)
|
116
|
-
when :str
|
117
|
-
first_param.children.first
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
def source_from_regex_literal(node)
|
122
|
-
regex, options = *node
|
123
|
-
source, = *regex
|
124
|
-
options, = *options
|
125
|
-
[source, options]
|
126
|
-
end
|
127
|
-
|
128
|
-
def source_from_regex_constructor(node)
|
129
|
-
_const, _init, regex = *node
|
130
|
-
case regex.type
|
131
|
-
when :regexp
|
132
|
-
source_from_regex_literal(regex)
|
133
|
-
when :str
|
134
|
-
source, = *regex
|
135
|
-
source
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
def range(node)
|
140
|
-
range_between(node.loc.selector.begin_pos, node.source_range.end_pos)
|
141
|
-
end
|
142
|
-
|
143
|
-
def replacement_method(node, first_source, second_source)
|
144
|
-
replacement = if second_source.empty? && first_source.length == 1
|
145
|
-
DELETE
|
146
|
-
else
|
147
|
-
TR
|
148
|
-
end
|
149
|
-
|
150
|
-
"#{replacement}#{BANG if node.bang_method?}"
|
151
|
-
end
|
152
|
-
|
153
|
-
def message(node, first_source, second_source)
|
154
|
-
replacement_method =
|
155
|
-
replacement_method(node, first_source, second_source)
|
156
|
-
|
157
|
-
format(MSG, prefer: replacement_method, current: node.method_name)
|
158
|
-
end
|
159
|
-
|
160
|
-
def method_suffix(node)
|
161
|
-
node.loc.end ? node.loc.end.source : ''
|
162
|
-
end
|
163
|
-
|
164
|
-
def remove_second_param(corrector, node, first_param)
|
165
|
-
end_range = range_between(first_param.source_range.end_pos,
|
166
|
-
node.source_range.end_pos)
|
167
|
-
|
168
|
-
corrector.replace(end_range, method_suffix(node))
|
169
|
-
end
|
170
|
-
end
|
171
|
-
end
|
172
|
-
end
|
173
|
-
end
|
@@ -1,71 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RuboCop
|
4
|
-
module Cop
|
5
|
-
module Performance
|
6
|
-
# This cop checks for .times.map calls.
|
7
|
-
# In most cases such calls can be replaced
|
8
|
-
# with an explicit array creation.
|
9
|
-
#
|
10
|
-
# @example
|
11
|
-
# # bad
|
12
|
-
# 9.times.map do |i|
|
13
|
-
# i.to_s
|
14
|
-
# end
|
15
|
-
#
|
16
|
-
# # good
|
17
|
-
# Array.new(9) do |i|
|
18
|
-
# i.to_s
|
19
|
-
# end
|
20
|
-
class TimesMap < Cop
|
21
|
-
MESSAGE = 'Use `Array.new(%<count>s)` with a block ' \
|
22
|
-
'instead of `.times.%<map_or_collect>s`'.freeze
|
23
|
-
MESSAGE_ONLY_IF = 'only if `%<count>s` is always 0 or more'.freeze
|
24
|
-
|
25
|
-
def on_send(node)
|
26
|
-
check(node)
|
27
|
-
end
|
28
|
-
|
29
|
-
def on_block(node)
|
30
|
-
check(node)
|
31
|
-
end
|
32
|
-
|
33
|
-
def autocorrect(node)
|
34
|
-
map_or_collect, count = times_map_call(node)
|
35
|
-
|
36
|
-
replacement =
|
37
|
-
"Array.new(#{count.source}" \
|
38
|
-
"#{map_or_collect.arguments.map { |arg| ", #{arg.source}" }.join})"
|
39
|
-
|
40
|
-
lambda do |corrector|
|
41
|
-
corrector.replace(map_or_collect.loc.expression, replacement)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
private
|
46
|
-
|
47
|
-
def check(node)
|
48
|
-
times_map_call(node) do |map_or_collect, count|
|
49
|
-
add_offense(node, message: message(map_or_collect, count))
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def message(map_or_collect, count)
|
54
|
-
template = if count.literal?
|
55
|
-
MESSAGE + '.'
|
56
|
-
else
|
57
|
-
"#{MESSAGE} #{MESSAGE_ONLY_IF}."
|
58
|
-
end
|
59
|
-
format(template,
|
60
|
-
count: count.source,
|
61
|
-
map_or_collect: map_or_collect.method_name)
|
62
|
-
end
|
63
|
-
|
64
|
-
def_node_matcher :times_map_call, <<-PATTERN
|
65
|
-
{(block $(send (send $!nil? :times) {:map :collect}) ...)
|
66
|
-
$(send (send $!nil? :times) {:map :collect} (block_pass ...))}
|
67
|
-
PATTERN
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
@@ -1,50 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RuboCop
|
4
|
-
module Cop
|
5
|
-
module Performance
|
6
|
-
# In Ruby 2.3 or later, use unary plus operator to unfreeze a string
|
7
|
-
# literal instead of `String#dup` and `String.new`.
|
8
|
-
# Unary plus operator is faster than `String#dup`.
|
9
|
-
#
|
10
|
-
# Note: `String.new` (without operator) is not exactly the same as `+''`.
|
11
|
-
# These differ in encoding. `String.new.encoding` is always `ASCII-8BIT`.
|
12
|
-
# However, `(+'').encoding` is the same as script encoding(e.g. `UTF-8`).
|
13
|
-
# So, if you expect `ASCII-8BIT` encoding, disable this cop.
|
14
|
-
#
|
15
|
-
# @example
|
16
|
-
# # bad
|
17
|
-
# ''.dup
|
18
|
-
# "something".dup
|
19
|
-
# String.new
|
20
|
-
# String.new('')
|
21
|
-
# String.new('something')
|
22
|
-
#
|
23
|
-
# # good
|
24
|
-
# +'something'
|
25
|
-
# +''
|
26
|
-
class UnfreezeString < Cop
|
27
|
-
extend TargetRubyVersion
|
28
|
-
|
29
|
-
minimum_target_ruby_version 2.3
|
30
|
-
|
31
|
-
MSG = 'Use unary plus to get an unfrozen string literal.'.freeze
|
32
|
-
|
33
|
-
def_node_matcher :dup_string?, <<-PATTERN
|
34
|
-
(send {str dstr} :dup)
|
35
|
-
PATTERN
|
36
|
-
|
37
|
-
def_node_matcher :string_new?, <<-PATTERN
|
38
|
-
{
|
39
|
-
(send (const nil? :String) :new {str dstr})
|
40
|
-
(send (const nil? :String) :new)
|
41
|
-
}
|
42
|
-
PATTERN
|
43
|
-
|
44
|
-
def on_send(node)
|
45
|
-
add_offense(node) if dup_string?(node) || string_new?(node)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|