rubocop 1.78.0 → 1.81.6

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 (117) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -3
  3. data/config/default.yml +46 -21
  4. data/exe/rubocop +1 -8
  5. data/lib/rubocop/cli/command/auto_generate_config.rb +2 -2
  6. data/lib/rubocop/cli.rb +6 -2
  7. data/lib/rubocop/config_loader.rb +3 -1
  8. data/lib/rubocop/config_store.rb +5 -0
  9. data/lib/rubocop/cop/autocorrect_logic.rb +4 -4
  10. data/lib/rubocop/cop/correctors/alignment_corrector.rb +7 -4
  11. data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +7 -2
  12. data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +3 -1
  13. data/lib/rubocop/cop/internal_affairs/node_type_group.rb +3 -2
  14. data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +1 -1
  15. data/lib/rubocop/cop/internal_affairs/useless_restrict_on_send.rb +1 -1
  16. data/lib/rubocop/cop/layout/class_structure.rb +1 -1
  17. data/lib/rubocop/cop/layout/dot_position.rb +1 -1
  18. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +30 -12
  19. data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +101 -0
  20. data/lib/rubocop/cop/layout/empty_lines_around_arguments.rb +8 -29
  21. data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +1 -1
  22. data/lib/rubocop/cop/layout/hash_alignment.rb +0 -5
  23. data/lib/rubocop/cop/layout/line_length.rb +9 -1
  24. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +8 -4
  25. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +8 -0
  26. data/lib/rubocop/cop/layout/space_around_keyword.rb +6 -1
  27. data/lib/rubocop/cop/layout/space_around_operators.rb +8 -0
  28. data/lib/rubocop/cop/layout/trailing_whitespace.rb +1 -1
  29. data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +3 -2
  30. data/lib/rubocop/cop/lint/cop_directive_syntax.rb +13 -7
  31. data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +4 -1
  32. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +5 -42
  33. data/lib/rubocop/cop/lint/empty_interpolation.rb +11 -0
  34. data/lib/rubocop/cop/lint/literal_as_condition.rb +12 -0
  35. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +1 -2
  36. data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +1 -0
  37. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +101 -2
  38. data/lib/rubocop/cop/lint/require_range_parentheses.rb +1 -1
  39. data/lib/rubocop/cop/lint/rescue_exception.rb +1 -4
  40. data/lib/rubocop/cop/lint/rescue_type.rb +1 -1
  41. data/lib/rubocop/cop/lint/self_assignment.rb +6 -5
  42. data/lib/rubocop/cop/lint/shadowed_argument.rb +7 -7
  43. data/lib/rubocop/cop/lint/uri_escape_unescape.rb +2 -0
  44. data/lib/rubocop/cop/lint/useless_numeric_operation.rb +1 -0
  45. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +121 -0
  46. data/lib/rubocop/cop/lint/void.rb +7 -0
  47. data/lib/rubocop/cop/message_annotator.rb +1 -1
  48. data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
  49. data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -7
  50. data/lib/rubocop/cop/mixin/trailing_comma.rb +1 -1
  51. data/lib/rubocop/cop/naming/method_name.rb +40 -1
  52. data/lib/rubocop/cop/naming/predicate_method.rb +19 -3
  53. data/lib/rubocop/cop/security/json_load.rb +33 -11
  54. data/lib/rubocop/cop/style/access_modifier_declarations.rb +1 -1
  55. data/lib/rubocop/cop/style/accessor_grouping.rb +13 -1
  56. data/lib/rubocop/cop/style/arguments_forwarding.rb +11 -17
  57. data/lib/rubocop/cop/style/array_intersect.rb +99 -35
  58. data/lib/rubocop/cop/style/array_intersect_with_single_element.rb +47 -0
  59. data/lib/rubocop/cop/style/bitwise_predicate.rb +8 -1
  60. data/lib/rubocop/cop/style/block_delimiters.rb +1 -1
  61. data/lib/rubocop/cop/style/conditional_assignment.rb +8 -4
  62. data/lib/rubocop/cop/style/dig_chain.rb +1 -1
  63. data/lib/rubocop/cop/style/double_negation.rb +1 -1
  64. data/lib/rubocop/cop/style/endless_method.rb +15 -2
  65. data/lib/rubocop/cop/style/explicit_block_argument.rb +1 -1
  66. data/lib/rubocop/cop/style/exponential_notation.rb +1 -0
  67. data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
  68. data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
  69. data/lib/rubocop/cop/style/inverse_methods.rb +1 -1
  70. data/lib/rubocop/cop/style/it_assignment.rb +69 -12
  71. data/lib/rubocop/cop/style/it_block_parameter.rb +2 -0
  72. data/lib/rubocop/cop/style/map_to_hash.rb +1 -3
  73. data/lib/rubocop/cop/style/map_to_set.rb +1 -3
  74. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -4
  75. data/lib/rubocop/cop/style/nil_comparison.rb +9 -7
  76. data/lib/rubocop/cop/style/one_line_conditional.rb +17 -9
  77. data/lib/rubocop/cop/style/parallel_assignment.rb +32 -20
  78. data/lib/rubocop/cop/style/redundant_begin.rb +34 -0
  79. data/lib/rubocop/cop/style/redundant_condition.rb +1 -1
  80. data/lib/rubocop/cop/style/redundant_exception.rb +1 -1
  81. data/lib/rubocop/cop/style/redundant_format.rb +26 -5
  82. data/lib/rubocop/cop/style/redundant_freeze.rb +1 -1
  83. data/lib/rubocop/cop/style/redundant_interpolation.rb +11 -2
  84. data/lib/rubocop/cop/style/redundant_line_continuation.rb +1 -1
  85. data/lib/rubocop/cop/style/redundant_parentheses.rb +29 -11
  86. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +4 -0
  87. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +8 -0
  88. data/lib/rubocop/cop/style/safe_navigation.rb +20 -1
  89. data/lib/rubocop/cop/style/semicolon.rb +20 -5
  90. data/lib/rubocop/cop/style/single_line_methods.rb +3 -3
  91. data/lib/rubocop/cop/style/sole_nested_conditional.rb +30 -1
  92. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +1 -1
  93. data/lib/rubocop/cop/style/string_concatenation.rb +17 -13
  94. data/lib/rubocop/cop/style/symbol_array.rb +1 -1
  95. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +45 -0
  96. data/lib/rubocop/cop/style/unless_else.rb +10 -9
  97. data/lib/rubocop/cop/utils/format_string.rb +10 -0
  98. data/lib/rubocop/cop/variable_force/variable.rb +1 -1
  99. data/lib/rubocop/cop/variable_force.rb +25 -8
  100. data/lib/rubocop/cops_documentation_generator.rb +5 -4
  101. data/lib/rubocop/formatter/disabled_config_formatter.rb +18 -5
  102. data/lib/rubocop/formatter/markdown_formatter.rb +1 -0
  103. data/lib/rubocop/formatter/pacman_formatter.rb +1 -0
  104. data/lib/rubocop/lsp/diagnostic.rb +21 -20
  105. data/lib/rubocop/lsp/routes.rb +65 -9
  106. data/lib/rubocop/lsp/runtime.rb +2 -2
  107. data/lib/rubocop/lsp/server.rb +2 -2
  108. data/lib/rubocop/lsp/stdin_runner.rb +0 -16
  109. data/lib/rubocop/result_cache.rb +14 -12
  110. data/lib/rubocop/runner.rb +6 -4
  111. data/lib/rubocop/target_finder.rb +9 -9
  112. data/lib/rubocop/target_ruby.rb +10 -1
  113. data/lib/rubocop/version.rb +1 -1
  114. data/lib/rubocop.rb +3 -0
  115. data/lib/ruby_lsp/rubocop/addon.rb +23 -8
  116. data/lib/ruby_lsp/rubocop/runtime_adapter.rb +49 -15
  117. metadata +9 -6
