rubocop 1.77.0 → 1.79.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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +36 -20
  4. data/lib/rubocop/cli.rb +12 -1
  5. data/lib/rubocop/config_loader.rb +1 -38
  6. data/lib/rubocop/cop/internal_affairs/example_description.rb +1 -1
  7. data/lib/rubocop/cop/internal_affairs/node_type_group.rb +3 -2
  8. data/lib/rubocop/cop/internal_affairs/useless_restrict_on_send.rb +1 -1
  9. data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +99 -0
  10. data/lib/rubocop/cop/layout/space_around_keyword.rb +6 -1
  11. data/lib/rubocop/cop/layout/space_around_operators.rb +8 -0
  12. data/lib/rubocop/cop/lint/duplicate_methods.rb +25 -4
  13. data/lib/rubocop/cop/lint/literal_as_condition.rb +3 -1
  14. data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +1 -0
  15. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +101 -2
  16. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +4 -4
  17. data/lib/rubocop/cop/lint/require_range_parentheses.rb +1 -1
  18. data/lib/rubocop/cop/lint/rescue_type.rb +1 -1
  19. data/lib/rubocop/cop/lint/useless_numeric_operation.rb +1 -0
  20. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +121 -0
  21. data/lib/rubocop/cop/naming/method_name.rb +102 -13
  22. data/lib/rubocop/cop/naming/predicate_method.rb +27 -2
  23. data/lib/rubocop/cop/security/eval.rb +2 -1
  24. data/lib/rubocop/cop/security/open.rb +1 -0
  25. data/lib/rubocop/cop/style/access_modifier_declarations.rb +1 -1
  26. data/lib/rubocop/cop/style/accessor_grouping.rb +13 -1
  27. data/lib/rubocop/cop/style/array_intersect.rb +51 -23
  28. data/lib/rubocop/cop/style/block_delimiters.rb +1 -1
  29. data/lib/rubocop/cop/style/conditional_assignment.rb +1 -1
  30. data/lib/rubocop/cop/style/dig_chain.rb +1 -1
  31. data/lib/rubocop/cop/style/exponential_notation.rb +1 -0
  32. data/lib/rubocop/cop/style/hash_conversion.rb +8 -9
  33. data/lib/rubocop/cop/style/inverse_methods.rb +1 -1
  34. data/lib/rubocop/cop/style/it_assignment.rb +69 -12
  35. data/lib/rubocop/cop/style/it_block_parameter.rb +3 -1
  36. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -4
  37. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +16 -0
  38. data/lib/rubocop/cop/style/parallel_assignment.rb +3 -0
  39. data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -9
  40. data/lib/rubocop/cop/style/redundant_freeze.rb +1 -1
  41. data/lib/rubocop/cop/style/redundant_line_continuation.rb +1 -1
  42. data/lib/rubocop/cop/style/redundant_parentheses.rb +1 -0
  43. data/lib/rubocop/cop/style/single_line_methods.rb +7 -4
  44. data/lib/rubocop/cop/style/sole_nested_conditional.rb +30 -1
  45. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +1 -1
  46. data/lib/rubocop/cop/variable_force.rb +16 -7
  47. data/lib/rubocop/cops_documentation_generator.rb +1 -0
  48. data/lib/rubocop/formatter/markdown_formatter.rb +1 -0
  49. data/lib/rubocop/formatter/pacman_formatter.rb +1 -0
  50. data/lib/rubocop/pending_cops_reporter.rb +56 -0
  51. data/lib/rubocop/server/cache.rb +4 -2
  52. data/lib/rubocop/server/client_command/base.rb +10 -0
  53. data/lib/rubocop/server/client_command/exec.rb +2 -1
  54. data/lib/rubocop/server/client_command/start.rb +11 -1
  55. data/lib/rubocop/version.rb +1 -1
  56. data/lib/rubocop.rb +3 -0
  57. metadata +23 -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
@@ -185,9 +185,9 @@ module RuboCop
185
185
  (hash (pair (sym :exception) false))
186
186
  PATTERN
187
187
 
188
- # rubocop:disable Metrics/AbcSize
188
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
189
189
  def on_send(node)
