rubocop 1.75.1 → 1.77.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 +20 -14
- data/config/default.yml +103 -25
- data/config/obsoletion.yml +6 -3
- data/lib/rubocop/config_validator.rb +6 -6
- data/lib/rubocop/cop/autocorrect_logic.rb +18 -10
- data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -1
- 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 +37 -15
- data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -1
- 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/internal_affairs/node_pattern_groups.rb +2 -0
- data/lib/rubocop/cop/internal_affairs/undefined_config.rb +6 -1
- data/lib/rubocop/cop/layout/block_alignment.rb +1 -2
- data/lib/rubocop/cop/layout/class_structure.rb +35 -0
- data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -1
- data/lib/rubocop/cop/layout/empty_line_between_defs.rb +2 -2
- data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +7 -3
- data/lib/rubocop/cop/layout/first_argument_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/hash_alignment.rb +2 -2
- data/lib/rubocop/cop/layout/leading_comment_space.rb +13 -1
- data/lib/rubocop/cop/layout/line_length.rb +26 -5
- data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +2 -4
- data/lib/rubocop/cop/layout/space_after_semicolon.rb +10 -0
- data/lib/rubocop/cop/layout/space_before_brackets.rb +5 -38
- data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +12 -3
- data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +3 -0
- data/lib/rubocop/cop/lint/ambiguous_range.rb +5 -0
- data/lib/rubocop/cop/lint/array_literal_in_regexp.rb +2 -3
- data/lib/rubocop/cop/lint/boolean_symbol.rb +1 -1
- data/lib/rubocop/cop/lint/circular_argument_reference.rb +2 -5
- data/lib/rubocop/cop/lint/deprecated_class_methods.rb +1 -1
- data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +1 -1
- data/lib/rubocop/cop/lint/duplicate_methods.rb +86 -5
- data/lib/rubocop/cop/lint/empty_interpolation.rb +3 -1
- data/lib/rubocop/cop/lint/float_comparison.rb +31 -4
- data/lib/rubocop/cop/lint/identity_comparison.rb +19 -15
- data/lib/rubocop/cop/lint/literal_as_condition.rb +31 -25
- data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +1 -1
- data/lib/rubocop/cop/lint/nested_method_definition.rb +1 -1
- data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +2 -2
- data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +1 -1
- data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +1 -1
- data/lib/rubocop/cop/lint/redundant_type_conversion.rb +7 -4
- data/lib/rubocop/cop/lint/return_in_void_context.rb +7 -2
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +4 -4
- data/lib/rubocop/cop/lint/self_assignment.rb +25 -0
- data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +5 -0
- data/lib/rubocop/cop/lint/suppressed_exception.rb +1 -1
- data/lib/rubocop/cop/lint/to_enum_arguments.rb +1 -1
- data/lib/rubocop/cop/lint/top_level_return_with_argument.rb +1 -1
- data/lib/rubocop/cop/lint/useless_access_modifier.rb +29 -4
- data/lib/rubocop/cop/lint/useless_assignment.rb +2 -0
- data/lib/rubocop/cop/lint/useless_default_value_argument.rb +90 -0
- data/lib/rubocop/cop/lint/useless_or.rb +98 -0
- data/lib/rubocop/cop/lint/useless_rescue.rb +1 -1
- data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +3 -3
- data/lib/rubocop/cop/lint/void.rb +2 -2
- data/lib/rubocop/cop/message_annotator.rb +7 -3
- data/lib/rubocop/cop/metrics/abc_size.rb +1 -1
- data/lib/rubocop/cop/mixin/alignment.rb +1 -1
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +2 -2
- data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +1 -1
- data/lib/rubocop/cop/mixin/def_node.rb +1 -1
- data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +1 -1
- data/lib/rubocop/cop/mixin/frozen_string_literal.rb +1 -2
- data/lib/rubocop/cop/mixin/gemspec_help.rb +22 -0
- data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +15 -14
- data/lib/rubocop/cop/mixin/line_length_help.rb +24 -8
- data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +2 -0
- data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
- data/lib/rubocop/cop/mixin/trailing_comma.rb +9 -5
- data/lib/rubocop/cop/naming/file_name.rb +2 -2
- data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
- data/lib/rubocop/cop/naming/method_name.rb +1 -1
- data/lib/rubocop/cop/naming/predicate_method.rb +281 -0
- data/lib/rubocop/cop/naming/{predicate_name.rb → predicate_prefix.rb} +4 -4
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +32 -10
- data/lib/rubocop/cop/style/arguments_forwarding.rb +8 -5
- data/lib/rubocop/cop/style/case_like_if.rb +1 -1
- data/lib/rubocop/cop/style/class_and_module_children.rb +19 -3
- data/lib/rubocop/cop/style/class_equality_comparison.rb +1 -1
- data/lib/rubocop/cop/style/collection_querying.rb +167 -0
- data/lib/rubocop/cop/style/command_literal.rb +1 -1
- data/lib/rubocop/cop/style/commented_keyword.rb +2 -2
- data/lib/rubocop/cop/style/comparable_between.rb +5 -2
- data/lib/rubocop/cop/style/conditional_assignment.rb +18 -4
- data/lib/rubocop/cop/style/data_inheritance.rb +7 -0
- data/lib/rubocop/cop/style/def_with_parentheses.rb +18 -5
- data/lib/rubocop/cop/style/double_negation.rb +1 -1
- data/lib/rubocop/cop/style/empty_literal.rb +4 -0
- data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +100 -0
- data/lib/rubocop/cop/style/eval_with_location.rb +3 -3
- data/lib/rubocop/cop/style/explicit_block_argument.rb +2 -2
- 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/frozen_string_literal_comment.rb +3 -2
- data/lib/rubocop/cop/style/global_std_stream.rb +3 -0
- data/lib/rubocop/cop/style/hash_conversion.rb +12 -3
- data/lib/rubocop/cop/style/hash_fetch_chain.rb +0 -1
- data/lib/rubocop/cop/style/hash_syntax.rb +3 -0
- data/lib/rubocop/cop/style/hash_transform_keys.rb +2 -2
- data/lib/rubocop/cop/style/hash_transform_values.rb +2 -2
- data/lib/rubocop/cop/style/identical_conditional_branches.rb +3 -3
- data/lib/rubocop/cop/style/if_unless_modifier.rb +33 -6
- data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +4 -7
- data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +1 -1
- data/lib/rubocop/cop/style/it_block_parameter.rb +33 -14
- data/lib/rubocop/cop/style/keyword_parameters_order.rb +1 -1
- data/lib/rubocop/cop/style/lambda_call.rb +7 -2
- data/lib/rubocop/cop/style/map_into_array.rb +3 -1
- data/lib/rubocop/cop/style/map_to_hash.rb +11 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +6 -3
- data/lib/rubocop/cop/style/min_max_comparison.rb +13 -5
- data/lib/rubocop/cop/style/multiline_if_modifier.rb +2 -0
- data/lib/rubocop/cop/style/percent_q_literals.rb +1 -1
- data/lib/rubocop/cop/style/redundant_array_flatten.rb +50 -0
- data/lib/rubocop/cop/style/redundant_condition.rb +13 -1
- data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +1 -1
- data/lib/rubocop/cop/style/redundant_format.rb +6 -1
- data/lib/rubocop/cop/style/redundant_interpolation.rb +1 -1
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +0 -3
- data/lib/rubocop/cop/style/redundant_parentheses.rb +41 -3
- data/lib/rubocop/cop/style/redundant_self.rb +8 -5
- data/lib/rubocop/cop/style/regexp_literal.rb +1 -1
- data/lib/rubocop/cop/style/return_nil.rb +2 -2
- data/lib/rubocop/cop/style/safe_navigation.rb +42 -14
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +6 -3
- data/lib/rubocop/cop/style/string_concatenation.rb +1 -2
- data/lib/rubocop/cop/style/struct_inheritance.rb +8 -1
- data/lib/rubocop/cop/style/super_arguments.rb +1 -2
- data/lib/rubocop/cop/style/symbol_proc.rb +1 -1
- data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +7 -1
- data/lib/rubocop/cop/style/trailing_comma_in_array_literal.rb +1 -1
- data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
- data/lib/rubocop/cop/style/trailing_comma_in_hash_literal.rb +1 -1
- data/lib/rubocop/cop/team.rb +1 -1
- data/lib/rubocop/cop/util.rb +1 -1
- data/lib/rubocop/cop/variable_force/assignment.rb +7 -3
- data/lib/rubocop/cop/variable_force/variable.rb +1 -1
- data/lib/rubocop/cops_documentation_generator.rb +6 -2
- data/lib/rubocop/formatter/disabled_config_formatter.rb +2 -1
- data/lib/rubocop/formatter/fuubar_style_formatter.rb +1 -1
- data/lib/rubocop/formatter/html_formatter.rb +1 -1
- data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
- data/lib/rubocop/formatter/pacman_formatter.rb +1 -1
- data/lib/rubocop/lsp/diagnostic.rb +4 -4
- data/lib/rubocop/magic_comment.rb +8 -0
- data/lib/rubocop/rspec/cop_helper.rb +2 -2
- data/lib/rubocop/rspec/expect_offense.rb +9 -3
- data/lib/rubocop/rspec/shared_contexts.rb +1 -2
- data/lib/rubocop/server/cache.rb +13 -10
- data/lib/rubocop/target_finder.rb +6 -2
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +8 -1
- data/lib/ruby_lsp/rubocop/addon.rb +2 -2
- metadata +14 -7
@@ -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
|
@@ -24,7 +24,7 @@ module RuboCop
|
|
24
24
|
gem_canonical_name(string_a) < gem_canonical_name(string_b)
|
25
25
|
end
|
26
26
|
|
27
|
-
def consecutive_lines(previous, current)
|
27
|
+
def consecutive_lines?(previous, current)
|
28
28
|
first_line = get_source_range(current, treat_comments_as_separators).first_line
|
29
29
|
previous.source_range.last_line == first_line - 1
|
30
30
|
end
|
@@ -107,7 +107,7 @@ module RuboCop
|
|
107
107
|
# of the argument is not considered multiline, even if the argument
|
108
108
|
# itself might span multiple lines.
|
109
109
|
def allowed_multiline_argument?(node)
|
110
|
-
elements(node).one? && !Util.begins_its_line?(node
|
110
|
+
elements(node).one? && !Util.begins_its_line?(node_end_location(node))
|
111
111
|
end
|
112
112
|
|
113
113
|
def elements(node)
|
@@ -127,18 +127,22 @@ module RuboCop
|
|
127
127
|
|
128
128
|
def no_elements_on_same_line?(node)
|
129
129
|
items = elements(node).map(&:source_range)
|
130
|
-
items << node
|
130
|
+
items << node_end_location(node)
|
131
131
|
items.each_cons(2).none? { |a, b| on_same_line?(a, b) }
|
132
132
|
end
|
133
133
|
|
134
|
+
def node_end_location(node)
|
135
|
+
node.loc.end || node.source_range.end.adjust(begin_pos: -1)
|
136
|
+
end
|
137
|
+
|
134
138
|
def on_same_line?(range1, range2)
|
135
139
|
range1.last_line == range2.line
|
136
140
|
end
|
137
141
|
|
138
142
|
def last_item_precedes_newline?(node)
|
139
|
-
after_last_item =
|
140
|
-
|
141
|
-
after_last_item.source
|
143
|
+
after_last_item = node.children.last.source_range.end.join(node.loc.end.begin)
|
144
|
+
|
145
|
+
after_last_item.source.start_with?(/,?\s*(#.*)?\n/)
|
142
146
|
end
|
143
147
|
|
144
148
|
def avoid_comma(kind, comma_begin_pos, extra_info)
|
@@ -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)
|
@@ -242,7 +242,7 @@ module RuboCop
|
|
242
242
|
def find_definition(node)
|
243
243
|
# Methods can be defined in a `def` or `defs`,
|
244
244
|
# or dynamically via a `block` node.
|
245
|
-
node.each_ancestor(:
|
245
|
+
node.each_ancestor(:any_def, :block).each do |ancestor|
|
246
246
|
method_node, method_name = method_definition?(ancestor)
|
247
247
|
return [method_node, method_name] if method_node
|
248
248
|
end
|
@@ -0,0 +1,281 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Naming
|
6
|
+
# Checks that predicate methods end with `?` and non-predicate methods do not.
|
7
|
+
#
|
8
|
+
# The names of predicate methods (methods that return a boolean value) should end
|
9
|
+
# in a question mark. Methods that don't return a boolean, shouldn't
|
10
|
+
# end in a question mark.
|
11
|
+
#
|
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. 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.).
|
16
|
+
#
|
17
|
+
# NOTE: Operator methods (`def ==`, etc.) are ignored.
|
18
|
+
#
|
19
|
+
# By default, the cop runs in `conservative` mode, which allows a method to be named
|
20
|
+
# with a question mark as long as at least one return value is boolean. In `aggressive`
|
21
|
+
# mode, methods with a question mark will register an offense if any known non-boolean
|
22
|
+
# return values are detected.
|
23
|
+
#
|
24
|
+
# The cop also has `AllowedMethods` configuration in order to prevent the cop from
|
25
|
+
# registering an offense from a method name that does not confirm to the naming
|
26
|
+
# guidelines. By default, `call` is allowed. The cop also has `AllowedPatterns`
|
27
|
+
# configuration to allow method names by regular expression.
|
28
|
+
#
|
29
|
+
# The cop can furthermore be configured to allow all bang methods (method names
|
30
|
+
# ending with `!`), with `AllowBangMethods: true` (default false).
|
31
|
+
#
|
32
|
+
# @example Mode: conservative (default)
|
33
|
+
# # bad
|
34
|
+
# def foo
|
35
|
+
# bar == baz
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# # good
|
39
|
+
# def foo?
|
40
|
+
# bar == baz
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# # bad
|
44
|
+
# def foo?
|
45
|
+
# 5
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# # good
|
49
|
+
# def foo
|
50
|
+
# 5
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# # bad
|
54
|
+
# def foo
|
55
|
+
# x == y
|
56
|
+
# end
|
57
|
+
#
|
58
|
+
# # good
|
59
|
+
# def foo?
|
60
|
+
# x == y
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
# # bad
|
64
|
+
# def foo
|
65
|
+
# !x
|
66
|
+
# end
|
67
|
+
#
|
68
|
+
# # good
|
69
|
+
# def foo?
|
70
|
+
# !x
|
71
|
+
# end
|
72
|
+
#
|
73
|
+
# # bad - returns the value of another predicate method
|
74
|
+
# def foo
|
75
|
+
# bar?
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
# # good
|
79
|
+
# def foo?
|
80
|
+
# bar?
|
81
|
+
# end
|
82
|
+
#
|
83
|
+
# # good - operator method
|
84
|
+
# def ==(other)
|
85
|
+
# hash == other.hash
|
86
|
+
# end
|
87
|
+
#
|
88
|
+
# # good - at least one return value is boolean
|
89
|
+
# def foo?
|
90
|
+
# return unless bar?
|
91
|
+
# true
|
92
|
+
# end
|
93
|
+
#
|
94
|
+
# # ok - return type is not known
|
95
|
+
# def foo?
|
96
|
+
# bar
|
97
|
+
# end
|
98
|
+
#
|
99
|
+
# # ok - return type is not known
|
100
|
+
# def foo
|
101
|
+
# bar?
|
102
|
+
# end
|
103
|
+
#
|
104
|
+
# @example Mode: aggressive
|
105
|
+
# # bad - the method returns nil in some cases
|
106
|
+
# def foo?
|
107
|
+
# return unless bar?
|
108
|
+
# true
|
109
|
+
# end
|
110
|
+
#
|
111
|
+
# @example AllowBangMethods: false (default)
|
112
|
+
# # bad
|
113
|
+
# def save!
|
114
|
+
# true
|
115
|
+
# end
|
116
|
+
#
|
117
|
+
# @example AllowBangMethods: true
|
118
|
+
# # good
|
119
|
+
# def save!
|
120
|
+
# true
|
121
|
+
# end
|
122
|
+
#
|
123
|
+
class PredicateMethod < Base
|
124
|
+
include AllowedMethods
|
125
|
+
include AllowedPattern
|
126
|
+
|
127
|
+
MSG_PREDICATE = 'Predicate method names should end with `?`.'
|
128
|
+
MSG_NON_PREDICATE = 'Non-predicate method names should not end with `?`.'
|
129
|
+
|
130
|
+
def on_def(node)
|
131
|
+
return if allowed?(node)
|
132
|
+
|
133
|
+
return_values = return_values(node.body)
|
134
|
+
return if acceptable?(return_values)
|
135
|
+
|
136
|
+
if node.predicate_method? && potential_non_predicate?(return_values)
|
137
|
+
add_offense(node.loc.name, message: MSG_NON_PREDICATE)
|
138
|
+
elsif !node.predicate_method? && all_return_values_boolean?(return_values)
|
139
|
+
add_offense(node.loc.name, message: MSG_PREDICATE)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
alias on_defs on_def
|
143
|
+
|
144
|
+
private
|
145
|
+
|
146
|
+
def allowed?(node)
|
147
|
+
allowed_method?(node.method_name) ||
|
148
|
+
matches_allowed_pattern?(node.method_name) ||
|
149
|
+
allowed_bang_method?(node) ||
|
150
|
+
node.operator_method? ||
|
151
|
+
node.body.nil?
|
152
|
+
end
|
153
|
+
|
154
|
+
def acceptable?(return_values)
|
155
|
+
# In `conservative` mode, if the method returns `super`, `zsuper`, or a
|
156
|
+
# non-comparison method call, the method name is acceptable.
|
157
|
+
return false unless conservative?
|
158
|
+
|
159
|
+
return_values.any? do |value|
|
160
|
+
value.type?(:super, :zsuper) || unknown_method_call?(value)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def unknown_method_call?(value)
|
165
|
+
return false unless value.call_type?
|
166
|
+
|
167
|
+
!value.comparison_method? && !value.predicate_method? && !value.negation_method?
|
168
|
+
end
|
169
|
+
|
170
|
+
def return_values(node)
|
171
|
+
# Collect all the (implicit and explicit) return values of a node
|
172
|
+
return_values = Set.new(node.begin_type? ? [] : [extract_return_value(node)])
|
173
|
+
|
174
|
+
node.each_descendant(:return) do |return_node|
|
175
|
+
return_values << extract_return_value(return_node)
|
176
|
+
end
|
177
|
+
|
178
|
+
last_value = last_value(node)
|
179
|
+
return_values << last_value if last_value
|
180
|
+
|
181
|
+
process_return_values(return_values)
|
182
|
+
end
|
183
|
+
|
184
|
+
def all_return_values_boolean?(return_values)
|
185
|
+
values = return_values.reject { |value| value.type?(:super, :zsuper) }
|
186
|
+
return false if values.empty?
|
187
|
+
|
188
|
+
values.all? { |value| boolean_return?(value) }
|
189
|
+
end
|
190
|
+
|
191
|
+
def boolean_return?(value)
|
192
|
+
return true if value.boolean_type?
|
193
|
+
return false unless value.call_type?
|
194
|
+
|
195
|
+
value.comparison_method? || value.predicate_method? || value.negation_method?
|
196
|
+
end
|
197
|
+
|
198
|
+
def potential_non_predicate?(return_values)
|
199
|
+
# Assumes a method to be non-predicate if all return values are non-boolean literals.
|
200
|
+
#
|
201
|
+
# In `Mode: conservative`, if any of the return values is a boolean,
|
202
|
+
# the method name is acceptable.
|
203
|
+
# In `Mode: aggressive`, all return values must be booleans for a predicate
|
204
|
+
# method, or else an offense will be registered.
|
205
|
+
return false if conservative? && return_values.any? { |value| boolean_return?(value) }
|
206
|
+
|
207
|
+
return_values.any? do |value|
|
208
|
+
value.literal? && !value.boolean_type?
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def extract_return_value(node)
|
213
|
+
return node unless node.return_type?
|
214
|
+
|
215
|
+
# `return` without a value is a `nil` return.
|
216
|
+
return s(:nil) if node.arguments.empty?
|
217
|
+
|
218
|
+
# When there's a multiple return, it cannot be a predicate
|
219
|
+
# so just return an `array` sexp for simplicity.
|
220
|
+
return s(:array) unless node.arguments.one?
|
221
|
+
|
222
|
+
node.first_argument
|
223
|
+
end
|
224
|
+
|
225
|
+
def last_value(node)
|
226
|
+
value = node.begin_type? ? node.children.last : node
|
227
|
+
value&.return_type? ? extract_return_value(value) : value
|
228
|
+
end
|
229
|
+
|
230
|
+
def process_return_values(return_values)
|
231
|
+
return_values.flat_map do |value|
|
232
|
+
if value.conditional?
|
233
|
+
process_return_values(extract_conditional_branches(value))
|
234
|
+
elsif and_or?(value)
|
235
|
+
process_return_values(extract_and_or_clauses(value))
|
236
|
+
else
|
237
|
+
value
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
def and_or?(node)
|
243
|
+
node.type?(:and, :or)
|
244
|
+
end
|
245
|
+
|
246
|
+
def extract_and_or_clauses(node)
|
247
|
+
# Recursively traverse an `and` or `or` node to collect all clauses within
|
248
|
+
return node unless and_or?(node)
|
249
|
+
|
250
|
+
[extract_and_or_clauses(node.lhs), extract_and_or_clauses(node.rhs)].flatten
|
251
|
+
end
|
252
|
+
|
253
|
+
def extract_conditional_branches(node)
|
254
|
+
return node unless node.conditional?
|
255
|
+
|
256
|
+
if node.type?(:while, :until)
|
257
|
+
# If there is no body, act as implicit `nil`.
|
258
|
+
node.body ? [last_value(node.body)] : [s(:nil)]
|
259
|
+
else
|
260
|
+
# Branches with no value act as an implicit `nil`.
|
261
|
+
node.branches.filter_map { |branch| branch ? last_value(branch) : s(:nil) }
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
def conservative?
|
266
|
+
cop_config.fetch('Mode', :conservative).to_sym == :conservative
|
267
|
+
end
|
268
|
+
|
269
|
+
def allowed_bang_method?(node)
|
270
|
+
return false unless allow_bang_methods?
|
271
|
+
|
272
|
+
node.bang_method?
|
273
|
+
end
|
274
|
+
|
275
|
+
def allow_bang_methods?
|
276
|
+
cop_config.fetch('AllowBangMethods', false)
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
@@ -100,12 +100,12 @@ module RuboCop
|
|
100
100
|
# # good
|
101
101
|
# def_node_matcher(:even?) { |value| }
|
102
102
|
#
|
103
|
-
class
|
103
|
+
class PredicatePrefix < Base
|
104
104
|
include AllowedMethods
|
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
|
@@ -143,7 +143,7 @@ module RuboCop
|
|
143
143
|
next if predicate_prefixes.include?(forbidden_prefix)
|
144
144
|
|
145
145
|
raise ValidationError, <<~MSG.chomp
|
146
|
-
The `Naming/
|
146
|
+
The `Naming/PredicatePrefix` cop is misconfigured. Prefix #{forbidden_prefix} must be included in NamePrefix because it is included in ForbiddenPrefixes.
|
147
147
|
MSG
|
148
148
|
end
|
149
149
|
end
|
@@ -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
|
@@ -195,15 +195,27 @@ module RuboCop
|
|
195
195
|
def autocorrect(corrector, node)
|
196
196
|
case style
|
197
197
|
when :group
|
198
|
-
|
199
|
-
return unless def_nodes.any?
|
200
|
-
|
201
|
-
replace_defs(corrector, node, def_nodes)
|
198
|
+
autocorrect_group_style(corrector, node)
|
202
199
|
when :inline
|
200
|
+
autocorrect_inline_style(corrector, node)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def autocorrect_group_style(corrector, node)
|
205
|
+
def_nodes = find_corresponding_def_nodes(node)
|
206
|
+
return unless def_nodes.any?
|
207
|
+
|
208
|
+
replace_defs(corrector, node, def_nodes)
|
209
|
+
end
|
210
|
+
|
211
|
+
def autocorrect_inline_style(corrector, node)
|
212
|
+
if node.parent&.begin_type?
|
213
|
+
remove_modifier_node_within_begin(corrector, node, node.parent)
|
214
|
+
else
|
203
215
|
remove_nodes(corrector, node)
|
204
|
-
|
205
|
-
|
206
|
-
|
216
|
+
end
|
217
|
+
select_grouped_def_nodes(node).each do |grouped_def_node|
|
218
|
+
insert_inline_modifier(corrector, grouped_def_node, node.method_name)
|
207
219
|
end
|
208
220
|
end
|
209
221
|
|
@@ -224,9 +236,13 @@ module RuboCop
|
|
224
236
|
end
|
225
237
|
|
226
238
|
def offense?(node)
|
227
|
-
|
228
|
-
|
229
|
-
|
239
|
+
if group_style?
|
240
|
+
return false if node.parent ? node.parent.if_type? : access_modifier_with_symbol?(node)
|
241
|
+
|
242
|
+
access_modifier_is_inlined?(node) && !right_siblings_same_inline_method?(node)
|
243
|
+
else
|
244
|
+
access_modifier_is_not_inlined?(node) && select_grouped_def_nodes(node).any?
|
245
|
+
end
|
230
246
|
end
|
231
247
|
|
232
248
|
def correctable_group_offense?(node)
|
@@ -331,6 +347,12 @@ module RuboCop
|
|
331
347
|
end
|
332
348
|
end
|
333
349
|
|
350
|
+
def remove_modifier_node_within_begin(corrector, modifier_node, begin_node)
|
351
|
+
def_node = begin_node.children[1]
|
352
|
+
range = modifier_node.source_range.begin.join(def_node.source_range.begin)
|
353
|
+
corrector.remove(range)
|
354
|
+
end
|
355
|
+
|
334
356
|
def def_source(node, def_nodes)
|
335
357
|
[
|
336
358
|
*processed_source.ast_with_comments[node].map(&:text),
|
@@ -16,16 +16,16 @@ module RuboCop
|
|
16
16
|
#
|
17
17
|
# In Ruby 3.2, anonymous args/kwargs forwarding has been added.
|
18
18
|
#
|
19
|
-
# This cop also identifies places where
|
20
|
-
# replaced by
|
21
|
-
# by setting `UseAnonymousForwarding: false`.
|
19
|
+
# This cop also identifies places where `+use_args(*args)+`/`+use_kwargs(**kwargs)+` can be
|
20
|
+
# replaced by `+use_args(*)+`/`+use_kwargs(**)+`; if desired, this functionality can be
|
21
|
+
# disabled by setting `UseAnonymousForwarding: false`.
|
22
22
|
#
|
23
23
|
# And this cop has `RedundantRestArgumentNames`, `RedundantKeywordRestArgumentNames`,
|
24
24
|
# and `RedundantBlockArgumentNames` options. This configuration is a list of redundant names
|
25
25
|
# that are sufficient for anonymizing meaningless naming.
|
26
26
|
#
|
27
27
|
# Meaningless names that are commonly used can be anonymized by default:
|
28
|
-
# e.g.,
|
28
|
+
# e.g., `+*args+`, `+**options+`, `&block`, and so on.
|
29
29
|
#
|
30
30
|
# Names not on this list are likely to be meaningful and are allowed by default.
|
31
31
|
#
|
@@ -146,7 +146,7 @@ module RuboCop
|
|
146
146
|
minimum_target_ruby_version 2.7
|
147
147
|
|
148
148
|
FORWARDING_LVAR_TYPES = %i[splat kwsplat block_pass].freeze
|
149
|
-
ADDITIONAL_ARG_TYPES = %i[lvar arg].freeze
|
149
|
+
ADDITIONAL_ARG_TYPES = %i[lvar arg optarg].freeze
|
150
150
|
|
151
151
|
FORWARDING_MSG = 'Use shorthand syntax `...` for arguments forwarding.'
|
152
152
|
ARGS_MSG = 'Use anonymous positional arguments forwarding (`*`).'
|
@@ -479,6 +479,9 @@ module RuboCop
|
|
479
479
|
end
|
480
480
|
|
481
481
|
def ruby_32_only_anonymous_forwarding?
|
482
|
+
# A block argument and an anonymous block argument are never passed together.
|
483
|
+
return false if @send_node.each_ancestor(:any_block).any?
|
484
|
+
|
482
485
|
def_all_anonymous_args?(@def_node) && send_all_anonymous_args?(@send_node)
|
483
486
|
end
|
484
487
|
|
@@ -131,21 +131,27 @@ module RuboCop
|
|
131
131
|
"#{node.body.children.first.const_name}"
|
132
132
|
end
|
133
133
|
|
134
|
+
# rubocop:disable Metrics/AbcSize
|
134
135
|
def remove_end(corrector, body)
|
135
|
-
remove_begin_pos = body.loc.
|
136
|
+
remove_begin_pos = if same_line?(body.loc.name, body.loc.end)
|
137
|
+
body.loc.name.end_pos
|
138
|
+
else
|
139
|
+
body.loc.end.begin_pos - leading_spaces(body).size
|
140
|
+
end
|
136
141
|
adjustment = processed_source.raw_source[remove_begin_pos] == ';' ? 0 : 1
|
137
142
|
range = range_between(remove_begin_pos, body.loc.end.end_pos + adjustment)
|
138
143
|
|
139
144
|
corrector.remove(range)
|
140
145
|
end
|
146
|
+
# rubocop:enable Metrics/AbcSize
|
141
147
|
|
142
148
|
def unindent(corrector, node)
|
143
149
|
return unless node.body.children.last
|
144
150
|
|
145
151
|
last_child_leading_spaces = leading_spaces(node.body.children.last)
|
146
|
-
return if leading_spaces(node)
|
152
|
+
return if spaces_size(leading_spaces(node)) == spaces_size(last_child_leading_spaces)
|
147
153
|
|
148
|
-
column_delta = configured_indentation_width - last_child_leading_spaces
|
154
|
+
column_delta = configured_indentation_width - spaces_size(last_child_leading_spaces)
|
149
155
|
return if column_delta.zero?
|
150
156
|
|
151
157
|
AlignmentCorrector.correct(corrector, processed_source, node, column_delta)
|
@@ -155,6 +161,16 @@ module RuboCop
|
|
155
161
|
node.source_range.source_line[/\A\s*/]
|
156
162
|
end
|
157
163
|
|
164
|
+
def spaces_size(spaces_string)
|
165
|
+
mapping = { "\t" => tab_indentation_width }
|
166
|
+
spaces_string.chars.sum { |character| mapping.fetch(character, 1) }
|
167
|
+
end
|
168
|
+
|
169
|
+
def tab_indentation_width
|
170
|
+
config.for_cop('Layout/IndentationStyle')['IndentationWidth'] ||
|
171
|
+
configured_indentation_width
|
172
|
+
end
|
173
|
+
|
158
174
|
def check_style(node, body, style)
|
159
175
|
return if node.identifier.namespace&.cbase_type?
|
160
176
|
|
@@ -68,7 +68,7 @@ module RuboCop
|
|
68
68
|
PATTERN
|
69
69
|
|
70
70
|
def on_send(node)
|
71
|
-
def_node = node.each_ancestor(:
|
71
|
+
def_node = node.each_ancestor(:any_def).first
|
72
72
|
return if def_node &&
|
73
73
|
(allowed_method?(def_node.method_name) ||
|
74
74
|
matches_allowed_pattern?(def_node.method_name))
|