rubocop 1.48.1 → 1.52.1

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 (164) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -3
  3. data/config/default.yml +59 -12
  4. data/lib/rubocop/cli/command/execute_runner.rb +7 -2
  5. data/lib/rubocop/cli.rb +6 -6
  6. data/lib/rubocop/config.rb +5 -1
  7. data/lib/rubocop/config_loader.rb +8 -8
  8. data/lib/rubocop/config_obsoletion.rb +2 -2
  9. data/lib/rubocop/cop/autocorrect_logic.rb +28 -12
  10. data/lib/rubocop/cop/base.rb +5 -1
  11. data/lib/rubocop/cop/cop.rb +2 -2
  12. data/lib/rubocop/cop/correctors/alignment_corrector.rb +1 -1
  13. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +1 -1
  14. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +2 -2
  15. data/lib/rubocop/cop/gemspec/deprecated_attribute_assignment.rb +1 -1
  16. data/lib/rubocop/cop/gemspec/development_dependencies.rb +1 -1
  17. data/lib/rubocop/cop/internal_affairs/cop_description.rb +33 -9
  18. data/lib/rubocop/cop/internal_affairs/example_heredoc_delimiter.rb +2 -2
  19. data/lib/rubocop/cop/internal_affairs/inherit_deprecated_cop_class.rb +1 -1
  20. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +2 -2
  21. data/lib/rubocop/cop/internal_affairs/redundant_source_range.rb +29 -2
  22. data/lib/rubocop/cop/layout/class_structure.rb +1 -0
  23. data/lib/rubocop/cop/layout/closing_heredoc_indentation.rb +0 -1
  24. data/lib/rubocop/cop/layout/empty_comment.rb +1 -1
  25. data/lib/rubocop/cop/layout/empty_lines.rb +1 -1
  26. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +2 -0
  27. data/lib/rubocop/cop/layout/end_alignment.rb +5 -1
  28. data/lib/rubocop/cop/layout/extra_spacing.rb +6 -1
  29. data/lib/rubocop/cop/layout/first_argument_indentation.rb +6 -1
  30. data/lib/rubocop/cop/layout/first_array_element_line_break.rb +25 -34
  31. data/lib/rubocop/cop/layout/first_hash_element_line_break.rb +7 -19
  32. data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +42 -52
  33. data/lib/rubocop/cop/layout/first_method_parameter_line_break.rb +38 -55
  34. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +4 -4
  35. data/lib/rubocop/cop/layout/initial_indentation.rb +1 -1
  36. data/lib/rubocop/cop/layout/multiline_array_line_breaks.rb +8 -27
  37. data/lib/rubocop/cop/layout/multiline_hash_key_line_breaks.rb +7 -26
  38. data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +4 -21
  39. data/lib/rubocop/cop/layout/multiline_method_parameter_line_breaks.rb +6 -30
  40. data/lib/rubocop/cop/layout/redundant_line_break.rb +6 -7
  41. data/lib/rubocop/cop/layout/space_before_first_arg.rb +1 -1
  42. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +2 -0
  43. data/lib/rubocop/cop/layout/space_inside_parens.rb +2 -2
  44. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +13 -1
  45. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +3 -3
  46. data/lib/rubocop/cop/lint/duplicate_match_pattern.rb +122 -0
  47. data/lib/rubocop/cop/lint/empty_interpolation.rb +1 -1
  48. data/lib/rubocop/cop/lint/erb_new_arguments.rb +3 -4
  49. data/lib/rubocop/cop/lint/identity_comparison.rb +0 -1
  50. data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +5 -3
  51. data/lib/rubocop/cop/lint/inherit_exception.rb +9 -0
  52. data/lib/rubocop/cop/lint/lambda_without_literal_block.rb +1 -1
  53. data/lib/rubocop/cop/lint/missing_super.rb +3 -0
  54. data/lib/rubocop/cop/lint/nested_method_definition.rb +2 -2
  55. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +2 -2
  56. data/lib/rubocop/cop/lint/ordered_magic_comments.rb +0 -1
  57. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +2 -2
  58. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +1 -1
  59. data/lib/rubocop/cop/lint/redundant_string_coercion.rb +35 -15
  60. data/lib/rubocop/cop/lint/send_with_mixin_argument.rb +1 -2
  61. data/lib/rubocop/cop/lint/shadowed_exception.rb +5 -11
  62. data/lib/rubocop/cop/lint/to_enum_arguments.rb +7 -1
  63. data/lib/rubocop/cop/lint/top_level_return_with_argument.rb +23 -9
  64. data/lib/rubocop/cop/lint/unreachable_loop.rb +3 -3
  65. data/lib/rubocop/cop/lint/useless_assignment.rb +59 -1
  66. data/lib/rubocop/cop/lint/useless_method_definition.rb +10 -2
  67. data/lib/rubocop/cop/lint/void.rb +69 -9
  68. data/lib/rubocop/cop/metrics/block_nesting.rb +1 -1
  69. data/lib/rubocop/cop/metrics/class_length.rb +1 -0
  70. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +1 -2
  71. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
  72. data/lib/rubocop/cop/mixin/allowed_receivers.rb +34 -0
  73. data/lib/rubocop/cop/mixin/comments_help.rb +7 -3
  74. data/lib/rubocop/cop/mixin/hash_transform_method.rb +1 -1
  75. data/lib/rubocop/cop/mixin/space_after_punctuation.rb +1 -1
  76. data/lib/rubocop/cop/mixin/statement_modifier.rb +1 -1
  77. data/lib/rubocop/cop/naming/ascii_identifiers.rb +1 -1
  78. data/lib/rubocop/cop/naming/constant_name.rb +1 -1
  79. data/lib/rubocop/cop/naming/inclusive_language.rb +23 -4
  80. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +22 -7
  81. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +11 -3
  82. data/lib/rubocop/cop/naming/variable_name.rb +6 -1
  83. data/lib/rubocop/cop/style/accessor_grouping.rb +5 -1
  84. data/lib/rubocop/cop/style/attr.rb +11 -1
  85. data/lib/rubocop/cop/style/begin_block.rb +1 -2
  86. data/lib/rubocop/cop/style/class_and_module_children.rb +2 -2
  87. data/lib/rubocop/cop/style/class_equality_comparison.rb +51 -40
  88. data/lib/rubocop/cop/style/collection_compact.rb +19 -6
  89. data/lib/rubocop/cop/style/colon_method_call.rb +2 -2
  90. data/lib/rubocop/cop/style/combinable_loops.rb +26 -6
  91. data/lib/rubocop/cop/style/conditional_assignment.rb +2 -2
  92. data/lib/rubocop/cop/style/copyright.rb +6 -3
  93. data/lib/rubocop/cop/style/data_inheritance.rb +75 -0
  94. data/lib/rubocop/cop/style/dir.rb +1 -1
  95. data/lib/rubocop/cop/style/dir_empty.rb +8 -14
  96. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +2 -2
  97. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +1 -1
  98. data/lib/rubocop/cop/style/documentation.rb +1 -1
  99. data/lib/rubocop/cop/style/double_negation.rb +2 -2
  100. data/lib/rubocop/cop/style/eval_with_location.rb +5 -5
  101. data/lib/rubocop/cop/style/exact_regexp_match.rb +68 -0
  102. data/lib/rubocop/cop/style/file_empty.rb +3 -3
  103. data/lib/rubocop/cop/style/file_read.rb +2 -2
  104. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +1 -1
  105. data/lib/rubocop/cop/style/guard_clause.rb +2 -0
  106. data/lib/rubocop/cop/style/hash_each_methods.rb +1 -22
  107. data/lib/rubocop/cop/style/hash_except.rb +23 -12
  108. data/lib/rubocop/cop/style/hash_syntax.rb +4 -1
  109. data/lib/rubocop/cop/style/hash_transform_keys.rb +2 -2
  110. data/lib/rubocop/cop/style/hash_transform_values.rb +2 -2
  111. data/lib/rubocop/cop/style/if_inside_else.rb +6 -0
  112. data/lib/rubocop/cop/style/if_unless_modifier.rb +41 -12
  113. data/lib/rubocop/cop/style/invertible_unless_condition.rb +9 -5
  114. data/lib/rubocop/cop/style/map_to_hash.rb +4 -1
  115. data/lib/rubocop/cop/style/map_to_set.rb +4 -1
  116. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +4 -9
  117. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +43 -36
  118. data/lib/rubocop/cop/style/multiline_method_signature.rb +6 -3
  119. data/lib/rubocop/cop/style/multiple_comparison.rb +14 -0
  120. data/lib/rubocop/cop/style/numeric_literals.rb +1 -1
  121. data/lib/rubocop/cop/style/parallel_assignment.rb +26 -18
  122. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +2 -3
  123. data/lib/rubocop/cop/style/percent_q_literals.rb +1 -1
  124. data/lib/rubocop/cop/style/redundant_array_constructor.rb +77 -0
  125. data/lib/rubocop/cop/style/redundant_fetch_block.rb +6 -4
  126. data/lib/rubocop/cop/style/redundant_filter_chain.rb +101 -0
  127. data/lib/rubocop/cop/style/redundant_line_continuation.rb +183 -0
  128. data/lib/rubocop/cop/style/redundant_parentheses.rb +1 -1
  129. data/lib/rubocop/cop/style/redundant_percent_q.rb +1 -1
  130. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +2 -2
  131. data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +46 -0
  132. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +1 -1
  133. data/lib/rubocop/cop/style/redundant_string_escape.rb +2 -3
  134. data/lib/rubocop/cop/style/regexp_literal.rb +11 -2
  135. data/lib/rubocop/cop/style/require_order.rb +11 -5
  136. data/lib/rubocop/cop/style/rescue_modifier.rb +1 -3
  137. data/lib/rubocop/cop/style/select_by_regexp.rb +15 -5
  138. data/lib/rubocop/cop/style/semicolon.rb +12 -1
  139. data/lib/rubocop/cop/style/single_line_methods.rb +1 -1
  140. data/lib/rubocop/cop/style/sole_nested_conditional.rb +5 -3
  141. data/lib/rubocop/cop/style/special_global_vars.rb +3 -4
  142. data/lib/rubocop/cop/style/struct_inheritance.rb +1 -1
  143. data/lib/rubocop/cop/style/trailing_body_on_class.rb +1 -0
  144. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
  145. data/lib/rubocop/cop/style/unless_logical_operators.rb +1 -0
  146. data/lib/rubocop/cop/team.rb +1 -1
  147. data/lib/rubocop/cop/variable_force/assignment.rb +33 -1
  148. data/lib/rubocop/cop/variable_force/variable_table.rb +2 -2
  149. data/lib/rubocop/cop/variable_force.rb +1 -0
  150. data/lib/rubocop/cops_documentation_generator.rb +10 -3
  151. data/lib/rubocop/ext/regexp_node.rb +1 -1
  152. data/lib/rubocop/ext/regexp_parser.rb +1 -1
  153. data/lib/rubocop/formatter/simple_text_formatter.rb +1 -1
  154. data/lib/rubocop/options.rb +4 -1
  155. data/lib/rubocop/result_cache.rb +2 -2
  156. data/lib/rubocop/rspec/cop_helper.rb +1 -1
  157. data/lib/rubocop/server/cache.rb +1 -1
  158. data/lib/rubocop/server/client_command/exec.rb +2 -1
  159. data/lib/rubocop/server/helper.rb +1 -1
  160. data/lib/rubocop/server/server_command/exec.rb +1 -1
  161. data/lib/rubocop/target_ruby.rb +3 -2
  162. data/lib/rubocop/version.rb +10 -6
  163. data/lib/rubocop.rb +8 -0
  164. metadata +20 -12