@@ -20,6 +20,15 @@ module RuboCop
20
20
  # In the example below, the safe navigation operator (`&.`) is unnecessary
21
21
  # because `NilClass` has methods like `respond_to?` and `is_a?`.
22
22
  #
23
+ # The `InferNonNilReceiver` option specifies whether to look into previous code
24
+ # paths to infer if the receiver can't be nil. This check is unsafe because the receiver
25
+ # can be redefined between the safe navigation call and previous regular method call.
26
+ # It does the inference only in the current scope, e.g. within the same method definition etc.
27
+ #
28
+ # The `AdditionalNilMethods` option specifies additional custom methods which are
29
+ # defined on `NilClass`. When `InferNonNilReceiver` is set, they are used to determine
30
+ # whether the receiver can be nil.
31
+ #
23
32
  # @safety
24
33
  # This cop is unsafe, because autocorrection can change the return type of
25
34
  # the expression. An offending expression that previously could return `nil`
@@ -33,6 +42,20 @@ module RuboCop
33
42
  # CamelCaseConst.do_something
34
43
  #
35
44
  # # bad
45
+ # foo.to_s&.strip
46
+ # foo.to_i&.zero?
47
+ # foo.to_f&.zero?
48
+ # foo.to_a&.size
49
+ # foo.to_h&.size
50
+ #
51
+ # # good
52
+ # foo.to_s.strip
53
+ # foo.to_i.zero?
54
+ # foo.to_f.zero?
55
+ # foo.to_a.size
56
+ # foo.to_h.size
57
+ #
58
+ # # bad
36
59
  # do_something if attrs&.respond_to?(:[])
