rubocop 0.45.0 → 0.46.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rubocop might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/config/default.yml +17 -0
- data/config/enabled.yml +29 -2
- data/lib/rubocop.rb +6 -1
- data/lib/rubocop/config.rb +0 -10
- data/lib/rubocop/config_loader.rb +21 -9
- data/lib/rubocop/cop/bundler/duplicated_gem.rb +69 -0
- data/lib/rubocop/cop/bundler/ordered_gems.rb +54 -0
- data/lib/rubocop/cop/cop.rb +1 -0
- data/lib/rubocop/cop/lint/debugger.rb +9 -1
- data/lib/rubocop/cop/lint/each_with_object_argument.rb +5 -6
- data/lib/rubocop/cop/lint/eval.rb +3 -7
- data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +6 -4
- data/lib/rubocop/cop/lint/unneeded_splat_expansion.rb +13 -4
- data/lib/rubocop/cop/lint/useless_comparison.rb +5 -9
- data/lib/rubocop/cop/lint/useless_setter_call.rb +1 -0
- data/lib/rubocop/cop/metrics/line_length.rb +16 -3
- data/lib/rubocop/cop/mixin/access_modifier_node.rb +9 -9
- data/lib/rubocop/cop/mixin/configurable_numbering.rb +14 -7
- data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +92 -20
- data/lib/rubocop/cop/performance/compare_with_block.rb +61 -0
- data/lib/rubocop/cop/performance/count.rb +21 -57
- data/lib/rubocop/cop/performance/detect.rb +15 -15
- data/lib/rubocop/cop/performance/flat_map.rb +23 -35
- data/lib/rubocop/cop/performance/sample.rb +84 -82
- data/lib/rubocop/cop/performance/string_replacement.rb +18 -43
- data/lib/rubocop/cop/rails/enum_uniqueness.rb +71 -0
- data/lib/rubocop/cop/rails/http_positional_arguments.rb +1 -1
- data/lib/rubocop/cop/rails/output.rb +8 -12
- data/lib/rubocop/cop/rails/read_write_attribute.rb +10 -6
- data/lib/rubocop/cop/rails/request_referer.rb +8 -9
- data/lib/rubocop/cop/rails/scope_args.rb +5 -11
- data/lib/rubocop/cop/style/access_modifier_indentation.rb +1 -1
- data/lib/rubocop/cop/style/and_or.rb +1 -1
- data/lib/rubocop/cop/style/array_join.rb +4 -8
- data/lib/rubocop/cop/style/block_comments.rb +1 -1
- data/lib/rubocop/cop/style/case_equality.rb +3 -3
- data/lib/rubocop/cop/style/character_literal.rb +2 -4
- data/lib/rubocop/cop/style/class_check.rb +6 -6
- data/lib/rubocop/cop/style/colon_method_call.rb +6 -6
- data/lib/rubocop/cop/style/each_with_object.rb +13 -17
- data/lib/rubocop/cop/style/empty_literal.rb +46 -36
- data/lib/rubocop/cop/style/empty_method.rb +96 -0
- data/lib/rubocop/cop/style/even_odd.rb +19 -50
- data/lib/rubocop/cop/style/hash_syntax.rb +4 -1
- data/lib/rubocop/cop/style/lambda.rb +8 -18
- data/lib/rubocop/cop/style/module_function.rb +14 -11
- data/lib/rubocop/cop/style/nil_comparison.rb +4 -7
- data/lib/rubocop/cop/style/non_nil_check.rb +18 -36
- data/lib/rubocop/cop/style/numeric_predicate.rb +9 -10
- data/lib/rubocop/cop/style/op_method.rb +7 -9
- data/lib/rubocop/cop/style/parallel_assignment.rb +1 -1
- data/lib/rubocop/cop/style/proc.rb +5 -9
- data/lib/rubocop/cop/style/redundant_freeze.rb +6 -7
- data/lib/rubocop/cop/style/send.rb +6 -3
- data/lib/rubocop/cop/style/space_inside_block_braces.rb +1 -1
- data/lib/rubocop/cop/style/special_global_vars.rb +3 -3
- data/lib/rubocop/cop/style/symbol_proc.rb +22 -43
- data/lib/rubocop/cop/style/ternary_parentheses.rb +67 -18
- data/lib/rubocop/cop/util.rb +1 -1
- data/lib/rubocop/cop/variable_force/assignment.rb +2 -0
- data/lib/rubocop/cop/variable_force/locatable.rb +8 -6
- data/lib/rubocop/cop/variable_force/reference.rb +2 -0
- data/lib/rubocop/formatter/base_formatter.rb +4 -8
- data/lib/rubocop/formatter/fuubar_style_formatter.rb +6 -0
- data/lib/rubocop/node_pattern.rb +7 -5
- data/lib/rubocop/processed_source.rb +1 -0
- data/lib/rubocop/rspec/cop_helper.rb +4 -0
- data/lib/rubocop/rspec/host_environment_simulation_helper.rb +1 -1
- data/lib/rubocop/version.rb +1 -1
- metadata +7 -3
- data/lib/rubocop/cop/performance/sort_with_block.rb +0 -53
@@ -6,7 +6,8 @@ module RuboCop
|
|
6
6
|
# This cop checks for non-local exit from iterator, without return value.
|
7
7
|
# It warns only when satisfies all of these: `return` doesn't have return
|
8
8
|
# value, the block is preceded by a method chain, the block has arguments,
|
9
|
-
# and the method which receives the block is not `define_method
|
9
|
+
# and the method which receives the block is not `define_method`
|
10
|
+
# or `define_singleton_method`.
|
10
11
|
#
|
11
12
|
# @example
|
12
13
|
#
|
@@ -44,8 +45,9 @@ module RuboCop
|
|
44
45
|
|
45
46
|
# `return` does not exit to outside of lambda block, this is safe.
|
46
47
|
break if block_node.lambda?
|
47
|
-
# if a proc is passed to `Module#define_method
|
48
|
-
#
|
48
|
+
# if a proc is passed to `Module#define_method` or
|
49
|
+
# `Object#define_singleton_method`, `return` will not cause a
|
50
|
+
# non-local exit error
|
49
51
|
break if define_method?(send_node)
|
50
52
|
|
51
53
|
next if args_node.children.empty?
|
@@ -67,7 +69,7 @@ module RuboCop
|
|
67
69
|
|
68
70
|
def define_method?(send_node)
|
69
71
|
_receiver, selector = *send_node
|
70
|
-
|
72
|
+
%i(define_method define_singleton_method).include? selector
|
71
73
|
end
|
72
74
|
end
|
73
75
|
end
|
@@ -64,7 +64,8 @@ module RuboCop
|
|
64
64
|
return unless ASSIGNMENT_TYPES.include?(node.parent.parent.type)
|
65
65
|
end
|
66
66
|
|
67
|
-
if array_splat?(node) &&
|
67
|
+
if array_splat?(node) &&
|
68
|
+
(method_argument?(node) || part_of_an_array?(node))
|
68
69
|
add_offense(node, :expression, ARRAY_PARAM_MSG)
|
69
70
|
else
|
70
71
|
add_offense(node, :expression)
|
@@ -76,13 +77,12 @@ module RuboCop
|
|
76
77
|
|
77
78
|
def autocorrect(node)
|
78
79
|
variable, = *node
|
79
|
-
parent = node.parent
|
80
80
|
loc = node.loc
|
81
81
|
|
82
82
|
lambda do |corrector|
|
83
83
|
if !variable.array_type?
|
84
84
|
corrector.replace(loc.expression, "[#{variable.source}]")
|
85
|
-
elsif unneeded_brackets?(
|
85
|
+
elsif unneeded_brackets?(node)
|
86
86
|
corrector.replace(loc.expression, remove_brackets(variable))
|
87
87
|
else
|
88
88
|
corrector.remove(loc.operator)
|
@@ -98,10 +98,19 @@ module RuboCop
|
|
98
98
|
node.parent.send_type?
|
99
99
|
end
|
100
100
|
|
101
|
+
def part_of_an_array?(node)
|
102
|
+
# The parent of a splat expansion is an array that does not have
|
103
|
+
# `begin` or `end`
|
104
|
+
parent = node.parent
|
105
|
+
parent.array_type? && parent.loc.begin && parent.loc.end
|
106
|
+
end
|
107
|
+
|
101
108
|
def unneeded_brackets?(node)
|
102
109
|
parent = node.parent
|
110
|
+
grandparent = node.parent.parent
|
103
111
|
|
104
|
-
|
112
|
+
parent.when_type? || parent.send_type? || part_of_an_array?(node) ||
|
113
|
+
(grandparent && grandparent.resbody_type?)
|
105
114
|
end
|
106
115
|
|
107
116
|
def remove_brackets(array)
|
@@ -10,18 +10,14 @@ module RuboCop
|
|
10
10
|
# x.top >= x.top
|
11
11
|
class UselessComparison < Cop
|
12
12
|
MSG = 'Comparison of something with itself detected.'.freeze
|
13
|
-
|
14
13
|
OPS = %w(== === != < > <= >= <=>).freeze
|
15
14
|
|
16
|
-
|
17
|
-
# lambda.() does not have a selector
|
18
|
-
return unless node.loc.selector
|
19
|
-
|
20
|
-
op = node.loc.selector.source
|
21
|
-
return unless OPS.include?(op)
|
15
|
+
def_node_matcher :comparison?, "(send $_ {:#{OPS.join(' :')}} $_)"
|
22
16
|
|
23
|
-
|
24
|
-
|
17
|
+
def on_send(node)
|
18
|
+
comparison?(node) do |receiver, args|
|
19
|
+
add_offense(node, :selector) if receiver == args
|
20
|
+
end
|
25
21
|
end
|
26
22
|
end
|
27
23
|
end
|
@@ -23,11 +23,11 @@ module RuboCop
|
|
23
23
|
|
24
24
|
def check_line(line, index, heredocs)
|
25
25
|
return if line.length <= max
|
26
|
+
return if ignored_line?(line, index, heredocs)
|
27
|
+
|
26
28
|
if ignore_cop_directives? && directive_on_source_line?(index)
|
27
29
|
return check_directive_line(line, index)
|
28
30
|
end
|
29
|
-
return if heredocs &&
|
30
|
-
line_in_whitelisted_heredoc?(heredocs, index.succ)
|
31
31
|
return check_uri_line(line, index) if allow_uri?
|
32
32
|
|
33
33
|
offense(
|
@@ -36,6 +36,11 @@ module RuboCop
|
|
36
36
|
)
|
37
37
|
end
|
38
38
|
|
39
|
+
def ignored_line?(line, index, heredocs)
|
40
|
+
matches_ignored_pattern?(line) ||
|
41
|
+
heredocs && line_in_whitelisted_heredoc?(heredocs, index.succ)
|
42
|
+
end
|
43
|
+
|
39
44
|
def offense(loc, line)
|
40
45
|
message = format(MSG, line.length, max)
|
41
46
|
add_offense(nil, loc, message) { self.max = line.length }
|
@@ -81,6 +86,14 @@ module RuboCop
|
|
81
86
|
end
|
82
87
|
end
|
83
88
|
|
89
|
+
def matches_ignored_pattern?(line)
|
90
|
+
ignored_patterns.any? { |pattern| Regexp.new(pattern).match(line) }
|
91
|
+
end
|
92
|
+
|
93
|
+
def ignored_patterns
|
94
|
+
cop_config['IgnoredPatterns'] || []
|
95
|
+
end
|
96
|
+
|
84
97
|
def allow_uri?
|
85
98
|
cop_config['AllowURI']
|
86
99
|
end
|
@@ -117,7 +130,7 @@ module RuboCop
|
|
117
130
|
end
|
118
131
|
|
119
132
|
def uri_regexp
|
120
|
-
@regexp ||= URI.
|
133
|
+
@regexp ||= URI::Parser.new.make_regexp(cop_config['URISchemes'])
|
121
134
|
end
|
122
135
|
|
123
136
|
def check_directive_line(line, index)
|
@@ -4,12 +4,12 @@ module RuboCop
|
|
4
4
|
module Cop
|
5
5
|
# Common functionality for checking modifier nodes.
|
6
6
|
module AccessModifierNode
|
7
|
-
extend
|
7
|
+
extend NodePattern::Macros
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
9
|
+
def_node_matcher :private_node?, '(send nil :private)'
|
10
|
+
def_node_matcher :protected_node?, '(send nil :protected)'
|
11
|
+
def_node_matcher :public_node?, '(send nil :public)'
|
12
|
+
def_node_matcher :module_function_node?, '(send nil :module_function)'
|
13
13
|
|
14
14
|
# Returns true when the node is an access modifier.
|
15
15
|
def modifier_node?(node)
|
@@ -18,10 +18,10 @@ module RuboCop
|
|
18
18
|
|
19
19
|
# Returns true when the node looks like an access modifier.
|
20
20
|
def modifier_structure?(node)
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
private_node?(node) ||
|
22
|
+
protected_node?(node) ||
|
23
|
+
public_node?(node) ||
|
24
|
+
module_function_node?(node)
|
25
25
|
end
|
26
26
|
|
27
27
|
# Returns true when the parent of what looks like an access modifier
|
@@ -7,23 +7,30 @@ module RuboCop
|
|
7
7
|
module ConfigurableNumbering
|
8
8
|
include ConfigurableEnforcedStyle
|
9
9
|
|
10
|
-
SNAKE_CASE = /(
|
11
|
-
NORMAL_CASE = /(
|
12
|
-
NON_INTEGER = /
|
10
|
+
SNAKE_CASE = /(?:[a-z_]|_\d+)$/
|
11
|
+
NORMAL_CASE = /(?:_\D*|[A-Za-z]\d*)$/
|
12
|
+
NON_INTEGER = /[A-Za-z_]$/
|
13
13
|
|
14
14
|
def check_name(node, name, name_range)
|
15
15
|
return if operator?(name)
|
16
16
|
|
17
|
-
if valid_name?(node, name)
|
17
|
+
if valid_name?(node, name, style)
|
18
18
|
correct_style_detected
|
19
19
|
else
|
20
|
-
add_offense(node, name_range, message(style))
|
20
|
+
add_offense(node, name_range, message(style)) do
|
21
|
+
(supported_styles - [style]).each do |other_style|
|
22
|
+
if valid_name?(node, name, other_style)
|
23
|
+
unexpected_style_detected(other_style)
|
24
|
+
break
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
21
28
|
end
|
22
29
|
end
|
23
30
|
|
24
|
-
def valid_name?(node, name)
|
31
|
+
def valid_name?(node, name, given_style)
|
25
32
|
pattern =
|
26
|
-
case
|
33
|
+
case given_style
|
27
34
|
when :snake_case
|
28
35
|
SNAKE_CASE
|
29
36
|
when :normalcase
|
@@ -11,13 +11,15 @@ module RuboCop
|
|
11
11
|
|
12
12
|
MSG_EXTRA = 'Extra empty line detected at %s body %s.'.freeze
|
13
13
|
MSG_MISSING = 'Empty line missing at %s body %s.'.freeze
|
14
|
+
MSG_DEFERRED = 'Empty line missing before first %s definition'.freeze
|
14
15
|
|
15
16
|
def_node_matcher :constant_definition?, '{class module}'
|
17
|
+
def_node_matcher :empty_line_required?, '{def defs class module}'
|
16
18
|
|
17
19
|
def autocorrect(args)
|
18
|
-
|
20
|
+
offense_style, range = args
|
19
21
|
lambda do |corrector|
|
20
|
-
case
|
22
|
+
case offense_style
|
21
23
|
when :no_empty_lines then
|
22
24
|
corrector.remove(range)
|
23
25
|
when :empty_lines then
|
@@ -40,33 +42,61 @@ module RuboCop
|
|
40
42
|
|
41
43
|
case style
|
42
44
|
when :empty_lines_except_namespace
|
43
|
-
|
44
|
-
|
45
|
+
check_empty_lines_except_namespace(body, first_line, last_line)
|
46
|
+
when :empty_lines_special
|
47
|
+
check_empty_lines_special(body, first_line, last_line)
|
48
|
+
else
|
49
|
+
check_both(style, first_line, last_line)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def check_empty_lines_except_namespace(body, first_line, last_line)
|
54
|
+
if namespace?(body, with_one_child: true)
|
55
|
+
check_both(:no_empty_lines, first_line, last_line)
|
56
|
+
else
|
57
|
+
check_both(:empty_lines, first_line, last_line)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def check_empty_lines_special(body, first_line, last_line)
|
62
|
+
return unless body
|
63
|
+
if namespace?(body, with_one_child: true)
|
64
|
+
check_both(:no_empty_lines, first_line, last_line)
|
65
|
+
else
|
66
|
+
if first_child_requires_empty_line?(body)
|
67
|
+
check_beginning(:empty_lines, first_line)
|
45
68
|
else
|
46
|
-
|
69
|
+
check_beginning(:no_empty_lines, first_line)
|
70
|
+
check_deferred_empty_line(body)
|
47
71
|
end
|
48
|
-
|
49
|
-
check_source(style, first_line, last_line)
|
72
|
+
check_ending(:empty_lines, last_line)
|
50
73
|
end
|
51
74
|
end
|
52
75
|
|
53
|
-
def
|
76
|
+
def check_both(style, first_line, last_line)
|
77
|
+
check_beginning(style, first_line)
|
78
|
+
check_ending(style, last_line)
|
79
|
+
end
|
80
|
+
|
81
|
+
def check_beginning(style, first_line)
|
82
|
+
check_source(style, first_line, 'beginning')
|
83
|
+
end
|
84
|
+
|
85
|
+
def check_ending(style, last_line)
|
86
|
+
check_source(style, last_line - 2, 'end')
|
87
|
+
end
|
88
|
+
|
89
|
+
def check_source(style, line_no, desc)
|
54
90
|
case style
|
55
91
|
when :no_empty_lines
|
56
|
-
|
92
|
+
check_line(style, line_no, message(MSG_EXTRA, desc), &:empty?)
|
57
93
|
when :empty_lines
|
58
|
-
|
94
|
+
check_line(style, line_no, message(MSG_MISSING, desc)) do |line|
|
59
95
|
!line.empty?
|
60
96
|
end
|
61
97
|
end
|
62
98
|
end
|
63
99
|
|
64
|
-
def check_both(style, start_line, end_line, msg, &block)
|
65
|
-
kind = self.class::KIND
|
66
|
-
check_line(style, start_line, format(msg, kind, 'beginning'), &block)
|
67
|
-
check_line(style, end_line - 2, format(msg, kind, 'end'), &block)
|
68
|
-
end
|
69
|
-
|
70
100
|
def check_line(style, line, msg)
|
71
101
|
return unless yield(processed_source.lines[line])
|
72
102
|
|
@@ -75,13 +105,55 @@ module RuboCop
|
|
75
105
|
add_offense([style, range], range, msg)
|
76
106
|
end
|
77
107
|
|
78
|
-
def
|
79
|
-
|
108
|
+
def check_deferred_empty_line(body)
|
109
|
+
node = first_empty_line_required_child(body)
|
110
|
+
return unless node
|
111
|
+
|
112
|
+
line = previous_line_ignoring_comments(node.loc.first_line)
|
113
|
+
return if processed_source[line].empty?
|
114
|
+
|
115
|
+
range = source_range(processed_source.buffer, line + 2, 0)
|
116
|
+
add_offense([:empty_lines, range], range, deferred_message(node))
|
117
|
+
end
|
118
|
+
|
119
|
+
def namespace?(body, with_one_child: false)
|
120
|
+
if body.begin_type?
|
80
121
|
return false if with_one_child
|
81
|
-
|
122
|
+
body.children.all? { |child| constant_definition?(child) }
|
82
123
|
else
|
83
|
-
constant_definition?(
|
124
|
+
constant_definition?(body)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def first_child_requires_empty_line?(body)
|
129
|
+
if body.begin_type?
|
130
|
+
empty_line_required?(body.children.first)
|
131
|
+
else
|
132
|
+
empty_line_required?(body)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def first_empty_line_required_child(body)
|
137
|
+
if body.begin_type?
|
138
|
+
body.children.find { |child| empty_line_required?(child) }
|
139
|
+
elsif empty_line_required?(body)
|
140
|
+
body
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def previous_line_ignoring_comments(send_line)
|
145
|
+
(send_line - 2).downto(0) do |line|
|
146
|
+
return line unless comment_line?(processed_source[line])
|
84
147
|
end
|
148
|
+
0
|
149
|
+
end
|
150
|
+
|
151
|
+
def message(type, desc)
|
152
|
+
format(type, self.class::KIND, desc)
|
153
|
+
end
|
154
|
+
|
155
|
+
def deferred_message(node)
|
156
|
+
format(MSG_DEFERRED, node.type)
|
85
157
|
end
|
86
158
|
end
|
87
159
|
end
|
@@ -0,0 +1,61 @@
|
|
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
|
+
#
|
16
|
+
# @good
|
17
|
+
# array.sort_by(&:foo)
|
18
|
+
# array.sort_by { |v| v.foo }
|
19
|
+
# array.sort_by do |var|
|
20
|
+
# var.foo
|
21
|
+
# end
|
22
|
+
# array.max_by(&:foo)
|
23
|
+
# array.min_by(&:foo)
|
24
|
+
class CompareWithBlock < Cop
|
25
|
+
MSG = 'Use `%s_by(&:%s)` instead of ' \
|
26
|
+
'`%s { |%s, %s| %s.%s <=> %s.%s }`.'.freeze
|
27
|
+
|
28
|
+
def_node_matcher :compare?, <<-END
|
29
|
+
(block $(send _ {:sort :min :max}) (args (arg $_a) (arg $_b)) (send (send (lvar _a) $_m) :<=> (send (lvar _b) $_m)))
|
30
|
+
END
|
31
|
+
|
32
|
+
def on_block(node)
|
33
|
+
compare?(node) do |send, var_a, var_b, method|
|
34
|
+
range = compare_range(send, node)
|
35
|
+
compare_method = send.method_name
|
36
|
+
add_offense(node, range,
|
37
|
+
format(MSG, compare_method, method,
|
38
|
+
compare_method, var_a, var_b,
|
39
|
+
var_a, method, var_b, method))
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def autocorrect(node)
|
44
|
+
send, = *node
|
45
|
+
|
46
|
+
lambda do |corrector|
|
47
|
+
method = node.children.last.children.last.children.last
|
48
|
+
corrector.replace(compare_range(send, node),
|
49
|
+
"#{send.method_name}_by(&:#{method})")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def compare_range(send, node)
|
56
|
+
range_between(send.loc.selector.begin_pos, node.loc.end.end_pos)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -42,32 +42,32 @@ module RuboCop
|
|
42
42
|
|
43
43
|
MSG = 'Use `count` instead of `%s...%s`.'.freeze
|
44
44
|
|
45
|
-
|
46
|
-
|
45
|
+
def_node_matcher :count_candidate?, <<-PATTERN
|
46
|
+
{
|
47
|
+
(send (block $(send _ ${:select :reject}) ...) ${:count :length :size})
|
48
|
+
(send $(send _ ${:select :reject} (:block_pass _)) ${:count :length :size})
|
49
|
+
}
|
50
|
+
PATTERN
|
47
51
|
|
48
52
|
def on_send(node)
|
49
53
|
return if rails_safe_mode?
|
50
54
|
|
51
|
-
|
55
|
+
count_candidate?(node) do |selector_node, selector, counter|
|
56
|
+
return unless eligible_node?(node)
|
52
57
|
|
53
|
-
|
58
|
+
range = source_starting_at(node) do
|
59
|
+
selector_node.loc.selector.begin_pos
|
60
|
+
end
|
61
|
+
|
62
|
+
add_offense(node, range, format(MSG, selector, counter))
|
63
|
+
end
|
54
64
|
end
|
55
65
|
|
56
66
|
private
|
57
67
|
|
58
|
-
attr_reader :selector, :selector_loc, :params, :counter
|
59
|
-
|
60
|
-
def check(node)
|
61
|
-
return unless eligible_node?(node) && eligible_params? &&
|
62
|
-
eligible_method_chain?
|
63
|
-
|
64
|
-
range = source_starting_at(node) { @selector_loc.begin_pos }
|
65
|
-
|
66
|
-
add_offense(node, range, format(MSG, @selector, @counter))
|
67
|
-
end
|
68
|
-
|
69
68
|
def autocorrect(node)
|
70
|
-
selector,
|
69
|
+
selector_node, selector, _counter = count_candidate?(node)
|
70
|
+
selector_loc = selector_node.loc.selector
|
71
71
|
|
72
72
|
return if selector == :reject
|
73
73
|
|
@@ -83,48 +83,12 @@ module RuboCop
|
|
83
83
|
!(node.parent && node.parent.block_type?)
|
84
84
|
end
|
85
85
|
|
86
|
-
def eligible_params?
|
87
|
-
!(params && !params.block_pass_type?)
|
88
|
-
end
|
89
|
-
|
90
|
-
def eligible_method_chain?
|
91
|
-
COUNTERS.include?(counter) && SELECTORS.include?(selector)
|
92
|
-
end
|
93
|
-
|
94
|
-
def parse(node)
|
95
|
-
head, counter = *node
|
96
|
-
expression, selector, params = *head
|
97
|
-
if selector.is_a?(Symbol)
|
98
|
-
selector_loc = selector_location(expression, head.loc)
|
99
|
-
else
|
100
|
-
_, selector, params = *expression
|
101
|
-
if contains_selector?(expression)
|
102
|
-
selector_loc = expression.loc.selector
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
[selector, selector_loc, params, counter]
|
107
|
-
end
|
108
|
-
|
109
|
-
def selector_location(expression, head_loc)
|
110
|
-
if expression && expression.parent.loc.respond_to?(:selector)
|
111
|
-
expression.parent.loc.selector
|
112
|
-
elsif head_loc.respond_to?(:selector)
|
113
|
-
head_loc.selector
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
def contains_selector?(node)
|
118
|
-
node.respond_to?(:loc) && node.loc.respond_to?(:selector)
|
119
|
-
end
|
120
|
-
|
121
86
|
def source_starting_at(node)
|
122
|
-
begin_pos =
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
end
|
87
|
+
begin_pos = if block_given?
|
88
|
+
yield node
|
89
|
+
else
|
90
|
+
node.source_range.begin_pos
|
91
|
+
end
|
128
92
|
|
129
93
|
range_between(begin_pos, node.source_range.end_pos)
|
130
94
|
end
|