rubocop 0.55.0 → 0.56.0

Sign up to get free protection for your applications and to get access to all the features.
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