37
60
  #
38
61
  # # good
@@ -81,17 +104,59 @@ module RuboCop
81
104
  # do_something if attrs.nil_safe_method(:[])
82
105
  # do_something if attrs&.not_nil_safe_method(:[])
83
106
  #
107
+ # @example InferNonNilReceiver: false (default)
108
+ # # good
109
+ # foo.bar
110
+ # foo&.baz
111
+ #
112
+ # @example InferNonNilReceiver: true
113
+ # # bad
114
+ # foo.bar
115
+ # foo&.baz # would raise on previous line if `foo` is nil
116
+ #
117
+ # # good
118
+ # foo.bar
119
+ # foo.baz
120
+ #
121
+ # # bad
122
+ # if foo.condition?
123
+ # foo&.bar
124
+ # end
125
+ #
126
+ # # good
127
+ # if foo.condition?
128
+ # foo.bar
129
+ # end
130
+ #
131
+ # # good (different scopes)
132
+ # def method1
133
+ # foo.bar
134
+ # end
135
+ #
136
+ # def method2
137
+ # foo&.bar
138
+ # end
139
+ #
140
+ # @example AdditionalNilMethods: [present?]
141
+ # # good
142
+ # foo.present?
143
+ # foo&.bar
144
+ #
84
145
  class RedundantSafeNavigation < Base
85
146
  include AllowedMethods
86
147
  extend AutoCorrector
87
148
 
88
149
  MSG = 'Redundant safe navigation detected, use `.` instead.'
89
150
  MSG_LITERAL = 'Redundant safe navigation with default literal detected.'
151
+ MSG_NON_NIL = 'Redundant safe navigation on non-nil receiver (detected by analyzing ' \
152
+ 'previous code/method invocations).'
90
153
 
91
154
  NIL_SPECIFIC_METHODS = (nil.methods - Object.new.methods).to_set.freeze
92
155
 
93
156
  SNAKE_CASE = /\A[[:digit:][:upper:]_]+\z/.freeze
94
157
 
158
+ GUARANTEED_INSTANCE_METHODS = %i[to_s to_i to_f to_a to_h].freeze
159
+
95
160
  # @!method respond_to_nil_specific_method?(node)
96
161
  def_node_matcher :respond_to_nil_specific_method?, <<~PATTERN
97
162
  (csend _ :respond_to? (sym %NIL_SPECIFIC_METHODS))
@@ -111,15 +176,27 @@ module RuboCop
111
176
 
112
177
  # rubocop:disable Metrics/AbcSize
113
178
  def on_csend(node)
