rubocop 1.84.2 → 1.86.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/config/default.yml +86 -14
- data/config/obsoletion.yml +5 -0
- data/lib/rubocop/cache_config.rb +1 -1
- data/lib/rubocop/cli/command/auto_generate_config.rb +1 -1
- data/lib/rubocop/cli/command/mcp.rb +19 -0
- data/lib/rubocop/cli/command/show_cops.rb +2 -2
- data/lib/rubocop/cli/command/show_docs_url.rb +1 -1
- data/lib/rubocop/cli.rb +6 -3
- data/lib/rubocop/config.rb +1 -1
- data/lib/rubocop/config_finder.rb +1 -1
- data/lib/rubocop/config_obsoletion/extracted_cop.rb +4 -2
- data/lib/rubocop/config_store.rb +1 -1
- data/lib/rubocop/config_validator.rb +1 -1
- data/lib/rubocop/cop/correctors/condition_corrector.rb +1 -1
- data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +2 -2
- data/lib/rubocop/cop/gemspec/require_mfa.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/itblock_handler.rb +69 -0
- data/lib/rubocop/cop/internal_affairs.rb +1 -0
- data/lib/rubocop/cop/layout/argument_alignment.rb +2 -2
- data/lib/rubocop/cop/layout/array_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/dot_position.rb +1 -1
- data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +9 -2
- data/lib/rubocop/cop/layout/empty_line_between_defs.rb +1 -1
- data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -0
- data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +12 -2
- data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +16 -2
- data/lib/rubocop/cop/layout/empty_lines_around_module_body.rb +16 -2
- data/lib/rubocop/cop/layout/end_alignment.rb +3 -2
- data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +7 -1
- data/lib/rubocop/cop/layout/hash_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
- data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +9 -2
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +28 -3
- data/lib/rubocop/cop/layout/parameter_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/redundant_line_break.rb +1 -1
- data/lib/rubocop/cop/layout/space_around_block_parameters.rb +1 -1
- data/lib/rubocop/cop/layout/space_around_keyword.rb +3 -1
- data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +1 -0
- data/lib/rubocop/cop/lint/constant_reassignment.rb +59 -9
- data/lib/rubocop/cop/lint/constant_resolution.rb +1 -1
- data/lib/rubocop/cop/lint/data_define_override.rb +63 -0
- data/lib/rubocop/cop/lint/duplicate_methods.rb +50 -8
- data/lib/rubocop/cop/lint/empty_block.rb +1 -1
- data/lib/rubocop/cop/lint/empty_conditional_body.rb +6 -1
- data/lib/rubocop/cop/lint/empty_in_pattern.rb +8 -1
- data/lib/rubocop/cop/lint/empty_when.rb +8 -1
- data/lib/rubocop/cop/lint/interpolation_check.rb +7 -2
- data/lib/rubocop/cop/lint/next_without_accumulator.rb +2 -0
- data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +3 -1
- data/lib/rubocop/cop/lint/number_conversion.rb +1 -1
- data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +0 -9
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +23 -6
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +17 -0
- data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +7 -1
- data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +1 -0
- data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +1 -0
- data/lib/rubocop/cop/lint/unreachable_pattern_branch.rb +113 -0
- data/lib/rubocop/cop/lint/useless_assignment.rb +1 -1
- data/lib/rubocop/cop/lint/useless_constant_scoping.rb +4 -4
- data/lib/rubocop/cop/lint/useless_default_value_argument.rb +2 -0
- data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +6 -5
- data/lib/rubocop/cop/lint/void.rb +32 -12
- data/lib/rubocop/cop/metrics/block_nesting.rb +23 -0
- data/lib/rubocop/cop/migration/department_name.rb +12 -1
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
- data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +2 -2
- data/lib/rubocop/cop/mixin/hash_transform_method/autocorrection.rb +63 -0
- data/lib/rubocop/cop/mixin/hash_transform_method.rb +10 -60
- data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
- data/lib/rubocop/cop/registry.rb +20 -13
- data/lib/rubocop/cop/security/eval.rb +15 -2
- data/lib/rubocop/cop/style/accessor_grouping.rb +4 -2
- data/lib/rubocop/cop/style/alias.rb +4 -1
- data/lib/rubocop/cop/style/and_or.rb +1 -0
- data/lib/rubocop/cop/style/arguments_forwarding.rb +25 -7
- data/lib/rubocop/cop/style/array_join.rb +4 -2
- data/lib/rubocop/cop/style/ascii_comments.rb +6 -3
- data/lib/rubocop/cop/style/attr.rb +5 -2
- data/lib/rubocop/cop/style/bare_percent_literals.rb +3 -1
- data/lib/rubocop/cop/style/begin_block.rb +3 -1
- data/lib/rubocop/cop/style/block_delimiters.rb +25 -33
- data/lib/rubocop/cop/style/case_equality.rb +4 -0
- data/lib/rubocop/cop/style/class_and_module_children.rb +10 -2
- data/lib/rubocop/cop/style/collection_compact.rb +36 -16
- data/lib/rubocop/cop/style/colon_method_call.rb +3 -1
- data/lib/rubocop/cop/style/concat_array_literals.rb +2 -0
- data/lib/rubocop/cop/style/conditional_assignment.rb +0 -4
- data/lib/rubocop/cop/style/copyright.rb +1 -1
- data/lib/rubocop/cop/style/each_for_simple_loop.rb +1 -1
- data/lib/rubocop/cop/style/each_with_object.rb +2 -0
- data/lib/rubocop/cop/style/empty_block_parameter.rb +1 -1
- data/lib/rubocop/cop/style/empty_class_definition.rb +43 -20
- data/lib/rubocop/cop/style/empty_lambda_parameter.rb +1 -1
- data/lib/rubocop/cop/style/encoding.rb +7 -1
- data/lib/rubocop/cop/style/end_block.rb +3 -1
- data/lib/rubocop/cop/style/endless_method.rb +8 -3
- data/lib/rubocop/cop/style/file_open.rb +84 -0
- data/lib/rubocop/cop/style/for.rb +3 -0
- data/lib/rubocop/cop/style/format_string_token.rb +29 -2
- data/lib/rubocop/cop/style/global_vars.rb +5 -2
- data/lib/rubocop/cop/style/hash_as_last_array_item.rb +21 -5
- data/lib/rubocop/cop/style/hash_lookup_method.rb +7 -0
- data/lib/rubocop/cop/style/hash_transform_keys.rb +17 -7
- data/lib/rubocop/cop/style/hash_transform_values.rb +17 -7
- data/lib/rubocop/cop/style/if_unless_modifier.rb +14 -3
- data/lib/rubocop/cop/style/if_with_semicolon.rb +7 -5
- data/lib/rubocop/cop/style/inline_comment.rb +4 -1
- data/lib/rubocop/cop/style/ip_addresses.rb +1 -2
- data/lib/rubocop/cop/style/magic_comment_format.rb +2 -2
- data/lib/rubocop/cop/style/map_join.rb +123 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +5 -3
- data/lib/rubocop/cop/style/multiline_if_then.rb +3 -1
- data/lib/rubocop/cop/style/mutable_constant.rb +1 -1
- data/lib/rubocop/cop/style/nil_comparison.rb +2 -3
- data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
- data/lib/rubocop/cop/style/non_nil_check.rb +5 -11
- data/lib/rubocop/cop/style/not.rb +2 -0
- data/lib/rubocop/cop/style/numeric_literals.rb +3 -2
- data/lib/rubocop/cop/style/one_class_per_file.rb +95 -0
- data/lib/rubocop/cop/style/one_line_conditional.rb +4 -3
- data/lib/rubocop/cop/style/parallel_assignment.rb +4 -0
- data/lib/rubocop/cop/style/partition_instead_of_double_select.rb +270 -0
- data/lib/rubocop/cop/style/percent_literal_delimiters.rb +2 -0
- data/lib/rubocop/cop/style/predicate_with_kind.rb +84 -0
- data/lib/rubocop/cop/style/proc.rb +3 -2
- data/lib/rubocop/cop/style/raise_args.rb +1 -1
- data/lib/rubocop/cop/style/reduce_to_hash.rb +184 -0
- data/lib/rubocop/cop/style/redundant_begin.rb +3 -3
- data/lib/rubocop/cop/style/redundant_each.rb +3 -3
- data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -1
- data/lib/rubocop/cop/style/redundant_interpolation_unfreeze.rb +26 -10
- data/lib/rubocop/cop/style/redundant_min_max_by.rb +93 -0
- data/lib/rubocop/cop/style/redundant_parentheses.rb +22 -22
- data/lib/rubocop/cop/style/redundant_percent_q.rb +4 -1
- data/lib/rubocop/cop/style/redundant_return.rb +3 -1
- data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +0 -5
- data/lib/rubocop/cop/style/redundant_struct_keyword_init.rb +104 -0
- data/lib/rubocop/cop/style/safe_navigation.rb +7 -7
- data/lib/rubocop/cop/style/select_by_kind.rb +158 -0
- data/lib/rubocop/cop/style/select_by_range.rb +197 -0
- data/lib/rubocop/cop/style/select_by_regexp.rb +51 -21
- data/lib/rubocop/cop/style/semicolon.rb +2 -0
- data/lib/rubocop/cop/style/single_line_block_params.rb +2 -2
- data/lib/rubocop/cop/style/single_line_do_end_block.rb +1 -1
- data/lib/rubocop/cop/style/single_line_methods.rb +3 -1
- data/lib/rubocop/cop/style/special_global_vars.rb +6 -1
- data/lib/rubocop/cop/style/symbol_proc.rb +4 -3
- data/lib/rubocop/cop/style/tally_method.rb +181 -0
- data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
- data/lib/rubocop/cop/style/trailing_method_end_statement.rb +1 -0
- data/lib/rubocop/cop/style/yoda_expression.rb +1 -1
- data/lib/rubocop/cop/variable_force/branch.rb +2 -2
- data/lib/rubocop/directive_comment.rb +2 -1
- data/lib/rubocop/formatter/disabled_config_formatter.rb +1 -1
- data/lib/rubocop/formatter/formatter_set.rb +1 -1
- data/lib/rubocop/formatter/junit_formatter.rb +1 -1
- data/lib/rubocop/formatter/simple_text_formatter.rb +0 -2
- data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -1
- data/lib/rubocop/formatter.rb +22 -21
- data/lib/rubocop/lsp/diagnostic.rb +1 -0
- data/lib/rubocop/lsp/routes.rb +10 -3
- data/lib/rubocop/mcp/server.rb +198 -0
- data/lib/rubocop/options.rb +10 -1
- data/lib/rubocop/path_util.rb +14 -2
- data/lib/rubocop/plugin/loader.rb +1 -1
- data/lib/rubocop/result_cache.rb +22 -10
- data/lib/rubocop/rspec/shared_contexts.rb +11 -2
- data/lib/rubocop/runner.rb +8 -3
- data/lib/rubocop/server/cache.rb +5 -7
- data/lib/rubocop/server/core.rb +2 -0
- data/lib/rubocop/target_finder.rb +1 -1
- data/lib/rubocop/target_ruby.rb +18 -12
- data/lib/rubocop/version.rb +2 -2
- data/lib/rubocop.rb +14 -0
- metadata +20 -3
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
module RuboCop
|
|
4
4
|
module Cop
|
|
5
5
|
module Style
|
|
6
|
-
# Checks for trailing inline comments.
|
|
6
|
+
# Checks for trailing inline comments. Inline comments can
|
|
7
|
+
# make lines harder to read, especially when they are long.
|
|
8
|
+
# Placing comments on their own line above the code they
|
|
9
|
+
# describe is often clearer.
|
|
7
10
|
#
|
|
8
11
|
# @example
|
|
9
12
|
#
|
|
@@ -48,8 +48,7 @@ module RuboCop
|
|
|
48
48
|
private
|
|
49
49
|
|
|
50
50
|
def allowed_addresses
|
|
51
|
-
allowed_addresses
|
|
52
|
-
Array(allowed_addresses).map(&:downcase)
|
|
51
|
+
@allowed_addresses ||= Array(cop_config['AllowedAddresses']).map(&:downcase).freeze
|
|
53
52
|
end
|
|
54
53
|
|
|
55
54
|
def potential_ip?(str)
|
|
@@ -176,7 +176,7 @@ module RuboCop
|
|
|
176
176
|
if first_non_comment_token
|
|
177
177
|
0...first_non_comment_token.line
|
|
178
178
|
else
|
|
179
|
-
|
|
179
|
+
0..
|
|
180
180
|
end
|
|
181
181
|
end
|
|
182
182
|
|
|
@@ -299,7 +299,7 @@ module RuboCop
|
|
|
299
299
|
end
|
|
300
300
|
|
|
301
301
|
def supported_capitalizations
|
|
302
|
-
cop_config['SupportedCapitalizations'].map(&:to_sym)
|
|
302
|
+
@supported_capitalizations ||= cop_config['SupportedCapitalizations'].map(&:to_sym).freeze
|
|
303
303
|
end
|
|
304
304
|
end
|
|
305
305
|
end
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Style
|
|
6
|
+
# Checks for `map { |x| x.to_s }.join` and similar calls where the
|
|
7
|
+
# `map` is redundant because `Array#join` implicitly calls `#to_s` on
|
|
8
|
+
# each element.
|
|
9
|
+
#
|
|
10
|
+
# @safety
|
|
11
|
+
# This cop is unsafe because it cannot guarantee that the receiver
|
|
12
|
+
# is an `Array` by static analysis. If the receiver does not have
|
|
13
|
+
# an `Array#join`-compatible implementation (i.e. one that calls
|
|
14
|
+
# `#to_s` on elements), the correction may change behavior.
|
|
15
|
+
#
|
|
16
|
+
# @example
|
|
17
|
+
# # bad
|
|
18
|
+
# array.map(&:to_s).join(', ')
|
|
19
|
+
#
|
|
20
|
+
# # bad
|
|
21
|
+
# array.map { |x| x.to_s }.join(', ')
|
|
22
|
+
#
|
|
23
|
+
# # bad
|
|
24
|
+
# array.collect(&:to_s).join
|
|
25
|
+
#
|
|
26
|
+
# # good
|
|
27
|
+
# array.join(', ')
|
|
28
|
+
#
|
|
29
|
+
# # good
|
|
30
|
+
# array.join
|
|
31
|
+
#
|
|
32
|
+
class MapJoin < Base
|
|
33
|
+
extend AutoCorrector
|
|
34
|
+
include RangeHelp
|
|
35
|
+
|
|
36
|
+
MSG = 'Remove redundant `%<method>s(&:to_s)` before `join`.'
|
|
37
|
+
RESTRICT_ON_SEND = %i[join].freeze
|
|
38
|
+
|
|
39
|
+
# map(&:to_s).join(...)
|
|
40
|
+
# @!method map_to_s_join?(node)
|
|
41
|
+
def_node_matcher :map_to_s_join?, <<~PATTERN
|
|
42
|
+
(call
|
|
43
|
+
$(call _ ${:map :collect} (block_pass (sym :to_s)))
|
|
44
|
+
:join ...)
|
|
45
|
+
PATTERN
|
|
46
|
+
|
|
47
|
+
# map { |x| x.to_s }.join(...)
|
|
48
|
+
# @!method map_to_s_block_join?(node)
|
|
49
|
+
def_node_matcher :map_to_s_block_join?, <<~PATTERN
|
|
50
|
+
(call
|
|
51
|
+
$(block
|
|
52
|
+
(call _ ${:map :collect})
|
|
53
|
+
(args (arg _x))
|
|
54
|
+
(send (lvar _x) :to_s))
|
|
55
|
+
:join ...)
|
|
56
|
+
PATTERN
|
|
57
|
+
|
|
58
|
+
# map { _1.to_s }.join(...)
|
|
59
|
+
# @!method map_to_s_numblock_join?(node)
|
|
60
|
+
def_node_matcher :map_to_s_numblock_join?, <<~PATTERN
|
|
61
|
+
(call
|
|
62
|
+
$(numblock
|
|
63
|
+
(call _ ${:map :collect})
|
|
64
|
+
1
|
|
65
|
+
(send (lvar :_1) :to_s))
|
|
66
|
+
:join ...)
|
|
67
|
+
PATTERN
|
|
68
|
+
|
|
69
|
+
# map { it.to_s }.join(...)
|
|
70
|
+
# @!method map_to_s_itblock_join?(node)
|
|
71
|
+
def_node_matcher :map_to_s_itblock_join?, <<~PATTERN
|
|
72
|
+
(call
|
|
73
|
+
$(itblock
|
|
74
|
+
(call _ ${:map :collect})
|
|
75
|
+
:it
|
|
76
|
+
(send (lvar :it) :to_s))
|
|
77
|
+
:join ...)
|
|
78
|
+
PATTERN
|
|
79
|
+
|
|
80
|
+
def on_send(node)
|
|
81
|
+
map_to_s_join?(node) { |m, n| register_offense(node, m, n) } ||
|
|
82
|
+
map_to_s_block_join?(node) { |m, n| register_offense(node, m, n) } ||
|
|
83
|
+
map_to_s_numblock_join?(node) { |m, n| register_offense(node, m, n) } ||
|
|
84
|
+
map_to_s_itblock_join?(node) { |m, n| register_offense(node, m, n) }
|
|
85
|
+
end
|
|
86
|
+
alias on_csend on_send
|
|
87
|
+
|
|
88
|
+
private
|
|
89
|
+
|
|
90
|
+
def register_offense(join_node, map_node, method_name)
|
|
91
|
+
map_send = map_node.any_block_type? ? map_node.send_node : map_node
|
|
92
|
+
message = format(MSG, method: method_name)
|
|
93
|
+
|
|
94
|
+
add_offense(map_send.loc.selector, message: message) do |corrector|
|
|
95
|
+
remove_map_call(corrector, join_node, map_node, map_send)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def remove_map_call(corrector, join_node, map_node, map_send)
|
|
100
|
+
receiver = map_send.receiver
|
|
101
|
+
if receiver
|
|
102
|
+
corrector.replace(removal_range(receiver, map_node, map_send), '')
|
|
103
|
+
else
|
|
104
|
+
corrector.replace(no_receiver_range(map_node, join_node), '')
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def removal_range(receiver, map_node, map_send)
|
|
109
|
+
start_pos = if receiver.last_line < map_send.loc.dot.line
|
|
110
|
+
receiver.source_range.end_pos
|
|
111
|
+
else
|
|
112
|
+
map_send.loc.dot.begin_pos
|
|
113
|
+
end
|
|
114
|
+
range_between(start_pos, map_node.source_range.end_pos)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def no_receiver_range(map_node, join_node)
|
|
118
|
+
range_between(map_node.source_range.begin_pos, join_node.loc.dot.end_pos)
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
@@ -33,16 +33,18 @@ module RuboCop
|
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
def included_macros_list
|
|
36
|
-
cop_config.fetch('IncludedMacros', []).map(&:to_sym)
|
|
36
|
+
@included_macros_list ||= cop_config.fetch('IncludedMacros', []).map(&:to_sym).freeze
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
def included_macro_patterns
|
|
40
|
-
|
|
40
|
+
@included_macro_patterns ||=
|
|
41
|
+
cop_config.fetch('IncludedMacroPatterns', [])
|
|
42
|
+
.map { |pattern| Regexp.new(pattern) }.freeze
|
|
41
43
|
end
|
|
42
44
|
|
|
43
45
|
def matches_included_macro_pattern?(method_name)
|
|
44
46
|
included_macro_patterns.any? do |pattern|
|
|
45
|
-
|
|
47
|
+
pattern.match?(method_name.to_s)
|
|
46
48
|
end
|
|
47
49
|
end
|
|
48
50
|
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
module RuboCop
|
|
4
4
|
module Cop
|
|
5
5
|
module Style
|
|
6
|
-
# Checks for uses of the `then` keyword in multi-line if statements.
|
|
6
|
+
# Checks for uses of the `then` keyword in multi-line `if` statements.
|
|
7
|
+
# In multi-line `if` statements, `then` is redundant because the newline
|
|
8
|
+
# already separates the condition from the body.
|
|
7
9
|
#
|
|
8
10
|
# @example
|
|
9
11
|
# # bad
|
|
@@ -125,7 +125,7 @@ module RuboCop
|
|
|
125
125
|
MSG = 'Freeze mutable objects assigned to constants.'
|
|
126
126
|
|
|
127
127
|
def on_casgn(node)
|
|
128
|
-
if node.expression.nil? # This is only the case for `CONST += ...` or
|
|
128
|
+
if node.expression.nil? # This is only the case for `CONST += ...` or similar
|
|
129
129
|
parent = node.parent
|
|
130
130
|
return unless parent.or_asgn_type? # We only care about `CONST ||= ...`
|
|
131
131
|
|
|
@@ -4,9 +4,8 @@ module RuboCop
|
|
|
4
4
|
module Cop
|
|
5
5
|
module Style
|
|
6
6
|
# Checks for comparison of something with nil using `==` and
|
|
7
|
-
# `nil?`.
|
|
8
|
-
#
|
|
9
|
-
# Supported styles are: predicate, comparison.
|
|
7
|
+
# `nil?`. Enforcing a consistent style (either the `nil?`
|
|
8
|
+
# predicate or `==` comparison) improves readability.
|
|
10
9
|
#
|
|
11
10
|
# @example EnforcedStyle: predicate (default)
|
|
12
11
|
#
|
|
@@ -43,7 +43,7 @@ module RuboCop
|
|
|
43
43
|
{ ({return next break} nil) (nil) }
|
|
44
44
|
PATTERN
|
|
45
45
|
|
|
46
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
46
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
47
47
|
return unless node.lambda_or_proc?
|
|
48
48
|
return unless nil_return?(node.body)
|
|
49
49
|
|
|
@@ -121,17 +121,11 @@ module RuboCop
|
|
|
121
121
|
end
|
|
122
122
|
|
|
123
123
|
def autocorrect_comparison(corrector, node)
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
expr.sub(/^(\S*)\s*!=\s*nil/, '!\1.nil?')
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
return if expr == new_code
|
|
133
|
-
|
|
134
|
-
corrector.replace(node, new_code)
|
|
124
|
+
if include_semantic_changes?
|
|
125
|
+
corrector.replace(node, node.receiver.source)
|
|
126
|
+
else
|
|
127
|
+
corrector.replace(node, "!#{node.receiver.source}.nil?")
|
|
128
|
+
end
|
|
135
129
|
end
|
|
136
130
|
|
|
137
131
|
def autocorrect_non_nil(corrector, node, inner_node)
|
|
@@ -4,7 +4,8 @@ module RuboCop
|
|
|
4
4
|
module Cop
|
|
5
5
|
module Style
|
|
6
6
|
# Checks for big numeric literals without `_` between groups
|
|
7
|
-
# of digits in them.
|
|
7
|
+
# of digits in them. Underscores make large numbers easier to
|
|
8
|
+
# read by visually separating groups of digits.
|
|
8
9
|
#
|
|
9
10
|
# Additional allowed patterns can be added by adding regexps to
|
|
10
11
|
# the `AllowedPatterns` configuration. All regexps are treated
|
|
@@ -116,7 +117,7 @@ module RuboCop
|
|
|
116
117
|
end
|
|
117
118
|
|
|
118
119
|
def allowed_numbers
|
|
119
|
-
cop_config.fetch('AllowedNumbers', []).map(&:to_s)
|
|
120
|
+
@allowed_numbers ||= cop_config.fetch('AllowedNumbers', []).map(&:to_s).freeze
|
|
120
121
|
end
|
|
121
122
|
|
|
122
123
|
def allowed_patterns
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Style
|
|
6
|
+
# Checks that each source file defines at most one top-level class or module.
|
|
7
|
+
#
|
|
8
|
+
# Keeping one class or module per file makes it easier to find and navigate
|
|
9
|
+
# code, and follows the convention used by most Ruby projects.
|
|
10
|
+
#
|
|
11
|
+
# Classes and modules listed in `AllowedClasses` are not counted toward the
|
|
12
|
+
# limit. This is useful for small ancillary classes like custom exception
|
|
13
|
+
# classes that logically belong with the main class.
|
|
14
|
+
#
|
|
15
|
+
# @example
|
|
16
|
+
# # bad
|
|
17
|
+
# class Foo
|
|
18
|
+
# end
|
|
19
|
+
#
|
|
20
|
+
# class Bar
|
|
21
|
+
# end
|
|
22
|
+
#
|
|
23
|
+
# # bad
|
|
24
|
+
# class Foo
|
|
25
|
+
# end
|
|
26
|
+
#
|
|
27
|
+
# module Bar
|
|
28
|
+
# end
|
|
29
|
+
#
|
|
30
|
+
# # good
|
|
31
|
+
# class Foo
|
|
32
|
+
# end
|
|
33
|
+
#
|
|
34
|
+
# # good
|
|
35
|
+
# class Foo
|
|
36
|
+
# class Bar
|
|
37
|
+
# end
|
|
38
|
+
# end
|
|
39
|
+
#
|
|
40
|
+
# @example AllowedClasses: ['AllowedClass']
|
|
41
|
+
# # good
|
|
42
|
+
# class Foo
|
|
43
|
+
# end
|
|
44
|
+
#
|
|
45
|
+
# class AllowedClass
|
|
46
|
+
# end
|
|
47
|
+
#
|
|
48
|
+
class OneClassPerFile < Base
|
|
49
|
+
include RangeHelp
|
|
50
|
+
|
|
51
|
+
MSG = 'Do not define multiple classes/modules at the top level in a single file.'
|
|
52
|
+
|
|
53
|
+
def on_new_investigation
|
|
54
|
+
@top_level_definitions = []
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def on_class(node)
|
|
58
|
+
check_top_level(node)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def on_module(node)
|
|
62
|
+
check_top_level(node)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def check_top_level(node)
|
|
68
|
+
return unless top_level_definition?(node)
|
|
69
|
+
return if allowed_class?(node)
|
|
70
|
+
|
|
71
|
+
@top_level_definitions << node
|
|
72
|
+
return unless @top_level_definitions.length > 1
|
|
73
|
+
|
|
74
|
+
add_offense(range_between(node.source_range.begin_pos, node.loc.name.end_pos))
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def top_level_definition?(node)
|
|
78
|
+
if node.parent&.begin_type?
|
|
79
|
+
node.parent.root?
|
|
80
|
+
else
|
|
81
|
+
node.root?
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def allowed_class?(node)
|
|
86
|
+
allowed_classes.include?(node.identifier.short_name)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def allowed_classes
|
|
90
|
+
@allowed_classes ||= cop_config.fetch('AllowedClasses', []).map(&:intern)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
module RuboCop
|
|
4
4
|
module Cop
|
|
5
5
|
module Style
|
|
6
|
-
# Checks for uses of if/then/else/end constructs on a single line.
|
|
7
|
-
#
|
|
8
|
-
#
|
|
6
|
+
# Checks for uses of `if/then/else/end` constructs on a single line.
|
|
7
|
+
# A ternary operator (`?:`) or multi-line `if` is more readable.
|
|
8
|
+
# `AlwaysCorrectToMultiline` config option can be set to `true` to autocorrect all offenses to
|
|
9
|
+
# multi-line constructs. When `AlwaysCorrectToMultiline` is `false` (default case) the
|
|
9
10
|
# autocorrect will first try converting them to ternary operators.
|
|
10
11
|
#
|
|
11
12
|
# @example
|
|
@@ -4,6 +4,10 @@ module RuboCop
|
|
|
4
4
|
module Cop
|
|
5
5
|
module Style
|
|
6
6
|
# Checks for simple usages of parallel assignment.
|
|
7
|
+
# Parallel assignment is less readable than individual
|
|
8
|
+
# assignments and makes it harder to follow what each
|
|
9
|
+
# variable is being set to.
|
|
10
|
+
#
|
|
7
11
|
# This will only complain when the number of variables
|
|
8
12
|
# being assigned matched the number of assigning variables.
|
|
9
13
|
#
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Style
|
|
6
|
+
# Checks for consecutive calls to `select`/`filter`/`find_all` and `reject`
|
|
7
|
+
# on the same receiver with the same block body, where `partition` could be
|
|
8
|
+
# used instead. Also detects two `select` or two `reject` calls where one
|
|
9
|
+
# block negates the other with `!`. Using `partition` reduces two collection
|
|
10
|
+
# traversals to one.
|
|
11
|
+
#
|
|
12
|
+
# @safety
|
|
13
|
+
# This cop is unsafe because:
|
|
14
|
+
#
|
|
15
|
+
# * `Hash#select` and `Hash#reject` return hashes, but `Hash#partition`
|
|
16
|
+
# returns nested arrays.
|
|
17
|
+
# * When the receiver has side effects, calling it once (with `partition`)
|
|
18
|
+
# versus twice (with `select` + `reject`) may produce different results.
|
|
19
|
+
# * Custom classes may override `select`/`reject` without providing a
|
|
20
|
+
# compatible `partition` method.
|
|
21
|
+
#
|
|
22
|
+
# @example
|
|
23
|
+
# # bad
|
|
24
|
+
# positives = array.select { |x| x > 0 }
|
|
25
|
+
# negatives = array.reject { |x| x > 0 }
|
|
26
|
+
#
|
|
27
|
+
# # bad
|
|
28
|
+
# positives = array.filter { |x| x > 0 }
|
|
29
|
+
# negatives = array.reject { |x| x > 0 }
|
|
30
|
+
#
|
|
31
|
+
# # bad
|
|
32
|
+
# negatives = array.reject { |x| x > 0 }
|
|
33
|
+
# positives = array.select { |x| x > 0 }
|
|
34
|
+
#
|
|
35
|
+
# # bad
|
|
36
|
+
# positives = array.select(&:positive?)
|
|
37
|
+
# negatives = array.reject(&:positive?)
|
|
38
|
+
#
|
|
39
|
+
# # bad
|
|
40
|
+
# positives = array.select(&:positive?)
|
|
41
|
+
# negatives = array.reject { |x| x.positive? }
|
|
42
|
+
#
|
|
43
|
+
# # bad
|
|
44
|
+
# positives = array.select { |x| x.positive? }
|
|
45
|
+
# non_positives = array.select { |x| !x.positive? }
|
|
46
|
+
#
|
|
47
|
+
# # good
|
|
48
|
+
# positives, negatives = array.partition { |x| x > 0 }
|
|
49
|
+
#
|
|
50
|
+
# # good
|
|
51
|
+
# positives, non_positives = array.partition { |x| x.positive? }
|
|
52
|
+
#
|
|
53
|
+
# # good
|
|
54
|
+
# positives, negatives = array.partition(&:positive?)
|
|
55
|
+
#
|
|
56
|
+
class PartitionInsteadOfDoubleSelect < Base
|
|
57
|
+
include RangeHelp
|
|
58
|
+
extend AutoCorrector
|
|
59
|
+
|
|
60
|
+
MSG = 'Use `partition` instead of consecutive `%<first>s` and `%<second>s` calls.'
|
|
61
|
+
|
|
62
|
+
SELECT_METHODS = %i[select filter find_all].freeze
|
|
63
|
+
CANDIDATE_METHODS = (SELECT_METHODS + %i[reject]).to_set.freeze
|
|
64
|
+
RESTRICT_ON_SEND = (SELECT_METHODS + %i[reject]).freeze
|
|
65
|
+
|
|
66
|
+
# @!method symbol_proc_method?(node)
|
|
67
|
+
def_node_matcher :symbol_proc_method?, <<~PATTERN
|
|
68
|
+
(block _ (args (arg _name)) (send (lvar _name) $_method_name))
|
|
69
|
+
PATTERN
|
|
70
|
+
|
|
71
|
+
def on_block(node)
|
|
72
|
+
return unless CANDIDATE_METHODS.include?(node.method_name)
|
|
73
|
+
|
|
74
|
+
find_and_register_offense(node)
|
|
75
|
+
end
|
|
76
|
+
alias on_numblock on_block
|
|
77
|
+
alias on_itblock on_block
|
|
78
|
+
|
|
79
|
+
def on_send(node)
|
|
80
|
+
return unless node.last_argument&.block_pass_type?
|
|
81
|
+
|
|
82
|
+
find_and_register_offense(node)
|
|
83
|
+
end
|
|
84
|
+
alias on_csend on_send
|
|
85
|
+
|
|
86
|
+
private
|
|
87
|
+
|
|
88
|
+
def find_and_register_offense(node)
|
|
89
|
+
container = node_container(node)
|
|
90
|
+
return unless container
|
|
91
|
+
|
|
92
|
+
sibling_container = container.left_sibling
|
|
93
|
+
sibling = find_matching_candidate(node, sibling_container)
|
|
94
|
+
return unless sibling
|
|
95
|
+
|
|
96
|
+
register_offense(node, sibling, container, sibling_container)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def node_container(node)
|
|
100
|
+
parent = node.parent
|
|
101
|
+
if parent&.begin_type?
|
|
102
|
+
node
|
|
103
|
+
elsif parent&.assignment? && parent.parent&.begin_type?
|
|
104
|
+
parent
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def find_matching_candidate(node, sibling_container)
|
|
109
|
+
return unless sibling_container
|
|
110
|
+
|
|
111
|
+
sibling = extract_candidate(sibling_container)
|
|
112
|
+
return unless sibling
|
|
113
|
+
return unless node.receiver == sibling.receiver
|
|
114
|
+
return unless matching_pair?(node, sibling)
|
|
115
|
+
|
|
116
|
+
sibling
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def matching_pair?(node, sibling)
|
|
120
|
+
(complementary_pair?(node, sibling) && equivalent_predicate?(node, sibling)) ||
|
|
121
|
+
(node.method?(sibling.method_name) && negated_predicate?(node, sibling))
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def extract_candidate(container)
|
|
125
|
+
extract_block(container) || extract_block_pass_send(container)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def extract_block(container)
|
|
129
|
+
if container.any_block_type?
|
|
130
|
+
container
|
|
131
|
+
elsif container.assignment?
|
|
132
|
+
rhs = container.children.last
|
|
133
|
+
rhs if rhs&.any_block_type?
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def extract_block_pass_send(container)
|
|
138
|
+
node = container.assignment? ? container.children.last : container
|
|
139
|
+
return unless node&.type?(:call)
|
|
140
|
+
return unless node.last_argument&.block_pass_type?
|
|
141
|
+
|
|
142
|
+
node
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def complementary_pair?(node1, node2)
|
|
146
|
+
m1 = node1.method_name
|
|
147
|
+
m2 = node2.method_name
|
|
148
|
+
(SELECT_METHODS.include?(m1) && m2 == :reject) ||
|
|
149
|
+
(m1 == :reject && SELECT_METHODS.include?(m2))
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def equivalent_predicate?(node1, node2)
|
|
153
|
+
if node1.any_block_type? && node2.any_block_type?
|
|
154
|
+
same_block_contents?(node1, node2)
|
|
155
|
+
elsif node1.any_block_type?
|
|
156
|
+
block_matches_block_pass?(node1, node2)
|
|
157
|
+
elsif node2.any_block_type?
|
|
158
|
+
block_matches_block_pass?(node2, node1)
|
|
159
|
+
else
|
|
160
|
+
node1.last_argument == node2.last_argument
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def same_block_contents?(block1, block2)
|
|
165
|
+
return false unless block1.type == block2.type
|
|
166
|
+
|
|
167
|
+
if block1.block_type?
|
|
168
|
+
block1.arguments == block2.arguments &&
|
|
169
|
+
block1.body == block2.body
|
|
170
|
+
else
|
|
171
|
+
block1.body == block2.body
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def block_matches_block_pass?(block_node, send_node)
|
|
176
|
+
method_name = symbol_proc_method?(block_node)
|
|
177
|
+
return false unless method_name
|
|
178
|
+
|
|
179
|
+
sym_node = send_node.last_argument.children.first
|
|
180
|
+
sym_node.sym_type? && sym_node.children.first == method_name
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def negated_predicate?(node1, node2)
|
|
184
|
+
return false unless node1.any_block_type? && node2.any_block_type?
|
|
185
|
+
return false unless node1.type == node2.type
|
|
186
|
+
return false if node1.block_type? && node1.arguments != node2.arguments
|
|
187
|
+
|
|
188
|
+
negated_body?(node1.body, node2.body) || negated_body?(node2.body, node1.body)
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def negated_body?(body1, body2)
|
|
192
|
+
body1&.send_type? && body1.method?(:!) && body1.receiver == body2
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def register_offense(node, sibling, container, sibling_container)
|
|
196
|
+
message = format(MSG, first: sibling.method_name, second: node.method_name)
|
|
197
|
+
|
|
198
|
+
add_offense(container, message: message) do |corrector|
|
|
199
|
+
next unless both_lvasgn?(container, sibling_container)
|
|
200
|
+
|
|
201
|
+
autocorrect(corrector, node, sibling, container, sibling_container)
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def both_lvasgn?(container, sibling_container)
|
|
206
|
+
container.lvasgn_type? && sibling_container.lvasgn_type?
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def autocorrect(corrector, node, sibling, container, sibling_container)
|
|
210
|
+
if complementary_pair?(node, sibling)
|
|
211
|
+
select_var, reject_var =
|
|
212
|
+
complementary_variable_order(sibling, container, sibling_container)
|
|
213
|
+
partition_node = select_node_for(sibling, container)
|
|
214
|
+
else
|
|
215
|
+
select_var, reject_var, partition_node =
|
|
216
|
+
negation_partition_args(node, sibling, container, sibling_container)
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
partition_call = build_partition_call(partition_node)
|
|
220
|
+
replacement = "#{select_var}, #{reject_var} = #{partition_call}"
|
|
221
|
+
|
|
222
|
+
corrector.replace(sibling_container, replacement)
|
|
223
|
+
range = range_by_whole_lines(container.source_range, include_final_newline: true)
|
|
224
|
+
corrector.remove(range)
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def complementary_variable_order(sibling, container, sibling_container)
|
|
228
|
+
if SELECT_METHODS.include?(sibling.method_name)
|
|
229
|
+
[sibling_container.children.first, container.children.first]
|
|
230
|
+
else
|
|
231
|
+
[container.children.first, sibling_container.children.first]
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def negation_partition_args(node, sibling, container, sibling_container)
|
|
236
|
+
node_is_negated = negated_body?(node.body, sibling.body)
|
|
237
|
+
is_select = SELECT_METHODS.include?(node.method_name)
|
|
238
|
+
# For select: non-negated is truthy (first). For reject: negated is truthy (first).
|
|
239
|
+
node_is_truthy = is_select != node_is_negated
|
|
240
|
+
partition_node = node_is_negated ? sibling : node
|
|
241
|
+
|
|
242
|
+
if node_is_truthy
|
|
243
|
+
[container.children.first, sibling_container.children.first, partition_node]
|
|
244
|
+
else
|
|
245
|
+
[sibling_container.children.first, container.children.first, partition_node]
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def select_node_for(sibling, container)
|
|
250
|
+
if SELECT_METHODS.include?(sibling.method_name)
|
|
251
|
+
sibling
|
|
252
|
+
else
|
|
253
|
+
container.children.last
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def build_partition_call(node)
|
|
258
|
+
source = node.source
|
|
259
|
+
send_node = node.any_block_type? ? node.send_node : node
|
|
260
|
+
selector = send_node.loc.selector
|
|
261
|
+
offset = node.source_range.begin_pos
|
|
262
|
+
method_start = selector.begin_pos - offset
|
|
263
|
+
method_end = selector.end_pos - offset
|
|
264
|
+
|
|
265
|
+
"#{source[0...method_start]}partition#{source[method_end..]}"
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
end
|
|
@@ -4,6 +4,8 @@ module RuboCop
|
|
|
4
4
|
module Cop
|
|
5
5
|
module Style
|
|
6
6
|
# Enforces the consistent usage of `%`-literal delimiters.
|
|
7
|
+
# Using consistent delimiters across the codebase reduces
|
|
8
|
+
# cognitive load when reading `%`-literals.
|
|
7
9
|
#
|
|
8
10
|
# Specify the 'default' key to set all preferred delimiters at once. You
|
|
9
11
|
# can continue to specify individual preferred delimiters to override the
|