rubocop 0.67.2 → 0.68.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|