179
+ range = node.loc.dot
180
+
181
+ if infer_non_nil_receiver?
182
+ checker = Lint::Utils::NilReceiverChecker.new(node.receiver, additional_nil_methods)
183
+
184
+ if checker.cant_be_nil?
185
+ add_offense(range, message: MSG_NON_NIL) { |corrector| corrector.replace(range, '.') }
186
+ return
187
+ end
188
+ end
189
+
114
190
  unless assume_receiver_instance_exists?(node.receiver)
115
- return unless check?(node) && allowed_method?(node.method_name)
191
+ return if !guaranteed_instance?(node.receiver) && !check?(node)
116
192
  return if respond_to_nil_specific_method?(node)
117
193
  end
118
194
 
119
- range = node.loc.dot
120
195
  add_offense(range) { |corrector| corrector.replace(range, '.') }
121
196
  end
197
+ # rubocop:enable Metrics/AbcSize
122
198
 
199
+ # rubocop:disable Metrics/AbcSize
123
200
  def on_or(node)
124
201
  conversion_with_default?(node) do |send_node|
125
202
  range = send_node.loc.dot.begin.join(node.source_range.end)
@@ -142,7 +219,20 @@ module RuboCop
142
219
  receiver.self_type? || (receiver.literal? && !receiver.nil_type?)
143
220
  end
144
221
 
222
+ def guaranteed_instance?(node)
223
+ receiver = if node.any_block_type?
224
+ node.send_node
225
+ else
226
+ node
227
+ end
228
+ return false unless receiver.send_type?
229
+
230
+ GUARANTEED_INSTANCE_METHODS.include?(receiver.method_name)
231
+ end
232
+
145
233
  def check?(node)
234
+ return false unless allowed_method?(node.method_name)
235
+
146
236
  parent = node.parent
147
237
  return false unless parent
148
238
 
@@ -154,6 +244,15 @@ module RuboCop
154
244
  def condition?(parent, node)
155
245
  (parent.conditional? || parent.post_condition_loop?) && parent.condition == node
156
246
  end
247
+
248
+ def infer_non_nil_receiver?
249
+ cop_config['InferNonNilReceiver']
250
+ end
251
+
252
+ def additional_nil_methods
253
+ @additional_nil_methods ||=
254
+ Array(cop_config.fetch('AdditionalNilMethods', []).map(&:to_sym))
255
+ end
157
256
  end
158
257
  end
159
258
  end
@@ -43,7 +43,7 @@ module RuboCop
43
43
  def on_irange(node)
44
44
  return if node.parent&.begin_type?
45
45
  return unless node.begin && node.end
46
- return if same_line?(node.begin, node.end)
46
+ return if same_line?(node.loc.operator, node.end)
47
47
 
48
48
  message = format(MSG, range: "#{node.begin.source}#{node.loc.operator.source}")
49
49
 
@@ -24,10 +24,7 @@ module RuboCop
24
24
  MSG = 'Avoid rescuing the `Exception` class. Perhaps you meant to rescue `StandardError`?'
25
25
 
26
26
  def on_resbody(node)
27
- return unless node.children.first
28
-
29
- rescue_args = node.children.first.children
30
- return unless rescue_args.any? { |a| targets_exception?(a) }
27
+ return unless node.exceptions.any? { |exception| targets_exception?(exception) }
31
28
 
32
29
  add_offense(node)
33
30
  end
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Lint
6
- # Check for arguments to `rescue` that will result in a `TypeError`
6
+ # Checks for arguments to `rescue` that will result in a `TypeError`
7
7
  # if an exception is raised.
8
8
  #
9
9
  # @example
@@ -45,7 +45,7 @@ module RuboCop
45
45
  return if allow_rbs_inline_annotation? && rbs_inline_annotation?(node.receiver)
46
46
 
47
47
  if node.method?(:[]=)
48
- handle_key_assignment(node) if node.arguments.size == 2
48
+ handle_key_assignment(node)
49
49
  elsif node.assignment_method?
50
50
  handle_attribute_assignment(node) if node.arguments.size == 1
51
51
  end
@@ -105,12 +105,13 @@ module RuboCop
105
105
  end
106
106
 
107
107
  def handle_key_assignment(node)
