rubocop 0.60.0 → 0.61.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 +4 -4
- data/config/default.yml +573 -560
- data/lib/rubocop.rb +5 -0
- data/lib/rubocop/ast/node.rb +1 -1
- data/lib/rubocop/ast/sexp.rb +1 -1
- data/lib/rubocop/cli.rb +9 -14
- data/lib/rubocop/config.rb +4 -3
- data/lib/rubocop/config_loader.rb +25 -22
- data/lib/rubocop/config_loader_resolver.rb +3 -2
- data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +53 -0
- data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +73 -0
- data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +138 -0
- data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +52 -46
- data/lib/rubocop/cop/generator.rb +13 -17
- data/lib/rubocop/cop/generator/configuration_injector.rb +60 -0
- data/lib/rubocop/cop/layout/align_hash.rb +3 -0
- data/lib/rubocop/cop/layout/comment_indentation.rb +32 -2
- data/lib/rubocop/cop/layout/indent_heredoc.rb +11 -5
- data/lib/rubocop/cop/layout/indentation_width.rb +7 -1
- data/lib/rubocop/cop/layout/multiline_array_brace_layout.rb +11 -11
- data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +1 -1
- data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +1 -1
- data/lib/rubocop/cop/layout/multiline_method_definition_brace_layout.rb +1 -1
- data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +16 -3
- data/lib/rubocop/cop/layout/space_around_block_parameters.rb +30 -17
- data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +11 -0
- data/lib/rubocop/cop/lint/shadowed_exception.rb +2 -5
- data/lib/rubocop/cop/lint/useless_access_modifier.rb +1 -1
- data/lib/rubocop/cop/metrics/line_length.rb +2 -2
- data/lib/rubocop/cop/mixin/trailing_comma.rb +11 -15
- data/lib/rubocop/cop/offense.rb +1 -1
- data/lib/rubocop/cop/performance/open_struct.rb +46 -0
- data/lib/rubocop/cop/performance/redundant_merge.rb +18 -4
- data/lib/rubocop/cop/rails/bulk_change_table.rb +2 -2
- data/lib/rubocop/cop/rails/dynamic_find_by.rb +15 -8
- data/lib/rubocop/cop/rails/http_positional_arguments.rb +17 -14
- data/lib/rubocop/cop/rails/http_status.rb +4 -4
- data/lib/rubocop/cop/rails/inverse_of.rb +2 -2
- data/lib/rubocop/cop/rails/reversible_migration.rb +1 -1
- data/lib/rubocop/cop/rails/skips_model_validations.rb +1 -1
- data/lib/rubocop/cop/rails/validation.rb +4 -4
- data/lib/rubocop/cop/security/open.rb +31 -11
- data/lib/rubocop/cop/style/begin_block.rb +6 -0
- data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +1 -1
- data/lib/rubocop/cop/style/empty_case_condition.rb +13 -7
- data/lib/rubocop/cop/style/for.rb +9 -78
- data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +6 -4
- data/lib/rubocop/cop/style/global_vars.rb +1 -1
- data/lib/rubocop/cop/style/infinite_loop.rb +42 -6
- data/lib/rubocop/cop/style/lambda.rb +4 -87
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +221 -16
- data/lib/rubocop/cop/style/raise_args.rb +1 -1
- data/lib/rubocop/cop/style/regexp_literal.rb +62 -10
- data/lib/rubocop/cop/style/unneeded_condition.rb +2 -2
- data/lib/rubocop/cop/variable_force.rb +4 -2
- data/lib/rubocop/cop/variable_force/variable.rb +2 -0
- data/lib/rubocop/magic_comment.rb +1 -1
- data/lib/rubocop/remote_config.rb +13 -4
- data/lib/rubocop/rspec/expect_offense.rb +1 -1
- data/lib/rubocop/runner.rb +15 -4
- data/lib/rubocop/version.rb +1 -1
- metadata +7 -2
@@ -47,11 +47,11 @@ module RuboCop
|
|
47
47
|
uniqueness
|
48
48
|
].freeze
|
49
49
|
|
50
|
-
|
51
|
-
|
50
|
+
DENYLIST = TYPES.map { |p| "validates_#{p}_of".to_sym }.freeze
|
51
|
+
ALLOWLIST = TYPES.map { |p| "validates :column, #{p}: value" }.freeze
|
52
52
|
|
53
53
|
def on_send(node)
|
54
|
-
return unless !node.receiver &&
|
54
|
+
return unless !node.receiver && DENYLIST.include?(node.method_name)
|
55
55
|
|
56
56
|
add_offense(node, location: :selector)
|
57
57
|
end
|
@@ -71,7 +71,7 @@ module RuboCop
|
|
71
71
|
end
|
72
72
|
|
73
73
|
def preferred_method(method)
|
74
|
-
|
74
|
+
ALLOWLIST[DENYLIST.index(method.to_sym)]
|
75
75
|
end
|
76
76
|
|
77
77
|
def correct_validate_type(corrector, node)
|
@@ -26,24 +26,44 @@ module RuboCop
|
|
26
26
|
(send nil? :open $!str ...)
|
27
27
|
PATTERN
|
28
28
|
|
29
|
+
def on_send(node)
|
30
|
+
open?(node) do |code|
|
31
|
+
return if safe?(code)
|
32
|
+
|
33
|
+
add_offense(node, location: :selector)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
29
39
|
def safe?(node)
|
30
|
-
if node
|
31
|
-
|
32
|
-
elsif node
|
33
|
-
safe?(node.
|
34
|
-
elsif node.send_type? && node.method_name == :+
|
35
|
-
safe?(node.child_nodes.first)
|
40
|
+
if simple_string?(node)
|
41
|
+
safe_argument?(node.str_content)
|
42
|
+
elsif composite_string?(node)
|
43
|
+
safe?(node.children.first)
|
36
44
|
else
|
37
45
|
false
|
38
46
|
end
|
39
47
|
end
|
40
48
|
|
41
|
-
def
|
42
|
-
|
43
|
-
|
49
|
+
def safe_argument?(argument)
|
50
|
+
!argument.empty? && !argument.start_with?('|')
|
51
|
+
end
|
44
52
|
|
45
|
-
|
46
|
-
|
53
|
+
def simple_string?(node)
|
54
|
+
node.str_type?
|
55
|
+
end
|
56
|
+
|
57
|
+
def composite_string?(node)
|
58
|
+
interpolated_string?(node) || concatenated_string?(node)
|
59
|
+
end
|
60
|
+
|
61
|
+
def interpolated_string?(node)
|
62
|
+
node.dstr_type?
|
63
|
+
end
|
64
|
+
|
65
|
+
def concatenated_string?(node)
|
66
|
+
node.send_type? && node.method?(:+) && node.receiver.str_type?
|
47
67
|
end
|
48
68
|
end
|
49
69
|
end
|
@@ -154,7 +154,7 @@ module RuboCop
|
|
154
154
|
end
|
155
155
|
|
156
156
|
def right_whole_line_range(loc_end)
|
157
|
-
if range_by_whole_lines(loc_end).source.strip =~
|
157
|
+
if range_by_whole_lines(loc_end).source.strip =~ /\A}\s*,?\z/
|
158
158
|
range_by_whole_lines(loc_end, include_final_newline: true)
|
159
159
|
else
|
160
160
|
loc_end
|
@@ -67,8 +67,11 @@ module RuboCop
|
|
67
67
|
private
|
68
68
|
|
69
69
|
def correct_case_when(corrector, case_node, when_nodes)
|
70
|
-
|
71
|
-
|
70
|
+
case_range = case_node.loc.keyword.join(when_nodes.first.loc.keyword)
|
71
|
+
|
72
|
+
corrector.replace(case_range, 'if')
|
73
|
+
|
74
|
+
keep_first_when_comment(case_node, when_nodes.first, corrector)
|
72
75
|
|
73
76
|
when_nodes[1..-1].each do |when_node|
|
74
77
|
corrector.replace(when_node.loc.keyword, 'elsif')
|
@@ -88,12 +91,15 @@ module RuboCop
|
|
88
91
|
end
|
89
92
|
end
|
90
93
|
|
91
|
-
def
|
92
|
-
|
93
|
-
|
94
|
-
)
|
94
|
+
def keep_first_when_comment(case_node, first_when_node, corrector)
|
95
|
+
comment = processed_source.comments_before_line(
|
96
|
+
first_when_node.loc.keyword.line
|
97
|
+
).map(&:text).join("\n")
|
98
|
+
|
99
|
+
line = range_by_whole_lines(case_node.source_range)
|
95
100
|
|
96
|
-
corrector.
|
101
|
+
corrector.insert_before(line, "#{comment}\n") if !comment.empty? &&
|
102
|
+
!case_node.parent
|
97
103
|
end
|
98
104
|
end
|
99
105
|
end
|
@@ -46,18 +46,6 @@ module RuboCop
|
|
46
46
|
PREFER_EACH = 'Prefer `each` over `for`.'.freeze
|
47
47
|
PREFER_FOR = 'Prefer `for` over `each`.'.freeze
|
48
48
|
|
49
|
-
def_node_matcher :deconstruct_for, <<-PATTERN
|
50
|
-
(for $_item $_enumerable _block)
|
51
|
-
PATTERN
|
52
|
-
|
53
|
-
def_node_matcher :deconstruct_each, <<-PATTERN
|
54
|
-
(block (send $_enumerable :each) $_ _block)
|
55
|
-
PATTERN
|
56
|
-
|
57
|
-
def_node_matcher :extract_variables, <<-PATTERN
|
58
|
-
(args $_)
|
59
|
-
PATTERN
|
60
|
-
|
61
49
|
def on_for(node)
|
62
50
|
if style == :each
|
63
51
|
add_offense(node, message: PREFER_EACH) do
|
@@ -69,13 +57,12 @@ module RuboCop
|
|
69
57
|
end
|
70
58
|
|
71
59
|
def on_block(node)
|
72
|
-
return
|
73
|
-
|
74
|
-
return unless node.send_node.method?(:each) &&
|
75
|
-
!node.send_node.arguments?
|
60
|
+
return unless suspect_enumerable?(node)
|
76
61
|
|
77
62
|
if style == :for
|
78
|
-
|
63
|
+
add_offense(node, message: PREFER_FOR) do
|
64
|
+
opposite_style_detected
|
65
|
+
end
|
79
66
|
else
|
80
67
|
correct_style_detected
|
81
68
|
end
|
@@ -83,73 +70,17 @@ module RuboCop
|
|
83
70
|
|
84
71
|
def autocorrect(node)
|
85
72
|
if style == :each
|
86
|
-
|
73
|
+
ForToEachCorrector.new(node)
|
87
74
|
else
|
88
|
-
|
75
|
+
EachToForCorrector.new(node)
|
89
76
|
end
|
90
77
|
end
|
91
78
|
|
92
79
|
private
|
93
80
|
|
94
|
-
def
|
95
|
-
|
96
|
-
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
def autocorrect_to_each(node)
|
101
|
-
item, enumerable = deconstruct_for(node)
|
102
|
-
|
103
|
-
end_pos = end_position(node, enumerable)
|
104
|
-
|
105
|
-
replacement_range = replacement_range(node, end_pos)
|
106
|
-
|
107
|
-
enum_source = enumerable_source(enumerable)
|
108
|
-
|
109
|
-
correction = "#{enum_source}.each do |#{item.source}|"
|
110
|
-
->(corrector) { corrector.replace(replacement_range, correction) }
|
111
|
-
end
|
112
|
-
|
113
|
-
def end_position(node, enumerable)
|
114
|
-
if node.do?
|
115
|
-
node.loc.begin.end_pos
|
116
|
-
elsif enumerable.begin_type?
|
117
|
-
enumerable.loc.end.end_pos
|
118
|
-
else
|
119
|
-
enumerable.loc.expression.end.end_pos
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
def enumerable_source(enumerable)
|
124
|
-
return "(#{enumerable.source})" if wrap_into_parentheses?(enumerable)
|
125
|
-
|
126
|
-
enumerable.source
|
127
|
-
end
|
128
|
-
|
129
|
-
def wrap_into_parentheses?(enumerable)
|
130
|
-
enumerable.irange_type? || enumerable.erange_type?
|
131
|
-
end
|
132
|
-
|
133
|
-
def autocorrect_to_for(node)
|
134
|
-
enumerable, items = deconstruct_each(node)
|
135
|
-
variables = extract_variables(items)
|
136
|
-
|
137
|
-
if variables.nil?
|
138
|
-
replacement_range = replacement_range(node, node.loc.begin.end_pos)
|
139
|
-
correction = "for _ in #{enumerable.source} do"
|
140
|
-
else
|
141
|
-
replacement_range = replacement_range(node,
|
142
|
-
items.loc.expression.end_pos)
|
143
|
-
correction = "for #{variables.source} in #{enumerable.source} do"
|
144
|
-
end
|
145
|
-
|
146
|
-
->(corrector) { corrector.replace(replacement_range, correction) }
|
147
|
-
end
|
148
|
-
|
149
|
-
def replacement_range(node, end_pos)
|
150
|
-
Parser::Source::Range.new(node.loc.expression.source_buffer,
|
151
|
-
node.loc.expression.begin_pos,
|
152
|
-
end_pos)
|
81
|
+
def suspect_enumerable?(node)
|
82
|
+
node.multiline? &&
|
83
|
+
node.send_node.method?(:each) && !node.send_node.arguments?
|
153
84
|
end
|
154
85
|
end
|
155
86
|
end
|
@@ -101,9 +101,9 @@ module RuboCop
|
|
101
101
|
token_number += 1
|
102
102
|
end
|
103
103
|
|
104
|
-
|
105
|
-
|
106
|
-
token =
|
104
|
+
next_token = processed_source.tokens[token_number]
|
105
|
+
if next_token && next_token.text =~ Encoding::ENCODING_PATTERN
|
106
|
+
token = next_token
|
107
107
|
end
|
108
108
|
|
109
109
|
token
|
@@ -155,7 +155,9 @@ module RuboCop
|
|
155
155
|
|
156
156
|
def proceeding_comment
|
157
157
|
last_special_comment = last_special_comment(processed_source)
|
158
|
-
|
158
|
+
following_line = processed_source.following_line(last_special_comment)
|
159
|
+
|
160
|
+
if following_line && following_line.empty?
|
159
161
|
"\n#{FROZEN_STRING_LITERAL_ENABLED}"
|
160
162
|
else
|
161
163
|
"\n#{FROZEN_STRING_LITERAL_ENABLED}\n"
|
@@ -23,7 +23,7 @@ module RuboCop
|
|
23
23
|
MSG = 'Do not introduce global variables.'.freeze
|
24
24
|
|
25
25
|
# built-in global variables and their English aliases
|
26
|
-
#
|
26
|
+
# https://www.zenspider.com/ruby/quickref.html
|
27
27
|
BUILT_IN_VARS = %w[
|
28
28
|
$: $LOAD_PATH
|
29
29
|
$" $LOADED_FEATURES
|
@@ -20,16 +20,21 @@ module RuboCop
|
|
20
20
|
|
21
21
|
MSG = 'Use `Kernel#loop` for infinite loops.'.freeze
|
22
22
|
|
23
|
-
def
|
24
|
-
|
23
|
+
def join_force?(force_class)
|
24
|
+
force_class == VariableForce
|
25
|
+
end
|
25
26
|
|
26
|
-
|
27
|
+
def after_leaving_scope(scope, _variable_table)
|
28
|
+
@variables ||= []
|
29
|
+
@variables.concat(scope.variables.values)
|
27
30
|
end
|
28
31
|
|
29
|
-
def
|
30
|
-
|
32
|
+
def on_while(node)
|
33
|
+
while_or_until(node) if node.condition.truthy_literal?
|
34
|
+
end
|
31
35
|
|
32
|
-
|
36
|
+
def on_until(node)
|
37
|
+
while_or_until(node) if node.condition.falsey_literal?
|
33
38
|
end
|
34
39
|
|
35
40
|
alias on_while_post on_while
|
@@ -47,6 +52,37 @@ module RuboCop
|
|
47
52
|
|
48
53
|
private
|
49
54
|
|
55
|
+
def while_or_until(node)
|
56
|
+
range = node.source_range
|
57
|
+
# Not every `while true` and `until false` can be turned into a
|
58
|
+
# `loop do` without further modification. The reason is that a
|
59
|
+
# variable that's introduced inside a while/until loop is in scope
|
60
|
+
# outside of that loop too, but a variable that's assigned for the
|
61
|
+
# first time inside a block can not be accessed after the block. In
|
62
|
+
# those more complicated cases we don't report an offense.
|
63
|
+
return if @variables.any? do |var|
|
64
|
+
assigned_inside_loop?(var, range) &&
|
65
|
+
!assigned_before_loop?(var, range) &&
|
66
|
+
referenced_after_loop?(var, range)
|
67
|
+
end
|
68
|
+
|
69
|
+
add_offense(node, location: :keyword)
|
70
|
+
end
|
71
|
+
|
72
|
+
def assigned_inside_loop?(var, range)
|
73
|
+
var.assignments.any? { |a| range.contains?(a.node.source_range) }
|
74
|
+
end
|
75
|
+
|
76
|
+
def assigned_before_loop?(var, range)
|
77
|
+
b = range.begin_pos
|
78
|
+
var.assignments.any? { |a| a.node.source_range.end_pos < b }
|
79
|
+
end
|
80
|
+
|
81
|
+
def referenced_after_loop?(var, range)
|
82
|
+
e = range.end_pos
|
83
|
+
var.references.any? { |r| r.node.source_range.begin_pos > e }
|
84
|
+
end
|
85
|
+
|
50
86
|
def replace_begin_end_with_modifier(node)
|
51
87
|
lambda do |corrector|
|
52
88
|
corrector.replace(node.body.loc.begin, 'loop do')
|
@@ -77,18 +77,12 @@ module RuboCop
|
|
77
77
|
end
|
78
78
|
|
79
79
|
def autocorrect(node)
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
# Don't autocorrect if this would change the meaning of the code
|
84
|
-
return if selector == '->' && arg_to_unparenthesized_call?(node)
|
85
|
-
|
86
|
-
lambda do |corrector|
|
87
|
-
if selector == 'lambda'
|
80
|
+
if node.send_node.source == 'lambda'
|
81
|
+
lambda do |corrector|
|
88
82
|
autocorrect_method_to_literal(corrector, node)
|
89
|
-
else
|
90
|
-
autocorrect_literal_to_method(corrector, node)
|
91
83
|
end
|
84
|
+
else
|
85
|
+
LambdaLiteralToMethodCorrector.new(node)
|
92
86
|
end
|
93
87
|
end
|
94
88
|
|
@@ -115,25 +109,6 @@ module RuboCop
|
|
115
109
|
end
|
116
110
|
end
|
117
111
|
|
118
|
-
def autocorrect_literal_to_method(corrector, node)
|
119
|
-
block_method, args = *node
|
120
|
-
|
121
|
-
# Check for unparenthesized args' preceding and trailing whitespaces.
|
122
|
-
remove_unparenthesized_whitespace(corrector, node)
|
123
|
-
|
124
|
-
# Avoid correcting to `lambdado` by inserting whitespace
|
125
|
-
# if none exists before or after the lambda arguments.
|
126
|
-
if needs_whitespace?(block_method, args, node)
|
127
|
-
corrector.insert_before(node.loc.begin, ' ')
|
128
|
-
end
|
129
|
-
corrector.replace(block_method.source_range, 'lambda')
|
130
|
-
corrector.remove(args.source_range) if args.source_range
|
131
|
-
return if args.children.empty?
|
132
|
-
|
133
|
-
arg_str = " |#{lambda_arg_string(args)}|"
|
134
|
-
corrector.insert_after(node.loc.begin, arg_str)
|
135
|
-
end
|
136
|
-
|
137
112
|
def autocorrect_method_to_literal(corrector, node)
|
138
113
|
block_method, args = *node
|
139
114
|
corrector.replace(block_method.source_range, '->')
|
@@ -145,67 +120,9 @@ module RuboCop
|
|
145
120
|
corrector.remove(whitespace_and_old_args)
|
146
121
|
end
|
147
122
|
|
148
|
-
def needs_whitespace?(block_method, args, node)
|
149
|
-
selector_end = block_method.loc.selector.end.end_pos
|
150
|
-
block_begin = node.loc.begin.begin_pos
|
151
|
-
|
152
|
-
(block_begin == end_pos(args) && selector_end == begin_pos(args)) ||
|
153
|
-
(block_begin == selector_end)
|
154
|
-
end
|
155
|
-
|
156
|
-
def begin_pos(node)
|
157
|
-
node.loc.begin && node.loc.begin.begin_pos
|
158
|
-
end
|
159
|
-
|
160
|
-
def end_pos(node)
|
161
|
-
node.loc.end && node.loc.end.end_pos
|
162
|
-
end
|
163
|
-
|
164
123
|
def lambda_arg_string(args)
|
165
124
|
args.children.map(&:source).join(', ')
|
166
125
|
end
|
167
|
-
|
168
|
-
def arg_to_unparenthesized_call?(arg_node)
|
169
|
-
parent = arg_node.parent
|
170
|
-
|
171
|
-
if parent && parent.pair_type?
|
172
|
-
arg_node = parent.parent
|
173
|
-
parent = arg_node.parent
|
174
|
-
end
|
175
|
-
|
176
|
-
return false unless parent && parent.send_type?
|
177
|
-
return false if parent.parenthesized_call?
|
178
|
-
|
179
|
-
arg_node.sibling_index > 1
|
180
|
-
end
|
181
|
-
|
182
|
-
def remove_unparenthesized_whitespace(corrector, node)
|
183
|
-
args = node.arguments
|
184
|
-
|
185
|
-
return unless unparenthesized_literal_args?(args)
|
186
|
-
|
187
|
-
remove_leading_whitespace(node, corrector)
|
188
|
-
remove_trailing_whitespace(node, corrector)
|
189
|
-
end
|
190
|
-
|
191
|
-
def remove_leading_whitespace(node, corrector)
|
192
|
-
corrector.remove_preceding(
|
193
|
-
node.arguments.source_range,
|
194
|
-
node.arguments.source_range.begin_pos -
|
195
|
-
node.send_node.source_range.end_pos
|
196
|
-
)
|
197
|
-
end
|
198
|
-
|
199
|
-
def remove_trailing_whitespace(node, corrector)
|
200
|
-
corrector.remove_preceding(
|
201
|
-
node.loc.begin,
|
202
|
-
node.loc.begin.begin_pos - node.arguments.source_range.end_pos - 1
|
203
|
-
)
|
204
|
-
end
|
205
|
-
|
206
|
-
def unparenthesized_literal_args?(args)
|
207
|
-
args.source_range && args.source_range.begin && !parentheses?(args)
|
208
|
-
end
|
209
126
|
end
|
210
127
|
end
|
211
128
|
end
|