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.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/config/default.yml +23 -1
- data/lib/rubocop/cli.rb +12 -1
- data/lib/rubocop/config_loader.rb +1 -38
- data/lib/rubocop/cop/correctors/parentheses_corrector.rb +5 -2
- data/lib/rubocop/cop/gemspec/attribute_assignment.rb +91 -0
- data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +0 -22
- data/lib/rubocop/cop/gemspec/require_mfa.rb +15 -1
- data/lib/rubocop/cop/internal_affairs/example_description.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +4 -4
- data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/line_length.rb +26 -5
- data/lib/rubocop/cop/layout/space_before_brackets.rb +2 -9
- data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +7 -2
- data/lib/rubocop/cop/lint/duplicate_methods.rb +25 -4
- data/lib/rubocop/cop/lint/float_comparison.rb +4 -4
- data/lib/rubocop/cop/lint/literal_as_condition.rb +5 -3
- data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +1 -1
- data/lib/rubocop/cop/lint/redundant_type_conversion.rb +4 -4
- data/lib/rubocop/cop/lint/self_assignment.rb +25 -0
- data/lib/rubocop/cop/lint/useless_access_modifier.rb +8 -0
- data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +3 -3
- data/lib/rubocop/cop/mixin/alignment.rb +1 -1
- data/lib/rubocop/cop/mixin/gemspec_help.rb +22 -0
- data/lib/rubocop/cop/mixin/line_length_help.rb +24 -8
- data/lib/rubocop/cop/naming/file_name.rb +2 -2
- data/lib/rubocop/cop/naming/method_name.rb +87 -12
- data/lib/rubocop/cop/naming/predicate_method.rb +64 -6
- data/lib/rubocop/cop/naming/predicate_prefix.rb +2 -2
- data/lib/rubocop/cop/security/eval.rb +2 -1
- data/lib/rubocop/cop/security/open.rb +1 -0
- data/lib/rubocop/cop/style/case_like_if.rb +1 -1
- data/lib/rubocop/cop/style/collection_querying.rb +167 -0
- data/lib/rubocop/cop/style/exponential_notation.rb +2 -2
- data/lib/rubocop/cop/style/fetch_env_var.rb +32 -6
- data/lib/rubocop/cop/style/hash_conversion.rb +16 -8
- data/lib/rubocop/cop/style/if_unless_modifier.rb +11 -2
- data/lib/rubocop/cop/style/it_block_parameter.rb +1 -1
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +16 -0
- data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -9
- data/lib/rubocop/cop/style/redundant_interpolation.rb +1 -1
- data/lib/rubocop/cop/style/redundant_parentheses.rb +4 -1
- data/lib/rubocop/cop/style/redundant_self.rb +3 -0
- data/lib/rubocop/cop/style/single_line_methods.rb +4 -1
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +2 -1
- data/lib/rubocop/cop/style/symbol_proc.rb +1 -1
- data/lib/rubocop/formatter/fuubar_style_formatter.rb +1 -1
- data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
- data/lib/rubocop/lsp/diagnostic.rb +4 -4
- data/lib/rubocop/pending_cops_reporter.rb +56 -0
- data/lib/rubocop/server/cache.rb +4 -2
- data/lib/rubocop/server/client_command/base.rb +10 -0
- data/lib/rubocop/server/client_command/exec.rb +2 -1
- data/lib/rubocop/server/client_command/start.rb +11 -1
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +3 -0
- data/lib/ruby_lsp/rubocop/addon.rb +2 -2
- 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
|
29
|
-
|
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
|
37
|
-
|
38
|
-
return nil unless
|
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 =
|
41
|
-
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
|
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? ||
|
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
|
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
|
91
|
-
return if
|
157
|
+
def handle_method_name(node, name)
|
158
|
+
return if !name || matches_allowed_pattern?(name)
|
92
159
|
|
93
|
-
if forbidden_name?(
|
160
|
+
if forbidden_name?(name.to_s)
|
94
161
|
register_forbidden_name(node)
|
95
162
|
else
|
96
|
-
check_name(node,
|
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
|
-
|
112
|
-
|
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
|
-
|
126
|
-
|
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
|
-
|
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.
|
14
|
-
#
|
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) ||
|
165
|
+
value.type?(:super, :zsuper) || unknown_method_call?(value)
|
130
166
|
end
|
131
167
|
end
|
132
168
|
|
133
|
-
def
|
134
|
-
|
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
|
-
|
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? #
|
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
|
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)
|