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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +45 -0
  4. data/config/disabled.yml +4 -4
  5. data/config/enabled.yml +32 -16
  6. data/lib/rubocop.rb +8 -2
  7. data/lib/rubocop/cli.rb +4 -0
  8. data/lib/rubocop/comment_config.rb +36 -9
  9. data/lib/rubocop/config.rb +8 -1
  10. data/lib/rubocop/cop/generator.rb +4 -3
  11. data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +101 -29
  12. data/lib/rubocop/cop/{style → layout}/empty_line_after_guard_clause.rb +1 -1
  13. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +5 -5
  14. data/lib/rubocop/cop/layout/first_parameter_indentation.rb +112 -2
  15. data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +8 -4
  16. data/lib/rubocop/cop/lint/erb_new_arguments.rb +107 -0
  17. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +4 -0
  18. data/lib/rubocop/cop/lint/nested_percent_literal.rb +0 -8
  19. data/lib/rubocop/cop/lint/percent_string_array.rb +1 -1
  20. data/lib/rubocop/cop/lint/percent_symbol_array.rb +1 -1
  21. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +3 -0
  22. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +16 -3
  23. data/lib/rubocop/cop/lint/splat_keyword_arguments.rb +36 -0
  24. data/lib/rubocop/cop/lint/unneeded_cop_enable_directive.rb +20 -2
  25. data/lib/rubocop/cop/performance/inefficient_hash_search.rb +95 -0
  26. data/lib/rubocop/cop/performance/unneeded_sort.rb +41 -6
  27. data/lib/rubocop/cop/rails/assert_not.rb +44 -0
  28. data/lib/rubocop/cop/rails/blank.rb +34 -28
  29. data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +1 -1
  30. data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +12 -2
  31. data/lib/rubocop/cop/rails/http_positional_arguments.rb +7 -7
  32. data/lib/rubocop/cop/rails/present.rb +31 -25
  33. data/lib/rubocop/cop/rails/refute_methods.rb +76 -0
  34. data/lib/rubocop/cop/rails/reversible_migration.rb +6 -4
  35. data/lib/rubocop/cop/rails/save_bang.rb +4 -1
  36. data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +1 -10
  37. data/lib/rubocop/cop/style/command_literal.rb +15 -3
  38. data/lib/rubocop/cop/style/comment_annotation.rb +6 -1
  39. data/lib/rubocop/cop/style/empty_method.rb +6 -3
  40. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +12 -2
  41. data/lib/rubocop/cop/style/method_missing_super.rb +34 -0
  42. data/lib/rubocop/cop/style/{method_missing.rb → missing_respond_to_missing.rb} +7 -29
  43. data/lib/rubocop/cop/style/parentheses_around_condition.rb +28 -2
  44. data/lib/rubocop/node_pattern.rb +1 -1
  45. data/lib/rubocop/processed_source.rb +12 -6
  46. data/lib/rubocop/result_cache.rb +9 -4
  47. data/lib/rubocop/rspec/shared_contexts.rb +4 -0
  48. data/lib/rubocop/runner.rb +8 -2
  49. data/lib/rubocop/target_finder.rb +40 -60
  50. data/lib/rubocop/version.rb +1 -1
  51. 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 within_reversible_block?(node)
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 within_reversible_block?(node)
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 within_reversible_block?(node)
264
+ def within_reversible_or_up_only_block?(node)
265
265
  node.each_ancestor(:block).any? do |ancestor|
266
- ancestor.block_type? && ancestor.send_node.method?(:reversible)
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 processed_source.comment_on_line?(right_brace_and_space.line)
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(preferred_delimiters).map(&:join)
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 preferred_delimiters
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']['%x'].split(//)
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
- arguments = node.arguments? ? node.arguments.source : ''
77
- scope = node.receiver ? "#{node.receiver.source}." : ''
76
+ has_parentheses = parentheses?(node.arguments)
78
77
 
79
- signature = [scope, node.method_name, arguments].join
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
- corrector.insert_after(args_end(node), ')')
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
- selector.end.resize(1)
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?` and falling back on `super`.
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
- class MethodMissing < Cop
25
- MSG = 'When using `method_missing`, %<instructions>s.'.freeze
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
- check(node)
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 parens_required?(node.condition)
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
@@ -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? const (send (const nil? {:Class :Module}) :new)))'
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