108
- value_node = node.arguments[1]
108
+ value_node = node.last_argument
109
+ node_arguments = node.arguments[0...-1]
109
110
 
110
- if value_node.send_type? && value_node.method?(:[]) &&
111
+ if value_node.respond_to?(:method?) && value_node.method?(:[]) &&
111
112
  node.receiver == value_node.receiver &&
112
- !node.first_argument.call_type? &&
113
- node.first_argument == value_node.first_argument
113
+ node_arguments.none?(&:call_type?) &&
114
+ node_arguments == value_node.arguments
114
115
  add_offense(node)
115
116
  end
116
117
  end
@@ -125,13 +125,13 @@ module RuboCop
125
125
  next false if assignment_node.shorthand_asgn?
126
126
  next false unless assignment_node.parent
127
127
 
128
- node_within_block_or_conditional =
129
- node_within_block_or_conditional?(assignment_node.parent, argument.scope.node)
128
+ conditional_assignment =
129
+ conditional_assignment?(assignment_node.parent, argument.scope.node)
130
130
 
131
131
  unless uses_var?(assignment_node, argument.name)
132
132
  # It's impossible to decide whether a branch or block is executed,
133
133
  # so the precise reassignment location is undecidable.
134
- next false if node_within_block_or_conditional
134
+ next false if conditional_assignment
135
135
 
136
136
  yield(assignment.node, location_known)
137
137
  break
@@ -147,13 +147,13 @@ module RuboCop
147
147
  node.source_range.begin_pos
148
148
  end
149
149
 
150
- # Check whether the given node is nested into block or conditional.
150
+ # Check whether the given node is always executed or not
151
151
  #
152
- def node_within_block_or_conditional?(node, stop_search_node)
152
+ def conditional_assignment?(node, stop_search_node)
153
153
  return false if node == stop_search_node
154
154
 
155
- node.conditional? || node.block_type? ||
156
- node_within_block_or_conditional?(node.parent, stop_search_node)
155
+ node.conditional? || node.type?(:block, :rescue) ||
156
+ conditional_assignment?(node.parent, stop_search_node)
157
157
  end
158
158
 
159
159
  # Get argument references without assignments' references
@@ -17,6 +17,7 @@ module RuboCop
17
17
  #
18
18
  # # good
19
19
  # CGI.escape('http://example.com')
20
+ # URI.encode_uri_component(uri) # Since Ruby 3.1
20
21
  # URI.encode_www_form([['example', 'param'], ['lang', 'en']])
21
22
  # URI.encode_www_form(page: 10, locale: 'en')
22
23
  # URI.encode_www_form_component('http://example.com')
@@ -27,6 +28,7 @@ module RuboCop
27
28
  #
28
29
  # # good
29
30
  # CGI.unescape(enc_uri)
31
+ # URI.decode_uri_component(uri) # Since Ruby 3.1
30
32
  # URI.decode_www_form(enc_uri)
31
33
  # URI.decode_www_form_component(enc_uri)
32
34
  class UriEscapeUnescape < Base
@@ -31,6 +31,7 @@ module RuboCop
31
31
  #
32
32
  class UselessNumericOperation < Base
33
33
  extend AutoCorrector
34
+
34
35
  MSG = 'Do not apply inconsequential numeric operations to variables.'
35
36
  RESTRICT_ON_SEND = %i[+ - * / **].freeze