@@ -14,8 +14,14 @@ module RuboCop
14
14
  #
15
15
  # # good
16
16
  # def foo(x, y = 1)
17
+ # # Alternatives to `__callee__` are `__method__` and `:foo`.
17
18
  # return to_enum(__callee__, x, y)
18
- # # alternatives to `__callee__` are `__method__` and `:foo`
19
+ # end
20
+ #
21
+ # # good
22
+ # def foo(x, y = 1)
23
+ # # It is also allowed if it is wrapped in some method like Sorbet.
24
+ # return to_enum(T.must(__callee__), x, y)
19
25
  # end
20
26
  #
21
27
  class ToEnumArguments < Base
@@ -8,25 +8,39 @@ module RuboCop
8
8
  # always ignored. This is detected automatically since Ruby 2.7.
9
9
  #
10
10
  # @example
11
+ # # bad
12
+ # return 1
11
13
  #
12
- # # Detected since Ruby 2.7
13
- # return 1 # 1 is always ignored.
14
+ # # good
15
+ # return
14
16
  class TopLevelReturnWithArgument < Base
15
- # This cop works by validating the ancestors of the return node. A
16
- # top-level return node's ancestors should not be of block, def, or
17
- # defs type.
17
+ extend AutoCorrector
18
18
 
19
19
  MSG = 'Top level return with argument detected.'
