rubocop 1.76.2 → 1.78.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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +23 -1
  4. data/lib/rubocop/cli.rb +12 -1
  5. data/lib/rubocop/config_loader.rb +1 -38
  6. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +5 -2
  7. data/lib/rubocop/cop/gemspec/attribute_assignment.rb +91 -0
  8. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +0 -22
  9. data/lib/rubocop/cop/gemspec/require_mfa.rb +15 -1
  10. data/lib/rubocop/cop/internal_affairs/example_description.rb +1 -1
  11. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +4 -4
  12. data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +1 -1
  13. data/lib/rubocop/cop/layout/line_length.rb +26 -5
  14. data/lib/rubocop/cop/layout/space_before_brackets.rb +2 -9
  15. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +7 -2
  16. data/lib/rubocop/cop/lint/duplicate_methods.rb +25 -4
  17. data/lib/rubocop/cop/lint/float_comparison.rb +4 -4
  18. data/lib/rubocop/cop/lint/literal_as_condition.rb +5 -3
  19. data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +1 -1
  20. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +4 -4
  21. data/lib/rubocop/cop/lint/self_assignment.rb +25 -0
  22. data/lib/rubocop/cop/lint/useless_access_modifier.rb +8 -0
  23. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +3 -3
  24. data/lib/rubocop/cop/mixin/alignment.rb +1 -1
  25. data/lib/rubocop/cop/mixin/gemspec_help.rb +22 -0
  26. data/lib/rubocop/cop/mixin/line_length_help.rb +24 -8
  27. data/lib/rubocop/cop/naming/file_name.rb +2 -2
  28. data/lib/rubocop/cop/naming/method_name.rb +87 -12
  29. data/lib/rubocop/cop/naming/predicate_method.rb +64 -6
  30. data/lib/rubocop/cop/naming/predicate_prefix.rb +2 -2
  31. data/lib/rubocop/cop/security/eval.rb +2 -1
  32. data/lib/rubocop/cop/security/open.rb +1 -0
  33. data/lib/rubocop/cop/style/case_like_if.rb +1 -1
  34. data/lib/rubocop/cop/style/collection_querying.rb +167 -0
  35. data/lib/rubocop/cop/style/exponential_notation.rb +2 -2
  36. data/lib/rubocop/cop/style/fetch_env_var.rb +32 -6
  37. data/lib/rubocop/cop/style/hash_conversion.rb +16 -8
  38. data/lib/rubocop/cop/style/if_unless_modifier.rb +11 -2
  39. data/lib/rubocop/cop/style/it_block_parameter.rb +1 -1
  40. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +1 -1
  41. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +16 -0
  42. data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -9
  43. data/lib/rubocop/cop/style/redundant_interpolation.rb +1 -1
  44. data/lib/rubocop/cop/style/redundant_parentheses.rb +4 -1
  45. data/lib/rubocop/cop/style/redundant_self.rb +3 -0
  46. data/lib/rubocop/cop/style/single_line_methods.rb +4 -1
  47. data/lib/rubocop/cop/style/sole_nested_conditional.rb +2 -1
  48. data/lib/rubocop/cop/style/symbol_proc.rb +1 -1
  49. data/lib/rubocop/formatter/fuubar_style_formatter.rb +1 -1
  50. data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
  51. data/lib/rubocop/lsp/diagnostic.rb +4 -4
  52. data/lib/rubocop/pending_cops_reporter.rb +56 -0
  53. data/lib/rubocop/server/cache.rb +4 -2
  54. data/lib/rubocop/server/client_command/base.rb +10 -0
  55. data/lib/rubocop/server/client_command/exec.rb +2 -1
  56. data/lib/rubocop/server/client_command/start.rb +11 -1
  57. data/lib/rubocop/version.rb +1 -1
  58. data/lib/rubocop.rb +3 -0
  59. data/lib/ruby_lsp/rubocop/addon.rb +2 -2
  60. metadata +7 -4