36
37
 
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ module Utils
7
+ # Utility class that checks if the receiver can't be nil.
8
+ class NilReceiverChecker
9
+ NIL_METHODS = (nil.methods + %i[!]).to_set.freeze
10
+
11
+ def initialize(receiver, additional_nil_methods)
12
+ @receiver = receiver
13
+ @additional_nil_methods = additional_nil_methods
14
+ @checked_nodes = {}.compare_by_identity
15
+ end
16
+
17
+ def cant_be_nil?
18
+ sole_condition_of_parent_if?(@receiver) || _cant_be_nil?(@receiver.parent, @receiver)
19
+ end
20
+
21
+ private
22
+
23
+ # rubocop:disable Metrics
24
+ def _cant_be_nil?(node, receiver)
25
+ return false unless node
26
+
27
+ # For some nodes, we check their parent and then some children for these parents.
28
+ # This is added to avoid infinite loops.
29
+ return false if @checked_nodes.key?(node)
30
+
31
+ @checked_nodes[node] = true
32
+
33
+ case node.type
34
+ when :def, :class, :module, :sclass
35
+ return false
36
+ when :send
37
+ return non_nil_method?(node.method_name) if node.receiver == receiver
38
+
39
+ node.arguments.each do |argument|
40
+ return true if _cant_be_nil?(argument, receiver)
41
+ end
42
+
43
+ return true if _cant_be_nil?(node.receiver, receiver)
44
+ when :begin
45
+ return true if _cant_be_nil?(node.children.first, receiver)
46
+ when :if, :case
47
+ return true if _cant_be_nil?(node.condition, receiver)
48
+ when :and, :or
49
+ return true if _cant_be_nil?(node.lhs, receiver)
50
+ when :pair
51
+ if _cant_be_nil?(node.key, receiver) ||
52
+ _cant_be_nil?(node.value, receiver)
53
+ return true
54
+ end
55
+ when :when
56
+ node.each_condition do |condition|
57
+ return true if _cant_be_nil?(condition, receiver)
58
+ end
59
+ when :lvasgn, :ivasgn, :cvasgn, :gvasgn, :casgn
60
+ return true if _cant_be_nil?(node.expression, receiver)
61
+ end
62
+
63
+ # Due to how `if/else` are implemented (`elsif` is a child of `if` or another `elsif`),
64
+ # using left_siblings will not work correctly for them.
65
+ if !else_branch?(node) || (node.if_type? && !node.elsif?)
66
+ node.left_siblings.reverse_each do |sibling|
67
+ next unless sibling.is_a?(AST::Node)
68
+
69
+ return true if _cant_be_nil?(sibling, receiver)
70
+ end
71
+ end
72
+
73
+ if node.parent
74
+ _cant_be_nil?(node.parent, receiver)
75
+ else
76
+ false
77
+ end
78
+ end
79
+ # rubocop:enable Metrics
80
+
81
+ def non_nil_method?(method_name)
82
+ !NIL_METHODS.include?(method_name) && !@additional_nil_methods.include?(method_name)
83
+ end
84
+
85
+ # rubocop:disable Metrics/PerceivedComplexity
86
+ def sole_condition_of_parent_if?(node)
87
+ parent = node.parent
88
+
89
+ while parent
90
+ if parent.if_type?
91
+ if parent.condition == node
92
+ return true
93
+ elsif parent.elsif?
94
+ parent = find_top_if(parent)
95
+ end
96
+ elsif else_branch?(parent)
97
+ # Find the top `if` for `else`.
98
+ parent = parent.parent
99
+ end
100
+
101
+ parent = parent&.parent
102
+ end
103
+
104
+ false
105
+ end
106
+ # rubocop:enable Metrics/PerceivedComplexity
107
+
108
+ def else_branch?(node)
109
+ node.parent&.if_type? && node.parent.else_branch == node
110
+ end
111
+
112
+ def find_top_if(node)
113
+ node = node.parent while node.elsif?
114
+
115
+ node
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -16,6 +16,12 @@ module RuboCop
16
16
  # enumerator.each { |item| item >= 2 } #=> [2, 3]
17
17
  # ----
18
18
  #
19
+ # NOTE: Return values in assignment method definitions such as `def foo=(arg)` are
20
+ # detected because they are in a void context. However, autocorrection does not remove
21
+ # the return value, as that would change behavior. In such cases, whether to remove
22
+ # the return value or rename the method to something more appropriate should be left to
23
+ # the user.
24
+ #
19
25
  # @example CheckForMethodsWithNoSideEffects: false (default)
20
26
  # # bad
21
27
  # def some_method
@@ -233,6 +239,7 @@ module RuboCop
233
239
 
234
240
  def autocorrect_void_expression(corrector, node)
235
241
  return if node.parent.if_type?
242
+ return if (def_node = node.each_ancestor(:any_def).first) && def_node.assignment_method?
236
243
 
