rubocop 0.82.0 → 0.83.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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/config/default.yml +29 -4
  4. data/lib/rubocop.rb +2 -1
  5. data/lib/rubocop/ast/node/send_node.rb +4 -0
  6. data/lib/rubocop/cli.rb +1 -1
  7. data/lib/rubocop/config.rb +5 -1
  8. data/lib/rubocop/config_loader.rb +15 -14
  9. data/lib/rubocop/config_loader_resolver.rb +27 -0
  10. data/lib/rubocop/config_validator.rb +2 -1
  11. data/lib/rubocop/cop/generator.rb +3 -2
  12. data/lib/rubocop/cop/layout/condition_position.rb +12 -2
  13. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +68 -0
  14. data/lib/rubocop/cop/layout/line_length.rb +4 -1
  15. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +13 -4
  16. data/lib/rubocop/cop/layout/space_around_operators.rb +18 -1
  17. data/lib/rubocop/cop/layout/trailing_whitespace.rb +2 -2
  18. data/lib/rubocop/cop/lint/ambiguous_operator.rb +38 -0
  19. data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +14 -0
  20. data/lib/rubocop/cop/lint/duplicate_methods.rb +1 -5
  21. data/lib/rubocop/cop/lint/empty_when.rb +29 -6
  22. data/lib/rubocop/cop/lint/ensure_return.rb +18 -1
  23. data/lib/rubocop/cop/lint/literal_as_condition.rb +10 -13
  24. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +21 -9
  25. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +1 -6
  26. data/lib/rubocop/cop/lint/suppressed_exception.rb +0 -6
  27. data/lib/rubocop/cop/lint/useless_access_modifier.rb +12 -0
  28. data/lib/rubocop/cop/lint/useless_assignment.rb +3 -2
  29. data/lib/rubocop/cop/lint/useless_else_without_rescue.rb +5 -0
  30. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +10 -1
  31. data/lib/rubocop/cop/mixin/hash_transform_method.rb +8 -1
  32. data/lib/rubocop/cop/mixin/line_length_help.rb +2 -1
  33. data/lib/rubocop/cop/mixin/parser_diagnostic.rb +1 -1
  34. data/lib/rubocop/cop/mixin/statement_modifier.rb +7 -23
  35. data/lib/rubocop/cop/mixin/target_ruby_version.rb +5 -1
  36. data/lib/rubocop/cop/naming/method_name.rb +1 -5
  37. data/lib/rubocop/cop/style/case_equality.rb +1 -1
  38. data/lib/rubocop/cop/style/empty_method.rb +0 -4
  39. data/lib/rubocop/cop/style/guard_clause.rb +25 -2
  40. data/lib/rubocop/cop/style/if_with_semicolon.rb +16 -0
  41. data/lib/rubocop/cop/style/lambda_call.rb +0 -20
  42. data/lib/rubocop/cop/style/multiline_when_then.rb +16 -1
  43. data/lib/rubocop/cop/style/optional_arguments.rb +1 -1
  44. data/lib/rubocop/cop/style/slicing_with_range.rb +39 -0
  45. data/lib/rubocop/cop/util.rb +24 -0
  46. data/lib/rubocop/cop/variable_force/assignment.rb +1 -0
  47. data/lib/rubocop/cop/variable_force/scope.rb +1 -0
  48. data/lib/rubocop/cop/variable_force/variable.rb +1 -0
  49. data/lib/rubocop/name_similarity.rb +12 -9
  50. data/lib/rubocop/options.rb +11 -4
  51. data/lib/rubocop/runner.rb +6 -1
  52. data/lib/rubocop/target_finder.rb +6 -4
  53. data/lib/rubocop/version.rb +1 -1
  54. metadata +4 -17
  55. data/lib/rubocop/string_util.rb +0 -14
@@ -160,7 +160,10 @@ module RuboCop
160
160
  end
161
161
 
162
162
  def highlight_start(line)
163
- max - indentation_difference(line)
163
+ # TODO: The max with 0 is a quick fix to avoid crashes when a line
164
+ # begins with many tabs, but getting a correct highlighting range
165
+ # when tabs are used for indentation doesn't work currently.
166
+ [max - indentation_difference(line), 0].max
164
167
  end
165
168
 
166
169
  def check_line(line, line_index)
@@ -6,30 +6,39 @@ module RuboCop
6
6
  # This cop checks the indentation of the right hand side operand in
7
7
  # binary operations that span more than one line.
8
8
  #
9
+ # The `aligned` style checks that operators are aligned if they are part
10
+ # of an `if` or `while` condition, a `return` statement, etc. In other
11
+ # contexts, the second operand should be indented regardless of enforced
12
+ # style.
13
+ #
9
14
  # @example EnforcedStyle: aligned (default)
