rubocop 0.55.0 → 0.56.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 +45 -0
- data/config/disabled.yml +4 -4
- data/config/enabled.yml +32 -16
- data/lib/rubocop.rb +8 -2
- data/lib/rubocop/cli.rb +4 -0
- data/lib/rubocop/comment_config.rb +36 -9
- data/lib/rubocop/config.rb +8 -1
- data/lib/rubocop/cop/generator.rb +4 -3
- data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +101 -29
- data/lib/rubocop/cop/{style → layout}/empty_line_after_guard_clause.rb +1 -1
- data/lib/rubocop/cop/layout/empty_line_between_defs.rb +5 -5
- data/lib/rubocop/cop/layout/first_parameter_indentation.rb +112 -2
- data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +8 -4
- data/lib/rubocop/cop/lint/erb_new_arguments.rb +107 -0
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +4 -0
- data/lib/rubocop/cop/lint/nested_percent_literal.rb +0 -8
- data/lib/rubocop/cop/lint/percent_string_array.rb +1 -1
- data/lib/rubocop/cop/lint/percent_symbol_array.rb +1 -1
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +3 -0
- data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +16 -3
- data/lib/rubocop/cop/lint/splat_keyword_arguments.rb +36 -0
- data/lib/rubocop/cop/lint/unneeded_cop_enable_directive.rb +20 -2
- data/lib/rubocop/cop/performance/inefficient_hash_search.rb +95 -0
- data/lib/rubocop/cop/performance/unneeded_sort.rb +41 -6
- data/lib/rubocop/cop/rails/assert_not.rb +44 -0
- data/lib/rubocop/cop/rails/blank.rb +34 -28
- data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +1 -1
- data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +12 -2
- data/lib/rubocop/cop/rails/http_positional_arguments.rb +7 -7
- data/lib/rubocop/cop/rails/present.rb +31 -25
- data/lib/rubocop/cop/rails/refute_methods.rb +76 -0
- data/lib/rubocop/cop/rails/reversible_migration.rb +6 -4
- data/lib/rubocop/cop/rails/save_bang.rb +4 -1
- data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +1 -10
- data/lib/rubocop/cop/style/command_literal.rb +15 -3
- data/lib/rubocop/cop/style/comment_annotation.rb +6 -1
- data/lib/rubocop/cop/style/empty_method.rb +6 -3
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +12 -2
- data/lib/rubocop/cop/style/method_missing_super.rb +34 -0
- data/lib/rubocop/cop/style/{method_missing.rb → missing_respond_to_missing.rb} +7 -29
- data/lib/rubocop/cop/style/parentheses_around_condition.rb +28 -2
- data/lib/rubocop/node_pattern.rb +1 -1
- data/lib/rubocop/processed_source.rb +12 -6
- data/lib/rubocop/result_cache.rb +9 -4
- data/lib/rubocop/rspec/shared_contexts.rb +4 -0
- data/lib/rubocop/runner.rb +8 -2
- data/lib/rubocop/target_finder.rb +40 -60
- data/lib/rubocop/version.rb +1 -1
- metadata +10 -4
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
#
|
7
|
+
# Use `assert_not` methods instead of `refute` methods.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# refute false
|
12
|
+
# refute_empty [1, 2, 3]
|
13
|
+
# refute_equal true, false
|
14
|
+
#
|
15
|
+
# # good
|
16
|
+
# assert_not false
|
17
|
+
# assert_not_empty [1, 2, 3]
|
18
|
+
# assert_not_equal true, false
|
19
|
+
#
|
20
|
+
class RefuteMethods < Cop
|
21
|
+
MSG = 'Prefer `%<assert_method>s` over `%<refute_method>s`.'.freeze
|
22
|
+
|
23
|
+
CORRECTIONS = {
|
24
|
+
refute: 'assert_not',
|
25
|
+
refute_empty: 'assert_not_empty',
|
26
|
+
refute_equal: 'assert_not_equal',
|
27
|
+
refute_in_delta: 'assert_not_in_delta',
|
28
|
+
refute_in_epsilon: 'assert_not_in_epsilon',
|
29
|
+
refute_includes: 'assert_not_includes',
|
30
|
+
refute_instance_of: 'assert_not_instance_of',
|
31
|
+
refute_kind_of: 'assert_not_kind_of',
|
32
|
+
refute_nil: 'assert_not_nil',
|
33
|
+
refute_operator: 'assert_not_operator',
|
34
|
+
refute_predicate: 'assert_not_predicate',
|
35
|
+
refute_respond_to: 'assert_not_respond_to',
|
36
|
+
refute_same: 'assert_not_same',
|
37
|
+
refute_match: 'assert_no_match'
|
38
|
+
}.freeze
|
39
|
+
|
40
|
+
OFFENSIVE_METHODS = CORRECTIONS.keys.freeze
|
41
|
+
|
42
|
+
def_node_matcher :offensive?, '(send nil? #refute_method? ...)'
|
43
|
+
|
44
|
+
def on_send(node)
|
45
|
+
return unless offensive?(node)
|
46
|
+
|
47
|
+
message = offense_message(node.method_name)
|
48
|
+
add_offense(node, location: :selector, message: message)
|
49
|
+
end
|
50
|
+
|
51
|
+
def autocorrect(node)
|
52
|
+
lambda do |corrector|
|
53
|
+
corrector.replace(
|
54
|
+
node.loc.selector,
|
55
|
+
CORRECTIONS[node.method_name]
|
56
|
+
)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def refute_method?(method_name)
|
63
|
+
OFFENSIVE_METHODS.include?(method_name)
|
64
|
+
end
|
65
|
+
|
66
|
+
def offense_message(method_name)
|
67
|
+
format(
|
68
|
+
MSG,
|
69
|
+
refute_method: method_name,
|
70
|
+
assert_method: CORRECTIONS[method_name]
|
71
|
+
)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -157,7 +157,7 @@ module RuboCop
|
|
157
157
|
|
158
158
|
def on_send(node)
|
159
159
|
return unless within_change_method?(node)
|
160
|
-
return if
|
160
|
+
return if within_reversible_or_up_only_block?(node)
|
161
161
|
|
162
162
|
check_irreversible_schema_statement_node(node)
|
163
163
|
check_drop_table_node(node)
|
@@ -168,7 +168,7 @@ module RuboCop
|
|
168
168
|
|
169
169
|
def on_block(node)
|
170
170
|
return unless within_change_method?(node)
|
171
|
-
return if
|
171
|
+
return if within_reversible_or_up_only_block?(node)
|
172
172
|
|
173
173
|
check_change_table_node(node.send_node, node.body)
|
174
174
|
end
|
@@ -261,9 +261,11 @@ module RuboCop
|
|
261
261
|
end
|
262
262
|
end
|
263
263
|
|
264
|
-
def
|
264
|
+
def within_reversible_or_up_only_block?(node)
|
265
265
|
node.each_ancestor(:block).any? do |ancestor|
|
266
|
-
ancestor.block_type? &&
|
266
|
+
ancestor.block_type? &&
|
267
|
+
ancestor.send_node.method?(:reversible) ||
|
268
|
+
ancestor.send_node.method?(:up_only)
|
267
269
|
end
|
268
270
|
end
|
269
271
|
|
@@ -36,6 +36,8 @@ module RuboCop
|
|
36
36
|
# # ...
|
37
37
|
# end
|
38
38
|
class SaveBang < Cop
|
39
|
+
include NegativeConditional
|
40
|
+
|
39
41
|
MSG = 'Use `%<prefer>s` instead of `%<current>s` if the return ' \
|
40
42
|
'value is not checked.'.freeze
|
41
43
|
CREATE_MSG = (MSG +
|
@@ -131,7 +133,8 @@ module RuboCop
|
|
131
133
|
def conditional?(node)
|
132
134
|
node.parent && (
|
133
135
|
node.parent.if_type? || node.parent.case_type? ||
|
134
|
-
node.parent.or_type? || node.parent.and_type?
|
136
|
+
node.parent.or_type? || node.parent.and_type? ||
|
137
|
+
single_negative?(node.parent)
|
135
138
|
)
|
136
139
|
end
|
137
140
|
|
@@ -126,13 +126,10 @@ module RuboCop
|
|
126
126
|
end
|
127
127
|
end
|
128
128
|
|
129
|
-
# rubocop:disable Metrics/AbcSize
|
130
129
|
def remove_braces_with_whitespace(corrector, node, space)
|
131
130
|
right_brace_and_space = right_brace_and_space(node.loc.end, space)
|
132
131
|
|
133
|
-
if
|
134
|
-
remove_braces(corrector, node)
|
135
|
-
elsif node.multiline?
|
132
|
+
if node.multiline?
|
136
133
|
remove_braces_with_range(corrector,
|
137
134
|
left_whole_line_range(node.loc.begin),
|
138
135
|
right_whole_line_range(node.loc.end))
|
@@ -143,7 +140,6 @@ module RuboCop
|
|
143
140
|
right_brace_and_space)
|
144
141
|
end
|
145
142
|
end
|
146
|
-
# rubocop:enable Metrics/AbcSize
|
147
143
|
|
148
144
|
def remove_braces_with_range(corrector, left_range, right_range)
|
149
145
|
corrector.remove(left_range)
|
@@ -184,11 +180,6 @@ module RuboCop
|
|
184
180
|
range_with_surrounding_comma(brace_and_space, :left)
|
185
181
|
end
|
186
182
|
|
187
|
-
def remove_braces(corrector, node)
|
188
|
-
corrector.remove(node.loc.begin)
|
189
|
-
corrector.remove(node.loc.end)
|
190
|
-
end
|
191
|
-
|
192
183
|
def add_braces(corrector, node)
|
193
184
|
corrector.insert_before(node.source_range, '{')
|
194
185
|
corrector.insert_after(node.source_range, '}')
|
@@ -95,7 +95,7 @@ module RuboCop
|
|
95
95
|
return if contains_backtick?(node)
|
96
96
|
|
97
97
|
replacement = if backtick_literal?(node)
|
98
|
-
['%x', ''].zip(
|
98
|
+
['%x', ''].zip(preferred_delimiter).map(&:join)
|
99
99
|
else
|
100
100
|
%w[` `]
|
101
101
|
end
|
@@ -169,9 +169,21 @@ module RuboCop
|
|
169
169
|
node.loc.begin.source == '`'
|
170
170
|
end
|
171
171
|
|
172
|
-
def
|
172
|
+
def preferred_delimiter
|
173
|
+
(command_delimiter || default_delimiter).split(//)
|
174
|
+
end
|
175
|
+
|
176
|
+
def command_delimiter
|
177
|
+
preferred_delimiters_config['%x']
|
178
|
+
end
|
179
|
+
|
180
|
+
def default_delimiter
|
181
|
+
preferred_delimiters_config['default']
|
182
|
+
end
|
183
|
+
|
184
|
+
def preferred_delimiters_config
|
173
185
|
config.for_cop('Style/PercentLiteralDelimiters') \
|
174
|
-
['PreferredDelimiters']
|
186
|
+
['PreferredDelimiters']
|
175
187
|
end
|
176
188
|
end
|
177
189
|
end
|
@@ -42,7 +42,8 @@ module RuboCop
|
|
42
42
|
|
43
43
|
def investigate(processed_source)
|
44
44
|
processed_source.comments.each_with_index do |comment, index|
|
45
|
-
next unless first_comment_line?(processed_source.comments, index)
|
45
|
+
next unless first_comment_line?(processed_source.comments, index) ||
|
46
|
+
inline_comment?(comment)
|
46
47
|
|
47
48
|
margin, first_word, colon, space, note = split_comment(comment)
|
48
49
|
next unless annotation?(comment) &&
|
@@ -74,6 +75,10 @@ module RuboCop
|
|
74
75
|
comments[index - 1].loc.line < comments[index].loc.line - 1
|
75
76
|
end
|
76
77
|
|
78
|
+
def inline_comment?(comment)
|
79
|
+
!comment_line?(comment.loc.expression.source_line)
|
80
|
+
end
|
81
|
+
|
77
82
|
def annotation_range(comment, margin, length)
|
78
83
|
start = comment.loc.expression.begin_pos + margin.length
|
79
84
|
range_between(start, start + length)
|
@@ -73,10 +73,13 @@ module RuboCop
|
|
73
73
|
end
|
74
74
|
|
75
75
|
def corrected(node)
|
76
|
-
|
77
|
-
scope = node.receiver ? "#{node.receiver.source}." : ''
|
76
|
+
has_parentheses = parentheses?(node.arguments)
|
78
77
|
|
79
|
-
|
78
|
+
arguments = node.arguments? ? node.arguments.source : ''
|
79
|
+
extra_space = node.arguments? && !has_parentheses ? ' ' : ''
|
80
|
+
scope = node.receiver ? "#{node.receiver.source}." : ''
|
81
|
+
|
82
|
+
signature = [scope, node.method_name, extra_space, arguments].join
|
80
83
|
|
81
84
|
["def #{signature}", 'end'].join(joint(node))
|
82
85
|
end
|
@@ -54,7 +54,10 @@ module RuboCop
|
|
54
54
|
def autocorrect(node)
|
55
55
|
lambda do |corrector|
|
56
56
|
corrector.replace(args_begin(node), '(')
|
57
|
-
|
57
|
+
|
58
|
+
unless args_parenthesized?(node)
|
59
|
+
corrector.insert_after(args_end(node), ')')
|
60
|
+
end
|
58
61
|
end
|
59
62
|
end
|
60
63
|
|
@@ -78,12 +81,19 @@ module RuboCop
|
|
78
81
|
loc = node.loc
|
79
82
|
selector =
|
80
83
|
node.super_type? || node.yield_type? ? loc.keyword : loc.selector
|
81
|
-
|
84
|
+
|
85
|
+
resize_by = args_parenthesized?(node) ? 2 : 1
|
86
|
+
selector.end.resize(resize_by)
|
82
87
|
end
|
83
88
|
|
84
89
|
def args_end(node)
|
85
90
|
node.loc.expression.end
|
86
91
|
end
|
92
|
+
|
93
|
+
def args_parenthesized?(node)
|
94
|
+
return false unless node.arguments.length == 1
|
95
|
+
node.arguments.first.parenthesized_call?
|
96
|
+
end
|
87
97
|
end
|
88
98
|
end
|
89
99
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# This cop checks for the presence of `method_missing` without
|
7
|
+
# falling back on `super`.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# #bad
|
11
|
+
# def method_missing(name, *args)
|
12
|
+
# # ...
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# #good
|
16
|
+
#
|
17
|
+
# def method_missing(name, *args)
|
18
|
+
# # ...
|
19
|
+
# super
|
20
|
+
# end
|
21
|
+
class MethodMissingSuper < Cop
|
22
|
+
MSG = 'When using `method_missing`, fall back on `super`.'.freeze
|
23
|
+
|
24
|
+
def on_def(node)
|
25
|
+
return unless node.method?(:method_missing)
|
26
|
+
return if node.descendants.any?(&:zsuper_type?)
|
27
|
+
|
28
|
+
add_offense(node)
|
29
|
+
end
|
30
|
+
alias on_defs on_def
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -4,7 +4,7 @@ module RuboCop
|
|
4
4
|
module Cop
|
5
5
|
module Style
|
6
6
|
# This cop checks for the presence of `method_missing` without also
|
7
|
-
# defining `respond_to_missing
|
7
|
+
# defining `respond_to_missing?`.
|
8
8
|
#
|
9
9
|
# @example
|
10
10
|
# #bad
|
@@ -19,44 +19,22 @@ module RuboCop
|
|
19
19
|
#
|
20
20
|
# def method_missing(name, *args)
|
21
21
|
# # ...
|
22
|
-
# super
|
23
22
|
# end
|
24
|
-
|
25
|
-
|
23
|
+
#
|
24
|
+
class MissingRespondToMissing < Cop
|
25
|
+
MSG =
|
26
|
+
'When using `method_missing`, define `respond_to_missing?`.'.freeze
|
26
27
|
|
27
28
|
def on_def(node)
|
28
29
|
return unless node.method?(:method_missing)
|
30
|
+
return if implements_respond_to_missing?(node)
|
29
31
|
|
30
|
-
|
32
|
+
add_offense(node)
|
31
33
|
end
|
32
34
|
alias on_defs on_def
|
33
35
|
|
34
36
|
private
|
35
37
|
|
36
|
-
def check(node)
|
37
|
-
return if calls_super?(node) && implements_respond_to_missing?(node)
|
38
|
-
|
39
|
-
add_offense(node)
|
40
|
-
end
|
41
|
-
|
42
|
-
def message(node)
|
43
|
-
instructions = []
|
44
|
-
|
45
|
-
unless implements_respond_to_missing?(node)
|
46
|
-
instructions << 'define `respond_to_missing?`'.freeze
|
47
|
-
end
|
48
|
-
|
49
|
-
unless calls_super?(node)
|
50
|
-
instructions << 'fall back on `super`'.freeze
|
51
|
-
end
|
52
|
-
|
53
|
-
format(MSG, instructions: instructions.join(' and '))
|
54
|
-
end
|
55
|
-
|
56
|
-
def calls_super?(node)
|
57
|
-
node.descendants.any?(&:zsuper_type?)
|
58
|
-
end
|
59
|
-
|
60
38
|
def implements_respond_to_missing?(node)
|
61
39
|
node.parent.each_child_node(node.type).any? do |sibling|
|
62
40
|
sibling.method?(:respond_to_missing?)
|
@@ -22,6 +22,23 @@ module RuboCop
|
|
22
22
|
# if x > 10
|
23
23
|
# elsif x < 3
|
24
24
|
# end
|
25
|
+
#
|
26
|
+
# @example AllowInMultilineConditions: false (default)
|
27
|
+
# # bad
|
28
|
+
# if (x > 10 &&
|
29
|
+
# y > 10)
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# # good
|
33
|
+
# if x > 10 &&
|
34
|
+
# y > 10
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# @example AllowInMultilineConditions: true
|
38
|
+
# # good
|
39
|
+
# if (x > 10 &&
|
40
|
+
# y > 10)
|
41
|
+
# end
|
25
42
|
class ParenthesesAroundCondition < Cop
|
26
43
|
include SafeAssignment
|
27
44
|
include Parentheses
|
@@ -52,8 +69,7 @@ module RuboCop
|
|
52
69
|
|
53
70
|
control_op_condition(cond) do |first_child|
|
54
71
|
return if modifier_op?(first_child)
|
55
|
-
return if
|
56
|
-
return if safe_assignment?(cond) && safe_assignment_allowed?
|
72
|
+
return if parens_allowed?(cond)
|
57
73
|
|
58
74
|
add_offense(cond)
|
59
75
|
end
|
@@ -71,6 +87,16 @@ module RuboCop
|
|
71
87
|
article = kw == 'while' ? 'a' : 'an'
|
72
88
|
"Don't use parentheses around the condition of #{article} `#{kw}`."
|
73
89
|
end
|
90
|
+
|
91
|
+
def parens_allowed?(node)
|
92
|
+
parens_required?(node) ||
|
93
|
+
(safe_assignment?(node) && safe_assignment_allowed?) ||
|
94
|
+
(node.multiline? && allow_multiline_conditions?)
|
95
|
+
end
|
96
|
+
|
97
|
+
def allow_multiline_conditions?
|
98
|
+
cop_config['AllowInMultilineConditions']
|
99
|
+
end
|
74
100
|
end
|
75
101
|
end
|
76
102
|
end
|
data/lib/rubocop/node_pattern.rb
CHANGED
@@ -74,7 +74,7 @@ module RuboCop
|
|
74
74
|
# You can nest arbitrarily deep:
|
75
75
|
#
|
76
76
|
# # matches node parsed from 'Const = Class.new' or 'Const = Module.new':
|
77
|
-
# '(casgn nil?
|
77
|
+
# '(casgn nil? :Const (send (const nil? {:Class :Module}) :new))'
|
78
78
|
# # matches a node parsed from an 'if', with a '==' comparison,
|
79
79
|
# # and no 'else' branch:
|
80
80
|
# '(if (send _ :== _) _ nil?)'
|
@@ -106,10 +106,6 @@ module RuboCop
|
|
106
106
|
comment_lines.include?(source_range.line)
|
107
107
|
end
|
108
108
|
|
109
|
-
def comment_on_line?(line)
|
110
|
-
comments.any? { |c| c.loc.line == line }
|
111
|
-
end
|
112
|
-
|
113
109
|
def comments_before_line(line)
|
114
110
|
comments.select { |c| c.location.line <= line }
|
115
111
|
end
|
@@ -127,6 +123,13 @@ module RuboCop
|
|
127
123
|
lines[token.line]
|
128
124
|
end
|
129
125
|
|
126
|
+
def line_indentation(line_number)
|
127
|
+
lines[line_number - 1]
|
128
|
+
.match(/^(\s*)/)[1]
|
129
|
+
.to_s
|
130
|
+
.length
|
131
|
+
end
|
132
|
+
|
130
133
|
private
|
131
134
|
|
132
135
|
def comment_lines
|
@@ -160,7 +163,7 @@ module RuboCop
|
|
160
163
|
[ast, comments, tokens]
|
161
164
|
end
|
162
165
|
|
163
|
-
# rubocop:disable Metrics/MethodLength
|
166
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength
|
164
167
|
def parser_class(ruby_version)
|
165
168
|
case ruby_version
|
166
169
|
when 2.1
|
@@ -178,11 +181,14 @@ module RuboCop
|
|
178
181
|
when 2.5
|
179
182
|
require 'parser/ruby25'
|
180
183
|
Parser::Ruby25
|
184
|
+
when 2.6
|
185
|
+
require 'parser/ruby26'
|
186
|
+
Parser::Ruby26
|
181
187
|
else
|
182
188
|
raise ArgumentError, "Unknown Ruby version: #{ruby_version.inspect}"
|
183
189
|
end
|
184
190
|
end
|
185
|
-
# rubocop:enable Metrics/MethodLength
|
191
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/MethodLength
|
186
192
|
|
187
193
|
def create_parser(ruby_version)
|
188
194
|
builder = RuboCop::AST::Builder.new
|