237
244
  corrector.remove(range_with_surrounding_space(range: node.source_range, side: :left))
238
245
  end
@@ -102,7 +102,7 @@ module RuboCop
102
102
  def reference_urls
103
103
  urls = cop_config
104
104
  .values_at('References', 'Reference') # Support legacy Reference key
105
- .flat_map { Array(_1) }
105
+ .flat_map { |url| Array(url) }
106
106
  .reject(&:empty?)
107
107
 
108
108
  urls unless urls.empty?
@@ -227,7 +227,7 @@ module RuboCop
227
227
 
228
228
  def chained_to_heredoc?(node)
229
229
  while (node = node.receiver)
230
- return true if node.type?(:str, :dstr, :xstr) && node.heredoc?
230
+ return true if node.any_str_type? && node.heredoc?
231
231
  end
232
232
 
233
233
  false
@@ -19,8 +19,7 @@ module RuboCop
19
19
  def check_end_kw_alignment(node, align_ranges)
20
20
  return if ignored_node?(node)
21
21
 
22
- end_loc = node.loc.end
23
- return if accept_end_kw_alignment?(end_loc)
22
+ return unless (end_loc = node.loc.end)
24
23
 
25
24
  matching = matching_ranges(end_loc, align_ranges)
26
25
 
@@ -57,11 +56,6 @@ module RuboCop
57
56
  add_offense(end_loc, message: msg) { |corrector| autocorrect(corrector, node) }
58
57
  end
59
58
 
60
- def accept_end_kw_alignment?(end_loc)
61
- end_loc.nil? || # Discard modifier forms of if/while/until.
62
- !/\A[ \t]*end/.match?(processed_source.lines[end_loc.line - 1])
63
- end
64
-
65
59
  def style_parameter_name
66
60
  'EnforcedStyleAlignWith'
67
61
  end
@@ -140,7 +140,7 @@ module RuboCop
140
140
  end
141
141
 
142
142
  def last_item_precedes_newline?(node)
143
- after_last_item = node.children.last.source_range.end.join(node.loc.end.begin)
143
+ after_last_item = node.children.last.source_range.end.join(node.source_range.end)
144
144
 
