rubocop 0.82.0 → 0.83.0

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