20
20
 
21
21
  def on_return(return_node)
22
- add_offense(return_node) if return_node.arguments? && ancestors_valid?(return_node)
22
+ return unless top_level_return_with_any_argument?(return_node)
23
+
24
+ add_offense(return_node) do |corrector|
25
+ remove_arguments(corrector, return_node)
26
+ end
23
27
  end
24
28
 
25
29
  private
26
30
 
27
- def ancestors_valid?(return_node)
28
- prohibited_ancestors = return_node.each_ancestor(:block, :def, :defs)
29
- prohibited_ancestors.none?
31
+ def top_level_return_with_any_argument?(return_node)
32
+ top_level_return?(return_node) && return_node.arguments?
33
+ end
34
+
35
+ def remove_arguments(corrector, return_node)
36
+ corrector.replace(return_node, 'return')
37
+ end
38
+
39
+ # This cop works by validating the ancestors of the return node. A
40
+ # top-level return node's ancestors should not be of block, def, or
41
+ # defs type.
42
+ def top_level_return?(return_node)
43
+ return_node.each_ancestor(:block, :def, :defs).none?
30
44
  end
31
45
  end
32
46
  end
@@ -111,9 +111,9 @@ module RuboCop
111
111
  return false unless node.block_type? || node.numblock_type?
112
112
 