145
145
  after_last_item.source.start_with?(/,?\s*(#.*)?\n/)
146
146
  end
@@ -53,6 +53,12 @@ module RuboCop
53
53
  # # good
54
54
  # Struct.new(:foo_bar)
55
55
  #
56
+ # # bad
57
+ # alias_method :fooBar, :some_method
58
+ #
59
+ # # good
60
+ # alias_method :foo_bar, :some_method
61
+ #
56
62
  # @example EnforcedStyle: camelCase
57
63
  # # bad
58
64
  # def foo_bar; end
@@ -74,6 +80,12 @@ module RuboCop
74
80
  # # good
75
81
  # Struct.new(:fooBar)
76
82
  #
83
+ # # bad
84
+ # alias_method :foo_bar, :some_method
85
+ #
86
+ # # good
87
+ # alias_method :fooBar, :some_method
88
+ #
77
89
  # @example ForbiddenIdentifiers: ['def', 'super']
78
90
  # # bad
79
91
  # def def; end
@@ -94,6 +106,9 @@ module RuboCop
94
106
  MSG = 'Use %<style>s for method names.'
95
107
  MSG_FORBIDDEN = '`%<identifier>s` is forbidden, use another method name instead.'
96
108
 
109
+ OPERATOR_METHODS = %i[| ^ & <=> == === =~ > >= < <= << >> + - * /
110
+ % ** ~ +@ -@ !@ ~@ [] []= ! != !~ `].to_set.freeze
111
+
97
112
  # @!method sym_name(node)
98
113
  def_node_matcher :sym_name, '(sym $_name)'
99
114
 
@@ -103,11 +118,18 @@ module RuboCop
103
118
  # @!method new_struct?(node)
104
119
  def_node_matcher :new_struct?, '(send (const {nil? cbase} :Struct) :new ...)'
105
120
 
121
+ # @!method define_data?(node)
122
+ def_node_matcher :define_data?, '(send (const {nil? cbase} :Data) :define ...)'
123
+
106
124
  def on_send(node)
107
125
  if node.method?(:define_method) || node.method?(:define_singleton_method)
108
126
  handle_define_method(node)
109
127
  elsif new_struct?(node)
110
128
  handle_new_struct(node)
129
+ elsif define_data?(node)
130
+ handle_define_data(node)
131
+ elsif node.method?(:alias_method)
132
+ handle_alias_method(node)
111
133
  else
112
134
  handle_attr_accessor(node)
113
135
  end
@@ -124,6 +146,10 @@ module RuboCop
124
146
  end
125
147
  alias on_defs on_def
126
148
 
149
+ def on_alias(node)
150
+ handle_method_name(node.new_identifier, node.new_identifier.value)
151
+ end
152
+
127
153
  private
128
154
 
129
155
  def handle_define_method(node)
@@ -139,6 +165,19 @@ module RuboCop
139
165
  end
140
166
  end
141
167
 
168
+ def handle_define_data(node)
169
+ node.arguments.select { |argument| argument.type?(:sym, :str) }.each do |name|
170
+ handle_method_name(name, name.value)
171
+ end
172
+ end
173
+
174
+ def handle_alias_method(node)
175
+ return unless node.arguments.size == 2
176
+ return unless node.first_argument.type?(:str, :sym)
177
+
178
+ handle_method_name(node.first_argument, node.first_argument.value)
179
+ end
180
+
142
181
  def handle_attr_accessor(node)
143
182
  return unless (attrs = node.attribute_accessor?)
144
183
 
@@ -159,7 +198,7 @@ module RuboCop
159
198
 
160
199
  if forbidden_name?(name.to_s)
161
200
  register_forbidden_name(node)
162
- else
201
+ elsif !OPERATOR_METHODS.include?(name.to_sym)
163
202
  check_name(node, name, range_position(node))
164
203
  end
165
204
  end
@@ -14,7 +14,7 @@ module RuboCop
14
14
  # method calls are assumed to return boolean values. The cop does not make an assessment
15
15
  # if the return type is unknown (non-predicate method calls, variables, etc.).
16
16
  #
17
- # NOTE: Operator methods (`def ==`, etc.) are ignored.
17
+ # NOTE: The `initialize` method and operator methods (`def ==`, etc.) are ignored.
18
18
  #
19
19
  # By default, the cop runs in `conservative` mode, which allows a method to be named
20
20
  # with a question mark as long as at least one return value is boolean. In `aggressive`
@@ -113,6 +113,18 @@ module RuboCop
113
113
  # true
114
114
  # end
115
115
  #
116
+ # @example AllowedMethods: [call] (default)
117
+ # # good
118
+ # def call
119
+ # foo == bar
120
+ # end
121
+ #
122
+ # @example AllowedPatterns: [\Afoo]
123
+ # # good
124
+ # def foo?
125
+ # 'foo'
126
+ # end
127
+ #
116
128
  # @example AllowBangMethods: false (default)
117
129
  # # bad
118
130
  # def save!
@@ -149,7 +161,8 @@ module RuboCop
149
161
  private
150
162
 
151
163
  def allowed?(node)
152
- allowed_method?(node.method_name) ||
164
+ node.method?(:initialize) ||
165
+ allowed_method?(node.method_name) ||
153
166
  matches_allowed_pattern?(node.method_name) ||
154
167
  allowed_bang_method?(node) ||
155
168
  node.operator_method? ||
@@ -269,7 +282,10 @@ module RuboCop
269
282
  node.body ? [last_value(node.body)] : [s(:nil)]
270
283
  else
271
284
  # Branches with no value act as an implicit `nil`.
272
- node.branches.filter_map { |branch| branch ? last_value(branch) : s(:nil) }
285
+ branches = node.branches.map { |branch| branch ? last_value(branch) : s(:nil) }
286
+ # Missing else branches also act as an implicit `nil`.
287
+ branches.push(s(:nil)) unless node.else_branch
288
+ branches
273
289
  end
274
290
  end
275
291