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.
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