190
- return if hash_or_set_with_block?(node)
190
+ return if node.arguments.any? || hash_or_set_with_block?(node)
191
191
 
192
192
  receiver = find_receiver(node)
193
193
  return unless literal_receiver?(node, receiver) ||
@@ -198,10 +198,10 @@ module RuboCop
198
198
  message = format(MSG, method: node.method_name)
199
199
 
200
200
  add_offense(node.loc.selector, message: message) do |corrector|
201
- corrector.remove(node.loc.dot.join(node.loc.selector))
201
+ corrector.remove(node.loc.dot.join(node.loc.end || node.loc.selector))
202
202
  end
203
203
  end
204
- # rubocop:enable Metrics/AbcSize
204
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
205
205
  alias on_csend on_send
206
206
 
207
207
  private
@@ -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
 
@@ -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
@@ -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
@@ -39,6 +39,20 @@ module RuboCop
39
39
  # # good
40
40
  # def foo_bar; end
41
41
  #
42
+ # # bad
43
+ # define_method :fooBar do
44
+ # end
45
+ #
46
+ # # good
47
+ # define_method :foo_bar do
48
+ # end
49
+ #
50
+ # # bad
51
+ # Struct.new(:fooBar)
52
+ #
53
+ # # good
54
+ # Struct.new(:foo_bar)
55
+ #
42
56
  # @example EnforcedStyle: camelCase
43
57
  # # bad
44
58
  # def foo_bar; end
@@ -46,6 +60,20 @@ module RuboCop
46
60
  # # good
47
61
  # def fooBar; end
48
62
  #
63
+ # # bad
64
+ # define_method :foo_bar do
65
+ # end
66
+ #
67
+ # # good
68
+ # define_method :fooBar do
69
+ # end
70
+ #
71
+ # # bad
72
+ # Struct.new(:foo_bar)
73
+ #
74
+ # # good
75
+ # Struct.new(:fooBar)
76
+ #
49
77
  # @example ForbiddenIdentifiers: ['def', 'super']
50
78
  # # bad
51
79
  # def def; end
@@ -66,13 +94,66 @@ module RuboCop
66
94
  MSG = 'Use %<style>s for method names.'
67
95
  MSG_FORBIDDEN = '`%<identifier>s` is forbidden, use another method name instead.'
68
96
 