@@ -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
@@ -23,6 +23,14 @@ module RuboCop
23
23
  # # good (method calls possibly can return different results)
24
24
  # hash[foo] = hash[foo]
25
25
  #
26
+ # @example AllowRBSInlineAnnotation:true
27
+ # # good
28
+ # foo = foo #: Integer
29
+ # foo, bar = foo, bar #: Integer
30
+ # Foo = Foo #: Integer
31
+ # hash['foo'] = hash['foo'] #: Integer
32
+ # obj.attr = obj.attr #: Integer
33
+ #
26
34
  class SelfAssignment < Base
27
35
  MSG = 'Self-assignment detected.'
28
36
 
@@ -34,6 +42,8 @@ module RuboCop
34
42
  }.freeze
35
43
 
36
44
  def on_send(node)
45
+ return if allow_rbs_inline_annotation? && rbs_inline_annotation?(node.receiver)
46
+
37
47
  if node.method?(:[]=)
38
48
  handle_key_assignment(node) if node.arguments.size == 2
39
49
  elsif node.assignment_method?
@@ -44,6 +54,7 @@ module RuboCop
44
54
 
45
55
  def on_lvasgn(node)
46
56
  return unless node.rhs
57
+ return if allow_rbs_inline_annotation? && rbs_inline_annotation?(node.rhs)
47
58
 
48
59
  rhs_type = ASSIGNMENT_TYPE_TO_RHS_TYPE[node.type]
49
60
 
@@ -55,16 +66,22 @@ module RuboCop
55
66
 
56
67
  def on_casgn(node)
57
68
  return unless node.rhs&.const_type?
69
+ return if allow_rbs_inline_annotation? && rbs_inline_annotation?(node.rhs)
58
70
 
59
71
  add_offense(node) if node.namespace == node.rhs.namespace &&
60
72
  node.short_name == node.rhs.short_name
61
73
  end
62
74
 
63
75
  def on_masgn(node)
76
+ first_lhs = node.lhs.assignments.first
77
+ return if allow_rbs_inline_annotation? && rbs_inline_annotation?(first_lhs)
78
+
64
79
  add_offense(node) if multiple_self_assignment?(node)
65
80
  end
66
81
 
67
82
  def on_or_asgn(node)
83
+ return if allow_rbs_inline_annotation? && rbs_inline_annotation?(node.lhs)
84
+
68
85
  add_offense(node) if rhs_matches_lhs?(node.rhs, node.lhs)
69
86
  end
70
87
  alias on_and_asgn on_or_asgn
@@ -108,6 +125,14 @@ module RuboCop
108
125
  add_offense(node)
109
126
  end
110
127
  end
128
+
129
+ def rbs_inline_annotation?(node)
130
+ processed_source.ast_with_comments[node].any? { |comment| comment.text.start_with?('#:') }
131
+ end
132
+
133
+ def allow_rbs_inline_annotation?
134
+ cop_config['AllowRBSInlineAnnotation']
135
+ end
111
136
  end
112
137
  end
113
138
  end
@@ -274,6 +274,10 @@ module RuboCop
274
274
 
275
275
  def any_method_definition?(child)
276
276
  cop_config.fetch('MethodCreatingMethods', []).any? do |m|
277
+ # Some users still have `"included"` in their `MethodCreatingMethods` configurations,
278
+ # so to prevent Ruby method redefinition warnings let's just skip this value.
279
+ next if m == 'included'
280
+
277
281
  matcher_name = :"#{m}_method?"
278
282
  unless respond_to?(matcher_name)
279
283
  self.class.def_node_matcher matcher_name, <<~PATTERN
@@ -296,7 +300,11 @@ module RuboCop
296
300
  end
297
301
 
298
302
  def any_context_creating_methods?(child)
303
+ # Some users still have `"included"` in their `ContextCreatingMethods` configurations,
304
+ # so to prevent Ruby method redefinition warnings let's just skip this value.
299
305
  cop_config.fetch('ContextCreatingMethods', []).any? do |m|
