rubocop 1.75.8 → 1.82.1
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 -16
- data/config/default.yml +142 -33
- data/config/obsoletion.yml +10 -3
- data/exe/rubocop +1 -8
- data/lib/rubocop/cli/command/auto_generate_config.rb +2 -2
- data/lib/rubocop/cli.rb +20 -4
- data/lib/rubocop/comment_config.rb +62 -17
- data/lib/rubocop/config_loader.rb +6 -40
- data/lib/rubocop/config_loader_resolver.rb +7 -6
- data/lib/rubocop/config_store.rb +5 -0
- data/lib/rubocop/cop/autocorrect_logic.rb +8 -4
- data/lib/rubocop/cop/bundler/ordered_gems.rb +2 -3
- data/lib/rubocop/cop/correctors/alignment_corrector.rb +8 -7
- data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +7 -2
- 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/ordered_dependencies.rb +2 -3
- data/lib/rubocop/cop/gemspec/require_mfa.rb +15 -1
- data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +10 -5
- data/lib/rubocop/cop/internal_affairs/example_description.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/location_exists.rb +28 -2
- data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +4 -4
- data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_processor.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +4 -1
- data/lib/rubocop/cop/internal_affairs/node_type_group.rb +3 -2
- data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/useless_restrict_on_send.rb +1 -1
- data/lib/rubocop/cop/layout/class_structure.rb +1 -1
- data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/dot_position.rb +1 -1
- data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +3 -0
- data/lib/rubocop/cop/layout/empty_line_between_defs.rb +30 -12
- data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +101 -0
- data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +1 -1
- data/lib/rubocop/cop/layout/empty_lines_around_arguments.rb +8 -29
- data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +1 -1
- data/lib/rubocop/cop/layout/end_alignment.rb +4 -0
- data/lib/rubocop/cop/layout/hash_alignment.rb +2 -5
- data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -2
- data/lib/rubocop/cop/layout/heredoc_indentation.rb +1 -4
- data/lib/rubocop/cop/layout/indentation_style.rb +1 -1
- data/lib/rubocop/cop/layout/indentation_width.rb +12 -1
- data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -1
- data/lib/rubocop/cop/layout/line_length.rb +43 -10
- data/lib/rubocop/cop/layout/multiline_block_layout.rb +2 -0
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +5 -1
- data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +8 -4
- data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +13 -3
- data/lib/rubocop/cop/layout/space_after_comma.rb +2 -10
- data/lib/rubocop/cop/layout/space_after_semicolon.rb +1 -1
- data/lib/rubocop/cop/layout/space_around_keyword.rb +7 -2
- data/lib/rubocop/cop/layout/space_around_operators.rb +8 -0
- 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/layout/trailing_whitespace.rb +1 -1
- data/lib/rubocop/cop/lint/ambiguous_range.rb +5 -0
- data/lib/rubocop/cop/lint/circular_argument_reference.rb +47 -3
- data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +3 -2
- data/lib/rubocop/cop/lint/cop_directive_syntax.rb +14 -8
- data/lib/rubocop/cop/lint/debugger.rb +0 -2
- data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +4 -1
- data/lib/rubocop/cop/lint/duplicate_match_pattern.rb +4 -4
- data/lib/rubocop/cop/lint/duplicate_methods.rb +25 -4
- data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +5 -42
- data/lib/rubocop/cop/lint/else_layout.rb +19 -0
- data/lib/rubocop/cop/lint/empty_interpolation.rb +14 -1
- data/lib/rubocop/cop/lint/float_comparison.rb +4 -4
- data/lib/rubocop/cop/lint/identity_comparison.rb +19 -15
- data/lib/rubocop/cop/lint/literal_as_condition.rb +38 -28
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +1 -1
- data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +17 -8
- data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +4 -0
- data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +1 -0
- data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +23 -9
- data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +1 -1
- data/lib/rubocop/cop/lint/redundant_require_statement.rb +4 -2
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +101 -2
- data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +7 -1
- data/lib/rubocop/cop/lint/redundant_type_conversion.rb +4 -4
- data/lib/rubocop/cop/lint/require_range_parentheses.rb +1 -1
- data/lib/rubocop/cop/lint/rescue_exception.rb +1 -4
- data/lib/rubocop/cop/lint/rescue_type.rb +1 -1
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +4 -4
- data/lib/rubocop/cop/lint/self_assignment.rb +39 -5
- data/lib/rubocop/cop/lint/shadowed_argument.rb +7 -7
- data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +5 -0
- data/lib/rubocop/cop/lint/unreachable_code.rb +5 -3
- data/lib/rubocop/cop/lint/uri_escape_unescape.rb +2 -0
- data/lib/rubocop/cop/lint/useless_access_modifier.rb +29 -4
- data/lib/rubocop/cop/lint/useless_assignment.rb +44 -16
- data/lib/rubocop/cop/lint/useless_default_value_argument.rb +90 -0
- data/lib/rubocop/cop/lint/useless_numeric_operation.rb +1 -0
- data/lib/rubocop/cop/lint/useless_or.rb +111 -0
- data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +3 -3
- data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +121 -0
- data/lib/rubocop/cop/lint/void.rb +7 -0
- data/lib/rubocop/cop/message_annotator.rb +1 -1
- data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +4 -3
- data/lib/rubocop/cop/mixin/alignment.rb +1 -1
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
- data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +2 -4
- data/lib/rubocop/cop/mixin/code_length.rb +1 -1
- data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -7
- data/lib/rubocop/cop/mixin/frozen_string_literal.rb +1 -1
- data/lib/rubocop/cop/mixin/gemspec_help.rb +22 -0
- data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +1 -1
- data/lib/rubocop/cop/mixin/line_length_help.rb +45 -10
- data/lib/rubocop/cop/mixin/method_complexity.rb +1 -1
- data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +1 -1
- data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +1 -1
- data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
- data/lib/rubocop/cop/mixin/space_after_punctuation.rb +5 -4
- data/lib/rubocop/cop/mixin/statement_modifier.rb +0 -6
- data/lib/rubocop/cop/mixin/trailing_comma.rb +8 -5
- data/lib/rubocop/cop/naming/file_name.rb +2 -2
- data/lib/rubocop/cop/naming/method_name.rb +129 -13
- data/lib/rubocop/cop/naming/predicate_method.rb +319 -0
- data/lib/rubocop/cop/naming/{predicate_name.rb → predicate_prefix.rb} +4 -4
- data/lib/rubocop/cop/security/eval.rb +2 -1
- data/lib/rubocop/cop/security/json_load.rb +33 -11
- data/lib/rubocop/cop/security/open.rb +1 -0
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +1 -1
- data/lib/rubocop/cop/style/accessor_grouping.rb +13 -1
- data/lib/rubocop/cop/style/arguments_forwarding.rb +11 -17
- data/lib/rubocop/cop/style/array_intersect.rb +99 -35
- data/lib/rubocop/cop/style/array_intersect_with_single_element.rb +47 -0
- data/lib/rubocop/cop/style/bare_percent_literals.rb +1 -2
- data/lib/rubocop/cop/style/bitwise_predicate.rb +8 -1
- data/lib/rubocop/cop/style/block_delimiters.rb +1 -1
- data/lib/rubocop/cop/style/case_equality.rb +11 -13
- data/lib/rubocop/cop/style/case_like_if.rb +1 -1
- data/lib/rubocop/cop/style/class_and_module_children.rb +1 -0
- data/lib/rubocop/cop/style/collection_querying.rb +167 -0
- data/lib/rubocop/cop/style/conditional_assignment.rb +12 -16
- data/lib/rubocop/cop/style/constant_visibility.rb +17 -12
- data/lib/rubocop/cop/style/dig_chain.rb +1 -1
- data/lib/rubocop/cop/style/double_negation.rb +1 -1
- data/lib/rubocop/cop/style/empty_method.rb +0 -6
- data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +100 -0
- data/lib/rubocop/cop/style/endless_method.rb +15 -2
- data/lib/rubocop/cop/style/explicit_block_argument.rb +1 -1
- data/lib/rubocop/cop/style/exponential_notation.rb +3 -2
- data/lib/rubocop/cop/style/fetch_env_var.rb +32 -6
- data/lib/rubocop/cop/style/float_division.rb +15 -1
- data/lib/rubocop/cop/style/guard_clause.rb +0 -11
- data/lib/rubocop/cop/style/hash_conversion.rb +16 -8
- data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
- data/lib/rubocop/cop/style/if_unless_modifier.rb +16 -9
- data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
- data/lib/rubocop/cop/style/inverse_methods.rb +1 -1
- data/lib/rubocop/cop/style/it_assignment.rb +69 -12
- data/lib/rubocop/cop/style/it_block_parameter.rb +36 -15
- data/lib/rubocop/cop/style/map_to_hash.rb +1 -3
- data/lib/rubocop/cop/style/map_to_set.rb +1 -3
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +4 -6
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +12 -1
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +33 -4
- data/lib/rubocop/cop/style/min_max_comparison.rb +13 -5
- data/lib/rubocop/cop/style/module_member_existence_check.rb +74 -0
- data/lib/rubocop/cop/style/multiline_method_signature.rb +2 -4
- data/lib/rubocop/cop/style/nil_comparison.rb +9 -7
- data/lib/rubocop/cop/style/one_line_conditional.rb +17 -9
- data/lib/rubocop/cop/style/operator_method_call.rb +11 -2
- data/lib/rubocop/cop/style/parallel_assignment.rb +34 -22
- data/lib/rubocop/cop/style/redundant_argument.rb +2 -0
- data/lib/rubocop/cop/style/redundant_array_flatten.rb +50 -0
- data/lib/rubocop/cop/style/redundant_begin.rb +34 -0
- data/lib/rubocop/cop/style/redundant_condition.rb +1 -1
- data/lib/rubocop/cop/style/redundant_exception.rb +1 -1
- data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -9
- data/lib/rubocop/cop/style/redundant_format.rb +26 -5
- data/lib/rubocop/cop/style/redundant_freeze.rb +1 -1
- data/lib/rubocop/cop/style/redundant_interpolation.rb +12 -3
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +1 -1
- data/lib/rubocop/cop/style/redundant_parentheses.rb +55 -16
- data/lib/rubocop/cop/style/redundant_percent_q.rb +1 -2
- data/lib/rubocop/cop/style/redundant_regexp_argument.rb +9 -0
- data/lib/rubocop/cop/style/redundant_regexp_escape.rb +8 -0
- data/lib/rubocop/cop/style/redundant_self.rb +8 -5
- data/lib/rubocop/cop/style/redundant_sort.rb +7 -7
- data/lib/rubocop/cop/style/safe_navigation.rb +44 -12
- data/lib/rubocop/cop/style/semicolon.rb +23 -7
- data/lib/rubocop/cop/style/single_line_methods.rb +7 -4
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +40 -3
- data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/string_concatenation.rb +17 -13
- data/lib/rubocop/cop/style/super_arguments.rb +2 -2
- data/lib/rubocop/cop/style/symbol_array.rb +1 -1
- data/lib/rubocop/cop/style/symbol_proc.rb +1 -1
- data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +45 -0
- data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
- data/lib/rubocop/cop/style/trailing_underscore_variable.rb +11 -11
- data/lib/rubocop/cop/style/unless_else.rb +10 -9
- data/lib/rubocop/cop/util.rb +2 -3
- data/lib/rubocop/cop/utils/format_string.rb +10 -0
- data/lib/rubocop/cop/variable_force/variable.rb +1 -1
- data/lib/rubocop/cop/variable_force.rb +25 -8
- data/lib/rubocop/cops_documentation_generator.rb +5 -4
- data/lib/rubocop/directive_comment.rb +46 -3
- data/lib/rubocop/formatter/disabled_config_formatter.rb +19 -5
- data/lib/rubocop/formatter/fuubar_style_formatter.rb +1 -1
- data/lib/rubocop/formatter/markdown_formatter.rb +1 -0
- data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
- data/lib/rubocop/formatter/pacman_formatter.rb +1 -0
- data/lib/rubocop/lsp/diagnostic.rb +14 -18
- data/lib/rubocop/lsp/routes.rb +35 -6
- data/lib/rubocop/lsp/stdin_runner.rb +0 -16
- data/lib/rubocop/magic_comment.rb +20 -0
- data/lib/rubocop/pending_cops_reporter.rb +56 -0
- data/lib/rubocop/rake_task.rb +1 -1
- data/lib/rubocop/remote_config.rb +7 -8
- data/lib/rubocop/result_cache.rb +51 -38
- data/lib/rubocop/rspec/expect_offense.rb +9 -3
- data/lib/rubocop/rspec/shared_contexts.rb +2 -2
- data/lib/rubocop/rspec/support.rb +1 -1
- data/lib/rubocop/runner.rb +10 -4
- 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/target_finder.rb +9 -9
- data/lib/rubocop/target_ruby.rb +11 -2
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +13 -1
- data/lib/ruby_lsp/rubocop/addon.rb +25 -10
- data/lib/ruby_lsp/rubocop/runtime_adapter.rb +49 -15
- metadata +20 -8
|
@@ -5,15 +5,28 @@ module RuboCop
|
|
|
5
5
|
module Style
|
|
6
6
|
# Checks for blocks with one argument where `it` block parameter can be used.
|
|
7
7
|
#
|
|
8
|
-
# It provides
|
|
8
|
+
# It provides four `EnforcedStyle` options:
|
|
9
9
|
#
|
|
10
|
-
# 1. `
|
|
11
|
-
# 2. `
|
|
12
|
-
# 3. `
|
|
10
|
+
# 1. `allow_single_line` (default) ... Always uses the `it` block parameter in a single line.
|
|
11
|
+
# 2. `only_numbered_parameters` ... Detects only numbered block parameters.
|
|
12
|
+
# 3. `always` ... Always uses the `it` block parameter.
|
|
13
|
+
# 4. `disallow` ... Disallows the `it` block parameter.
|
|
13
14
|
#
|
|
14
|
-
# A single numbered parameter is detected when `
|
|
15
|
+
# A single numbered parameter is detected when `allow_single_line`,
|
|
16
|
+
# `only_numbered_parameters`, or `always`.
|
|
15
17
|
#
|
|
16
|
-
# @example EnforcedStyle:
|
|
18
|
+
# @example EnforcedStyle: allow_single_line (default)
|
|
19
|
+
# # bad
|
|
20
|
+
# block do
|
|
21
|
+
# do_something(it)
|
|
22
|
+
# end
|
|
23
|
+
# block { do_something(_1) }
|
|
24
|
+
#
|
|
25
|
+
# # good
|
|
26
|
+
# block { do_something(it) }
|
|
27
|
+
# block { |named_param| do_something(named_param) }
|
|
28
|
+
#
|
|
29
|
+
# @example EnforcedStyle: only_numbered_parameters
|
|
17
30
|
# # bad
|
|
18
31
|
# block { do_something(_1) }
|
|
19
32
|
#
|
|
@@ -42,8 +55,9 @@ module RuboCop
|
|
|
42
55
|
extend TargetRubyVersion
|
|
43
56
|
extend AutoCorrector
|
|
44
57
|
|
|
45
|
-
|
|
46
|
-
|
|
58
|
+
MSG_USE_IT_PARAMETER = 'Use `it` block parameter.'
|
|
59
|
+
MSG_AVOID_IT_PARAMETER = 'Avoid using `it` block parameter.'
|
|
60
|
+
MSG_AVOID_IT_PARAMETER_MULTILINE = 'Avoid using `it` block parameter for multi-line blocks.'
|
|
47
61
|
|
|
48
62
|
minimum_target_ruby_version 3.4
|
|
49
63
|
|
|
@@ -57,7 +71,7 @@ module RuboCop
|
|
|
57
71
|
variables = find_block_variables(node, node.first_argument.source)
|
|
58
72
|
|
|
59
73
|
variables.each do |variable|
|
|
60
|
-
add_offense(variable, message:
|
|
74
|
+
add_offense(variable, message: MSG_USE_IT_PARAMETER) do |corrector|
|
|
61
75
|
corrector.remove(node.arguments)
|
|
62
76
|
corrector.replace(variable, 'it')
|
|
63
77
|
end
|
|
@@ -71,26 +85,33 @@ module RuboCop
|
|
|
71
85
|
variables = find_block_variables(node, '_1')
|
|
72
86
|
|
|
73
87
|
variables.each do |variable|
|
|
74
|
-
add_offense(variable, message:
|
|
88
|
+
add_offense(variable, message: MSG_USE_IT_PARAMETER) do |corrector|
|
|
75
89
|
corrector.replace(variable, 'it')
|
|
76
90
|
end
|
|
77
91
|
end
|
|
78
92
|
end
|
|
79
93
|
|
|
80
94
|
def on_itblock(node)
|
|
81
|
-
|
|
95
|
+
case style
|
|
96
|
+
when :allow_single_line
|
|
97
|
+
return if node.single_line?
|
|
82
98
|
|
|
83
|
-
|
|
99
|
+
add_offense(node, message: MSG_AVOID_IT_PARAMETER_MULTILINE)
|
|
100
|
+
when :disallow
|
|
101
|
+
variables = find_block_variables(node, 'it')
|
|
84
102
|
|
|
85
|
-
|
|
86
|
-
|
|
103
|
+
variables.each do |variable|
|
|
104
|
+
add_offense(variable, message: MSG_AVOID_IT_PARAMETER)
|
|
105
|
+
end
|
|
87
106
|
end
|
|
88
107
|
end
|
|
89
108
|
|
|
90
109
|
private
|
|
91
110
|
|
|
92
111
|
def find_block_variables(node, block_argument_name)
|
|
93
|
-
node.
|
|
112
|
+
return [] unless node.body
|
|
113
|
+
|
|
114
|
+
node.body.each_descendant(:lvar).select do |descendant|
|
|
94
115
|
descendant.source == block_argument_name
|
|
95
116
|
end
|
|
96
117
|
end
|
|
@@ -56,12 +56,10 @@ module RuboCop
|
|
|
56
56
|
|
|
57
57
|
def on_send(node)
|
|
58
58
|
return unless (to_h_node, map_node = map_to_h(node))
|
|
59
|
+
return if to_h_node.block_literal?
|
|
59
60
|
|
|
60
61
|
message = format(MSG, method: map_node.loc.selector.source, dot: to_h_node.loc.dot.source)
|
|
61
62
|
add_offense(map_node.loc.selector, message: message) do |corrector|
|
|
62
|
-
# If the `to_h` call already has a block, do not autocorrect.
|
|
63
|
-
next if to_h_node.block_literal?
|
|
64
|
-
|
|
65
63
|
autocorrect(corrector, to_h_node, map_node)
|
|
66
64
|
end
|
|
67
65
|
end
|
|
@@ -40,12 +40,10 @@ module RuboCop
|
|
|
40
40
|
|
|
41
41
|
def on_send(node)
|
|
42
42
|
return unless (to_set_node, map_node = map_to_set?(node))
|
|
43
|
+
return if to_set_node.block_literal?
|
|
43
44
|
|
|
44
45
|
message = format(MSG, method: map_node.loc.selector.source)
|
|
45
46
|
add_offense(map_node.loc.selector, message: message) do |corrector|
|
|
46
|
-
# If the `to_set` call already has a block, do not autocorrect.
|
|
47
|
-
next if to_set_node.block_literal?
|
|
48
|
-
|
|
49
47
|
autocorrect(corrector, to_set_node, map_node)
|
|
50
48
|
end
|
|
51
49
|
end
|
|
@@ -167,7 +167,7 @@ module RuboCop
|
|
|
167
167
|
def call_in_match_pattern?(node)
|
|
168
168
|
return false unless (parent = node.parent)
|
|
169
169
|
|
|
170
|
-
parent.
|
|
170
|
+
parent.any_match_pattern_type?
|
|
171
171
|
end
|
|
172
172
|
|
|
173
173
|
def hash_literal_in_arguments?(node)
|
|
@@ -222,11 +222,9 @@ module RuboCop
|
|
|
222
222
|
end
|
|
223
223
|
|
|
224
224
|
def unary_literal?(node)
|
|
225
|
-
|
|
226
|
-
return node.source.match?(/\A[+-]/) if node.complex_type?
|
|
225
|
+
return true if node.numeric_type? && node.sign?
|
|
227
226
|
|
|
228
|
-
|
|
229
|
-
(node.parent&.send_type? && node.parent.unary_operation?)
|
|
227
|
+
node.parent&.send_type? && node.parent.unary_operation?
|
|
230
228
|
end
|
|
231
229
|
|
|
232
230
|
def assigned_before?(node, target)
|
|
@@ -251,7 +249,7 @@ module RuboCop
|
|
|
251
249
|
return false unless (last_argument = node.last_argument)
|
|
252
250
|
return true if last_argument.forwarded_restarg_type?
|
|
253
251
|
|
|
254
|
-
last_argument.hash_type? && last_argument.children.
|
|
252
|
+
last_argument.hash_type? && last_argument.children.any?(&:forwarded_kwrestarg_type?)
|
|
255
253
|
end
|
|
256
254
|
end
|
|
257
255
|
# rubocop:enable Metrics/ModuleLength, Metrics/CyclomaticComplexity
|
|
@@ -36,10 +36,21 @@ module RuboCop
|
|
|
36
36
|
cop_config.fetch('IncludedMacros', []).map(&:to_sym)
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
+
def included_macro_patterns
|
|
40
|
+
cop_config.fetch('IncludedMacroPatterns', [])
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def matches_included_macro_pattern?(method_name)
|
|
44
|
+
included_macro_patterns.any? do |pattern|
|
|
45
|
+
Regexp.new(pattern).match?(method_name.to_s)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
39
49
|
def ignored_macro?(node)
|
|
40
50
|
cop_config['IgnoreMacros'] &&
|
|
41
51
|
node.macro? &&
|
|
42
|
-
!included_macros_list.include?(node.method_name)
|
|
52
|
+
!included_macros_list.include?(node.method_name) &&
|
|
53
|
+
!matches_included_macro_pattern?(node.method_name)
|
|
43
54
|
end
|
|
44
55
|
end
|
|
45
56
|
end
|
|
@@ -9,17 +9,20 @@ module RuboCop
|
|
|
9
9
|
# In the default style (require_parentheses), macro methods are allowed.
|
|
10
10
|
# Additional methods can be added to the `AllowedMethods` or
|
|
11
11
|
# `AllowedPatterns` list. These options are valid only in the default
|
|
12
|
-
# style. Macros can be included by either setting `IgnoreMacros` to false
|
|
13
|
-
#
|
|
12
|
+
# style. Macros can be included by either setting `IgnoreMacros` to false,
|
|
13
|
+
# adding specific macros to the `IncludedMacros` list, or using
|
|
14
|
+
# `IncludedMacroPatterns` for pattern-based matching.
|
|
14
15
|
#
|
|
15
16
|
# Precedence of options is as follows:
|
|
16
17
|
#
|
|
17
18
|
# 1. `AllowedMethods`
|
|
18
19
|
# 2. `AllowedPatterns`
|
|
19
20
|
# 3. `IncludedMacros`
|
|
21
|
+
# 4. `IncludedMacroPatterns`
|
|
20
22
|
#
|
|
21
|
-
# If a method is listed in both `IncludedMacros`
|
|
22
|
-
# then the latter takes precedence (that is, the
|
|
23
|
+
# If a method is listed in both `IncludedMacros`/`IncludedMacroPatterns`
|
|
24
|
+
# and `AllowedMethods`, then the latter takes precedence (that is, the
|
|
25
|
+
# method is allowed).
|
|
23
26
|
#
|
|
24
27
|
# In the alternative style (omit_parentheses), there are three additional
|
|
25
28
|
# options.
|
|
@@ -132,6 +135,32 @@ module RuboCop
|
|
|
132
135
|
# bar :baz
|
|
133
136
|
# end
|
|
134
137
|
#
|
|
138
|
+
# @example AllowedMethods: ["puts", "print"]
|
|
139
|
+
#
|
|
140
|
+
# # good
|
|
141
|
+
# puts "Hello world"
|
|
142
|
+
# print "Hello world"
|
|
143
|
+
# # still enforces parentheses on other methods
|
|
144
|
+
# array.delete(e)
|
|
145
|
+
#
|
|
146
|
+
# @example AllowedPatterns: ["^assert"]
|
|
147
|
+
#
|
|
148
|
+
# # good
|
|
149
|
+
# assert_equal 'test', x
|
|
150
|
+
# assert_match(/foo/, bar)
|
|
151
|
+
# # still enforces parentheses on other methods
|
|
152
|
+
# array.delete(e)
|
|
153
|
+
#
|
|
154
|
+
# @example IncludedMacroPatterns: ["^assert", "^refute"]
|
|
155
|
+
#
|
|
156
|
+
# # bad
|
|
157
|
+
# assert_equal 'test', x
|
|
158
|
+
# refute_nil value
|
|
159
|
+
#
|
|
160
|
+
# # good
|
|
161
|
+
# assert_equal('test', x)
|
|
162
|
+
# refute_nil(value)
|
|
163
|
+
#
|
|
135
164
|
# @example AllowParenthesesInMultilineCall: false (default)
|
|
136
165
|
#
|
|
137
166
|
# # bad
|
|
@@ -39,13 +39,21 @@ module RuboCop
|
|
|
39
39
|
include RangeHelp
|
|
40
40
|
|
|
41
41
|
MSG = 'Use `%<prefer>s` instead.'
|
|
42
|
-
|
|
42
|
+
GREATER_OPERATORS = %i[> >=].freeze
|
|
43
43
|
LESS_OPERATORS = %i[< <=].freeze
|
|
44
|
-
COMPARISON_OPERATORS =
|
|
44
|
+
COMPARISON_OPERATORS = (GREATER_OPERATORS + LESS_OPERATORS).to_set.freeze
|
|
45
|
+
|
|
46
|
+
# @!method comparison_condition(node, name)
|
|
47
|
+
def_node_matcher :comparison_condition, <<~PATTERN
|
|
48
|
+
{
|
|
49
|
+
(send $_lhs $COMPARISON_OPERATORS $_rhs)
|
|
50
|
+
(begin (send $_lhs $COMPARISON_OPERATORS $_rhs))
|
|
51
|
+
}
|
|
52
|
+
PATTERN
|
|
45
53
|
|
|
46
54
|
def on_if(node)
|
|
47
|
-
lhs, operator, rhs =
|
|
48
|
-
return unless
|
|
55
|
+
lhs, operator, rhs = comparison_condition(node.condition)
|
|
56
|
+
return unless operator
|
|
49
57
|
|
|
50
58
|
if_branch = node.if_branch
|
|
51
59
|
else_branch = node.else_branch
|
|
@@ -63,7 +71,7 @@ module RuboCop
|
|
|
63
71
|
|
|
64
72
|
def preferred_method(operator, lhs, rhs, if_branch, else_branch)
|
|
65
73
|
if lhs == if_branch && rhs == else_branch
|
|
66
|
-
|
|
74
|
+
GREATER_OPERATORS.include?(operator) ? 'max' : 'min'
|
|
67
75
|
elsif lhs == else_branch && rhs == if_branch
|
|
68
76
|
LESS_OPERATORS.include?(operator) ? 'max' : 'min'
|
|
69
77
|
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Style
|
|
6
|
+
# Checks for usage of `Module` methods returning arrays that can be replaced
|
|
7
|
+
# with equivalent predicates.
|
|
8
|
+
#
|
|
9
|
+
# Calling a method returning an array then checking if an element is inside
|
|
10
|
+
# it is much slower than using an equivalent predicate method. For example,
|
|
11
|
+
# `instance_methods.include?` will return an array of all public and protected
|
|
12
|
+
# instance methods in the module, then check if a given method is inside that
|
|
13
|
+
# array, while `method_defined?` will do direct method lookup, which is much
|
|
14
|
+
# faster and consumes less memory.
|
|
15
|
+
#
|
|
16
|
+
# @example
|
|
17
|
+
# # bad
|
|
18
|
+
# Array.instance_methods.include?(:size)
|
|
19
|
+
# Array.instance_methods.member?(:size)
|
|
20
|
+
# Array.instance_methods(true).include?(:size)
|
|
21
|
+
#
|
|
22
|
+
# Array.instance_methods(false).include?(:find)
|
|
23
|
+
#
|
|
24
|
+
# # good
|
|
25
|
+
# Array.method_defined?(:size)
|
|
26
|
+
#
|
|
27
|
+
# Array.method_defined?(:find, false)
|
|
28
|
+
#
|
|
29
|
+
class ModuleMemberExistenceCheck < Base
|
|
30
|
+
extend AutoCorrector
|
|
31
|
+
|
|
32
|
+
MSG = 'Use `%<replacement>s` instead.'
|
|
33
|
+
|
|
34
|
+
RESTRICT_ON_SEND = %i[instance_methods].freeze
|
|
35
|
+
|
|
36
|
+
# @!method instance_methods_inclusion?(node)
|
|
37
|
+
def_node_matcher :instance_methods_inclusion?, <<~PATTERN
|
|
38
|
+
(call
|
|
39
|
+
(call _ :instance_methods _?)
|
|
40
|
+
{:include? :member?}
|
|
41
|
+
_)
|
|
42
|
+
PATTERN
|
|
43
|
+
|
|
44
|
+
def on_send(node) # rubocop:disable Metrics/AbcSize
|
|
45
|
+
return unless (parent = node.parent)
|
|
46
|
+
return unless instance_methods_inclusion?(parent)
|
|
47
|
+
return unless simple_method_argument?(node) && simple_method_argument?(parent)
|
|
48
|
+
|
|
49
|
+
offense_range = node.location.selector.join(parent.source_range.end)
|
|
50
|
+
replacement =
|
|
51
|
+
if node.first_argument.nil? || node.first_argument.true_type?
|
|
52
|
+
"method_defined?(#{parent.first_argument.source})"
|
|
53
|
+
else
|
|
54
|
+
"method_defined?(#{parent.first_argument.source}, #{node.first_argument.source})"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
add_offense(offense_range, message: format(MSG, replacement: replacement)) do |corrector|
|
|
58
|
+
corrector.replace(offense_range, replacement)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
alias on_csend on_send
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
def simple_method_argument?(node)
|
|
66
|
+
return false if node.splat_argument? || node.block_argument?
|
|
67
|
+
return false if node.first_argument&.hash_type?
|
|
68
|
+
|
|
69
|
+
true
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -75,6 +75,8 @@ module RuboCop
|
|
|
75
75
|
end
|
|
76
76
|
|
|
77
77
|
def correction_exceeds_max_line_length?(node)
|
|
78
|
+
return false unless max_line_length
|
|
79
|
+
|
|
78
80
|
indentation_width(node) + definition_width(node) > max_line_length
|
|
79
81
|
end
|
|
80
82
|
|
|
@@ -85,10 +87,6 @@ module RuboCop
|
|
|
85
87
|
def definition_width(node)
|
|
86
88
|
node.source_range.begin.join(node.arguments.source_range.end).length
|
|
87
89
|
end
|
|
88
|
-
|
|
89
|
-
def max_line_length
|
|
90
|
-
config.for_cop('Layout/LineLength')['Max'] || 120
|
|
91
|
-
end
|
|
92
90
|
end
|
|
93
91
|
end
|
|
94
92
|
end
|
|
@@ -43,24 +43,26 @@ module RuboCop
|
|
|
43
43
|
# @!method nil_check?(node)
|
|
44
44
|
def_node_matcher :nil_check?, '(send _ :nil?)'
|
|
45
45
|
|
|
46
|
+
# rubocop:disable Metrics/AbcSize
|
|
46
47
|
def on_send(node)
|
|
47
48
|
return unless node.receiver
|
|
48
49
|
|
|
49
50
|
style_check?(node) do
|
|
50
51
|
add_offense(node.loc.selector) do |corrector|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
52
|
+
if prefer_comparison?
|
|
53
|
+
range = node.loc.dot.join(node.loc.selector.end)
|
|
54
|
+
corrector.replace(range, ' == nil')
|
|
55
|
+
else
|
|
56
|
+
range = node.receiver.source_range.end.join(node.source_range.end)
|
|
57
|
+
corrector.replace(range, '.nil?')
|
|
58
|
+
end
|
|
58
59
|
|
|
59
60
|
parent = node.parent
|
|
60
61
|
corrector.wrap(node, '(', ')') if parent.respond_to?(:method?) && parent.method?(:!)
|
|
61
62
|
end
|
|
62
63
|
end
|
|
63
64
|
end
|
|
65
|
+
# rubocop:enable Metrics/AbcSize
|
|
64
66
|
|
|
65
67
|
private
|
|
66
68
|
|
|
@@ -55,19 +55,21 @@ module RuboCop
|
|
|
55
55
|
include OnNormalIfUnless
|
|
56
56
|
extend AutoCorrector
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
MSG_SUFFIX = 'over single-line `%<keyword>s/then/else/end` constructs.'
|
|
59
|
+
MSG_TERNARY = "Favor the ternary operator (`?:`) #{MSG_SUFFIX}"
|
|
60
|
+
MSG_MULTILINE = "Favor multi-line `%<keyword>s` #{MSG_SUFFIX}"
|
|
60
61
|
|
|
61
62
|
def on_normal_if_unless(node)
|
|
62
63
|
return unless node.single_line?
|
|
63
64
|
return unless node.else_branch
|
|
64
65
|
return if node.elsif? || node.if_branch&.begin_type?
|
|
65
66
|
|
|
66
|
-
|
|
67
|
-
|
|
67
|
+
multiline = multiline?(node)
|
|
68
|
+
|
|
69
|
+
add_offense(node, message: message(node, multiline)) do |corrector|
|
|
68
70
|
next if part_of_ignored_node?(node)
|
|
69
71
|
|
|
70
|
-
autocorrect(corrector, node)
|
|
72
|
+
autocorrect(corrector, node, multiline)
|
|
71
73
|
|
|
72
74
|
ignore_node(node)
|
|
73
75
|
end
|
|
@@ -75,12 +77,18 @@ module RuboCop
|
|
|
75
77
|
|
|
76
78
|
private
|
|
77
79
|
|
|
78
|
-
def
|
|
79
|
-
|
|
80
|
+
def multiline?(node)
|
|
81
|
+
always_multiline? || cannot_replace_to_ternary?(node)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def message(node, multiline)
|
|
85
|
+
template = multiline ? MSG_MULTILINE : MSG_TERNARY
|
|
86
|
+
|
|
87
|
+
format(template, keyword: node.keyword)
|
|
80
88
|
end
|
|
81
89
|
|
|
82
|
-
def autocorrect(corrector, node)
|
|
83
|
-
if
|
|
90
|
+
def autocorrect(corrector, node, multiline)
|
|
91
|
+
if multiline
|
|
84
92
|
IfThenCorrector.new(node, indentation: configured_indentation_width).call(corrector)
|
|
85
93
|
else
|
|
86
94
|
corrector.replace(node, ternary_correction(node))
|
|
@@ -26,9 +26,10 @@ module RuboCop
|
|
|
26
26
|
splat kwsplat forwarded_args forwarded_restarg forwarded_kwrestarg block_pass
|
|
27
27
|
].freeze
|
|
28
28
|
|
|
29
|
-
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
|
29
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
30
30
|
def on_send(node)
|
|
31
31
|
return unless (dot = node.loc.dot)
|
|
32
|
+
return if unary_method_no_operator?(node)
|
|
32
33
|
return if node.receiver.const_type? || !node.arguments.one?
|
|
33
34
|
|
|
34
35
|
return unless (rhs = node.first_argument)
|
|
@@ -43,10 +44,18 @@ module RuboCop
|
|
|
43
44
|
corrector.insert_after(selector, ' ') if insert_space_after?(node)
|
|
44
45
|
end
|
|
45
46
|
end
|
|
46
|
-
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
|
47
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
47
48
|
|
|
48
49
|
private
|
|
49
50
|
|
|
51
|
+
# `foo.~@` and `foo.!@` call the method `~` and `!` respectively. While those
|
|
52
|
+
# are operator methods, we don't want to actually consider them as such.
|
|
53
|
+
def unary_method_no_operator?(node)
|
|
54
|
+
return false unless node.nonmutating_unary_operator_method?
|
|
55
|
+
|
|
56
|
+
node.method_name.to_s != node.selector.source
|
|
57
|
+
end
|
|
58
|
+
|
|
50
59
|
# Checks for an acceptable case of `foo.+(bar).baz`.
|
|
51
60
|
def method_call_with_parenthesized_arg?(argument)
|
|
52
61
|
return false unless argument.parent.parent&.send_type?
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'tsort'
|
|
4
|
-
|
|
5
3
|
module RuboCop
|
|
6
4
|
module Cop
|
|
7
5
|
module Style
|
|
@@ -16,7 +14,7 @@ module RuboCop
|
|
|
16
14
|
#
|
|
17
15
|
# # good
|
|
18
16
|
# one, two = *foo
|
|
19
|
-
# a, b = foo
|
|
17
|
+
# a, b = foo
|
|
20
18
|
# a, b = b, a
|
|
21
19
|
#
|
|
22
20
|
# a = 1
|
|
@@ -29,6 +27,8 @@ module RuboCop
|
|
|
29
27
|
MSG = 'Do not use parallel assignment.'
|
|
30
28
|
|
|
31
29
|
def on_masgn(node) # rubocop:disable Metrics/AbcSize
|
|
30
|
+
return if part_of_ignored_node?(node)
|
|
31
|
+
|
|
32
32
|
rhs = node.rhs
|
|
33
33
|
rhs = rhs.body if rhs.rescue_type?
|
|
34
34
|
rhs_elements = Array(rhs).compact # edge case for one constant
|
|
@@ -41,6 +41,7 @@ module RuboCop
|
|
|
41
41
|
add_offense(range) do |corrector|
|
|
42
42
|
autocorrect(corrector, node, rhs)
|
|
43
43
|
end
|
|
44
|
+
ignore_node(node)
|
|
44
45
|
end
|
|
45
46
|
|
|
46
47
|
private
|
|
@@ -91,15 +92,9 @@ module RuboCop
|
|
|
91
92
|
def find_valid_order(left_elements, right_elements)
|
|
92
93
|
# arrange left_elements in an order such that no corresponding right
|
|
93
94
|
# element refers to a left element earlier in the sequence
|
|
94
|
-
# this can be done using an algorithm called a "topological sort"
|
|
95
|
-
# fortunately for us, Ruby's stdlib contains an implementation
|
|
96
95
|
assignments = left_elements.zip(right_elements)
|
|
97
96
|
|
|
98
|
-
|
|
99
|
-
AssignmentSorter.new(assignments).tsort
|
|
100
|
-
rescue TSort::Cyclic
|
|
101
|
-
nil
|
|
102
|
-
end
|
|
97
|
+
AssignmentSorter.new(assignments).tsort
|
|
103
98
|
end
|
|
104
99
|
|
|
105
100
|
# Converts (send nil :something) nodes to (send (:self) :something).
|
|
@@ -114,10 +109,9 @@ module RuboCop
|
|
|
114
109
|
# @!method implicit_self_getter?(node)
|
|
115
110
|
def_node_matcher :implicit_self_getter?, '(send nil? $_)'
|
|
116
111
|
|
|
117
|
-
#
|
|
118
|
-
#
|
|
112
|
+
# Topologically sorts the assignments with Kahn's algorithm.
|
|
113
|
+
# https://en.wikipedia.org/wiki/Topological_sorting#Kahn's_algorithm
|
|
119
114
|
class AssignmentSorter
|
|
120
|
-
include TSort
|
|
121
115
|
extend RuboCop::NodePattern::Macros
|
|
122
116
|
|
|
123
117
|
# @!method var_name(node)
|
|
@@ -133,21 +127,39 @@ module RuboCop
|
|
|
133
127
|
@assignments = assignments
|
|
134
128
|
end
|
|
135
129
|
|
|
136
|
-
def
|
|
137
|
-
@assignments.
|
|
130
|
+
def tsort
|
|
131
|
+
dependencies = @assignments.to_h do |assignment|
|
|
132
|
+
[assignment, dependencies_for_assignment(assignment)]
|
|
133
|
+
end
|
|
134
|
+
result = []
|
|
135
|
+
|
|
136
|
+
while (matched_node, = dependencies.find { |_node, edges| edges.empty? })
|
|
137
|
+
dependencies.delete(matched_node)
|
|
138
|
+
result.push(matched_node)
|
|
139
|
+
|
|
140
|
+
dependencies.each do |node, edges|
|
|
141
|
+
dependencies[node].delete(matched_node) if edges.include?(matched_node)
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
# Cyclic dependency
|
|
145
|
+
return nil if dependencies.any?
|
|
146
|
+
|
|
147
|
+
result
|
|
138
148
|
end
|
|
139
149
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
150
|
+
# Returns all the assignments which must come after `assignment`
|
|
151
|
+
# (due to dependencies on the previous value of the assigned var)
|
|
152
|
+
def dependencies_for_assignment(assignment)
|
|
143
153
|
my_lhs, _my_rhs = *assignment
|
|
144
154
|
|
|
145
|
-
@assignments.
|
|
146
|
-
|
|
155
|
+
@assignments.filter_map do |other|
|
|
156
|
+
# Exclude self, there are no dependencies in cases such as `a, b = a, b`.
|
|
157
|
+
next if other == assignment
|
|
147
158
|
|
|
159
|
+
_other_lhs, other_rhs = *other
|
|
148
160
|
next unless dependency?(my_lhs, other_rhs)
|
|
149
161
|
|
|
150
|
-
|
|
162
|
+
other
|
|
151
163
|
end
|
|
152
164
|
end
|
|
153
165
|
|
|
@@ -211,7 +223,7 @@ module RuboCop
|
|
|
211
223
|
# __FILE__ is treated as a StrNode but has no begin
|
|
212
224
|
if node.str_type? && loc.respond_to?(:begin) && loc.begin.nil?
|
|
213
225
|
"'#{node.source}'"
|
|
214
|
-
elsif node.sym_type? && loc
|
|
226
|
+
elsif node.sym_type? && !node.loc?(:begin)
|
|
215
227
|
":#{node.source}"
|
|
216
228
|
else
|
|
217
229
|
node.source
|
|
@@ -37,6 +37,7 @@ module RuboCop
|
|
|
37
37
|
# array.sum(0)
|
|
38
38
|
# exit(true)
|
|
39
39
|
# exit!(false)
|
|
40
|
+
# string.to_i(10)
|
|
40
41
|
# string.split(" ")
|
|
41
42
|
# "first\nsecond".split(" ")
|
|
42
43
|
# string.chomp("\n")
|
|
@@ -49,6 +50,7 @@ module RuboCop
|
|
|
49
50
|
# array.sum
|
|
50
51
|
# exit
|
|
51
52
|
# exit!
|
|
53
|
+
# string.to_i
|
|
52
54
|
# string.split
|
|
53
55
|
# "first second".split
|
|
54
56
|
# string.chomp
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Style
|
|
6
|
+
# Checks for redundant calls of `Array#flatten`.
|
|
7
|
+
#
|
|
8
|
+
# `Array#join` joins nested arrays recursively, so flattening an array
|
|
9
|
+
# beforehand is redundant.
|
|
10
|
+
#
|
|
11
|
+
# @safety
|
|
12
|
+
# Cop is unsafe because the receiver of `flatten` method might not
|
|
13
|
+
# be an `Array`, so it's possible it won't respond to `join` method,
|
|
14
|
+
# or the end result would be different.
|
|
15
|
+
# Also, if the global variable `$,` is set to a value other than the default `nil`,
|
|
16
|
+
# false positives may occur.
|
|
17
|
+
#
|
|
18
|
+
# @example
|
|
19
|
+
# # bad
|
|
20
|
+
# x.flatten.join
|
|
21
|
+
# x.flatten(1).join
|
|
22
|
+
#
|
|
23
|
+
# # good
|
|
24
|
+
# x.join
|
|
25
|
+
#
|
|
26
|
+
class RedundantArrayFlatten < Base
|
|
27
|
+
extend AutoCorrector
|
|
28
|
+
|
|
29
|
+
MSG = 'Remove the redundant `flatten`.'
|
|
30
|
+
|
|
31
|
+
RESTRICT_ON_SEND = %i[flatten].freeze
|
|
32
|
+
|
|
33
|
+
# @!method flatten_join?(node)
|
|
34
|
+
def_node_matcher :flatten_join?, <<~PATTERN
|
|
35
|
+
(call (call !nil? :flatten _?) :join (nil)?)
|
|
36
|
+
PATTERN
|
|
37
|
+
|
|
38
|
+
def on_send(node)
|
|
39
|
+
return unless flatten_join?(node.parent)
|
|
40
|
+
|
|
41
|
+
range = node.loc.dot.begin.join(node.source_range.end)
|
|
42
|
+
add_offense(range) do |corrector|
|
|
43
|
+
corrector.remove(range)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
alias on_csend on_send
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|