10
15
  # # bad
11
16
  # if a +
12
17
  # b
13
- # something
18
+ # something &&
19
+ # something_else
14
20
  # end
15
21
  #
16
22
  # # good
17
23
  # if a +
18
24
  # b
19
- # something
25
+ # something &&
26
+ # something_else
20
27
  # end
21
28
  #
22
29
  # @example EnforcedStyle: indented
23
30
  # # bad
24
31
  # if a +
25
32
  # b
26
- # something
33
+ # something &&
34
+ # something_else
27
35
  # end
28
36
  #
29
37
  # # good
30
38
  # if a +
31
39
  # b
32
- # something
40
+ # something &&
41
+ # something_else
33
42
  # end
34
43
  #
35
44
  class MultilineOperationIndentation < Cop
@@ -139,7 +139,7 @@ module RuboCop
139
139
  elsif range.source.end_with?("\n")
140
140
  corrector.replace(range, " #{range.source.strip}\n")
141
141
  else
142
- corrector.replace(range, " #{range.source.strip} ")
142
+ enclose_operator_with_space(corrector, range)
143
143
  end
144
144
  end
145
145
  end
@@ -170,6 +170,19 @@ module RuboCop
170
170
  yield msg if msg
171
171
  end
172
172
 
173
+ def enclose_operator_with_space(corrector, range)
174
+ operator = range.source
175
+
176
+ # If `ForceEqualSignAlignment` is true, `Layout/ExtraSpacing` cop
177
+ # inserts spaces before operator. If `Layout/SpaceAroundOperators` cop
178
+ # inserts a space, it collides and raises the infinite loop error.
179
+ if force_equal_sign_alignment?
180
+ corrector.insert_after(range, ' ') unless operator.end_with?(' ')
181
+ else
182
+ corrector.replace(range, " #{operator.strip} ")
183
+ end
184
+ end
185
+
173
186
  def offense_message(type, operator, with_space, right_operand)
174
187
  if should_not_have_surrounding_space?(operator)
175
188
  return if with_space.is?(operator.source)
@@ -216,6 +229,10 @@ module RuboCop
216
229
  cop_config['EnforcedStyleForExponentOperator'] == 'space'
217
230
  end
218
231
 
232
+ def force_equal_sign_alignment?
233
+ config.for_cop('Layout/ExtraSpacing')['ForceEqualSignAlignment']
234
+ end
235
+
219
236
  def should_not_have_surrounding_space?(operator)
220
237
  operator.is?('**') ? !space_around_exponent_operator? : false
221
238
  end
@@ -14,14 +14,14 @@ module RuboCop
14
14
  # # good
15
15
  # x = 0
16
16
  #
17
- # @example AllowInHeredoc: false (default)
17
+ # @example AllowInHeredoc: false
18
18
  # # The line in this example contains spaces after the 0.
19
19
  # # bad
20
20
  # code = <<~RUBY
21
21
  # x = 0
22
22
  # RUBY
23
23
  #
24
- # @example AllowInHeredoc: true
24
+ # @example AllowInHeredoc: true (default)
25
25
  # # The line in this example contains spaces after the 0.
26
26
  # # good
27
27
  # code = <<~RUBY
@@ -38,17 +38,55 @@ module RuboCop
38
38
  'a whitespace to the right of the `%<operator>s` if it ' \
39
39
  'should be a %<possible>s.'
40
40
 
41
+ def autocorrect(node)
42
+ lambda do |corrector|
43
+ add_parentheses(node, corrector)
44
+ end
45
+ end
46
+
41
47
  private
42
48
 
43
49
  def relevant_diagnostic?(diagnostic)
44
50
  diagnostic.reason == :ambiguous_prefix
45
51
  end
46
52
 
53
+ def find_offense_node_by(diagnostic)
54
+ ast = processed_source.ast
55
+ ast.each_node(:splat, :block_pass, :kwsplat) do |node|
56
+ next unless offense_position?(node, diagnostic)
57
+
58
+ offense_node = offense_node(node)
59
+ return offense_node if offense_node
60
+ end
61
+
62
+ ast.each_node(:send).find do |send_node|
63
+ offense_position?(send_node.first_argument, diagnostic) &&
64
+ unary_operator?(send_node.first_argument, diagnostic)
65
+ end
66
+ end
67
+
47
68
  def alternative_message(diagnostic)
48
69
  operator = diagnostic.location.source
49
70
  hash = AMBIGUITIES[operator]
50
71
  format(MSG_FORMAT, hash)
51
72
  end
