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,108 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RuboCop
|
4
|
-
module Cop
|
5
|
-
module Performance
|
6
|
-
# This cop identifies places where a case-insensitive string comparison
|
7
|
-
# can better be implemented using `casecmp`.
|
8
|
-
#
|
9
|
-
# @example
|
10
|
-
# # bad
|
11
|
-
# str.downcase == 'abc'
|
12
|
-
# str.upcase.eql? 'ABC'
|
13
|
-
# 'abc' == str.downcase
|
14
|
-
# 'ABC'.eql? str.upcase
|
15
|
-
# str.downcase == str.downcase
|
16
|
-
#
|
17
|
-
# # good
|
18
|
-
# str.casecmp('ABC').zero?
|
19
|
-
# 'abc'.casecmp(str).zero?
|
20
|
-
class Casecmp < Cop
|
21
|
-
MSG = 'Use `%<good>s` instead of `%<bad>s`.'.freeze
|
22
|
-
CASE_METHODS = %i[downcase upcase].freeze
|
23
|
-
|
24
|
-
def_node_matcher :downcase_eq, <<-PATTERN
|
25
|
-
(send
|
26
|
-
$(send _ ${:downcase :upcase})
|
27
|
-
${:== :eql? :!=}
|
28
|
-
${str (send _ {:downcase :upcase} ...) (begin str)})
|
29
|
-
PATTERN
|
30
|
-
|
31
|
-
def_node_matcher :eq_downcase, <<-PATTERN
|
32
|
-
(send
|
33
|
-
{str (send _ {:downcase :upcase} ...) (begin str)}
|
34
|
-
${:== :eql? :!=}
|
35
|
-
$(send _ ${:downcase :upcase}))
|
36
|
-
PATTERN
|
37
|
-
|
38
|
-
def_node_matcher :downcase_downcase, <<-PATTERN
|
39
|
-
(send
|
40
|
-
$(send _ ${:downcase :upcase})
|
41
|
-
${:== :eql? :!=}
|
42
|
-
$(send _ ${:downcase :upcase}))
|
43
|
-
PATTERN
|
44
|
-
|
45
|
-
def on_send(node)
|
46
|
-
return unless downcase_eq(node) || eq_downcase(node)
|
47
|
-
return unless (parts = take_method_apart(node))
|
48
|
-
|
49
|
-
_, _, arg, variable = parts
|
50
|
-
good_method = build_good_method(arg, variable)
|
51
|
-
|
52
|
-
add_offense(
|
53
|
-
node,
|
54
|
-
message: format(MSG, good: good_method, bad: node.source)
|
55
|
-
)
|
56
|
-
end
|
57
|
-
|
58
|
-
def autocorrect(node)
|
59
|
-
return unless (parts = take_method_apart(node))
|
60
|
-
|
61
|
-
receiver, method, arg, variable = parts
|
62
|
-
|
63
|
-
correction(node, receiver, method, arg, variable)
|
64
|
-
end
|
65
|
-
|
66
|
-
private
|
67
|
-
|
68
|
-
def take_method_apart(node)
|
69
|
-
if downcase_downcase(node)
|
70
|
-
receiver, method, rhs = *node
|
71
|
-
arg, = *rhs
|
72
|
-
elsif downcase_eq(node)
|
73
|
-
receiver, method, arg = *node
|
74
|
-
elsif eq_downcase(node)
|
75
|
-
arg, method, receiver = *node
|
76
|
-
else
|
77
|
-
return
|
78
|
-
end
|
79
|
-
|
80
|
-
variable, = *receiver
|
81
|
-
|
82
|
-
[receiver, method, arg, variable]
|
83
|
-
end
|
84
|
-
|
85
|
-
def correction(node, _receiver, method, arg, variable)
|
86
|
-
lambda do |corrector|
|
87
|
-
corrector.insert_before(node.loc.expression, '!') if method == :!=
|
88
|
-
|
89
|
-
replacement = build_good_method(arg, variable)
|
90
|
-
|
91
|
-
corrector.replace(node.loc.expression, replacement)
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
def build_good_method(arg, variable)
|
96
|
-
# We want resulting call to be parenthesized
|
97
|
-
# if arg already includes one or more sets of parens, don't add more
|
98
|
-
# or if method call already used parens, again, don't add more
|
99
|
-
if arg.send_type? || !parentheses?(arg)
|
100
|
-
"#{variable.source}.casecmp(#{arg.source}).zero?"
|
101
|
-
else
|
102
|
-
"#{variable.source}.casecmp#{arg.source}.zero?"
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
107
|
-
end
|
108
|
-
end
|
@@ -1,78 +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
|
7
|
-
# @example
|
8
|
-
# # bad
|
9
|
-
# array = ["a", "b", "c"]
|
10
|
-
# array.compact.flatten.map { |x| x.downcase }
|
11
|
-
#
|
12
|
-
# Each of these methods (`compact`, `flatten`, `map`) will generate a
|
13
|
-
# new intermediate array that is promptly thrown away. Instead it is
|
14
|
-
# faster to mutate when we know it's safe.
|
15
|
-
#
|
16
|
-
# @example
|
17
|
-
# # good.
|
18
|
-
# array = ["a", "b", "c"]
|
19
|
-
# array.compact!
|
20
|
-
# array.flatten!
|
21
|
-
# array.map! { |x| x.downcase }
|
22
|
-
# array
|
23
|
-
class ChainArrayAllocation < Cop
|
24
|
-
include RangeHelp
|
25
|
-
|
26
|
-
# These methods return a new array but only sometimes. They must be
|
27
|
-
# called with an argument. For example:
|
28
|
-
#
|
29
|
-
# [1,2].first # => 1
|
30
|
-
# [1,2].first(1) # => [1]
|
31
|
-
#
|
32
|
-
RETURN_NEW_ARRAY_WHEN_ARGS = ':first :last :pop :sample :shift '.freeze
|
33
|
-
|
34
|
-
# These methods return a new array only when called without a block.
|
35
|
-
RETURNS_NEW_ARRAY_WHEN_NO_BLOCK = ':zip :product '.freeze
|
36
|
-
|
37
|
-
# These methods ALWAYS return a new array
|
38
|
-
# after they're called it's safe to mutate the the resulting array
|
39
|
-
ALWAYS_RETURNS_NEW_ARRAY = ':* :+ :- :collect :compact :drop '\
|
40
|
-
':drop_while :flatten :map :reject ' \
|
41
|
-
':reverse :rotate :select :shuffle :sort ' \
|
42
|
-
':take :take_while :transpose :uniq ' \
|
43
|
-
':values_at :| '.freeze
|
44
|
-
|
45
|
-
# These methods have a mutation alternative. For example :collect
|
46
|
-
# can be called as :collect!
|
47
|
-
HAS_MUTATION_ALTERNATIVE = ':collect :compact :flatten :map :reject '\
|
48
|
-
':reverse :rotate :select :shuffle :sort '\
|
49
|
-
':uniq '.freeze
|
50
|
-
MSG = 'Use unchained `%<method>s!` and `%<second_method>s!` '\
|
51
|
-
'(followed by `return array` if required) instead of chaining '\
|
52
|
-
'`%<method>s...%<second_method>s`.'.freeze
|
53
|
-
|
54
|
-
def_node_matcher :flat_map_candidate?, <<-PATTERN
|
55
|
-
{
|
56
|
-
(send (send _ ${#{RETURN_NEW_ARRAY_WHEN_ARGS}} {int lvar ivar cvar gvar}) ${#{HAS_MUTATION_ALTERNATIVE}} $...)
|
57
|
-
(send (block (send _ ${#{ALWAYS_RETURNS_NEW_ARRAY} }) ...) ${#{HAS_MUTATION_ALTERNATIVE}} $...)
|
58
|
-
(send (send _ ${#{ALWAYS_RETURNS_NEW_ARRAY + RETURNS_NEW_ARRAY_WHEN_NO_BLOCK}} ...) ${#{HAS_MUTATION_ALTERNATIVE}} $...)
|
59
|
-
}
|
60
|
-
PATTERN
|
61
|
-
|
62
|
-
def on_send(node)
|
63
|
-
flat_map_candidate?(node) do |fm, sm, _|
|
64
|
-
range = range_between(
|
65
|
-
node.loc.dot.begin_pos,
|
66
|
-
node.source_range.end_pos
|
67
|
-
)
|
68
|
-
add_offense(
|
69
|
-
node,
|
70
|
-
location: range,
|
71
|
-
message: format(MSG, method: fm, second_method: sm)
|
72
|
-
)
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
@@ -1,122 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RuboCop
|
4
|
-
module Cop
|
5
|
-
module Performance
|
6
|
-
# This cop identifies places where `sort { |a, b| a.foo <=> b.foo }`
|
7
|
-
# can be replaced by `sort_by(&:foo)`.
|
8
|
-
# This cop also checks `max` and `min` methods.
|
9
|
-
#
|
10
|
-
# @example
|
11
|
-
# # bad
|
12
|
-
# array.sort { |a, b| a.foo <=> b.foo }
|
13
|
-
# array.max { |a, b| a.foo <=> b.foo }
|
14
|
-
# array.min { |a, b| a.foo <=> b.foo }
|
15
|
-
# array.sort { |a, b| a[:foo] <=> b[:foo] }
|
16
|
-
#
|
17
|
-
# # good
|
18
|
-
# array.sort_by(&:foo)
|
19
|
-
# array.sort_by { |v| v.foo }
|
20
|
-
# array.sort_by do |var|
|
21
|
-
# var.foo
|
22
|
-
# end
|
23
|
-
# array.max_by(&:foo)
|
24
|
-
# array.min_by(&:foo)
|
25
|
-
# array.sort_by { |a| a[:foo] }
|
26
|
-
class CompareWithBlock < Cop
|
27
|
-
include RangeHelp
|
28
|
-
|
29
|
-
MSG = 'Use `%<compare_method>s_by%<instead>s` instead of ' \
|
30
|
-
'`%<compare_method>s { |%<var_a>s, %<var_b>s| %<str_a>s ' \
|
31
|
-
'<=> %<str_b>s }`.'.freeze
|
32
|
-
|
33
|
-
def_node_matcher :compare?, <<-PATTERN
|
34
|
-
(block
|
35
|
-
$(send _ {:sort :min :max})
|
36
|
-
(args (arg $_a) (arg $_b))
|
37
|
-
$send)
|
38
|
-
PATTERN
|
39
|
-
|
40
|
-
def_node_matcher :replaceable_body?, <<-PATTERN
|
41
|
-
(send
|
42
|
-
(send (lvar %1) $_method $...)
|
43
|
-
:<=>
|
44
|
-
(send (lvar %2) _method $...))
|
45
|
-
PATTERN
|
46
|
-
|
47
|
-
def on_block(node)
|
48
|
-
compare?(node) do |send, var_a, var_b, body|
|
49
|
-
replaceable_body?(body, var_a, var_b) do |method, args_a, args_b|
|
50
|
-
return unless slow_compare?(method, args_a, args_b)
|
51
|
-
|
52
|
-
range = compare_range(send, node)
|
53
|
-
|
54
|
-
add_offense(
|
55
|
-
node,
|
56
|
-
location: range,
|
57
|
-
message: message(send, method, var_a, var_b, args_a)
|
58
|
-
)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
def autocorrect(node)
|
64
|
-
lambda do |corrector|
|
65
|
-
send, var_a, var_b, body = compare?(node)
|
66
|
-
method, arg, = replaceable_body?(body, var_a, var_b)
|
67
|
-
replacement =
|
68
|
-
if method == :[]
|
69
|
-
"#{send.method_name}_by { |a| a[#{arg.first.source}] }"
|
70
|
-
else
|
71
|
-
"#{send.method_name}_by(&:#{method})"
|
72
|
-
end
|
73
|
-
corrector.replace(compare_range(send, node),
|
74
|
-
replacement)
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
private
|
79
|
-
|
80
|
-
def slow_compare?(method, args_a, args_b)
|
81
|
-
return false unless args_a == args_b
|
82
|
-
|
83
|
-
if method == :[]
|
84
|
-
return false unless args_a.size == 1
|
85
|
-
|
86
|
-
key = args_a.first
|
87
|
-
return false unless %i[sym str int].include?(key.type)
|
88
|
-
else
|
89
|
-
return false unless args_a.empty?
|
90
|
-
end
|
91
|
-
true
|
92
|
-
end
|
93
|
-
|
94
|
-
# rubocop:disable Metrics/MethodLength
|
95
|
-
def message(send, method, var_a, var_b, args)
|
96
|
-
compare_method = send.method_name
|
97
|
-
if method == :[]
|
98
|
-
key = args.first
|
99
|
-
instead = " { |a| a[#{key.source}] }"
|
100
|
-
str_a = "#{var_a}[#{key.source}]"
|
101
|
-
str_b = "#{var_b}[#{key.source}]"
|
102
|
-
else
|
103
|
-
instead = "(&:#{method})"
|
104
|
-
str_a = "#{var_a}.#{method}"
|
105
|
-
str_b = "#{var_b}.#{method}"
|
106
|
-
end
|
107
|
-
format(MSG, compare_method: compare_method,
|
108
|
-
instead: instead,
|
109
|
-
var_a: var_a,
|
110
|
-
var_b: var_b,
|
111
|
-
str_a: str_a,
|
112
|
-
str_b: str_b)
|
113
|
-
end
|
114
|
-
# rubocop:enable Metrics/MethodLength
|
115
|
-
|
116
|
-
def compare_range(send, node)
|
117
|
-
range_between(send.loc.selector.begin_pos, node.loc.end.end_pos)
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
122
|
-
end
|
@@ -1,102 +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 `Enumerable` that
|
7
|
-
# follow calls to `select` or `reject`. Querying logic can instead be
|
8
|
-
# passed to the `count` call.
|
9
|
-
#
|
10
|
-
# @example
|
11
|
-
# # bad
|
12
|
-
# [1, 2, 3].select { |e| e > 2 }.size
|
13
|
-
# [1, 2, 3].reject { |e| e > 2 }.size
|
14
|
-
# [1, 2, 3].select { |e| e > 2 }.length
|
15
|
-
# [1, 2, 3].reject { |e| e > 2 }.length
|
16
|
-
# [1, 2, 3].select { |e| e > 2 }.count { |e| e.odd? }
|
17
|
-
# [1, 2, 3].reject { |e| e > 2 }.count { |e| e.even? }
|
18
|
-
# array.select(&:value).count
|
19
|
-
#
|
20
|
-
# # good
|
21
|
-
# [1, 2, 3].count { |e| e > 2 }
|
22
|
-
# [1, 2, 3].count { |e| e < 2 }
|
23
|
-
# [1, 2, 3].count { |e| e > 2 && e.odd? }
|
24
|
-
# [1, 2, 3].count { |e| e < 2 && e.even? }
|
25
|
-
# Model.select('field AS field_one').count
|
26
|
-
# Model.select(:value).count
|
27
|
-
#
|
28
|
-
# `ActiveRecord` compatibility:
|
29
|
-
# `ActiveRecord` will ignore the block that is passed to `count`.
|
30
|
-
# Other methods, such as `select`, will convert the association to an
|
31
|
-
# array and then run the block on the array. A simple work around to
|
32
|
-
# make `count` work with a block is to call `to_a.count {...}`.
|
33
|
-
#
|
34
|
-
# Example:
|
35
|
-
# Model.where(id: [1, 2, 3].select { |m| m.method == true }.size
|
36
|
-
#
|
37
|
-
# becomes:
|
38
|
-
#
|
39
|
-
# Model.where(id: [1, 2, 3]).to_a.count { |m| m.method == true }
|
40
|
-
class Count < Cop
|
41
|
-
include SafeMode
|
42
|
-
include RangeHelp
|
43
|
-
|
44
|
-
MSG = 'Use `count` instead of `%<selector>s...%<counter>s`.'.freeze
|
45
|
-
|
46
|
-
def_node_matcher :count_candidate?, <<-PATTERN
|
47
|
-
{
|
48
|
-
(send (block $(send _ ${:select :reject}) ...) ${:count :length :size})
|
49
|
-
(send $(send _ ${:select :reject} (:block_pass _)) ${:count :length :size})
|
50
|
-
}
|
51
|
-
PATTERN
|
52
|
-
|
53
|
-
def on_send(node)
|
54
|
-
return if rails_safe_mode?
|
55
|
-
|
56
|
-
count_candidate?(node) do |selector_node, selector, counter|
|
57
|
-
return unless eligible_node?(node)
|
58
|
-
|
59
|
-
range = source_starting_at(node) do
|
60
|
-
selector_node.loc.selector.begin_pos
|
61
|
-
end
|
62
|
-
|
63
|
-
add_offense(node,
|
64
|
-
location: range,
|
65
|
-
message: format(MSG, selector: selector,
|
66
|
-
counter: counter))
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
def autocorrect(node)
|
71
|
-
selector_node, selector, _counter = count_candidate?(node)
|
72
|
-
selector_loc = selector_node.loc.selector
|
73
|
-
|
74
|
-
return if selector == :reject
|
75
|
-
|
76
|
-
range = source_starting_at(node) { |n| n.loc.dot.begin_pos }
|
77
|
-
|
78
|
-
lambda do |corrector|
|
79
|
-
corrector.remove(range)
|
80
|
-
corrector.replace(selector_loc, 'count')
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
private
|
85
|
-
|
86
|
-
def eligible_node?(node)
|
87
|
-
!(node.parent && node.parent.block_type?)
|
88
|
-
end
|
89
|
-
|
90
|
-
def source_starting_at(node)
|
91
|
-
begin_pos = if block_given?
|
92
|
-
yield node
|
93
|
-
else
|
94
|
-
node.source_range.begin_pos
|
95
|
-
end
|
96
|
-
|
97
|
-
range_between(begin_pos, node.source_range.end_pos)
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
@@ -1,110 +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
|
7
|
-
# `select.first`, `select.last`, `find_all.first`, and `find_all.last`
|
8
|
-
# and change them to use `detect` instead.
|
9
|
-
#
|
10
|
-
# @example
|
11
|
-
# # bad
|
12
|
-
# [].select { |item| true }.first
|
13
|
-
# [].select { |item| true }.last
|
14
|
-
# [].find_all { |item| true }.first
|
15
|
-
# [].find_all { |item| true }.last
|
16
|
-
#
|
17
|
-
# # good
|
18
|
-
# [].detect { |item| true }
|
19
|
-
# [].reverse.detect { |item| true }
|
20
|
-
#
|
21
|
-
# `ActiveRecord` compatibility:
|
22
|
-
# `ActiveRecord` does not implement a `detect` method and `find` has its
|
23
|
-
# own meaning. Correcting ActiveRecord methods with this cop should be
|
24
|
-
# considered unsafe.
|
25
|
-
class Detect < Cop
|
26
|
-
include SafeMode
|
27
|
-
|
28
|
-
MSG = 'Use `%<prefer>s` instead of ' \
|
29
|
-
'`%<first_method>s.%<second_method>s`.'.freeze
|
30
|
-
REVERSE_MSG = 'Use `reverse.%<prefer>s` instead of ' \
|
31
|
-
'`%<first_method>s.%<second_method>s`.'.freeze
|
32
|
-
|
33
|
-
def_node_matcher :detect_candidate?, <<-PATTERN
|
34
|
-
{
|
35
|
-
(send $(block (send _ {:select :find_all}) ...) ${:first :last} $...)
|
36
|
-
(send $(send _ {:select :find_all} ...) ${:first :last} $...)
|
37
|
-
}
|
38
|
-
PATTERN
|
39
|
-
|
40
|
-
def on_send(node)
|
41
|
-
return if rails_safe_mode?
|
42
|
-
|
43
|
-
detect_candidate?(node) do |receiver, second_method, args|
|
44
|
-
return unless args.empty?
|
45
|
-
return unless receiver
|
46
|
-
|
47
|
-
receiver, _args, body = *receiver if receiver.block_type?
|
48
|
-
return if accept_first_call?(receiver, body)
|
49
|
-
|
50
|
-
register_offense(node, receiver, second_method)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def autocorrect(node)
|
55
|
-
receiver, first_method = *node
|
56
|
-
|
57
|
-
replacement = if first_method == :last
|
58
|
-
"reverse.#{preferred_method}"
|
59
|
-
else
|
60
|
-
preferred_method
|
61
|
-
end
|
62
|
-
|
63
|
-
first_range = receiver.source_range.end.join(node.loc.selector)
|
64
|
-
|
65
|
-
receiver, _args, _body = *receiver if receiver.block_type?
|
66
|
-
|
67
|
-
lambda do |corrector|
|
68
|
-
corrector.remove(first_range)
|
69
|
-
corrector.replace(receiver.loc.selector, replacement)
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
private
|
74
|
-
|
75
|
-
def accept_first_call?(receiver, body)
|
76
|
-
caller, _first_method, args = *receiver
|
77
|
-
|
78
|
-
# check that we have usual block or block pass
|
79
|
-
return true if body.nil? && (args.nil? || !args.block_pass_type?)
|
80
|
-
|
81
|
-
lazy?(caller)
|
82
|
-
end
|
83
|
-
|
84
|
-
def register_offense(node, receiver, second_method)
|
85
|
-
_caller, first_method, _args = *receiver
|
86
|
-
range = receiver.loc.selector.join(node.loc.selector)
|
87
|
-
|
88
|
-
message = second_method == :last ? REVERSE_MSG : MSG
|
89
|
-
formatted_message = format(message, prefer: preferred_method,
|
90
|
-
first_method: first_method,
|
91
|
-
second_method: second_method)
|
92
|
-
|
93
|
-
add_offense(node, location: range, message: formatted_message)
|
94
|
-
end
|
95
|
-
|
96
|
-
def preferred_method
|
97
|
-
config.for_cop('Style/CollectionMethods') \
|
98
|
-
['PreferredMethods']['detect'] || 'detect'
|
99
|
-
end
|
100
|
-
|
101
|
-
def lazy?(node)
|
102
|
-
return false unless node
|
103
|
-
|
104
|
-
receiver, method, _args = *node
|
105
|
-
method == :lazy && !receiver.nil?
|
106
|
-
end
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end
|
110
|
-
end
|