306
+ next if m == 'included'
307
+
300
308
  matcher_name = :"#{m}_block?"
301
309
  unless respond_to?(matcher_name)
302
310
  self.class.def_node_matcher matcher_name, <<~PATTERN
@@ -89,7 +89,7 @@ module RuboCop
89
89
  private
90
90
 
91
91
  def inspect_def(node, def_node)
92
- return if allowed_arguments(def_node.arguments)
92
+ return if allowed_arguments?(def_node.arguments)
93
93
 
94
94
  add_offense(node.loc.selector, message: format(MSG, method_name: def_node.method_name))
95
95
  end
@@ -101,7 +101,7 @@ module RuboCop
101
101
  definition = find_method_definition(node, method_name)
102
102
 
103
103
  return unless definition
104
- return if allowed_arguments(definition.arguments)
104
+ return if allowed_arguments?(definition.arguments)
105
105
 
106
106
  add_offense(node, message: format(MSG, method_name: method_name))
107
107
  end
@@ -115,7 +115,7 @@ module RuboCop
115
115
  end
116
116
 
117
117
  # `ruby2_keywords` is only allowed if there's a `restarg` and no keyword arguments
118
- def allowed_arguments(arguments)
118
+ def allowed_arguments?(arguments)
119
119
  return false if arguments.empty?
120
120
 
121
121
  arguments.each_child_node(:restarg).any? &&
@@ -66,7 +66,7 @@ module RuboCop
66
66
  end
67
67
 
68
68
  # @deprecated Use processed_source.line_with_comment?(line)
69
- def end_of_line_comment(line)
69
+ def end_of_line_comment(line) # rubocop:disable Naming/PredicateMethod
70
70
  warn Rainbow(<<~WARNING).yellow, uplevel: 1
71
71
  `end_of_line_comment` is deprecated. Use `processed_source.line_with_comment?` instead.
72
72
  WARNING
@@ -25,6 +25,28 @@ module RuboCop
25
25
  (args
26
26
  (arg $_)) ...)
27
27
  PATTERN