113
113
  send_node = node.send_node
114
- return false if matches_allowed_pattern?(send_node.source)
115
-
116
- send_node.enumerable_method? || send_node.enumerator_method? || send_node.method?(:loop)
114
+ loopable = send_node.enumerable_method? || send_node.enumerator_method? ||
115
+ send_node.method?(:loop)
116
+ loopable && !matches_allowed_pattern?(send_node.source)
117
117
  end
118
118
 
119
119
  def check(node)
@@ -13,6 +13,12 @@ module RuboCop
13
13
  # reassignments and properly handles varied cases such as branch, loop,
14
14
  # rescue, ensure, etc.
15
15
  #
16
+ # @safety
17
+ # This cop's autocorrection is unsafe because removing assignment from
18
+ # operator assignment can cause NameError if this assignment has been used to declare
19
+ # local variable. For example, replacing `a ||= 1` to `a || 1` may cause
20
+ # "undefined local variable or method `a' for main:Object (NameError)".
21
+ #
16
22
  # @example
17
23
  #
18
24
  # # bad
@@ -31,6 +37,10 @@ module RuboCop
31
37
  # do_something(some_var)
32
38
  # end
33
39
  class UselessAssignment < Base
40
+ extend AutoCorrector
41
+
42
+ include RangeHelp
43
+
34
44
  MSG = 'Useless assignment to variable - `%<variable>s`.'
35
45
 
36
46
  def self.joining_forces
@@ -55,7 +65,9 @@ module RuboCop
55
65
  assignment.node.loc.name
56
66
  end
57
67
 
58
- add_offense(location, message: message)
68
+ add_offense(location, message: message) do |corrector|
69
+ autocorrect(corrector, assignment)
70
+ end
59
71
  end
60
72
  end
61
73
 
@@ -119,6 +131,52 @@ module RuboCop
119
131
 
120
132
  node.receiver.nil? && !node.arguments?
121
133
  end
134
+
135
+ # rubocop:disable Metrics/AbcSize
136
+ def autocorrect(corrector, assignment)
137
+ if assignment.exception_assignment?
138
+ remove_exception_assignment_part(corrector, assignment.node)
139
+ elsif assignment.multiple_assignment? || assignment.rest_assignment? ||
140
+ assignment.for_assignment?
141
+ rename_variable_with_underscore(corrector, assignment.node)
142
+ elsif assignment.operator_assignment?
143
+ remove_trailing_character_from_operator(corrector, assignment.node)
144
+ elsif assignment.regexp_named_capture?
145
+ replace_named_capture_group_with_non_capturing_group(corrector, assignment.node,
146
+ assignment.variable.name)
147
+ else
148
+ remove_local_variable_assignment_part(corrector, assignment.node)
149
+ end
150
+ end
151
+ # rubocop:enable Metrics/AbcSize
152
+
153
+ def remove_exception_assignment_part(corrector, node)
154
+ corrector.remove(
155
+ range_between(
156
+ (node.parent.children.first&.source_range || node.parent.location.keyword).end_pos,
157
+ node.source_range.end_pos
158
+ )
159
+ )
160
+ end
161
+
162
+ def rename_variable_with_underscore(corrector, node)
163
+ corrector.replace(node, '_')
164
+ end
165
+
166
+ def remove_trailing_character_from_operator(corrector, node)
167
+ corrector.remove(node.parent.location.operator.end.adjust(begin_pos: -1))
168
+ end
169
+
170
+ def replace_named_capture_group_with_non_capturing_group(corrector, node, variable_name)
171
+ corrector.replace(
172
+ node.children.first,
173
+ node.children.first.source.sub(/\(\?<#{variable_name}>/, '(?:')
174
+ )
175
+ end
176
+
177
+ def remove_local_variable_assignment_part(corrector, node)
178
+ corrector.replace(node, node.expression.source)
179
+ end
122
180
  end
123
181
  end
124
182
  end
@@ -41,15 +41,23 @@ module RuboCop
41
41
  MSG = 'Useless method definition detected.'
42
42
 
43
43
  def on_def(node)
44
- return if use_rest_or_optional_args?(node)
44
+ return if method_definition_with_modifier?(node) || use_rest_or_optional_args?(node)
45
45
  return unless delegating?(node.body, node)
46
46
 
47
- add_offense(node) { |corrector| corrector.remove(node) }
47
+ add_offense(node) do |corrector|
48
+ range = node.parent&.send_type? ? node.parent : node
49
+
50
+ corrector.remove(range)
51
+ end
48
52
  end
49
53
  alias on_defs on_def
50
54
 
51
55
  private
52
56
 
57
+ def method_definition_with_modifier?(node)
58
+ node.parent&.send_type? && !node.parent&.non_bare_access_modifier?
59
+ end
60
+
53
61
  def use_rest_or_optional_args?(node)
54
62
  node.arguments.any? { |arg| arg.restarg_type? || arg.optarg_type? || arg.kwoptarg_type? }
55
63
  end
@@ -41,6 +41,10 @@ module RuboCop
41
41
  # do_something(some_array)
42
42
  # end
43
43
  class Void < Base
44
+ extend AutoCorrector
45
+
46
+ include RangeHelp
47
+
44
48
  OP_MSG = 'Operator `%<op>s` used in void context.'
45
49
  VAR_MSG = 'Variable `%<var>s` used in void context.'
46
50
  LIT_MSG = 'Literal `%<lit>s` used in void context.'
@@ -59,10 +63,10 @@ module RuboCop
59
63
  shuffle slice sort sort_by squeeze strip sub
60
64
  succ swapcase tr tr_s transform_values
61
65
  unicode_normalize uniq upcase].freeze
62
- METHODS_REPLACABLE_BY_EACH = %i[collect map].freeze
66
+ METHODS_REPLACEABLE_BY_EACH = %i[collect map].freeze
63
67
 
64
68
  NONMUTATING_METHODS = (NONMUTATING_METHODS_WITH_BANG_VERSION +
65
- METHODS_REPLACABLE_BY_EACH).freeze
69
+ METHODS_REPLACEABLE_BY_EACH).freeze
66
70
 
