rubocop 1.84.2 → 1.86.2
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 +99 -16
- data/config/obsoletion.yml +5 -0
- data/lib/rubocop/cache_config.rb +1 -1
- data/lib/rubocop/cli/command/auto_generate_config.rb +28 -2
- data/lib/rubocop/cli/command/list_enabled_cops_for.rb +40 -0
- 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 +4 -8
- data/lib/rubocop/cli/command/suggest_extensions.rb +1 -1
- data/lib/rubocop/cli.rb +7 -7
- data/lib/rubocop/comment_config.rb +12 -15
- data/lib/rubocop/config.rb +14 -10
- data/lib/rubocop/config_finder.rb +1 -1
- data/lib/rubocop/config_loader_resolver.rb +2 -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/autocorrect_logic.rb +2 -1
- data/lib/rubocop/cop/correctors/condition_corrector.rb +1 -1
- data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +1 -5
- data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +2 -2
- data/lib/rubocop/cop/correctors.rb +28 -0
- data/lib/rubocop/cop/documentation.rb +2 -3
- data/lib/rubocop/cop/exclude_limit.rb +31 -5
- data/lib/rubocop/cop/gemspec/require_mfa.rb +4 -4
- data/lib/rubocop/cop/internal_affairs/itblock_handler.rb +69 -0
- data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +1 -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 +6 -3
- 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/line_length.rb +5 -3
- data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +9 -2
- data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +1 -1
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +53 -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 +55 -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/parentheses_as_grouped_expression.rb +3 -13
- 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/require_relative_self_path.rb +2 -0
- 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/syntax.rb +25 -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/unused_method_argument.rb +10 -0
- data/lib/rubocop/cop/lint/useless_assignment.rb +4 -9
- 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 +35 -9
- 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/configurable_max.rb +6 -5
- 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/mixin.rb +85 -0
- data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
- data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
- data/lib/rubocop/cop/offense.rb +8 -0
- data/lib/rubocop/cop/registry.rb +39 -37
- data/lib/rubocop/cop/security/eval.rb +15 -2
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +14 -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 +22 -11
- data/lib/rubocop/cop/style/date_time.rb +2 -2
- data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +6 -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/guard_clause.rb +9 -6
- data/lib/rubocop/cop/style/hash_as_last_array_item.rb +21 -5
- data/lib/rubocop/cop/style/hash_lookup_method.rb +19 -7
- 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_inside_else.rb +16 -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/module_member_existence_check.rb +7 -14
- 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 +115 -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 +200 -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_line_continuation.rb +16 -0
- data/lib/rubocop/cop/style/redundant_min_max_by.rb +93 -0
- data/lib/rubocop/cop/style/redundant_parentheses.rb +25 -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.rb +2 -2
- data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +0 -5
- data/lib/rubocop/cop/style/redundant_struct_keyword_init.rb +114 -0
- data/lib/rubocop/cop/style/regexp_literal.rb +29 -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/sole_nested_conditional.rb +4 -2
- data/lib/rubocop/cop/style/special_global_vars.rb +6 -1
- data/lib/rubocop/cop/style/symbol_proc.rb +7 -6
- 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/while_until_modifier.rb +16 -0
- data/lib/rubocop/cop/style/yoda_expression.rb +1 -1
- data/lib/rubocop/cop/team.rb +86 -35
- 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 +5 -2
- 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/lsp/runtime.rb +1 -2
- data/lib/rubocop/mcp/server.rb +200 -0
- data/lib/rubocop/options.rb +17 -4
- 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/cop_helper.rb +8 -0
- data/lib/rubocop/rspec/shared_contexts.rb +32 -2
- data/lib/rubocop/runner.rb +78 -51
- data/lib/rubocop/server/cache.rb +5 -7
- data/lib/rubocop/server/core.rb +2 -0
- data/lib/rubocop/target_finder.rb +14 -7
- data/lib/rubocop/target_ruby.rb +18 -12
- data/lib/rubocop/version.rb +2 -2
- data/lib/rubocop.rb +21 -96
- metadata +25 -5
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Style
|
|
6
|
+
# Looks for places where a subset of an Enumerable (array,
|
|
7
|
+
# range, set, etc.; see note below) is calculated based on a range
|
|
8
|
+
# check, and suggests `grep` or `grep_v` instead.
|
|
9
|
+
#
|
|
10
|
+
# NOTE: Hashes do not behave as you may expect with `grep`, which
|
|
11
|
+
# means that `hash.grep` is not equivalent to `hash.select`. Although
|
|
12
|
+
# RuboCop is limited by static analysis, this cop attempts to avoid
|
|
13
|
+
# registering an offense when the receiver is a hash (hash literal,
|
|
14
|
+
# `Hash.new`, `Hash#[]`, or `to_h`/`to_hash`).
|
|
15
|
+
#
|
|
16
|
+
# @safety
|
|
17
|
+
# Autocorrection is marked as unsafe because the cop cannot guarantee
|
|
18
|
+
# that the receiver is actually an array by static analysis, so the
|
|
19
|
+
# correction may not be actually equivalent.
|
|
20
|
+
#
|
|
21
|
+
# @example
|
|
22
|
+
# # bad (select or find_all)
|
|
23
|
+
# array.select { |x| x.between?(1, 10) }
|
|
24
|
+
# array.select { |x| (1..10).cover?(x) }
|
|
25
|
+
# array.select { |x| (1..10).include?(x) }
|
|
26
|
+
#
|
|
27
|
+
# # bad (reject)
|
|
28
|
+
# array.reject { |x| x.between?(1, 10) }
|
|
29
|
+
#
|
|
30
|
+
# # bad (find or detect)
|
|
31
|
+
# array.find { |x| x.between?(1, 10) }
|
|
32
|
+
# array.detect { |x| (1..10).cover?(x) }
|
|
33
|
+
#
|
|
34
|
+
# # bad (negative form)
|
|
35
|
+
# array.reject { |x| !x.between?(1, 10) }
|
|
36
|
+
# array.find { |x| !(1..10).cover?(x) }
|
|
37
|
+
#
|
|
38
|
+
# # good
|
|
39
|
+
# array.grep(1..10)
|
|
40
|
+
# array.grep_v(1..10)
|
|
41
|
+
# array.grep(1..10).first
|
|
42
|
+
# array.grep_v(1..10).first
|
|
43
|
+
class SelectByRange < Base
|
|
44
|
+
extend AutoCorrector
|
|
45
|
+
include RangeHelp
|
|
46
|
+
|
|
47
|
+
MSG = 'Prefer `%<replacement>s` to `%<original_method>s` with a range check.'
|
|
48
|
+
RESTRICT_ON_SEND = %i[select filter find_all reject find detect].freeze
|
|
49
|
+
SELECT_METHODS = %i[select filter find_all].freeze
|
|
50
|
+
FIND_METHODS = %i[find detect].freeze
|
|
51
|
+
|
|
52
|
+
# @!method range_check?(node)
|
|
53
|
+
# Matches: x.between?(min, max) or (min..max).cover?(x) or (min..max).include?(x)
|
|
54
|
+
def_node_matcher :range_check?, <<~PATTERN
|
|
55
|
+
{
|
|
56
|
+
(block call (args (arg $_)) ${(send (lvar _) :between? _ _)})
|
|
57
|
+
(block call (args (arg $_)) ${(send {range (begin range)} {:cover? :include?} (lvar _))})
|
|
58
|
+
(block call (args (arg $_)) ${(send (send (lvar _) :between? _ _) :!)})
|
|
59
|
+
(block call (args (arg $_)) ${(send (send {range (begin range)} {:cover? :include?} (lvar _)) :!)})
|
|
60
|
+
(block call (args (arg $_)) ${(send (begin (send (lvar _) :between? _ _)) :!)})
|
|
61
|
+
(block call (args (arg $_)) ${(send (begin (send {range (begin range)} {:cover? :include?} (lvar _))) :!)})
|
|
62
|
+
(numblock call $1 ${(send (lvar _) :between? _ _)})
|
|
63
|
+
(numblock call $1 ${(send {range (begin range)} {:cover? :include?} (lvar _))})
|
|
64
|
+
(numblock call $1 ${(send (send (lvar _) :between? _ _) :!)})
|
|
65
|
+
(numblock call $1 ${(send (send {range (begin range)} {:cover? :include?} (lvar _)) :!)})
|
|
66
|
+
(numblock call $1 ${(send (begin (send (lvar _) :between? _ _)) :!)})
|
|
67
|
+
(numblock call $1 ${(send (begin (send {range (begin range)} {:cover? :include?} (lvar _))) :!)})
|
|
68
|
+
(itblock call $_ ${(send (lvar _) :between? _ _)})
|
|
69
|
+
(itblock call $_ ${(send {range (begin range)} {:cover? :include?} (lvar _))})
|
|
70
|
+
(itblock call $_ ${(send (send (lvar _) :between? _ _) :!)})
|
|
71
|
+
(itblock call $_ ${(send (send {range (begin range)} {:cover? :include?} (lvar _)) :!)})
|
|
72
|
+
(itblock call $_ ${(send (begin (send (lvar _) :between? _ _)) :!)})
|
|
73
|
+
(itblock call $_ ${(send (begin (send {range (begin range)} {:cover? :include?} (lvar _))) :!)})
|
|
74
|
+
}
|
|
75
|
+
PATTERN
|
|
76
|
+
|
|
77
|
+
# Returns true if a node appears to return a hash
|
|
78
|
+
# @!method creates_hash?(node)
|
|
79
|
+
def_node_matcher :creates_hash?, <<~PATTERN
|
|
80
|
+
{
|
|
81
|
+
(call (const _ :Hash) {:new :[]} ...)
|
|
82
|
+
(block (call (const _ :Hash) :new ...) ...)
|
|
83
|
+
(call _ { :to_h :to_hash } ...)
|
|
84
|
+
}
|
|
85
|
+
PATTERN
|
|
86
|
+
|
|
87
|
+
# @!method env_const?(node)
|
|
88
|
+
def_node_matcher :env_const?, <<~PATTERN
|
|
89
|
+
(const {nil? cbase} :ENV)
|
|
90
|
+
PATTERN
|
|
91
|
+
|
|
92
|
+
# @!method between_call?(node, name)
|
|
93
|
+
def_node_matcher :between_call?, <<~PATTERN
|
|
94
|
+
(send (lvar %1) :between? _ _)
|
|
95
|
+
PATTERN
|
|
96
|
+
|
|
97
|
+
# @!method range_cover_call?(node, name)
|
|
98
|
+
def_node_matcher :range_cover_call?, <<~PATTERN
|
|
99
|
+
(send {range (begin range)} {:cover? :include?} (lvar %1))
|
|
100
|
+
PATTERN
|
|
101
|
+
|
|
102
|
+
def on_send(node)
|
|
103
|
+
return unless (block_node = node.block_node)
|
|
104
|
+
return if block_node.body&.begin_type?
|
|
105
|
+
return if receiver_allowed?(block_node.receiver)
|
|
106
|
+
return unless (range_check_send_node = extract_send_node(block_node))
|
|
107
|
+
|
|
108
|
+
replacement = replacement(range_check_send_node, node)
|
|
109
|
+
range_literal = find_range(range_check_send_node)
|
|
110
|
+
|
|
111
|
+
register_offense(node, block_node, range_literal, replacement)
|
|
112
|
+
end
|
|
113
|
+
alias on_csend on_send
|
|
114
|
+
|
|
115
|
+
private
|
|
116
|
+
|
|
117
|
+
def receiver_allowed?(node)
|
|
118
|
+
return false unless node
|
|
119
|
+
|
|
120
|
+
node.hash_type? || creates_hash?(node) || env_const?(node)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def replacement(range_check_send_node, node)
|
|
124
|
+
negated = negated?(range_check_send_node)
|
|
125
|
+
method_name = node.method_name
|
|
126
|
+
|
|
127
|
+
if SELECT_METHODS.include?(method_name)
|
|
128
|
+
negated ? 'grep_v' : 'grep'
|
|
129
|
+
elsif FIND_METHODS.include?(method_name)
|
|
130
|
+
negated ? 'grep_v(...).first' : 'grep(...).first'
|
|
131
|
+
else # reject
|
|
132
|
+
negated ? 'grep' : 'grep_v'
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def register_offense(node, block_node, range_literal, replacement)
|
|
137
|
+
message = format(MSG, replacement: replacement, original_method: node.method_name)
|
|
138
|
+
|
|
139
|
+
add_offense(block_node, message: message) do |corrector|
|
|
140
|
+
if range_literal
|
|
141
|
+
range = range_between(node.loc.selector.begin_pos, block_node.loc.end.end_pos)
|
|
142
|
+
grep_method = replacement.include?('grep_v') ? 'grep_v' : 'grep'
|
|
143
|
+
suffix = replacement.include?('.first') ? '.first' : ''
|
|
144
|
+
corrector.replace(range, "#{grep_method}(#{range_literal})#{suffix}")
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def extract_send_node(block_node)
|
|
150
|
+
return unless (block_arg_name, range_check_send_node = range_check?(block_node))
|
|
151
|
+
|
|
152
|
+
block_arg_name = :"_#{block_arg_name}" if block_node.numblock_type?
|
|
153
|
+
block_arg_name = :it if block_node.type?(:itblock)
|
|
154
|
+
|
|
155
|
+
inner_node = unwrap_negation(range_check_send_node)
|
|
156
|
+
|
|
157
|
+
range_check_send_node if calls_lvar_in_range_check?(inner_node, block_arg_name)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def calls_lvar_in_range_check?(node, block_arg_name)
|
|
161
|
+
between_call?(node, block_arg_name) || range_cover_call?(node, block_arg_name)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def negated?(range_check_send_node)
|
|
165
|
+
range_check_send_node.send_type? && range_check_send_node.method?(:!)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def unwrap_negation(node)
|
|
169
|
+
if node.send_type? && node.method?(:!)
|
|
170
|
+
receiver = node.receiver
|
|
171
|
+
receiver = receiver.children.first if receiver.begin_type?
|
|
172
|
+
receiver
|
|
173
|
+
else
|
|
174
|
+
node
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def find_range(node)
|
|
179
|
+
inner = unwrap_negation(node)
|
|
180
|
+
|
|
181
|
+
if inner.method?(:between?)
|
|
182
|
+
# x.between?(min, max) -> min..max
|
|
183
|
+
min = inner.first_argument.source
|
|
184
|
+
max = inner.arguments[1].source
|
|
185
|
+
"#{min}..#{max}"
|
|
186
|
+
else
|
|
187
|
+
# (min..max).cover?(x) or (min..max).include?(x)
|
|
188
|
+
receiver = inner.receiver
|
|
189
|
+
# Unwrap begin node from parentheses
|
|
190
|
+
receiver = receiver.children.first if receiver.begin_type?
|
|
191
|
+
receiver.source
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
@@ -39,6 +39,9 @@ module RuboCop
|
|
|
39
39
|
# array.reject { |x| x =~ /regexp/ }
|
|
40
40
|
# array.reject { |x| /regexp/ =~ x }
|
|
41
41
|
#
|
|
42
|
+
# # bad (negative form)
|
|
43
|
+
# array.reject { |x| !x.match? /regexp/ }
|
|
44
|
+
#
|
|
42
45
|
# # good
|
|
43
46
|
# array.grep(regexp)
|
|
44
47
|
# array.grep_v(regexp)
|
|
@@ -48,18 +51,19 @@ module RuboCop
|
|
|
48
51
|
|
|
49
52
|
MSG = 'Prefer `%<replacement>s` to `%<original_method>s` with a regexp match.'
|
|
50
53
|
RESTRICT_ON_SEND = %i[select filter find_all reject].freeze
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}.freeze
|
|
55
|
-
REGEXP_METHODS = %i[match? =~ !~].to_set.freeze
|
|
54
|
+
SELECT_METHODS = %i[select filter find_all].freeze
|
|
55
|
+
REGEXP_METHODS = %i[match? =~].to_set.freeze
|
|
56
|
+
REGEXP_METHODS_NEGATED = %i[!~].to_set.freeze
|
|
56
57
|
|
|
57
58
|
# @!method regexp_match?(node)
|
|
58
59
|
def_node_matcher :regexp_match?, <<~PATTERN
|
|
59
60
|
{
|
|
60
|
-
(block call (args (arg $_)) ${(send _ %REGEXP_METHODS _) match-with-lvasgn})
|
|
61
|
-
(
|
|
62
|
-
(
|
|
61
|
+
(block call (args (arg $_)) ${(send _ %REGEXP_METHODS _) (send _ %REGEXP_METHODS_NEGATED _) match-with-lvasgn})
|
|
62
|
+
(block call (args (arg $_)) ${(send (send _ %REGEXP_METHODS _) :!) (send (begin (send _ %REGEXP_METHODS _)) :!) (send match-with-lvasgn :!) (send (begin match-with-lvasgn) :!)})
|
|
63
|
+
(numblock call $1 ${(send _ %REGEXP_METHODS _) (send _ %REGEXP_METHODS_NEGATED _) match-with-lvasgn})
|
|
64
|
+
(numblock call $1 ${(send (send _ %REGEXP_METHODS _) :!) (send (begin (send _ %REGEXP_METHODS _)) :!) (send match-with-lvasgn :!) (send (begin match-with-lvasgn) :!)})
|
|
65
|
+
(itblock call $_ ${(send _ %REGEXP_METHODS _) (send _ %REGEXP_METHODS_NEGATED _) match-with-lvasgn})
|
|
66
|
+
(itblock call $_ ${(send (send _ %REGEXP_METHODS _) :!) (send (begin (send _ %REGEXP_METHODS _)) :!) (send match-with-lvasgn :!) (send (begin match-with-lvasgn) :!)})
|
|
63
67
|
}
|
|
64
68
|
PATTERN
|
|
65
69
|
|
|
@@ -84,6 +88,12 @@ module RuboCop
|
|
|
84
88
|
(send (lvar %1) ...)
|
|
85
89
|
(send ... (lvar %1))
|
|
86
90
|
(match-with-lvasgn regexp (lvar %1))
|
|
91
|
+
(send (send (lvar %1) ...) :!)
|
|
92
|
+
(send (send ... (lvar %1)) :!)
|
|
93
|
+
(send (match-with-lvasgn regexp (lvar %1)) :!)
|
|
94
|
+
(send (begin (send (lvar %1) ...)) :!)
|
|
95
|
+
(send (begin (send ... (lvar %1))) :!)
|
|
96
|
+
(send (begin (match-with-lvasgn regexp (lvar %1))) :!)
|
|
87
97
|
}
|
|
88
98
|
PATTERN
|
|
89
99
|
|
|
@@ -97,7 +107,7 @@ module RuboCop
|
|
|
97
107
|
return if match_predicate_without_receiver?(regexp_method_send_node)
|
|
98
108
|
|
|
99
109
|
replacement = replacement(regexp_method_send_node, node)
|
|
100
|
-
return if target_ruby_version <= 2.2 && replacement
|
|
110
|
+
return if target_ruby_version <= 2.2 && replacement.include?('grep_v')
|
|
101
111
|
|
|
102
112
|
regexp = find_regexp(regexp_method_send_node, block_node)
|
|
103
113
|
|
|
@@ -115,11 +125,14 @@ module RuboCop
|
|
|
115
125
|
end
|
|
116
126
|
|
|
117
127
|
def replacement(regexp_method_send_node, node)
|
|
118
|
-
|
|
119
|
-
|
|
128
|
+
negated = negated?(regexp_method_send_node)
|
|
120
129
|
method_name = node.method_name
|
|
121
130
|
|
|
122
|
-
|
|
131
|
+
if SELECT_METHODS.include?(method_name)
|
|
132
|
+
negated ? 'grep_v' : 'grep'
|
|
133
|
+
else # reject
|
|
134
|
+
negated ? 'grep' : 'grep_v'
|
|
135
|
+
end
|
|
123
136
|
end
|
|
124
137
|
|
|
125
138
|
def register_offense(node, block_node, regexp, replacement)
|
|
@@ -138,30 +151,47 @@ module RuboCop
|
|
|
138
151
|
return unless (block_arg_name, regexp_method_send_node = regexp_match?(block_node))
|
|
139
152
|
|
|
140
153
|
block_arg_name = :"_#{block_arg_name}" if block_node.numblock_type?
|
|
154
|
+
block_arg_name = :it if block_node.type?(:itblock)
|
|
141
155
|
|
|
142
156
|
return unless calls_lvar?(regexp_method_send_node, block_arg_name)
|
|
143
157
|
|
|
144
158
|
regexp_method_send_node
|
|
145
159
|
end
|
|
146
160
|
|
|
147
|
-
def
|
|
148
|
-
regexp_method_send_node.send_type? && regexp_method_send_node.method?(
|
|
161
|
+
def negated?(regexp_method_send_node)
|
|
162
|
+
return true if regexp_method_send_node.send_type? && regexp_method_send_node.method?(:!)
|
|
163
|
+
|
|
164
|
+
inner = unwrap_negation(regexp_method_send_node)
|
|
165
|
+
inner.send_type? && inner.method?(:!~)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def unwrap_negation(node)
|
|
169
|
+
if node.send_type? && node.method?(:!)
|
|
170
|
+
receiver = node.receiver
|
|
171
|
+
receiver = receiver.children.first if receiver.begin_type?
|
|
172
|
+
receiver
|
|
173
|
+
else
|
|
174
|
+
node
|
|
175
|
+
end
|
|
149
176
|
end
|
|
150
177
|
|
|
151
178
|
def find_regexp(node, block)
|
|
152
|
-
|
|
179
|
+
inner = unwrap_negation(node)
|
|
180
|
+
|
|
181
|
+
return inner.child_nodes.first if inner.match_with_lvasgn_type?
|
|
153
182
|
|
|
154
|
-
if
|
|
183
|
+
if inner.receiver.lvar_type? &&
|
|
155
184
|
(block.type?(:numblock, :itblock) ||
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
elsif
|
|
159
|
-
|
|
185
|
+
inner.receiver.source == block.first_argument.source)
|
|
186
|
+
inner.first_argument
|
|
187
|
+
elsif inner.first_argument&.lvar_type?
|
|
188
|
+
inner.receiver
|
|
160
189
|
end
|
|
161
190
|
end
|
|
162
191
|
|
|
163
192
|
def match_predicate_without_receiver?(node)
|
|
164
|
-
|
|
193
|
+
inner = unwrap_negation(node)
|
|
194
|
+
inner.send_type? && inner.method?(:match?) && inner.receiver.nil?
|
|
165
195
|
end
|
|
166
196
|
end
|
|
167
197
|
end
|
|
@@ -5,6 +5,8 @@ module RuboCop
|
|
|
5
5
|
module Style
|
|
6
6
|
# Checks for multiple expressions placed on the same line.
|
|
7
7
|
# It also checks for lines terminated with a semicolon.
|
|
8
|
+
# In idiomatic Ruby, each expression should be on its own line
|
|
9
|
+
# for readability.
|
|
8
10
|
#
|
|
9
11
|
# This cop has `AllowAsExpressionSeparator` configuration option.
|
|
10
12
|
# It allows `;` to separate several expressions on the same line.
|
|
@@ -33,7 +33,7 @@ module RuboCop
|
|
|
33
33
|
|
|
34
34
|
MSG = 'Name `%<method>s` block params `|%<params>s|`.'
|
|
35
35
|
|
|
36
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
36
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
37
37
|
return unless node.single_line?
|
|
38
38
|
|
|
39
39
|
return unless eligible_method?(node)
|
|
@@ -89,7 +89,7 @@ module RuboCop
|
|
|
89
89
|
end
|
|
90
90
|
|
|
91
91
|
def method_names
|
|
92
|
-
methods.map { |method| method_name(method).to_sym }
|
|
92
|
+
@method_names ||= methods.map { |method| method_name(method).to_sym }.freeze
|
|
93
93
|
end
|
|
94
94
|
|
|
95
95
|
def method_name(method)
|
|
@@ -38,7 +38,7 @@ module RuboCop
|
|
|
38
38
|
|
|
39
39
|
# rubocop:disable Metrics/AbcSize
|
|
40
40
|
def on_block(node)
|
|
41
|
-
return if
|
|
41
|
+
return if node.multiline? || node.braces?
|
|
42
42
|
return if single_line_blocks_preferred? && suitable_as_single_line?(node)
|
|
43
43
|
|
|
44
44
|
add_offense(node) do |corrector|
|
|
@@ -4,7 +4,9 @@ module RuboCop
|
|
|
4
4
|
module Cop
|
|
5
5
|
module Style
|
|
6
6
|
# Checks for single-line method definitions that contain a body.
|
|
7
|
-
#
|
|
7
|
+
# Single-line methods with a body are harder to read and debug
|
|
8
|
+
# than their multi-line equivalents. It will accept single-line
|
|
9
|
+
# methods with no body.
|
|
8
10
|
#
|
|
9
11
|
# Endless methods added in Ruby 3.0 are also accepted by this cop.
|
|
10
12
|
#
|
|
@@ -65,7 +65,10 @@ module RuboCop
|
|
|
65
65
|
|
|
66
66
|
message = format(MSG, conditional_type: node.keyword)
|
|
67
67
|
add_offense(if_branch.loc.keyword, message: message) do |corrector|
|
|
68
|
+
next if ignored_node?(node)
|
|
69
|
+
|
|
68
70
|
autocorrect(corrector, node, if_branch)
|
|
71
|
+
ignore_node(if_branch)
|
|
69
72
|
end
|
|
70
73
|
end
|
|
71
74
|
|
|
@@ -115,9 +118,8 @@ module RuboCop
|
|
|
115
118
|
end
|
|
116
119
|
|
|
117
120
|
def correct_node(corrector, node)
|
|
118
|
-
corrector.replace(node.loc.keyword, 'if') if node.unless?
|
|
121
|
+
corrector.replace(node.loc.keyword, 'if') if node.unless?
|
|
119
122
|
corrector.replace(node.condition, chainable_condition(node))
|
|
120
|
-
ignore_node(node)
|
|
121
123
|
end
|
|
122
124
|
|
|
123
125
|
def correct_for_guard_condition_style(corrector, node, if_branch)
|
|
@@ -4,7 +4,12 @@ module RuboCop
|
|
|
4
4
|
module Cop
|
|
5
5
|
module Style
|
|
6
6
|
# Looks for uses of Perl-style global variables.
|
|
7
|
-
#
|
|
7
|
+
# Perl-style global variables like `$;` or `$/` are cryptic
|
|
8
|
+
# and hard to understand without consulting documentation.
|
|
9
|
+
# The `English` library provides descriptive aliases like
|
|
10
|
+
# `$FIELD_SEPARATOR` and `$INPUT_RECORD_SEPARATOR`.
|
|
11
|
+
#
|
|
12
|
+
# Correcting to global variables in the `English` library
|
|
8
13
|
# will add a require statement to the top of the file if
|
|
9
14
|
# enabled by RequireEnglish config.
|
|
10
15
|
#
|
|
@@ -179,7 +179,7 @@ module RuboCop
|
|
|
179
179
|
return if allowed_method_name?(dispatch_node.method_name)
|
|
180
180
|
return if allow_if_method_has_argument?(node.send_node)
|
|
181
181
|
return if node.block_type? && destructuring_block_argument?(arguments_node)
|
|
182
|
-
return if allow_comments?
|
|
182
|
+
return if allow_comments?(node)
|
|
183
183
|
|
|
184
184
|
register_offense(node, method_name, dispatch_node.method_name)
|
|
185
185
|
end
|
|
@@ -260,10 +260,10 @@ module RuboCop
|
|
|
260
260
|
end
|
|
261
261
|
|
|
262
262
|
def begin_pos_for_replacement(node)
|
|
263
|
-
|
|
263
|
+
send_node = node.send_node
|
|
264
264
|
|
|
265
|
-
if
|
|
266
|
-
|
|
265
|
+
if send_node.parenthesized? && send_node.arguments.empty?
|
|
266
|
+
send_node.loc.begin.begin_pos
|
|
267
267
|
else
|
|
268
268
|
node.loc.begin.begin_pos
|
|
269
269
|
end
|
|
@@ -273,8 +273,9 @@ module RuboCop
|
|
|
273
273
|
!!cop_config.fetch('AllowMethodsWithArguments', false) && send_node.arguments.any?
|
|
274
274
|
end
|
|
275
275
|
|
|
276
|
-
def allow_comments?
|
|
277
|
-
cop_config.fetch('AllowComments', false)
|
|
276
|
+
def allow_comments?(node)
|
|
277
|
+
cop_config.fetch('AllowComments', false) && contains_comments?(node) &&
|
|
278
|
+
!comments_contain_disables?(node, name)
|
|
278
279
|
end
|
|
279
280
|
end
|
|
280
281
|
end
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Style
|
|
6
|
+
# Checks for manual counting patterns that can be replaced by `Enumerable#tally`.
|
|
7
|
+
#
|
|
8
|
+
# The cop detects the following patterns:
|
|
9
|
+
#
|
|
10
|
+
# - `each_with_object(Hash.new(0)) { |item, counts| counts[item] += 1 }`
|
|
11
|
+
# - `group_by(&:itself).transform_values(&:count)`
|
|
12
|
+
# - `group_by { |x| x }.transform_values(&:size)`
|
|
13
|
+
# - `group_by { |x| x }.transform_values { |v| v.length }`
|
|
14
|
+
#
|
|
15
|
+
# @safety
|
|
16
|
+
# This cop is unsafe because it cannot guarantee that the receiver
|
|
17
|
+
# is an `Enumerable` by static analysis, so the correction may
|
|
18
|
+
# not be actually equivalent.
|
|
19
|
+
#
|
|
20
|
+
# @example
|
|
21
|
+
# # bad
|
|
22
|
+
# array.each_with_object(Hash.new(0)) { |item, counts| counts[item] += 1 }
|
|
23
|
+
#
|
|
24
|
+
# # bad
|
|
25
|
+
# array.group_by(&:itself).transform_values(&:count)
|
|
26
|
+
#
|
|
27
|
+
# # bad
|
|
28
|
+
# array.group_by { |item| item }.transform_values(&:size)
|
|
29
|
+
#
|
|
30
|
+
# # bad
|
|
31
|
+
# array.group_by { |item| item }.transform_values { |v| v.length }
|
|
32
|
+
#
|
|
33
|
+
# # good
|
|
34
|
+
# array.tally
|
|
35
|
+
#
|
|
36
|
+
class TallyMethod < Base
|
|
37
|
+
extend AutoCorrector
|
|
38
|
+
extend TargetRubyVersion
|
|
39
|
+
include RangeHelp
|
|
40
|
+
|
|
41
|
+
minimum_target_ruby_version 2.7
|
|
42
|
+
|
|
43
|
+
MSG_EACH_WITH_OBJECT = 'Use `tally` instead of `each_with_object`.'
|
|
44
|
+
MSG_GROUP_BY = 'Use `tally` instead of `group_by` and `transform_values`.'
|
|
45
|
+
RESTRICT_ON_SEND = %i[each_with_object transform_values].freeze
|
|
46
|
+
COUNTING_METHODS = %i[count size length].to_set.freeze
|
|
47
|
+
|
|
48
|
+
# Pattern 1: collection.each_with_object(Hash.new(0)) { |elem, hash| hash[elem] += 1 }
|
|
49
|
+
# @!method tally_each_with_object?(node)
|
|
50
|
+
def_node_matcher :tally_each_with_object?, <<~PATTERN
|
|
51
|
+
{
|
|
52
|
+
(block
|
|
53
|
+
(call _ :each_with_object
|
|
54
|
+
(send (const {nil? cbase} :Hash) :new (int 0)))
|
|
55
|
+
(args (arg _elem) (arg _hash))
|
|
56
|
+
(op_asgn
|
|
57
|
+
(send (lvar _hash) :[] (lvar _elem)) :+ (int 1)))
|
|
58
|
+
(numblock
|
|
59
|
+
(call _ :each_with_object
|
|
60
|
+
(send (const {nil? cbase} :Hash) :new (int 0)))
|
|
61
|
+
2
|
|
62
|
+
(op_asgn
|
|
63
|
+
(send (lvar :_2) :[] (lvar :_1)) :+ (int 1)))
|
|
64
|
+
}
|
|
65
|
+
PATTERN
|
|
66
|
+
|
|
67
|
+
# Pattern 2: collection.group_by(&:itself).transform_values(&:count/size/length)
|
|
68
|
+
# @!method tally_group_by_symbol?(node)
|
|
69
|
+
def_node_matcher :tally_group_by_symbol?, <<~PATTERN
|
|
70
|
+
(call
|
|
71
|
+
(call _ :group_by (block_pass (sym :itself)))
|
|
72
|
+
:transform_values
|
|
73
|
+
(block_pass (sym %COUNTING_METHODS)))
|
|
74
|
+
PATTERN
|
|
75
|
+
|
|
76
|
+
# Pattern 3: collection.group_by { |x| x }.transform_values(&:count/size/length)
|
|
77
|
+
# @!method tally_group_by_identity_block?(node)
|
|
78
|
+
def_node_matcher :tally_group_by_identity_block?, <<~PATTERN
|
|
79
|
+
(call
|
|
80
|
+
{
|
|
81
|
+
(block (call _ :group_by) (args (arg _x)) (lvar _x))
|
|
82
|
+
(numblock (call _ :group_by) 1 (lvar :_1))
|
|
83
|
+
(itblock (call _ :group_by) :it (lvar :it))
|
|
84
|
+
}
|
|
85
|
+
:transform_values
|
|
86
|
+
(block_pass (sym %COUNTING_METHODS)))
|
|
87
|
+
PATTERN
|
|
88
|
+
|
|
89
|
+
# Pattern 4: collection.group_by(&:itself).transform_values { |v| v.count/size/length }
|
|
90
|
+
# collection.group_by { |x| x }.transform_values { |v| v.count/size/length }
|
|
91
|
+
# @!method tally_group_by_transform_block?(node)
|
|
92
|
+
def_node_matcher :tally_group_by_transform_block?, <<~PATTERN
|
|
93
|
+
{
|
|
94
|
+
(block
|
|
95
|
+
(call
|
|
96
|
+
{
|
|
97
|
+
(call _ :group_by (block_pass (sym :itself)))
|
|
98
|
+
(block (call _ :group_by) (args (arg _x)) (lvar _x))
|
|
99
|
+
(numblock (call _ :group_by) 1 (lvar :_1))
|
|
100
|
+
(itblock (call _ :group_by) :it (lvar :it))
|
|
101
|
+
}
|
|
102
|
+
:transform_values)
|
|
103
|
+
(args (arg _v))
|
|
104
|
+
(send (lvar _v) %COUNTING_METHODS))
|
|
105
|
+
(numblock
|
|
106
|
+
(call
|
|
107
|
+
{
|
|
108
|
+
(call _ :group_by (block_pass (sym :itself)))
|
|
109
|
+
(block (call _ :group_by) (args (arg _x)) (lvar _x))
|
|
110
|
+
(numblock (call _ :group_by) 1 (lvar :_1))
|
|
111
|
+
(itblock (call _ :group_by) :it (lvar :it))
|
|
112
|
+
}
|
|
113
|
+
:transform_values)
|
|
114
|
+
1
|
|
115
|
+
(send (lvar :_1) %COUNTING_METHODS))
|
|
116
|
+
(itblock
|
|
117
|
+
(call
|
|
118
|
+
{
|
|
119
|
+
(call _ :group_by (block_pass (sym :itself)))
|
|
120
|
+
(block (call _ :group_by) (args (arg _x)) (lvar _x))
|
|
121
|
+
(numblock (call _ :group_by) 1 (lvar :_1))
|
|
122
|
+
(itblock (call _ :group_by) :it (lvar :it))
|
|
123
|
+
}
|
|
124
|
+
:transform_values)
|
|
125
|
+
:it
|
|
126
|
+
(send (lvar :it) %COUNTING_METHODS))
|
|
127
|
+
}
|
|
128
|
+
PATTERN
|
|
129
|
+
def on_send(node)
|
|
130
|
+
if node.method?(:each_with_object)
|
|
131
|
+
check_each_with_object(node)
|
|
132
|
+
elsif node.method?(:transform_values)
|
|
133
|
+
check_transform_values(node)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
alias on_csend on_send
|
|
137
|
+
|
|
138
|
+
private
|
|
139
|
+
|
|
140
|
+
def check_each_with_object(node)
|
|
141
|
+
block_node = node.block_node
|
|
142
|
+
return unless block_node
|
|
143
|
+
return unless tally_each_with_object?(block_node)
|
|
144
|
+
|
|
145
|
+
add_offense(node.loc.selector, message: MSG_EACH_WITH_OBJECT) do |corrector|
|
|
146
|
+
corrector.replace(replacement_range(node, block_node), 'tally')
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def check_transform_values(node)
|
|
151
|
+
if tally_group_by_symbol?(node) || tally_group_by_identity_block?(node)
|
|
152
|
+
register_group_by_offense(node, node)
|
|
153
|
+
elsif (block_node = node.block_node) && tally_group_by_transform_block?(block_node)
|
|
154
|
+
register_group_by_offense(node, block_node)
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def register_group_by_offense(transform_node, end_node)
|
|
159
|
+
group_by_node = group_by_send_node(transform_node)
|
|
160
|
+
|
|
161
|
+
add_offense(group_by_node.loc.selector, message: MSG_GROUP_BY) do |corrector|
|
|
162
|
+
corrector.replace(replacement_range(group_by_node, end_node), 'tally')
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def group_by_send_node(transform_node)
|
|
167
|
+
receiver = transform_node.receiver
|
|
168
|
+
if receiver.type?(:any_block)
|
|
169
|
+
receiver.send_node
|
|
170
|
+
else
|
|
171
|
+
receiver
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def replacement_range(start_node, end_node)
|
|
176
|
+
range_between(start_node.loc.selector.begin_pos, end_node.source_range.end_pos)
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
@@ -64,7 +64,7 @@ module RuboCop
|
|
|
64
64
|
|
|
65
65
|
MSG = 'Useless trailing comma present in block arguments.'
|
|
66
66
|
|
|
67
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
67
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
68
68
|
# lambda literal (`->`) never has block arguments.
|
|
69
69
|
return if node.send_node.lambda_literal?
|
|
70
70
|
return unless useless_trailing_comma?(node)
|