28
+
29
+ # @!method assignment_method_declarations(node)
30
+ def_node_search :assignment_method_declarations, <<~PATTERN
31
+ (send
32
+ (lvar {#match_block_variable_name? :_1 :it}) _ ...)
33
+ PATTERN
34
+
35
+ # @!method indexed_assignment_method_declarations(node)
36
+ def_node_search :indexed_assignment_method_declarations, <<~PATTERN
37
+ (send
38
+ (send (lvar {#match_block_variable_name? :_1 :it}) _)
39
+ :[]=
40
+ literal?
41
+ _
42
+ )
43
+ PATTERN
44
+
45
+ def match_block_variable_name?(receiver_name)
46
+ gem_specification(processed_source.ast) do |block_variable_name|
47
+ return block_variable_name == receiver_name
48
+ end
49
+ end
28
50
  end
29
51
  end
30
52
  end
@@ -25,20 +25,24 @@ module RuboCop
25
25
  config.for_cop('Layout/LineLength')['AllowURI']
26
26
  end
27
27
 
28
- def allowed_uri_position?(line, uri_range)
29
- uri_range.begin < max_line_length && uri_range.end == line_length(line)
28
+ def allow_qualified_name?
29
+ config.for_cop('Layout/LineLength')['AllowQualifiedName']
30
+ end
31
+
32
+ def allowed_position?(line, range)
33
+ range.begin < max_line_length && range.end == line_length(line)
30
34
  end
31
35
 
32
36
  def line_length(line)
33
37
  line.length + indentation_difference(line)
34
38
  end
35
39
 
36
- def find_excessive_uri_range(line)
37
- last_uri_match = match_uris(line).last
38
- return nil unless last_uri_match
40
+ def find_excessive_range(line, type)
41
+ last_match = (type == :uri ? match_uris(line) : match_qualified_names(line)).last
42
+ return nil unless last_match
39
43
 
40
- begin_position, end_position = last_uri_match.offset(0)
41
- end_position = extend_uri_end_position(line, end_position)
44
+ begin_position, end_position = last_match.offset(0)
45
+ end_position = extend_end_position(line, end_position)
42
46
 
43
47
  line_indentation_difference = indentation_difference(line)
44
48
  begin_position += line_indentation_difference
@@ -57,6 +61,14 @@ module RuboCop
57
61
  matches
58
62
  end
59
63
 
64
+ def match_qualified_names(string)
65
+ matches = []
66
+ string.scan(qualified_name_regexp) do
67
+ matches << $LAST_MATCH_INFO
68
+ end
69
+ matches
70
+ end
71
+
60
72
  def indentation_difference(line)
61
73
  return 0 unless tab_indentation_width
62
74
 
@@ -70,7 +82,7 @@ module RuboCop
70
82
  index * (tab_indentation_width - 1)
71
83
  end
72
84
 
73
- def extend_uri_end_position(line, end_position)
85
+ def extend_end_position(line, end_position)
74
86
  # Extend the end position YARD comments with linked URLs of the form {<uri> <title>}
75
87
  if line&.match(/{(\s|\S)*}$/)
76
88
  match = line[end_position..line_length(line)]&.match(/(\s|\S)*}/)
@@ -101,6 +113,10 @@ module RuboCop
101
113
  end
102
114
  end
103
115
 
116
+ def qualified_name_regexp
117
+ /\b(?:[A-Z][A-Za-z0-9_]*::)+[A-Za-z_][A-Za-z0-9_]*\b/
118
+ end
119
+
104
120
  def valid_uri?(uri_ish_string)
105
121
  URI.parse(uri_ish_string)
106
122
  true
@@ -152,7 +152,7 @@ module RuboCop
152
152
 
153
153
  const_namespace, const_name = *const
154
154
  next if name != const_name && !match_acronym?(name, const_name)
155
- next unless namespace.empty? || match_namespace(child, const_namespace, namespace)
155
+ next unless namespace.empty? || namespace_matches?(child, const_namespace, namespace)
156
156
 
157
157
  return node
158
158
  end
@@ -169,7 +169,7 @@ module RuboCop
169
169
  s(:const, namespace, name) if name
170
170
  end
171
171
 
172
- def match_namespace(node, namespace, expected)
172
+ def namespace_matches?(node, namespace, expected)
173
173
  match_partial = partial_matcher!(expected)
174
174
 
175
175
  match_partial.call(namespace)
@@ -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
@@ -72,7 +100,46 @@ module RuboCop
72
100
  # @!method str_name(node)
73
101
  def_node_matcher :str_name, '(str $_name)'
74
102
 
103
+ # @!method new_struct?(node)
104
+ def_node_matcher :new_struct?, '(send (const {nil? cbase} :Struct) :new ...)'
105
+
75
106
  def on_send(node)
107
+ if node.method?(:define_method) || node.method?(:define_singleton_method)
108
+ handle_define_method(node)
109
+ elsif new_struct?(node)
110
+ handle_new_struct(node)
111
+ else
112
+ handle_attr_accessor(node)
113
+ end
114
+ end
115
+
116
+ def on_def(node)
117
+ return if node.operator_method? || matches_allowed_pattern?(node.method_name)
118
+
119
+ if forbidden_name?(node.method_name.to_s)
120
+ register_forbidden_name(node)
121
+ else
122
+ check_name(node, node.method_name, node.loc.name)
123
+ end
124
+ end
125
+ alias on_defs on_def
126
+
127
+ private
128
+
129
+ def handle_define_method(node)
130
+ return unless node.first_argument&.type?(:str, :sym)
131
+
132
+ handle_method_name(node, node.first_argument.value)
133
+ end
134
+
135
+ def handle_new_struct(node)
136
+ arguments = node.first_argument&.str_type? ? node.arguments[1..] : node.arguments
137
+ arguments.select { |argument| argument.type?(:sym, :str) }.each do |name|
138
+ handle_method_name(name, name.value)
139
+ end
140
+ end
141
+
142
+ def handle_attr_accessor(node)
76
143
  return unless (attrs = node.attribute_accessor?)
77
144
 
78
145
  attrs.last.each do |name_item|
@@ -87,45 +154,53 @@ module RuboCop
87
154
  end
88
155
  end
89
156
 
90
- def on_def(node)
91
- return if node.operator_method? || matches_allowed_pattern?(node.method_name)
157
+ def handle_method_name(node, name)
158
+ return if !name || matches_allowed_pattern?(name)
92
159
 
93
- if forbidden_name?(node.method_name.to_s)
160
+ if forbidden_name?(name.to_s)
94
161
  register_forbidden_name(node)
95
162
  else
96
- check_name(node, node.method_name, node.loc.name)
163
+ check_name(node, name, range_position(node))
97
164
  end
98
165
  end
99
- alias on_defs on_def
100
-
101
- private
102
166
 
103
167
  def forbidden_name?(name)
104
168
  forbidden_identifier?(name) || forbidden_pattern?(name)
105
169
  end
106
170
 
171
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
107
172
  def register_forbidden_name(node)
108
173
  if node.any_def_type?
109
174
  name_node = node.loc.name
110
175
  method_name = node.method_name
111
- else
112
- attrs = node.attribute_accessor?
176
+ elsif node.literal?
177
+ name_node = node
178
+ method_name = node.value
179
+ elsif (attrs = node.attribute_accessor?)
113
180
  name_node = attrs.last.last
114
181
  method_name = attr_name(name_node)
182
+ else
183
+ name_node = node.first_argument
184
+ method_name = node.first_argument.value
115
185
  end
116
186
  message = format(MSG_FORBIDDEN, identifier: method_name)
117
187
  add_offense(name_node, message: message)
118
188
  end
189
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
119
190
 
120
191
  def attr_name(name_item)
121
192
  sym_name(name_item) || str_name(name_item)
122
193
  end
123
194
 
124
195
  def range_position(node)
125
- selector_end_pos = node.loc.selector.end_pos + 1
126
- expr_end_pos = node.source_range.end_pos
196
+ if node.loc.respond_to?(:selector)
197
+ selector_end_pos = node.loc.selector.end_pos + 1
198
+ expr_end_pos = node.source_range.end_pos
127
199
 
128
- range_between(selector_end_pos, expr_end_pos)
200
+ range_between(selector_end_pos, expr_end_pos)
201
+ else
202
+ node.source_range
203
+ end
129
204
  end
130
205
 
131
206
  def message(style)
@@ -10,8 +10,9 @@ module RuboCop
10
10
  # end in a question mark.
11
11
  #
12
12
  # The cop assesses a predicate method as one that returns boolean values. Likewise,
13
- # a method that only returns literal values is assessed as non-predicate. The cop does
14
- # not make an assessment if the return type is unknown (method calls, variables, etc.).
13
+ # a method that only returns literal values is assessed as non-predicate. Other predicate
14
+ # method calls are assumed to return boolean values. The cop does not make an assessment
15
+ # if the return type is unknown (non-predicate method calls, variables, etc.).
15
16
  #
16
17
  # NOTE: Operator methods (`def ==`, etc.) are ignored.
17
18
  #
@@ -25,6 +26,11 @@ module RuboCop
25
26
  # guidelines. By default, `call` is allowed. The cop also has `AllowedPatterns`
26
27
  # configuration to allow method names by regular expression.
27
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
+ #
28
34
  # The cop can furthermore be configured to allow all bang methods (method names
29
35
  # ending with `!`), with `AllowBangMethods: true` (default false).
30
36
  #
@@ -49,6 +55,36 @@ module RuboCop
49
55
  # 5
50
56
  # end
51
57
  #
58
+ # # bad
59
+ # def foo
60
+ # x == y
61
+ # end
62
+ #
63
+ # # good
64
+ # def foo?
65
+ # x == y
66
+ # end
67
+ #
68
+ # # bad
69
+ # def foo
70
+ # !x
71
+ # end
72
+ #
73
+ # # good
74
+ # def foo?
75
+ # !x
76
+ # end
77
+ #
78
+ # # bad - returns the value of another predicate method
79
+ # def foo
80
+ # bar?
81
+ # end
82
+ #
83
+ # # good
84
+ # def foo?
85
+ # bar?
86
+ # end
87
+ #
52
88
  # # good - operator method
53
89
  # def ==(other)
54
90
  # hash == other.hash
@@ -126,12 +162,14 @@ module RuboCop
126
162
  return false unless conservative?
127
163
 
128
164
  return_values.any? do |value|
129
- value.type?(:super, :zsuper) || non_comparison_call?(value)
165
+ value.type?(:super, :zsuper) || unknown_method_call?(value)
130
166
  end
131
167
  end
132
168
 
133
- def non_comparison_call?(value)
134
- value.call_type? && !value.comparison_method?
169
+ def unknown_method_call?(value)
170
+ return false unless value.call_type?
171
+
172
+ !method_returning_boolean?(value)
135
173
  end
136
174
 
137
175
  def return_values(node)
@@ -156,7 +194,16 @@ module RuboCop
156
194
  end
157
195
 
158
196
  def boolean_return?(value)
159
- value.boolean_type? || (value.call_type? && value.comparison_method?)
197
+ return true if value.boolean_type?
198
+
199
+ method_returning_boolean?(value)
200
+ end
201
+
202
+ def method_returning_boolean?(value)
203
+ return false unless value.call_type?
204
+ return false if wayward_predicate?(value.method_name)
205
+
206
+ value.comparison_method? || value.predicate_method? || value.negation_method?
160
207
  end
161
208
 
162
209
  def potential_non_predicate?(return_values)
@@ -239,6 +286,17 @@ module RuboCop
239
286
  def allow_bang_methods?
240
287
  cop_config.fetch('AllowBangMethods', false)
241
288
  end
289
+
290
+ # If a method ending in `?` is known to not return a boolean value,
291
+ # (for example, `Numeric#nonzero?`) it should be treated as a non-boolean
292
+ # value, despite the method naming.
293
+ def wayward_predicate?(name)
294
+ wayward_predicates.include?(name.to_s)
295
+ end
296
+
297
+ def wayward_predicates
298
+ Array(cop_config.fetch('WaywardPredicates', []))
299
+ end
242
300
  end
243
301
  end
244
302
  end
@@ -105,7 +105,7 @@ module RuboCop
105
105
 
106
106
  # @!method dynamic_method_define(node)
107
107
  def_node_matcher :dynamic_method_define, <<~PATTERN
108
- (send nil? #method_definition_macros
108
+ (send nil? #method_definition_macro?
109
109
  (sym $_)
110
110
  ...)
111
111
  PATTERN
@@ -195,7 +195,7 @@ module RuboCop
195
195
  cop_config['UseSorbetSigs']
196
196
  end
197
197
 
198
- def method_definition_macros(macro_name)
198
+ def method_definition_macro?(macro_name)
199
199
  cop_config['MethodDefinitionMacros'].include?(macro_name.to_s)
200
200
  end
201
201
  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
@@ -269,7 +269,7 @@ module RuboCop
269
269
  end
270
270
 
271
271
  def regexp_with_named_captures?(node)
272
- node.regexp_type? && node.each_capture(named: true).count.positive?
272
+ node.regexp_type? && node.each_capture(named: true).any?
273
273
  end
274
274
  end
275
275
  end