67
71
  def on_block(node)
68
72
  return unless node.body && !node.body.begin_type?
@@ -100,31 +104,43 @@ module RuboCop
100
104
  def check_void_op(node)
101
105
  return unless node.send_type? && OPERATORS.include?(node.method_name)
102
106
 
103
- add_offense(node.loc.selector, message: format(OP_MSG, op: node.method_name))
107
+ add_offense(node.loc.selector,
108
+ message: format(OP_MSG, op: node.method_name)) do |corrector|
109
+ autocorrect_void_op(corrector, node)
110
+ end
104
111
  end
105
112
 
106
113
  def check_var(node)
107
114
  return unless node.variable? || node.const_type?
108
115
 
109
- add_offense(node.loc.name, message: format(VAR_MSG, var: node.loc.name.source))
116
+ add_offense(node.loc.name,
117
+ message: format(VAR_MSG, var: node.loc.name.source)) do |corrector|
118
+ autocorrect_void_var(corrector, node)
119
+ end
110
120
  end
111
121
 
112
122
  def check_literal(node)
113
123
  return if !node.literal? || node.xstr_type? || node.range_type?
114
124
 
115
- add_offense(node, message: format(LIT_MSG, lit: node.source))
125
+ add_offense(node, message: format(LIT_MSG, lit: node.source)) do |corrector|
126
+ autocorrect_void_literal(corrector, node)
127
+ end
116
128
  end
117
129
 
118
130
  def check_self(node)
119
131
  return unless node.self_type?
120
132
 
121
- add_offense(node, message: SELF_MSG)
133
+ add_offense(node, message: SELF_MSG) do |corrector|
134
+ autocorrect_void_self(corrector, node)
135
+ end
122
136
  end
123
137
 
124
138
  def check_void_expression(node)
125
139
  return unless node.defined_type? || node.lambda_or_proc?
126
140
 
127
- add_offense(node, message: format(EXPRESSION_MSG, expression: node.source))
141
+ add_offense(node, message: format(EXPRESSION_MSG, expression: node.source)) do |corrector|
142
+ autocorrect_void_expression(corrector, node)
143
+ end
128
144
  end
129
145
 
130
146
  def check_nonmutating(node)
@@ -133,9 +149,16 @@ module RuboCop
133
149
  method_name = node.method_name
134
150
  return unless NONMUTATING_METHODS.include?(method_name)
135
151
 
136
- suggestion = METHODS_REPLACABLE_BY_EACH.include?(method_name) ? 'each' : "#{method_name}!"
152
+ suggestion = if METHODS_REPLACEABLE_BY_EACH.include?(method_name)
153
+ 'each'
154
+ else
155
+ "#{method_name}!"
156
+ end
137
157
  add_offense(node,
138
- message: format(NONMUTATING_MSG, method: method_name, suggest: suggestion))
158
+ message: format(NONMUTATING_MSG, method: method_name,
159
+ suggest: suggestion)) do |corrector|
160
+ autocorrect_nonmutating_send(corrector, node, suggestion)
161
+ end
139
162
  end
140
163
 
141
164
  def in_void_context?(node)
@@ -145,6 +168,43 @@ module RuboCop
145
168
 
146
169
  VOID_CONTEXT_TYPES.include?(parent.type) && parent.void_context?
147
170
  end
