rubocop 0.60.0 → 0.61.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -4
  3. data/config/default.yml +573 -560
  4. data/lib/rubocop.rb +5 -0
  5. data/lib/rubocop/ast/node.rb +1 -1
  6. data/lib/rubocop/ast/sexp.rb +1 -1
  7. data/lib/rubocop/cli.rb +9 -14
  8. data/lib/rubocop/config.rb +4 -3
  9. data/lib/rubocop/config_loader.rb +25 -22
  10. data/lib/rubocop/config_loader_resolver.rb +3 -2
  11. data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +53 -0
  12. data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +73 -0
  13. data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +138 -0
  14. data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +52 -46
  15. data/lib/rubocop/cop/generator.rb +13 -17
  16. data/lib/rubocop/cop/generator/configuration_injector.rb +60 -0
  17. data/lib/rubocop/cop/layout/align_hash.rb +3 -0
  18. data/lib/rubocop/cop/layout/comment_indentation.rb +32 -2
  19. data/lib/rubocop/cop/layout/indent_heredoc.rb +11 -5
  20. data/lib/rubocop/cop/layout/indentation_width.rb +7 -1
  21. data/lib/rubocop/cop/layout/multiline_array_brace_layout.rb +11 -11
  22. data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +1 -1
  23. data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +1 -1
  24. data/lib/rubocop/cop/layout/multiline_method_definition_brace_layout.rb +1 -1
  25. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +16 -3
  26. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +30 -17
  27. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +11 -0
  28. data/lib/rubocop/cop/lint/shadowed_exception.rb +2 -5
  29. data/lib/rubocop/cop/lint/useless_access_modifier.rb +1 -1
  30. data/lib/rubocop/cop/metrics/line_length.rb +2 -2
  31. data/lib/rubocop/cop/mixin/trailing_comma.rb +11 -15
  32. data/lib/rubocop/cop/offense.rb +1 -1
  33. data/lib/rubocop/cop/performance/open_struct.rb +46 -0
  34. data/lib/rubocop/cop/performance/redundant_merge.rb +18 -4
  35. data/lib/rubocop/cop/rails/bulk_change_table.rb +2 -2
  36. data/lib/rubocop/cop/rails/dynamic_find_by.rb +15 -8
  37. data/lib/rubocop/cop/rails/http_positional_arguments.rb +17 -14
  38. data/lib/rubocop/cop/rails/http_status.rb +4 -4
  39. data/lib/rubocop/cop/rails/inverse_of.rb +2 -2
  40. data/lib/rubocop/cop/rails/reversible_migration.rb +1 -1
  41. data/lib/rubocop/cop/rails/skips_model_validations.rb +1 -1
  42. data/lib/rubocop/cop/rails/validation.rb +4 -4
  43. data/lib/rubocop/cop/security/open.rb +31 -11
  44. data/lib/rubocop/cop/style/begin_block.rb +6 -0
  45. data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +1 -1
  46. data/lib/rubocop/cop/style/empty_case_condition.rb +13 -7
  47. data/lib/rubocop/cop/style/for.rb +9 -78
  48. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +6 -4
  49. data/lib/rubocop/cop/style/global_vars.rb +1 -1
  50. data/lib/rubocop/cop/style/infinite_loop.rb +42 -6
  51. data/lib/rubocop/cop/style/lambda.rb +4 -87
  52. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +221 -16
  53. data/lib/rubocop/cop/style/raise_args.rb +1 -1
  54. data/lib/rubocop/cop/style/regexp_literal.rb +62 -10
  55. data/lib/rubocop/cop/style/unneeded_condition.rb +2 -2
  56. data/lib/rubocop/cop/variable_force.rb +4 -2
  57. data/lib/rubocop/cop/variable_force/variable.rb +2 -0
  58. data/lib/rubocop/magic_comment.rb +1 -1
  59. data/lib/rubocop/remote_config.rb +13 -4
  60. data/lib/rubocop/rspec/expect_offense.rb +1 -1
  61. data/lib/rubocop/runner.rb +15 -4
  62. data/lib/rubocop/version.rb +1 -1
  63. metadata +7 -2
@@ -47,11 +47,11 @@ module RuboCop
47
47
  uniqueness
48
48
  ].freeze
49
49
 
50
- BLACKLIST = TYPES.map { |p| "validates_#{p}_of".to_sym }.freeze
51
- WHITELIST = TYPES.map { |p| "validates :column, #{p}: value" }.freeze
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 && BLACKLIST.include?(node.method_name)
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
- WHITELIST[BLACKLIST.index(method.to_sym)]
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.str_type?
31
- !node.str_content.empty? && !node.str_content.start_with?('|')
32
- elsif node.dstr_type?
33
- safe?(node.child_nodes.first)
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 on_send(node)
42
- open?(node) do |code|
43
- return if safe?(code)
49
+ def safe_argument?(argument)
50
+ !argument.empty? && !argument.start_with?('|')
51
+ end
44
52
 
45
- add_offense(node, location: :selector)
46
- end
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
@@ -3,7 +3,13 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
+ #
6
7
  # This cop checks for BEGIN blocks.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # BEGIN { test }
12
+ #
7
13
  class BeginBlock < Cop
8
14
  MSG = 'Avoid the use of `BEGIN` blocks.'.freeze
9
15
 
@@ -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 =~ /}\s*,?\z/
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
- remove_case_node(corrector, case_node)
71
- corrector.replace(when_nodes.first.loc.keyword, 'if')
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 remove_case_node(corrector, case_node)
92
- range = range_by_whole_lines(
93
- case_node.loc.keyword, include_final_newline: true
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.remove(range)
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 if node.single_line?
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
- incorrect_style_detected(node)
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
- autocorrect_to_each(node)
73
+ ForToEachCorrector.new(node)
87
74
  else
88
- autocorrect_to_for(node)
75
+ EachToForCorrector.new(node)
89
76
  end
90
77
  end
91
78
 
92
79
  private
93
80
 
94
- def incorrect_style_detected(node)
95
- add_offense(node, message: PREFER_FOR) do
96
- opposite_style_detected
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
- if processed_source.tokens[token_number].text =~
105
- Encoding::ENCODING_PATTERN
106
- token = processed_source.tokens[token_number]
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
- if processed_source.following_line(last_special_comment).empty?
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
- # http://www.zenspider.com/Languages/Ruby/QuickRef.html
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 on_while(node)
24
- return unless node.condition.truthy_literal?
23
+ def join_force?(force_class)
24
+ force_class == VariableForce
25
+ end
25
26
 
26
- add_offense(node, location: :keyword)
27
+ def after_leaving_scope(scope, _variable_table)
28
+ @variables ||= []
29
+ @variables.concat(scope.variables.values)
27
30
  end
28
31
 
29
- def on_until(node)
30
- return unless node.condition.falsey_literal?
32
+ def on_while(node)
33
+ while_or_until(node) if node.condition.truthy_literal?
34
+ end
31
35
 
32
- add_offense(node, location: :keyword)
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
- block_method, _args = *node
81
- selector = block_method.source
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