rubocop 0.60.0 → 0.61.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 +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
|