171
+
172
+ def autocorrect_void_op(corrector, node)
173
+ if node.arguments.empty?
174
+ corrector.replace(node, node.receiver.source)
175
+ else
176
+ corrector.replace(
177
+ range_with_surrounding_space(range: node.loc.selector, side: :both,
178
+ newlines: false),
179
+ "\n"
180
+ )
181
+ end
182
+ end
183
+
184
+ def autocorrect_void_var(corrector, node)
185
+ corrector.remove(range_with_surrounding_space(range: node.loc.name, side: :left))
186
+ end
187
+
188
+ def autocorrect_void_literal(corrector, node)
189
+ corrector.remove(range_with_surrounding_space(range: node.source_range, side: :left))
190
+ end
191
+
192
+ def autocorrect_void_self(corrector, node)
193
+ corrector.remove(range_with_surrounding_space(range: node.source_range, side: :left))
194
+ end
195
+
196
+ def autocorrect_void_expression(corrector, node)
197
+ corrector.remove(range_with_surrounding_space(range: node.source_range, side: :left))
198
+ end
199
+
200
+ def autocorrect_nonmutating_send(corrector, node, suggestion)
201
+ send_node = if node.send_type?
202
+ node
203
+ else
204
+ node.send_node
205
+ end
206
+ corrector.replace(send_node.loc.selector, suggestion)
207
+ end
148
208
  end
149
209
  end
150
210
  end
@@ -44,7 +44,7 @@ module RuboCop
44
44
  def consider_node?(node)
45
45
  return true if NESTING_BLOCKS.include?(node.type)
46
46
 
47
- count_blocks? && node.block_type?
47
+ count_blocks? && (node.block_type? || node.numblock_type?)
48
48
  end
49
49
 
50
50
  def message(max)
@@ -42,6 +42,7 @@ module RuboCop
42
42
  def on_class(node)
43
43
  check_code_length(node)
44
44
  end
45
+ alias on_sclass on_class
45
46
 
46
47
  def on_casgn(node)
47
48
  parent = node.parent
@@ -117,8 +117,7 @@ module RuboCop
117
117
  end
118
118
 
119
119
  def capturing_variable?(name)
120
- # TODO: Remove `Symbol#to_s` after supporting only Ruby >= 2.7.
121
- name && !name.to_s.start_with?('_')
120
+ name && !name.start_with?('_')
122
121
  end
123
122
 
124
123
  def branch?(node)
@@ -10,7 +10,7 @@ module RuboCop
10
10
  include Util
11
11
 
12
12
  FOLDABLE_TYPES = %i[array hash heredoc send csend].freeze
13
- CLASSLIKE_TYPES = %i[class module].freeze
13
+ CLASSLIKE_TYPES = %i[class module sclass].freeze
14
14
  private_constant :FOLDABLE_TYPES, :CLASSLIKE_TYPES
15
15
 
16
16
  def initialize(node, processed_source, count_comments: false, foldable_types: [])
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # This module encapsulates the ability to allow certain receivers in a cop.
6
+ module AllowedReceivers
7
+ def allowed_receiver?(receiver)
8
+ receiver_name = receiver_name(receiver)
9
+
10
+ allowed_receivers.include?(receiver_name)
11
+ end
12
+
13
+ def receiver_name(receiver)
14
+ if receiver.receiver && !receiver.receiver.const_type?
15
+ return receiver_name(receiver.receiver)
16
+ end
17
+
18
+ if receiver.send_type?
19
+ if receiver.receiver
20
+ "#{receiver_name(receiver.receiver)}.#{receiver.method_name}"
21
+ else
22
+ receiver.method_name.to_s
23
+ end
24
+ else
25
+ receiver.source
26
+ end
27
+ end
28
+
29
+ def allowed_receivers
30
+ cop_config.fetch('AllowedReceivers', [])
31
+ end
32
+ end
33
+ end
34
+ end
@@ -62,21 +62,25 @@ module RuboCop
62
62
  # Returns the end line of a node, which might be a comment and not part of the AST
63
63
  # End line is considered either the line at which another node starts, or
64
64
  # the line at which the parent node ends.
65
- # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
65
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity, Lint/DuplicateBranch
66
66
  def find_end_line(node)
67
67
  if node.if_type? && node.else?
68
68
  node.loc.else.line
69
69
  elsif node.if_type? && node.ternary?
70
70
  node.else_branch.loc.line
71
+ elsif node.if_type? && node.elsif?
72
+ node.each_ancestor(:if).find(&:if?).loc.end.line
73
+ elsif node.block_type? || node.numblock_type?
74
+ node.loc.end.line
71
75
  elsif (next_sibling = node.right_sibling) && next_sibling.is_a?(AST::Node)
72
76
  next_sibling.loc.line
73
77
  elsif (parent = node.parent)
74
- parent.loc.end ? parent.loc.end.line : parent.loc.line
78
+ parent.loc.respond_to?(:end) && parent.loc.end ? parent.loc.end.line : parent.loc.line
75
79
  else
76
80
  node.loc.end.line
77
81
  end
78
82
  end
79
- # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
83
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity, Lint/DuplicateBranch
80
84
  end