73
+
74
+ def offense_position?(node, diagnostic)
75
+ node.source_range.begin_pos == diagnostic.location.begin_pos
76
+ end
77
+
78
+ def offense_node(node)
79
+ case node.type
80
+ when :splat, :block_pass
81
+ node.parent
82
+ when :kwsplat
83
+ node.parent.parent
84
+ end
85
+ end
86
+
87
+ def unary_operator?(node, diagnostic)
88
+ node.source.start_with?(diagnostic.arguments[:prefix])
89
+ end
52
90
  end
53
91
  end
54
92
  end
@@ -28,12 +28,26 @@ module RuboCop
28
28
  "if it's surely a regexp literal, or add a whitespace to the " \
29
29
  'right of the `/` if it should be a division.'
30
30
 
31
+ def autocorrect(node)
32
+ lambda do |corrector|
33
+ add_parentheses(node, corrector)
34
+ end
35
+ end
36
+
31
37
  private
32
38
 
33
39
  def relevant_diagnostic?(diagnostic)
34
40
  diagnostic.reason == :ambiguous_literal
35
41
  end
36
42
 
43
+ def find_offense_node_by(diagnostic)
44
+ node = processed_source.ast.each_node(:regexp).find do |regexp_node|
45
+ regexp_node.source_range.begin_pos == diagnostic.location.begin_pos
46
+ end
47
+
48
+ node.parent
49
+ end
50
+
37
51
  def alternative_message(_diagnostic)
38
52
  MSG
39
53
  end
@@ -95,10 +95,6 @@ module RuboCop
95
95
  (send nil? :alias_method (sym $_name) _)
96
96
  PATTERN
97
97
 
98
- def_node_matcher :attr?, <<~PATTERN
99
- (send nil? ${:attr_reader :attr_writer :attr_accessor :attr} $...)
100
- PATTERN
101
-
102
98
  def_node_matcher :sym_name, '(sym $_name)'
103
99
 
104
100
  def on_send(node)
@@ -108,7 +104,7 @@ module RuboCop
108
104
  return if possible_dsl?(node)
109
105
 
110
106
  found_instance_method(node, name)
111
- elsif (attr = attr?(node))
107
+ elsif (attr = node.attribute_accessor?)
112
108
  on_attr(node, *attr)
113
109
  end
114
110
  end
@@ -8,26 +8,49 @@ module RuboCop
8
8
  # @example
9
9
  #
10
10
  # # bad
11
- #
12
11
  # case foo
13
- # when bar then 1
14
- # when baz then # nothing
12
+ # when bar
13
+ # do_something
14
+ # when baz
15
15
  # end
16
16
  #
17
17
  # @example
18
18
  #
19
19
  # # good
20
+ # case condition
21
+ # when foo
22
+ # do_something
23
+ # when bar
24
+ # nil
25
+ # end
20
26
  #
21
- # case foo
22
- # when bar then 1
23
- # when baz then 2
27
+ # @example AllowComments: true (default)
28
+ #
29
+ # # good
30
+ # case condition
31
+ # when foo
32
+ # do_something
33
+ # when bar
34
+ # # noop
24
35
  # end
36
+ #
37
+ # @example AllowComments: false
38
+ #
39
+ # # bad
40
+ # case condition
41
+ # when foo
42
+ # do_something
43
+ # when bar
44
+ # # do nothing
45
+ # end
46
+ #
25
47
  class EmptyWhen < Cop
26
48
  MSG = 'Avoid `when` branches without a body.'
27
49
 
28
50
  def on_case(node)
29
51
  node.each_when do |when_node|
30
52
  next if when_node.body
53
+ next if cop_config['AllowComments'] && comment_lines?(node)
31
54
 
32
55
  add_offense(when_node, location: when_node.source_range)
33
56
  end
@@ -29,6 +29,8 @@ module RuboCop
29
29
  # do_something_else
30
30
  # end
31
31
  class EnsureReturn < Cop
32
+ include RangeHelp
33
+
32
34
  MSG = 'Do not return from an `ensure` block.'
33
35
 
34
36
  def on_ensure(node)
@@ -37,7 +39,22 @@ module RuboCop
37
39
  return unless ensure_body
38
40
 
39
41
  ensure_body.each_node(:return) do |return_node|
40
- add_offense(return_node)
42
+ next if return_node.arguments.size >= 2
43
+
44
+ add_offense(return_node, location: :keyword)
45
+ end
46
+ end
47
+
48
+ def autocorrect(node)
49
+ lambda do |corrector|
50
+ if node.arguments?
51
+ corrector.replace(node, node.source.gsub(/return\s*/, ''))
52
+ else
53
+ range = range_by_whole_lines(
54
+ node.loc.expression, include_final_newline: true
55
+ )
56
+ corrector.remove(range)
57
+ end
41
58
  end