97
+ OPERATOR_METHODS = %i[| ^ & <=> == === =~ > >= < <= << >> + - * /
98
+ % ** ~ +@ -@ !@ ~@ [] []= ! != !~ `].to_set.freeze
99
+
69
100
  # @!method sym_name(node)
70
101
  def_node_matcher :sym_name, '(sym $_name)'
71
102
 
72
103
  # @!method str_name(node)
73
104
  def_node_matcher :str_name, '(str $_name)'
74
105
 
106
+ # @!method new_struct?(node)
107
+ def_node_matcher :new_struct?, '(send (const {nil? cbase} :Struct) :new ...)'
108
+
109
+ # @!method define_data?(node)
110
+ def_node_matcher :define_data?, '(send (const {nil? cbase} :Data) :define ...)'
111
+
75
112
  def on_send(node)
113
+ if node.method?(:define_method) || node.method?(:define_singleton_method)
114
+ handle_define_method(node)
115
+ elsif new_struct?(node)
116
+ handle_new_struct(node)
117
+ elsif define_data?(node)
118
+ handle_define_data(node)
119
+ else
120
+ handle_attr_accessor(node)
121
+ end
122
+ end
123
+
124
+ def on_def(node)
125
+ return if node.operator_method? || matches_allowed_pattern?(node.method_name)
126
+
127
+ if forbidden_name?(node.method_name.to_s)
128
+ register_forbidden_name(node)
129
+ else
130
+ check_name(node, node.method_name, node.loc.name)
131
+ end
132
+ end
133
+ alias on_defs on_def
134
+
135
+ private
136
+
137
+ def handle_define_method(node)
138
+ return unless node.first_argument&.type?(:str, :sym)
139
+
140
+ handle_method_name(node, node.first_argument.value)
141
+ end
142
+
143
+ def handle_new_struct(node)
144
+ arguments = node.first_argument&.str_type? ? node.arguments[1..] : node.arguments
145
+ arguments.select { |argument| argument.type?(:sym, :str) }.each do |name|
146
+ handle_method_name(name, name.value)
147
+ end
148
+ end
149
+
150
+ def handle_define_data(node)
151
+ node.arguments.select { |argument| argument.type?(:sym, :str) }.each do |name|
152
+ handle_method_name(name, name.value)
153
+ end
154
+ end
155
+
156
+ def handle_attr_accessor(node)
76
157
  return unless (attrs = node.attribute_accessor?)
77
158
 
78
159
  attrs.last.each do |name_item|
@@ -87,45 +168,53 @@ module RuboCop
87
168
  end
88
169
  end
89
170
 
90
- def on_def(node)
91
- return if node.operator_method? || matches_allowed_pattern?(node.method_name)
171
+ def handle_method_name(node, name)
172
+ return if !name || matches_allowed_pattern?(name)
92
173
 
93
- if forbidden_name?(node.method_name.to_s)
174
+ if forbidden_name?(name.to_s)
94
175
  register_forbidden_name(node)
95
- else
96
- check_name(node, node.method_name, node.loc.name)
176
+ elsif !OPERATOR_METHODS.include?(name)
177
+ check_name(node, name, range_position(node))
97
178
  end
98
179
  end
99
- alias on_defs on_def
100
-
101
- private
102
180
 
103
181
  def forbidden_name?(name)
104
182
  forbidden_identifier?(name) || forbidden_pattern?(name)
105
183
  end
106
184
 
185
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
107
186
  def register_forbidden_name(node)
108
187
  if node.any_def_type?
109
188
  name_node = node.loc.name
110
189
  method_name = node.method_name
111
- else
112
- attrs = node.attribute_accessor?
190
+ elsif node.literal?
191
+ name_node = node
192
+ method_name = node.value
193
+ elsif (attrs = node.attribute_accessor?)
113
194
  name_node = attrs.last.last
114
195
  method_name = attr_name(name_node)
196
+ else
197
+ name_node = node.first_argument
198
+ method_name = node.first_argument.value
115
199
  end
116
200
  message = format(MSG_FORBIDDEN, identifier: method_name)
117
201
  add_offense(name_node, message: message)
118
202
  end
203
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
119
204
 
120
205
  def attr_name(name_item)
121
206
  sym_name(name_item) || str_name(name_item)
122
207
  end
123
208
 
124
209
  def range_position(node)
125
- selector_end_pos = node.loc.selector.end_pos + 1
126
- expr_end_pos = node.source_range.end_pos
210
+ if node.loc.respond_to?(:selector)
211
+ selector_end_pos = node.loc.selector.end_pos + 1
212
+ expr_end_pos = node.source_range.end_pos
127
213
 
128
- range_between(selector_end_pos, expr_end_pos)
214
+ range_between(selector_end_pos, expr_end_pos)
215
+ else
216
+ node.source_range
217
+ end
129
218
  end
130
219
 
131
220
  def message(style)
@@ -26,6 +26,11 @@ module RuboCop
26
26
  # guidelines. By default, `call` is allowed. The cop also has `AllowedPatterns`
27
27
  # configuration to allow method names by regular expression.
28
28
  #
29
+ # Although returning a call to another predicate method is treated as a boolean value,
30
+ # certain method names can be known to not return a boolean, despite ending in a `?`
31
+ # (for example, `Numeric#nonzero?` returns `self` or `nil`). These methods can be
32
+ # configured using `NonBooleanPredicates`.
33
+ #
29
34
  # The cop can furthermore be configured to allow all bang methods (method names
30
35
  # ending with `!`), with `AllowBangMethods: true` (default false).
31
36
  #
@@ -164,7 +169,7 @@ module RuboCop
164
169
  def unknown_method_call?(value)
165
170
  return false unless value.call_type?
166
171
 
167
- !value.comparison_method? && !value.predicate_method? && !value.negation_method?
172
+ !method_returning_boolean?(value)
168
173
  end
169
174
 
170
175
  def return_values(node)
@@ -190,7 +195,13 @@ module RuboCop
190
195
 
191
196
  def boolean_return?(value)
192
197
  return true if value.boolean_type?
198
+
199
+ method_returning_boolean?(value)
200
+ end
201
+
202
+ def method_returning_boolean?(value)
193
203
  return false unless value.call_type?
204
+ return false if wayward_predicate?(value.method_name)
194
205
 
195
206
  value.comparison_method? || value.predicate_method? || value.negation_method?
196
207
  end
@@ -258,7 +269,10 @@ module RuboCop
258
269
  node.body ? [last_value(node.body)] : [s(:nil)]
259
270
  else
260
271
  # Branches with no value act as an implicit `nil`.
261
- node.branches.filter_map { |branch| branch ? last_value(branch) : s(:nil) }
272
+ branches = node.branches.map { |branch| branch ? last_value(branch) : s(:nil) }
273
+ # Missing else branches also act as an implicit `nil`.
274
+ branches.push(s(:nil)) unless node.else_branch
275
+ branches
262
276
  end
263
277
  end
264
278
 
@@ -275,6 +289,17 @@ module RuboCop
275
289
  def allow_bang_methods?
276
290
  cop_config.fetch('AllowBangMethods', false)
277
291
  end
292
+
293
+ # If a method ending in `?` is known to not return a boolean value,
294
+ # (for example, `Numeric#nonzero?`) it should be treated as a non-boolean
295
+ # value, despite the method naming.
296
+ def wayward_predicate?(name)
297
+ wayward_predicates.include?(name.to_s)
298
+ end
299
+
300
+ def wayward_predicates
301
+ Array(cop_config.fetch('WaywardPredicates', []))
302
+ end
278
303
  end
279
304
  end
280
305
  end
@@ -11,13 +11,14 @@ module RuboCop
11
11
  #
12
12
  # eval(something)
13
13
  # binding.eval(something)
14
+ # Kernel.eval(something)
14
15
  class Eval < Base
15
16
  MSG = 'The use of `eval` is a serious security risk.'
16
17
  RESTRICT_ON_SEND = %i[eval].freeze
17
18
 
18
19
  # @!method eval?(node)
19
20
  def_node_matcher :eval?, <<~PATTERN
20
- (send {nil? (send nil? :binding)} :eval $!str ...)
21
+ (send {nil? (send nil? :binding) (const {cbase nil?} :Kernel)} :eval $!str ...)
21
22
  PATTERN
22
23
 
23
24
  def on_send(node)
@@ -34,6 +34,7 @@ module RuboCop
34
34
  # # good (literal strings)
35
35
  # open("foo.text")
36
36
  # URI.open("http://example.com")
37
+ # URI.parse(url).open
37
38
  class Open < Base
38
39
  MSG = 'The use of `%<receiver>sopen` is a serious security risk.'
39
40
  RESTRICT_ON_SEND = %i[open].freeze
@@ -348,7 +348,7 @@ module RuboCop
348
348
  end
349
349
 
350
350
  def remove_modifier_node_within_begin(corrector, modifier_node, begin_node)
351
- def_node = begin_node.children[1]
351
+ def_node = begin_node.children[begin_node.children.index(modifier_node) + 1]
352
352
  range = modifier_node.source_range.begin.join(def_node.source_range.begin)
353
353
  corrector.remove(range)
354
354
  end
@@ -84,7 +84,10 @@ module RuboCop
84
84
 
85
85
  def autocorrect(corrector, node)
86
86
  if (preferred_accessors = preferred_accessors(node))
87
- corrector.replace(node, preferred_accessors)
87
+ corrector.replace(
88
+ grouped_style? ? node : range_with_trailing_argument_comment(node),
89
+ preferred_accessors
90
+ )
88
91
  else
89
92
  range = range_with_surrounding_space(node.source_range, side: :left)
90
93
  corrector.remove(range)
@@ -196,6 +199,15 @@ module RuboCop
196
199
  end
197
200
  end.join("\n")
198
201
  end
202
+
203
+ def range_with_trailing_argument_comment(node)
204
+ comment = processed_source.ast_with_comments[node.last_argument].last
205
+ if comment
206
+ add_range(node.source_range, comment.source_range)
207
+ else
208
+ node
209
+ end
210
+ end
199
211
  end
200
212
  end
201
213
  end