81
85
  end
82
86
  end
@@ -175,7 +175,7 @@ module RuboCop
175
175
  end
176
176
 
177
177
  def set_new_arg_name(transformed_argname, corrector)
178
- corrector.replace(block_node.arguments.source_range, "|#{transformed_argname}|")
178
+ corrector.replace(block_node.arguments, "|#{transformed_argname}|")
179
179
  end
180
180
 
181
181
  def set_new_body_expression(transforming_body_expr, corrector)
@@ -36,7 +36,7 @@ module RuboCop
36
36
  end
37
37
 
38
38
  def allowed_type?(token)
39
- %i[tRPAREN tRBRACK tPIPE].include?(token.type)
39
+ %i[tRPAREN tRBRACK tPIPE tSTRING_DEND].include?(token.type)
40
40
  end
41
41
 
42
42
  def space_forbidden_before_rcurly?
@@ -69,7 +69,7 @@ module RuboCop
69
69
  end
70
70
 
71
71
  def first_line_comment(node)
72
- comment = processed_source.find_comment { |c| same_line?(c, node) }
72
+ comment = processed_source.comments.find { |c| same_line?(c, node) }
73
73
  return unless comment
74
74
 
75
75
  comment_source = comment.source
@@ -57,7 +57,7 @@ module RuboCop
57
57
  CONSTANT_MSG = 'Use only ascii symbols in constants.'
58
58
 
59
59
  def on_new_investigation
60
- processed_source.each_token do |token|
60
+ processed_source.tokens.each do |token|
61
61
  next if !should_check?(token) || token.text.ascii_only?
62
62
 
63
63
  message = token.type == :tIDENTIFIER ? IDENTIFIER_MSG : CONSTANT_MSG
@@ -76,7 +76,7 @@ module RuboCop
76
76
  end
77
77
 
78
78
  def contains_constant?(node)
79
- node.branches.any?(&:const_type?)
79
+ node.branches.compact.any?(&:const_type?)
80
80
  end
81
81
  end
82
82
  end
@@ -5,6 +5,7 @@ module RuboCop
5
5
  module Naming
6
6
  # Recommends the use of inclusive language instead of problematic terms.
7
7
  # The cop can check the following locations for offenses:
8
+ #
8
9
  # - identifiers
9
10
  # - constants
10
11
  # - variables
@@ -12,6 +13,7 @@ module RuboCop
12
13
  # - symbols
13
14
  # - comments
14
15
  # - file paths
16
+ #
15
17
  # Each of these locations can be individually enabled/disabled via configuration,
16
18
  # for example CheckIdentifiers = true/false.
17
19
  #
@@ -22,6 +24,9 @@ module RuboCop
22
24
  # `WholeWord: true` can be set on a flagged term to indicate the cop should only match when
23
25
  # a term matches the whole word (partial matches will not be offenses).
24
26
  #
27
+ # The cop supports autocorrection when there is only one suggestion. When there are multiple
28
+ # suggestions, the best suggestion cannot be identified and will not be autocorrected.
29
+ #
25
30
  # @example FlaggedTerms: { whitelist: { Suggestions: ['allowlist'] } }
26
31
  # # Suggest replacing identifier whitelist with allowlist
27
32
  #
@@ -68,6 +73,7 @@ module RuboCop
68
73
  # TeslaVehicle
69
74
  class InclusiveLanguage < Base
70
75
  include RangeHelp
76
+ extend AutoCorrector
71
77
 
72
78
  EMPTY_ARRAY = [].freeze
73
79
  MSG = "Consider replacing '%<term>s'%<suffix>s."
@@ -92,7 +98,7 @@ module RuboCop
92
98
  private
93
99
 
94
100
  def investigate_tokens
95
- processed_source.each_token do |token|
101
+ processed_source.tokens.each do |token|
96
102
  next unless check_token?(token.type)
97
103
 
98
104
  word_locations = scan_for_words(token.text)
@@ -104,9 +110,16 @@ module RuboCop
104
110
 
105
111
  def add_offenses_for_token(token, word_locations)
106
112
  word_locations.each do |word_location|
107
- start_position = token.pos.begin_pos + token.pos.source.index(word_location.word)
108
- range = range_between(start_position, start_position + word_location.word.length)
109
- add_offense(range, message: create_message(word_location.word))
113
+ word = word_location.word
114
+ range = offense_range(token, word)
115
+
116
+ add_offense(range, message: create_message(word)) do |corrector|
117
+ suggestions = find_flagged_term(word)['Suggestions']
118
+
119
+ next unless suggestions.is_a?(String)
120
+
121
+ corrector.replace(range, suggestions)
122
+ end
110
123
  end
111
124
  end
112
125
 
@@ -264,6 +277,12 @@ module RuboCop
264
277
  end
265
278
  " with #{suggestion_str}"
266
279
  end