42
59
  end
43
60
  end
@@ -10,26 +10,25 @@ module RuboCop
10
10
  # @example
11
11
  #
12
12
  # # bad
13
- #
14
13
  # if 20
15
14
  # do_something
16
15
  # end
17
16
  #
18
- # @example
19
- #
20
17
  # # bad
21
- #
22
18
  # if some_var && true
23
19
  # do_something
24
20
  # end
25
21
  #
26
- # @example
27
- #
28
22
  # # good
29
- #
30
23
  # if some_var && some_condition
31
24
  # do_something
32
25
  # end
26
+ #
27
+ # # good
28
+ # # When using a boolean value for an infinite loop.
29
+ # while true
30
+ # break if condition
31
+ # end
33
32
  class LiteralAsCondition < Cop
34
33
  MSG = 'Literal `%<literal>s` appeared as a condition.'
35
34
 
@@ -38,20 +37,18 @@ module RuboCop
38
37
  end
39
38
 
40
39
  def on_while(node)
41
- check_for_literal(node)
42
- end
40
+ return if condition(node).true_type?
43
41
 
44
- def on_while_post(node)
45
42
  check_for_literal(node)
46
43
  end
44
+ alias on_while_post on_while
47
45
 
48
46
  def on_until(node)
49
- check_for_literal(node)
50
- end
47
+ return if condition(node).false_type?
51
48
 
52
- def on_until_post(node)
53
49
  check_for_literal(node)
54
50
  end
51
+ alias on_until_post on_until
55
52
 
56
53
  def on_case(case_node)
57
54
  if case_node.condition
@@ -9,14 +9,12 @@ module RuboCop
9
9
  # @example
10
10
  #
11
11
  # # bad
12
- #
13
- # puts (x + y)
14
- #
15
- # @example
12
+ # do_something (foo)
16
13
  #
17
14
  # # good
18
- #
19
- # puts(x + y)
15
+ # do_something(foo)
16
+ # do_something (2 + 3) * 4
17
+ # do_something (foo * bar).baz
20
18
  class ParenthesesAsGroupedExpression < Cop
21
19
  include RangeHelp
22
20
 
@@ -25,20 +23,34 @@ module RuboCop
25
23
  def on_send(node)
26
24
  return unless node.arguments.one?
27
25
  return if node.operator_method? || node.setter_method?
28
-
29
- return unless node.first_argument.source.start_with?('(')
26
+ return if grouped_parentheses?(node)
30
27
 
31
28
  space_length = spaces_before_left_parenthesis(node)
32
29
  return unless space_length.positive?
33
30
 
34
31
  range = space_range(node.first_argument.source_range, space_length)
35
32
 
36
- add_offense(nil, location: range)
33
+ add_offense(node, location: range)
37
34
  end
38
35
  alias on_csend on_send
39
36
 
37
+ def autocorrect(node)
38
+ space_length = spaces_before_left_parenthesis(node)
39
+ range = space_range(node.first_argument.source_range, space_length)
40
+
41
+ lambda do |corrector|
42
+ corrector.remove(range)
43
+ end
44
+ end
45
+
40
46
  private
41
47
 
48
+ def grouped_parentheses?(node)
49
+ first_argument = node.first_argument
50
+
51
+ first_argument.send_type? && first_argument.receiver&.begin_type?
52
+ end
53
+
42
54
  def spaces_before_left_parenthesis(node)
43
55
  receiver = node.receiver
44
56
  receiver_length = if receiver
@@ -26,7 +26,6 @@ module RuboCop
26
26
  # # good
27
27
  # x += 1
28
28
  class RedundantCopDisableDirective < Cop
29
- include NameSimilarity
30
29
  include RangeHelp
31
30
 
32
31
  COP_NAME = 'Lint/RedundantCopDisableDirective'
@@ -235,7 +234,7 @@ module RuboCop
235
234
  elsif all_cop_names.include?(cop)
236
235
  "`#{cop}`"
237
236
  else
238
- similar = find_similar_name(cop, [])
237
+ similar = NameSimilarity.find_similar_name(cop, all_cop_names)
239
238
  if similar
240
239
  "`#{cop}` (did you mean `#{similar}`?)"
241
240
  else
@@ -244,10 +243,6 @@ module RuboCop
244
243
  end
245
244
  end
246
245
 
247
- def collect_variable_like_names(scope)
248
- all_cop_names.each { |name| scope << name }
249
- end
250
-
251
246
  def all_cop_names
252
247
  @all_cop_names ||= Cop.registry.names
253
248
  end