280
+
281
+ def offense_range(token, word)
282
+ start_position = token.pos.begin_pos + token.pos.source.index(word)
283
+
284
+ range_between(start_position, start_position + word.length)
285
+ end
267
286
  end
268
287
  end
269
288
  end
@@ -17,7 +17,8 @@ module RuboCop
17
17
  # @safety
18
18
  # This cop relies on the pattern `@instance_var ||= ...`,
19
19
  # but this is sometimes used for other purposes than memoization
20
- # so this cop is considered unsafe.
20
+ # so this cop is considered unsafe. Also, its autocorrection is unsafe
21
+ # because it may conflict with instance variable names already in use.
21
22
  #
22
23
  # @example EnforcedStyleForLeadingUnderscores: disallowed (default)
23
24
  # # bad
@@ -145,6 +146,8 @@ module RuboCop
145
146
  # @_foo ||= calculate_expensive_thing
146
147
  # end
147
148
  class MemoizedInstanceVariableName < Base
149
+ extend AutoCorrector
150
+
148
151
  include ConfigurableEnforcedStyle
149
152
 
150
153
  MSG = 'Memoized variable `%<var>s` does not match ' \
@@ -163,6 +166,7 @@ module RuboCop
163
166
  PATTERN
164
167
 
165
168
  # rubocop:disable Metrics/AbcSize
169
+ # rubocop:disable Metrics/MethodLength
166
170
  def on_or_asgn(node)
167
171
  lhs, _value = *node
168
172
  return unless lhs.ivasgn_type?
@@ -175,14 +179,18 @@ module RuboCop
175
179
 
176
180
  return if matches?(method_name, lhs)
177
181
 
182
+ suggested_var = suggested_var(method_name)
178
183
  msg = format(
179
184
  message(lhs.children.first.to_s),
180
185
  var: lhs.children.first.to_s,
181
- suggested_var: suggested_var(method_name),
186
+ suggested_var: suggested_var,
182
187
  method: method_name
183
188
  )
184
- add_offense(lhs, message: msg)
189
+ add_offense(lhs, message: msg) do |corrector|
190
+ corrector.replace(lhs.loc.name, "@#{suggested_var}")
191
+ end
185
192
  end
193
+ # rubocop:enable Metrics/MethodLength
186
194
  # rubocop:enable Metrics/AbcSize
187
195
 
188
196
  # @!method defined_memoized?(node, ivar)
@@ -205,15 +213,22 @@ module RuboCop
205
213
  defined_memoized?(method_node.body, var_name) do |defined_ivar, return_ivar, ivar_assign|
206
214
  return if matches?(method_name, ivar_assign)
207
215
 
216
+ suggested_var = suggested_var(method_name)
208
217
  msg = format(
209
218
  message(var_name.to_s),
210
219
  var: var_name.to_s,
211
- suggested_var: suggested_var(method_name),
220
+ suggested_var: suggested_var,
212
221
  method: method_name
213
222
  )
214
- add_offense(defined_ivar, message: msg)
215
- add_offense(return_ivar, message: msg)
216
- add_offense(ivar_assign.loc.name, message: msg)
223
+ add_offense(defined_ivar, message: msg) do |corrector|
224
+ corrector.replace(defined_ivar, "@#{suggested_var}")
225
+ end
226
+ add_offense(return_ivar, message: msg) do |corrector|
227
+ corrector.replace(return_ivar, "@#{suggested_var}")
228
+ end
229
+ add_offense(ivar_assign.loc.name, message: msg) do |corrector|
230
+ corrector.replace(ivar_assign.loc.name, "@#{suggested_var}")
231
+ end
217
232
  end
218
233
  end
219
234
  # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
@@ -82,9 +82,7 @@ module RuboCop
82
82
  message = message(node)
83
83
 
84
84
  add_offense(range, message: message) do |corrector|
85
- corrector.replace(range, preferred_name)
86
-
87
- correct_node(corrector, node.body, offending_name, preferred_name)
85
+ autocorrect(corrector, node, range, offending_name, preferred_name)
88
86
  end
89
87
  end
90
88
 
@@ -95,6 +93,16 @@ module RuboCop
95
93
  variable.source_range
96
94
  end
97
95
 
96
+ def autocorrect(corrector, node, range, offending_name, preferred_name)
97
+ corrector.replace(range, preferred_name)
98
+ correct_node(corrector, node.body, offending_name, preferred_name)
99
+ return unless (kwbegin_node = node.parent.each_ancestor(:kwbegin).first)
100
+
101
+ kwbegin_node.right_siblings.each do |child_node|
102
+ correct_node(corrector, child_node, offending_name, preferred_name)
103
+ end
104
+ end
105
+
98
106
  def variable_name_matches?(node, name)
99
107
  if node.masgn_type?
100
108
  node.each_descendant(:lvasgn).any? do |lvasgn_node|