rubocop 1.58.0 → 1.60.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/LICENSE.txt +1 -1
- data/README.md +3 -3
- data/config/default.yml +9 -1
- data/lib/rubocop/config.rb +0 -2
- data/lib/rubocop/config_loader.rb +0 -1
- data/lib/rubocop/config_validator.rb +0 -2
- data/lib/rubocop/cop/base.rb +6 -0
- data/lib/rubocop/cop/exclude_limit.rb +1 -1
- data/lib/rubocop/cop/layout/end_alignment.rb +5 -1
- data/lib/rubocop/cop/layout/first_array_element_indentation.rb +16 -1
- data/lib/rubocop/cop/layout/heredoc_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +1 -1
- data/lib/rubocop/cop/layout/redundant_line_break.rb +2 -1
- data/lib/rubocop/cop/layout/single_line_block_chain.rb +1 -0
- data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +2 -2
- data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +1 -1
- data/lib/rubocop/cop/lint/hash_compare_by_identity.rb +2 -1
- data/lib/rubocop/cop/lint/it_without_arguments_in_block.rb +56 -0
- data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +23 -2
- data/lib/rubocop/cop/lint/next_without_accumulator.rb +6 -21
- data/lib/rubocop/cop/lint/number_conversion.rb +9 -4
- data/lib/rubocop/cop/lint/redundant_with_index.rb +2 -2
- data/lib/rubocop/cop/lint/redundant_with_object.rb +2 -2
- data/lib/rubocop/cop/lint/self_assignment.rb +1 -0
- data/lib/rubocop/cop/lint/shadowed_argument.rb +1 -0
- data/lib/rubocop/cop/lint/syntax.rb +6 -3
- data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +2 -2
- data/lib/rubocop/cop/lint/useless_times.rb +1 -1
- data/lib/rubocop/cop/metrics/abc_size.rb +3 -3
- data/lib/rubocop/cop/mixin/configurable_formatting.rb +1 -0
- data/lib/rubocop/cop/naming/block_forwarding.rb +10 -2
- data/lib/rubocop/cop/naming/constant_name.rb +1 -2
- data/lib/rubocop/cop/registry.rb +4 -4
- data/lib/rubocop/cop/security/open.rb +2 -2
- data/lib/rubocop/cop/style/arguments_forwarding.rb +16 -3
- data/lib/rubocop/cop/style/array_first_last.rb +1 -1
- data/lib/rubocop/cop/style/class_check.rb +1 -0
- data/lib/rubocop/cop/style/collection_compact.rb +7 -6
- data/lib/rubocop/cop/style/combinable_loops.rb +11 -0
- data/lib/rubocop/cop/style/concat_array_literals.rb +1 -0
- data/lib/rubocop/cop/style/conditional_assignment.rb +1 -1
- data/lib/rubocop/cop/style/date_time.rb +5 -4
- data/lib/rubocop/cop/style/each_for_simple_loop.rb +7 -7
- data/lib/rubocop/cop/style/each_with_object.rb +2 -2
- data/lib/rubocop/cop/style/eval_with_location.rb +0 -11
- data/lib/rubocop/cop/style/exact_regexp_match.rb +2 -1
- data/lib/rubocop/cop/style/hash_each_methods.rb +29 -4
- data/lib/rubocop/cop/style/hash_except.rb +2 -1
- data/lib/rubocop/cop/style/identical_conditional_branches.rb +4 -1
- data/lib/rubocop/cop/style/invertible_unless_condition.rb +39 -2
- data/lib/rubocop/cop/style/map_compact_with_conditional_block.rb +3 -2
- data/lib/rubocop/cop/style/map_to_hash.rb +9 -4
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +14 -5
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +1 -3
- data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +20 -0
- data/lib/rubocop/cop/style/multiline_ternary_operator.rb +1 -3
- data/lib/rubocop/cop/style/next.rb +1 -1
- data/lib/rubocop/cop/style/numeric_literal_prefix.rb +1 -1
- data/lib/rubocop/cop/style/operator_method_call.rb +2 -2
- data/lib/rubocop/cop/style/parallel_assignment.rb +2 -2
- data/lib/rubocop/cop/style/parentheses_around_condition.rb +8 -0
- data/lib/rubocop/cop/style/redundant_argument.rb +1 -0
- data/lib/rubocop/cop/style/redundant_each.rb +7 -4
- data/lib/rubocop/cop/style/redundant_fetch_block.rb +3 -3
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +8 -1
- data/lib/rubocop/cop/style/redundant_parentheses.rb +15 -7
- data/lib/rubocop/cop/style/redundant_self.rb +17 -2
- data/lib/rubocop/cop/style/redundant_sort.rb +8 -7
- data/lib/rubocop/cop/style/redundant_sort_by.rb +2 -2
- data/lib/rubocop/cop/style/sample.rb +2 -1
- data/lib/rubocop/cop/style/select_by_regexp.rb +6 -5
- data/lib/rubocop/cop/style/single_argument_dig.rb +5 -2
- data/lib/rubocop/cop/style/slicing_with_range.rb +76 -10
- data/lib/rubocop/cop/style/string_chars.rb +1 -0
- data/lib/rubocop/cop/style/strip.rb +7 -4
- data/lib/rubocop/cop/style/symbol_proc.rb +36 -0
- data/lib/rubocop/cop/style/unpack_first.rb +11 -14
- data/lib/rubocop/cops_documentation_generator.rb +11 -1
- data/lib/rubocop/ext/regexp_node.rb +9 -4
- data/lib/rubocop/formatter/disabled_config_formatter.rb +17 -6
- data/lib/rubocop/formatter/json_formatter.rb +0 -1
- data/lib/rubocop/formatter.rb +1 -1
- data/lib/rubocop/lsp/routes.rb +1 -1
- data/lib/rubocop/options.rb +0 -8
- data/lib/rubocop/rspec/shared_contexts.rb +6 -0
- data/lib/rubocop/rspec/support.rb +1 -0
- data/lib/rubocop/server/cache.rb +0 -1
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +1 -0
- metadata +8 -7
- /data/lib/rubocop/formatter/{git_hub_actions_formatter.rb → github_actions_formatter.rb} +0 -0
| @@ -4,8 +4,8 @@ module RuboCop | |
| 4 4 | 
             
              module Cop
         | 
| 5 5 | 
             
                module Style
         | 
| 6 6 | 
             
                  # Checks for redundant dot before operator method call.
         | 
| 7 | 
            -
                  # The target operator methods are `|`, `^`, `&`,  | 
| 8 | 
            -
                  #  | 
| 7 | 
            +
                  # The target operator methods are `|`, `^`, `&`, ``<=>``, `==`, `===`, `=~`, `>`, `>=`, `<`,
         | 
| 8 | 
            +
                  # ``<=``, `<<`, `>>`, `+`, `-`, `*`, `/`, `%`, `**`, `~`, `!`, `!=`, and `!~`.
         | 
| 9 9 | 
             
                  #
         | 
| 10 10 | 
             
                  # @example
         | 
| 11 11 | 
             
                  #
         | 
| @@ -81,6 +81,7 @@ module RuboCop | |
| 81 81 | 
             
                      cond = node.condition
         | 
| 82 82 |  | 
| 83 83 | 
             
                      control_op_condition(cond) do |first_child, rest_children|
         | 
| 84 | 
            +
                        return if require_parentheses?(node, first_child)
         | 
| 84 85 | 
             
                        return if semicolon_separated_expressions?(first_child, rest_children)
         | 
| 85 86 | 
             
                        return if modifier_op?(first_child)
         | 
| 86 87 | 
             
                        return if parens_allowed?(cond)
         | 
| @@ -92,6 +93,13 @@ module RuboCop | |
| 92 93 | 
             
                      end
         | 
| 93 94 | 
             
                    end
         | 
| 94 95 |  | 
| 96 | 
            +
                    def require_parentheses?(node, condition_body)
         | 
| 97 | 
            +
                      return false if !node.while_type? && !node.until_type?
         | 
| 98 | 
            +
                      return false if !condition_body.block_type? && !condition_body.numblock_type?
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                      condition_body.send_node.block_literal? && condition_body.keywords?
         | 
| 101 | 
            +
                    end
         | 
| 102 | 
            +
             | 
| 95 103 | 
             
                    def semicolon_separated_expressions?(first_exp, rest_exps)
         | 
| 96 104 | 
             
                      return false unless (second_exp = rest_exps.first)
         | 
| 97 105 |  | 
| @@ -56,6 +56,7 @@ module RuboCop | |
| 56 56 | 
             
                        end
         | 
| 57 57 | 
             
                      end
         | 
| 58 58 | 
             
                    end
         | 
| 59 | 
            +
                    alias on_csend on_send
         | 
| 59 60 |  | 
| 60 61 | 
             
                    private
         | 
| 61 62 |  | 
| @@ -64,7 +65,7 @@ module RuboCop | |
| 64 65 | 
             
                      return if node.last_argument&.block_pass_type?
         | 
| 65 66 |  | 
| 66 67 | 
             
                      if node.method?(:each) && !node.parent&.block_type?
         | 
| 67 | 
            -
                        ancestor_node = node.each_ancestor(:send).detect do |ancestor|
         | 
| 68 | 
            +
                        ancestor_node = node.each_ancestor(:send, :csend).detect do |ancestor|
         | 
| 68 69 | 
             
                          ancestor.receiver == node &&
         | 
| 69 70 | 
             
                            (RESTRICT_ON_SEND.include?(ancestor.method_name) || ancestor.method?(:reverse_each))
         | 
| 70 71 | 
             
                        end
         | 
| @@ -83,10 +84,12 @@ module RuboCop | |
| 83 84 | 
             
                    # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
         | 
| 84 85 |  | 
| 85 86 | 
             
                    def range(node)
         | 
| 86 | 
            -
                       | 
| 87 | 
            -
             | 
| 87 | 
            +
                      return node.selector unless node.method?(:each)
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                      if node.parent.call_type?
         | 
| 90 | 
            +
                        node.selector.join(node.parent.loc.dot)
         | 
| 88 91 | 
             
                      else
         | 
| 89 | 
            -
                        node.loc.selector
         | 
| 92 | 
            +
                        node.loc.dot.join(node.selector)
         | 
| 90 93 | 
             
                      end
         | 
| 91 94 | 
             
                    end
         | 
| 92 95 |  | 
| @@ -47,7 +47,7 @@ module RuboCop | |
| 47 47 | 
             
                    # @!method redundant_fetch_block_candidate?(node)
         | 
| 48 48 | 
             
                    def_node_matcher :redundant_fetch_block_candidate?, <<~PATTERN
         | 
| 49 49 | 
             
                      (block
         | 
| 50 | 
            -
                        $( | 
| 50 | 
            +
                        $(call _ :fetch _)
         | 
| 51 51 | 
             
                        (args)
         | 
| 52 52 | 
             
                        ${nil? #basic_literal? #const_type?})
         | 
| 53 53 | 
             
                    PATTERN
         | 
| @@ -61,10 +61,10 @@ module RuboCop | |
| 61 61 | 
             
                        bad = build_bad_method(send, body)
         | 
| 62 62 |  | 
| 63 63 | 
             
                        add_offense(range, message: format(MSG, good: good, bad: bad)) do |corrector|
         | 
| 64 | 
            -
                           | 
| 64 | 
            +
                          _, _, key = send.children
         | 
| 65 65 | 
             
                          default_value = body ? body.source : 'nil'
         | 
| 66 66 |  | 
| 67 | 
            -
                          corrector.replace( | 
| 67 | 
            +
                          corrector.replace(range, "fetch(#{key.source}, #{default_value})")
         | 
| 68 68 | 
             
                        end
         | 
| 69 69 | 
             
                      end
         | 
| 70 70 | 
             
                    end
         | 
| @@ -94,7 +94,8 @@ module RuboCop | |
| 94 94 | 
             
                      !ends_with_backslash_without_comment?(range.source_line) ||
         | 
| 95 95 | 
             
                        string_concatenation?(range.source_line) ||
         | 
| 96 96 | 
             
                        start_with_arithmetic_operator?(processed_source[range.line]) ||
         | 
| 97 | 
            -
                        inside_string_literal_or_method_with_argument?(range)
         | 
| 97 | 
            +
                        inside_string_literal_or_method_with_argument?(range) ||
         | 
| 98 | 
            +
                        leading_dot_method_chain_with_blank_line?(range)
         | 
| 98 99 | 
             
                    end
         | 
| 99 100 |  | 
| 100 101 | 
             
                    def ends_with_backslash_without_comment?(source_line)
         | 
| @@ -113,6 +114,12 @@ module RuboCop | |
| 113 114 | 
             
                      end
         | 
| 114 115 | 
             
                    end
         | 
| 115 116 |  | 
| 117 | 
            +
                    def leading_dot_method_chain_with_blank_line?(range)
         | 
| 118 | 
            +
                      return false unless range.source_line.strip.start_with?('.', '&.')
         | 
| 119 | 
            +
             | 
| 120 | 
            +
                      processed_source[range.line].strip.empty?
         | 
| 121 | 
            +
                    end
         | 
| 122 | 
            +
             | 
| 116 123 | 
             
                    def redundant_line_continuation?(range)
         | 
| 117 124 | 
             
                      return true unless (node = find_node_for_line(range.line))
         | 
| 118 125 | 
             
                      return false if argument_newline?(node)
         | 
| @@ -22,9 +22,6 @@ module RuboCop | |
| 22 22 | 
             
                    # @!method square_brackets?(node)
         | 
| 23 23 | 
             
                    def_node_matcher :square_brackets?, '(send {(send _recv _msg) str array hash} :[] ...)'
         | 
| 24 24 |  | 
| 25 | 
            -
                    # @!method range_end?(node)
         | 
| 26 | 
            -
                    def_node_matcher :range_end?, '^^{irange erange}'
         | 
| 27 | 
            -
             | 
| 28 25 | 
             
                    # @!method method_node_and_args(node)
         | 
| 29 26 | 
             
                    def_node_matcher :method_node_and_args, '$(call _recv _msg $...)'
         | 
| 30 27 |  | 
| @@ -56,7 +53,7 @@ module RuboCop | |
| 56 53 | 
             
                    def ignore_syntax?(node)
         | 
| 57 54 | 
             
                      return false unless (parent = node.parent)
         | 
| 58 55 |  | 
| 59 | 
            -
                      parent.while_post_type? || parent.until_post_type? ||
         | 
| 56 | 
            +
                      parent.while_post_type? || parent.until_post_type? || parent.match_with_lvasgn_type? ||
         | 
| 60 57 | 
             
                        like_method_argument_parentheses?(parent)
         | 
| 61 58 | 
             
                    end
         | 
| 62 59 |  | 
| @@ -64,7 +61,8 @@ module RuboCop | |
| 64 61 | 
             
                      allowed_ancestor?(node) ||
         | 
| 65 62 | 
             
                        allowed_method_call?(node) ||
         | 
| 66 63 | 
             
                        allowed_multiple_expression?(node) ||
         | 
| 67 | 
            -
                        allowed_ternary?(node)
         | 
| 64 | 
            +
                        allowed_ternary?(node) ||
         | 
| 65 | 
            +
                        node.parent&.range_type?
         | 
| 68 66 | 
             
                    end
         | 
| 69 67 |  | 
| 70 68 | 
             
                    def allowed_ancestor?(node)
         | 
| @@ -144,12 +142,16 @@ module RuboCop | |
| 144 142 | 
             
                      return 'a literal' if disallowed_literal?(begin_node, node)
         | 
| 145 143 | 
             
                      return 'a variable' if node.variable?
         | 
| 146 144 | 
             
                      return 'a constant' if node.const_type?
         | 
| 147 | 
            -
                       | 
| 145 | 
            +
                      if node.lambda_or_proc? && (node.braces? || node.send_node.lambda_literal?)
         | 
| 146 | 
            +
                        return 'an expression'
         | 
| 147 | 
            +
                      end
         | 
| 148 148 | 
             
                      return 'an interpolated expression' if interpolation?(begin_node)
         | 
| 149 149 |  | 
| 150 150 | 
             
                      return if begin_node.chained?
         | 
| 151 151 |  | 
| 152 152 | 
             
                      if node.and_type? || node.or_type?
         | 
| 153 | 
            +
                        return if node.semantic_operator? && begin_node.parent
         | 
| 154 | 
            +
                        return if node.multiline? && allow_in_multiline_conditions?
         | 
| 153 155 | 
             
                        return if ALLOWED_NODE_TYPES.include?(begin_node.parent&.type)
         | 
| 154 156 | 
             
                        return if begin_node.parent&.if_type? && begin_node.parent&.ternary?
         | 
| 155 157 |  | 
| @@ -165,6 +167,13 @@ module RuboCop | |
| 165 167 | 
             
                    # @!method interpolation?(node)
         | 
| 166 168 | 
             
                    def_node_matcher :interpolation?, '[^begin ^^dstr]'
         | 
| 167 169 |  | 
| 170 | 
            +
                    def allow_in_multiline_conditions?
         | 
| 171 | 
            +
                      parentheses_around_condition_config = config.for_cop('Style/ParenthesesAroundCondition')
         | 
| 172 | 
            +
                      return false unless parentheses_around_condition_config['Enabled']
         | 
| 173 | 
            +
             | 
| 174 | 
            +
                      !!parentheses_around_condition_config['AllowInMultilineConditions']
         | 
| 175 | 
            +
                    end
         | 
| 176 | 
            +
             | 
| 168 177 | 
             
                    def check_send(begin_node, node)
         | 
| 169 178 | 
             
                      return check_unary(begin_node, node) if node.unary_operation?
         | 
| 170 179 |  | 
| @@ -229,7 +238,6 @@ module RuboCop | |
| 229 238 | 
             
                    def method_call_with_redundant_parentheses?(node)
         | 
| 230 239 | 
             
                      return false unless node.call_type?
         | 
| 231 240 | 
             
                      return false if node.prefix_not?
         | 
| 232 | 
            -
                      return false if range_end?(node)
         | 
| 233 241 |  | 
| 234 242 | 
             
                      send_node, args = method_node_and_args(node)
         | 
| 235 243 |  | 
| @@ -17,7 +17,8 @@ module RuboCop | |
| 17 17 | 
             
                  # protected scope, you cannot send private messages this way.
         | 
| 18 18 | 
             
                  #
         | 
| 19 19 | 
             
                  # Note we allow uses of `self` with operators because it would be awkward
         | 
| 20 | 
            -
                  # otherwise.
         | 
| 20 | 
            +
                  # otherwise. Also allows the use of `self.it` without arguments in blocks,
         | 
| 21 | 
            +
                  # as in `0.times { self.it }`, following `Lint/ItWithoutArgumentsInBlock` cop.
         | 
| 21 22 | 
             
                  #
         | 
| 22 23 | 
             
                  # @example
         | 
| 23 24 | 
             
                  #
         | 
| @@ -107,8 +108,8 @@ module RuboCop | |
| 107 108 | 
             
                    def on_send(node)
         | 
| 108 109 | 
             
                      return unless node.self_receiver? && regular_method_call?(node)
         | 
| 109 110 | 
             
                      return if node.parent&.mlhs_type?
         | 
| 110 | 
            -
             | 
| 111 111 | 
             
                      return if allowed_send_node?(node)
         | 
| 112 | 
            +
                      return if it_method_in_block?(node)
         | 
| 112 113 |  | 
| 113 114 | 
             
                      add_offense(node.receiver) do |corrector|
         | 
| 114 115 | 
             
                        corrector.remove(node.receiver)
         | 
| @@ -155,6 +156,20 @@ module RuboCop | |
| 155 156 | 
             
                        KERNEL_METHODS.include?(node.method_name)
         | 
| 156 157 | 
             
                    end
         | 
| 157 158 |  | 
| 159 | 
            +
                    # Respects `Lint/ItWithoutArgumentsInBlock` cop and the following Ruby 3.3's warning:
         | 
| 160 | 
            +
                    #
         | 
| 161 | 
            +
                    # $ ruby -e '0.times { begin; it; end }'
         | 
| 162 | 
            +
                    # -e:1: warning: `it` calls without arguments will refer to the first block param in
         | 
| 163 | 
            +
                    # Ruby 3.4; use it() or self.it
         | 
| 164 | 
            +
                    #
         | 
| 165 | 
            +
                    def it_method_in_block?(node)
         | 
| 166 | 
            +
                      return false unless node.method?(:it)
         | 
| 167 | 
            +
                      return false unless (block_node = node.each_ancestor(:block).first)
         | 
| 168 | 
            +
                      return false unless block_node.arguments.empty_and_without_delimiters?
         | 
| 169 | 
            +
             | 
| 170 | 
            +
                      node.arguments.empty? && !node.block_literal?
         | 
| 171 | 
            +
                    end
         | 
| 172 | 
            +
             | 
| 158 173 | 
             
                    def regular_method_call?(node)
         | 
| 159 174 | 
             
                      !(node.operator_method? ||
         | 
| 160 175 | 
             
                        KEYWORDS.include?(node.method_name) ||
         | 
| @@ -16,7 +16,7 @@ module RuboCop | |
| 16 16 | 
             
                  #   This cop is unsafe, because `sort...last` and `max` may not return the
         | 
| 17 17 | 
             
                  #   same element in all cases.
         | 
| 18 18 | 
             
                  #
         | 
| 19 | 
            -
                  #   In an enumerable where there are multiple elements where  | 
| 19 | 
            +
                  #   In an enumerable where there are multiple elements where ``a <=> b == 0``,
         | 
| 20 20 | 
             
                  #   or where the transformation done by the `sort_by` block has the
         | 
| 21 21 | 
             
                  #   same result, `sort.last` and `max` (or `sort_by.last` and `max_by`)
         | 
| 22 22 | 
             
                  #   will return different elements. `sort.last` will return the last
         | 
| @@ -87,15 +87,15 @@ module RuboCop | |
| 87 87 | 
             
                    # @!method redundant_sort?(node)
         | 
| 88 88 | 
             
                    def_node_matcher :redundant_sort?, <<~MATCHER
         | 
| 89 89 | 
             
                      {
         | 
| 90 | 
            -
                        ( | 
| 91 | 
            -
                        ( | 
| 90 | 
            +
                        (call $(call _ $:sort) ${:last :first})
         | 
| 91 | 
            +
                        (call $(call _ $:sort) ${:[] :at :slice} {(int 0) (int -1)})
         | 
| 92 92 |  | 
| 93 | 
            -
                        ( | 
| 93 | 
            +
                        (call $(call _ $:sort_by _) ${:last :first})
         | 
| 94 94 | 
             
                        (send $(send _ $:sort_by _) ${:[] :at :slice} {(int 0) (int -1)})
         | 
| 95 95 |  | 
| 96 | 
            -
                        ( | 
| 97 | 
            -
                        ( | 
| 98 | 
            -
                          ({block numblock} $( | 
| 96 | 
            +
                        (call ({block numblock} $(call _ ${:sort_by :sort}) ...) ${:last :first})
         | 
| 97 | 
            +
                        (call
         | 
| 98 | 
            +
                          ({block numblock} $(call _ ${:sort_by :sort}) ...)
         | 
| 99 99 | 
             
                          ${:[] :at :slice} {(int 0) (int -1)}
         | 
| 100 100 | 
             
                        )
         | 
| 101 101 | 
             
                      }
         | 
| @@ -108,6 +108,7 @@ module RuboCop | |
| 108 108 |  | 
| 109 109 | 
             
                      register_offense(ancestor, sort_node, sorter, accessor)
         | 
| 110 110 | 
             
                    end
         | 
| 111 | 
            +
                    alias on_csend on_send
         | 
| 111 112 |  | 
| 112 113 | 
             
                    private
         | 
| 113 114 |  | 
| @@ -46,12 +46,12 @@ module RuboCop | |
| 46 46 |  | 
| 47 47 | 
             
                    # @!method redundant_sort_by_block(node)
         | 
| 48 48 | 
             
                    def_node_matcher :redundant_sort_by_block, <<~PATTERN
         | 
| 49 | 
            -
                      (block $( | 
| 49 | 
            +
                      (block $(call _ :sort_by) (args (arg $_x)) (lvar _x))
         | 
| 50 50 | 
             
                    PATTERN
         | 
| 51 51 |  | 
| 52 52 | 
             
                    # @!method redundant_sort_by_numblock(node)
         | 
| 53 53 | 
             
                    def_node_matcher :redundant_sort_by_numblock, <<~PATTERN
         | 
| 54 | 
            -
                      (numblock $( | 
| 54 | 
            +
                      (numblock $(call _ :sort_by) 1 (lvar :_1))
         | 
| 55 55 | 
             
                    PATTERN
         | 
| 56 56 |  | 
| 57 57 | 
             
                    def sort_by_range(send, node)
         | 
| @@ -35,7 +35,7 @@ module RuboCop | |
| 35 35 |  | 
| 36 36 | 
             
                    # @!method sample_candidate?(node)
         | 
| 37 37 | 
             
                    def_node_matcher :sample_candidate?, <<~PATTERN
         | 
| 38 | 
            -
                      ( | 
| 38 | 
            +
                      (call $(call _ :shuffle $...) ${:#{RESTRICT_ON_SEND.join(' :')}} $...)
         | 
| 39 39 | 
             
                    PATTERN
         | 
| 40 40 |  | 
| 41 41 | 
             
                    def on_send(node)
         | 
| @@ -52,6 +52,7 @@ module RuboCop | |
| 52 52 | 
             
                        end
         | 
| 53 53 | 
             
                      end
         | 
| 54 54 | 
             
                    end
         | 
| 55 | 
            +
                    alias on_csend on_send
         | 
| 55 56 |  | 
| 56 57 | 
             
                    private
         | 
| 57 58 |  | 
| @@ -55,8 +55,8 @@ module RuboCop | |
| 55 55 | 
             
                    # @!method regexp_match?(node)
         | 
| 56 56 | 
             
                    def_node_matcher :regexp_match?, <<~PATTERN
         | 
| 57 57 | 
             
                      {
         | 
| 58 | 
            -
                        (block  | 
| 59 | 
            -
                        (numblock  | 
| 58 | 
            +
                        (block call (args (arg $_)) ${(send _ %REGEXP_METHODS _) match-with-lvasgn})
         | 
| 59 | 
            +
                        (numblock call $1 ${(send _ %REGEXP_METHODS _) match-with-lvasgn})
         | 
| 60 60 | 
             
                      }
         | 
| 61 61 | 
             
                    PATTERN
         | 
| 62 62 |  | 
| @@ -64,9 +64,9 @@ module RuboCop | |
| 64 64 | 
             
                    # @!method creates_hash?(node)
         | 
| 65 65 | 
             
                    def_node_matcher :creates_hash?, <<~PATTERN
         | 
| 66 66 | 
             
                      {
         | 
| 67 | 
            -
                        ( | 
| 68 | 
            -
                        (block ( | 
| 69 | 
            -
                        ( | 
| 67 | 
            +
                        (call (const _ :Hash) {:new :[]} ...)
         | 
| 68 | 
            +
                        (block (call (const _ :Hash) :new ...) ...)
         | 
| 69 | 
            +
                        (call _ { :to_h :to_hash } ...)
         | 
| 70 70 | 
             
                      }
         | 
| 71 71 | 
             
                    PATTERN
         | 
| 72 72 |  | 
| @@ -100,6 +100,7 @@ module RuboCop | |
| 100 100 | 
             
                      register_offense(node, block_node, regexp, replacement)
         | 
| 101 101 | 
             
                    end
         | 
| 102 102 | 
             
                    # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
         | 
| 103 | 
            +
                    alias on_csend on_send
         | 
| 103 104 |  | 
| 104 105 | 
             
                    private
         | 
| 105 106 |  | 
| @@ -3,8 +3,11 @@ | |
| 3 3 | 
             
            module RuboCop
         | 
| 4 4 | 
             
              module Cop
         | 
| 5 5 | 
             
                module Style
         | 
| 6 | 
            -
                  # Sometimes using dig method ends up with just a single
         | 
| 7 | 
            -
                  # argument. In such cases, dig should be replaced with [] | 
| 6 | 
            +
                  # Sometimes using `dig` method ends up with just a single
         | 
| 7 | 
            +
                  # argument. In such cases, dig should be replaced with `[]`.
         | 
| 8 | 
            +
                  #
         | 
| 9 | 
            +
                  # Since replacing `hash&.dig(:key)` with `hash[:key]` could potentially lead to error,
         | 
| 10 | 
            +
                  # calls to the `dig` method using safe navigation will be ignored.
         | 
| 8 11 | 
             
                  #
         | 
| 9 12 | 
             
                  # @safety
         | 
| 10 13 | 
             
                  #   This cop is unsafe because it cannot be guaranteed that the receiver
         | 
| @@ -3,8 +3,9 @@ | |
| 3 3 | 
             
            module RuboCop
         | 
| 4 4 | 
             
              module Cop
         | 
| 5 5 | 
             
                module Style
         | 
| 6 | 
            -
                  # Checks that arrays are sliced with  | 
| 7 | 
            -
                  # `ary[start..-1]` on Ruby 2.6 | 
| 6 | 
            +
                  # Checks that arrays are not sliced with the redundant `ary[0..-1]`, replacing it with `ary`,
         | 
| 7 | 
            +
                  # and ensures arrays are sliced with endless ranges instead of `ary[start..-1]` on Ruby 2.6+,
         | 
| 8 | 
            +
                  # and with beginless ranges instead of `ary[nil..end]` on Ruby 2.7+.
         | 
| 8 9 | 
             
                  #
         | 
| 9 10 | 
             
                  # @safety
         | 
| 10 11 | 
             
                  #   This cop is unsafe because `x..-1` and `x..` are only guaranteed to
         | 
| @@ -21,29 +22,94 @@ module RuboCop | |
| 21 22 | 
             
                  #
         | 
| 22 23 | 
             
                  # @example
         | 
| 23 24 | 
             
                  #   # bad
         | 
| 24 | 
            -
                  #   items[ | 
| 25 | 
            +
                  #   items[0..-1]
         | 
| 26 | 
            +
                  #   items[0..nil]
         | 
| 27 | 
            +
                  #   items[0...nil]
         | 
| 25 28 | 
             
                  #
         | 
| 26 29 | 
             
                  #   # good
         | 
| 27 | 
            -
                  #   items | 
| 30 | 
            +
                  #   items
         | 
| 31 | 
            +
                  #
         | 
| 32 | 
            +
                  #   # bad
         | 
| 33 | 
            +
                  #   items[1..-1]   # Ruby 2.6+
         | 
| 34 | 
            +
                  #   items[1..nil]  # Ruby 2.6+
         | 
| 35 | 
            +
                  #
         | 
| 36 | 
            +
                  #   # good
         | 
| 37 | 
            +
                  #   items[1..]     # Ruby 2.6+
         | 
| 38 | 
            +
                  #
         | 
| 39 | 
            +
                  #   # bad
         | 
| 40 | 
            +
                  #   items[nil..42] # Ruby 2.7+
         | 
| 41 | 
            +
                  #
         | 
| 42 | 
            +
                  #   # good
         | 
| 43 | 
            +
                  #   items[..42]    # Ruby 2.7+
         | 
| 44 | 
            +
                  #   items[0..42]   # Ruby 2.7+
         | 
| 45 | 
            +
                  #
         | 
| 28 46 | 
             
                  class SlicingWithRange < Base
         | 
| 29 47 | 
             
                    extend AutoCorrector
         | 
| 30 48 | 
             
                    extend TargetRubyVersion
         | 
| 31 49 |  | 
| 32 50 | 
             
                    minimum_target_ruby_version 2.6
         | 
| 33 51 |  | 
| 34 | 
            -
                    MSG = 'Prefer  | 
| 52 | 
            +
                    MSG = 'Prefer `%<prefer>s` over `%<current>s`.'
         | 
| 53 | 
            +
                    MSG_USELESS_RANGE = 'Remove the useless `%<prefer>s`.'
         | 
| 35 54 | 
             
                    RESTRICT_ON_SEND = %i[[]].freeze
         | 
| 36 55 |  | 
| 56 | 
            +
                    # @!method range_from_zero_till_minus_one?(node)
         | 
| 57 | 
            +
                    def_node_matcher :range_from_zero_till_minus_one?, <<~PATTERN
         | 
| 58 | 
            +
                      {
         | 
| 59 | 
            +
                        (irange (int 0) {(int -1) nil})
         | 
| 60 | 
            +
                        (erange (int 0) nil)
         | 
| 61 | 
            +
                      }
         | 
| 62 | 
            +
                    PATTERN
         | 
| 63 | 
            +
             | 
| 37 64 | 
             
                    # @!method range_till_minus_one?(node)
         | 
| 38 | 
            -
                    def_node_matcher :range_till_minus_one?,  | 
| 65 | 
            +
                    def_node_matcher :range_till_minus_one?, <<~PATTERN
         | 
| 66 | 
            +
                      {
         | 
| 67 | 
            +
                        (irange !nil? {(int -1) nil})
         | 
| 68 | 
            +
                        (erange !nil? nil)
         | 
| 69 | 
            +
                      }
         | 
| 70 | 
            +
                    PATTERN
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                    # @!method range_from_zero?(node)
         | 
| 73 | 
            +
                    def_node_matcher :range_from_zero?, <<~PATTERN
         | 
| 74 | 
            +
                      (irange nil !nil?)
         | 
| 75 | 
            +
                    PATTERN
         | 
| 39 76 |  | 
| 40 77 | 
             
                    def on_send(node)
         | 
| 41 | 
            -
                      return unless node.arguments. | 
| 42 | 
            -
                      return unless range_till_minus_one?(node.first_argument)
         | 
| 78 | 
            +
                      return unless node.arguments.one?
         | 
| 43 79 |  | 
| 44 | 
            -
                       | 
| 45 | 
            -
             | 
| 80 | 
            +
                      range_node = node.first_argument
         | 
| 81 | 
            +
                      selector = node.loc.selector
         | 
| 82 | 
            +
                      unless (message, removal_range = offense_message_with_removal_range(range_node, selector))
         | 
| 83 | 
            +
                        return
         | 
| 46 84 | 
             
                      end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                      add_offense(selector, message: message) do |corrector|
         | 
| 87 | 
            +
                        corrector.remove(removal_range)
         | 
| 88 | 
            +
                      end
         | 
| 89 | 
            +
                    end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                    private
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                    def offense_message_with_removal_range(range_node, selector)
         | 
| 94 | 
            +
                      if range_from_zero_till_minus_one?(range_node)
         | 
| 95 | 
            +
                        [format(MSG_USELESS_RANGE, prefer: selector.source), selector]
         | 
| 96 | 
            +
                      elsif range_till_minus_one?(range_node)
         | 
| 97 | 
            +
                        [
         | 
| 98 | 
            +
                          format(MSG, prefer: endless(range_node), current: selector.source), range_node.end
         | 
| 99 | 
            +
                        ]
         | 
| 100 | 
            +
                      elsif range_from_zero?(range_node) && target_ruby_version >= 2.7
         | 
| 101 | 
            +
                        [
         | 
| 102 | 
            +
                          format(MSG, prefer: beginless(range_node), current: selector.source), range_node.begin
         | 
| 103 | 
            +
                        ]
         | 
| 104 | 
            +
                      end
         | 
| 105 | 
            +
                    end
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                    def endless(range_node)
         | 
| 108 | 
            +
                      "[#{range_node.begin.source}#{range_node.loc.operator.source}]"
         | 
| 109 | 
            +
                    end
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                    def beginless(range_node)
         | 
| 112 | 
            +
                      "[#{range_node.loc.operator.source}#{range_node.end.source}]"
         | 
| 47 113 | 
             
                    end
         | 
| 48 114 | 
             
                  end
         | 
| 49 115 | 
             
                end
         | 
| @@ -22,20 +22,23 @@ module RuboCop | |
| 22 22 |  | 
| 23 23 | 
             
                    # @!method lstrip_rstrip(node)
         | 
| 24 24 | 
             
                    def_node_matcher :lstrip_rstrip, <<~PATTERN
         | 
| 25 | 
            -
                      { | 
| 26 | 
            -
             | 
| 25 | 
            +
                      {
         | 
| 26 | 
            +
                        (call $(call _ :rstrip) :lstrip)
         | 
| 27 | 
            +
                        (call $(call _ :lstrip) :rstrip)
         | 
| 28 | 
            +
                      }
         | 
| 27 29 | 
             
                    PATTERN
         | 
| 28 30 |  | 
| 29 31 | 
             
                    def on_send(node)
         | 
| 30 | 
            -
                      lstrip_rstrip(node) do |first_send | 
| 32 | 
            +
                      lstrip_rstrip(node) do |first_send|
         | 
| 31 33 | 
             
                        range = range_between(first_send.loc.selector.begin_pos, node.source_range.end_pos)
         | 
| 32 | 
            -
                        message = format(MSG, methods:  | 
| 34 | 
            +
                        message = format(MSG, methods: range.source)
         | 
| 33 35 |  | 
| 34 36 | 
             
                        add_offense(range, message: message) do |corrector|
         | 
| 35 37 | 
             
                          corrector.replace(range, 'strip')
         | 
| 36 38 | 
             
                        end
         | 
| 37 39 | 
             
                      end
         | 
| 38 40 | 
             
                    end
         | 
| 41 | 
            +
                    alias on_csend on_send
         | 
| 39 42 | 
             
                  end
         | 
| 40 43 | 
             
                end
         | 
| 41 44 | 
             
              end
         | 
| @@ -37,6 +37,42 @@ module RuboCop | |
| 37 37 | 
             
                  #   # ArgumentError: wrong number of arguments (given 1, expected 0)
         | 
| 38 38 | 
             
                  #   ----
         | 
| 39 39 | 
             
                  #
         | 
| 40 | 
            +
                  #   It is also unsafe because `Symbol#to_proc` does not work with
         | 
| 41 | 
            +
                  #   `protected` methods which would otherwise be accessible.
         | 
| 42 | 
            +
                  #
         | 
| 43 | 
            +
                  #   For example:
         | 
| 44 | 
            +
                  #
         | 
| 45 | 
            +
                  #   [source,ruby]
         | 
| 46 | 
            +
                  #   ----
         | 
| 47 | 
            +
                  #   class Box
         | 
| 48 | 
            +
                  #     def initialize
         | 
| 49 | 
            +
                  #       @secret = rand
         | 
| 50 | 
            +
                  #     end
         | 
| 51 | 
            +
                  #
         | 
| 52 | 
            +
                  #     def normal_matches?(*others)
         | 
| 53 | 
            +
                  #       others.map { |other| other.secret }.any?(secret)
         | 
| 54 | 
            +
                  #     end
         | 
| 55 | 
            +
                  #
         | 
| 56 | 
            +
                  #     def symbol_to_proc_matches?(*others)
         | 
| 57 | 
            +
                  #       others.map(&:secret).any?(secret)
         | 
| 58 | 
            +
                  #     end
         | 
| 59 | 
            +
                  #
         | 
| 60 | 
            +
                  #     protected
         | 
| 61 | 
            +
                  #
         | 
| 62 | 
            +
                  #     attr_reader :secret
         | 
| 63 | 
            +
                  #   end
         | 
| 64 | 
            +
                  #
         | 
| 65 | 
            +
                  #   boxes = [Box.new, Box.new]
         | 
| 66 | 
            +
                  #   Box.new.normal_matches?(*boxes)
         | 
| 67 | 
            +
                  #   # => false
         | 
| 68 | 
            +
                  #   boxes.first.normal_matches?(*boxes)
         | 
| 69 | 
            +
                  #   # => true
         | 
| 70 | 
            +
                  #   Box.new.symbol_to_proc_matches?(*boxes)
         | 
| 71 | 
            +
                  #   # => NoMethodError: protected method `secret' called for #<Box...>
         | 
| 72 | 
            +
                  #   boxes.first.symbol_to_proc_matches?(*boxes)
         | 
| 73 | 
            +
                  #   # => NoMethodError: protected method `secret' called for #<Box...>
         | 
| 74 | 
            +
                  #   ----
         | 
| 75 | 
            +
                  #
         | 
| 40 76 | 
             
                  # @example
         | 
| 41 77 | 
             
                  #   # bad
         | 
| 42 78 | 
             
                  #   something.map { |s| s.upcase }
         | 
| @@ -23,38 +23,35 @@ module RuboCop | |
| 23 23 |  | 
| 24 24 | 
             
                    minimum_target_ruby_version 2.4
         | 
| 25 25 |  | 
| 26 | 
            -
                    MSG = 'Use  | 
| 27 | 
            -
                          '`%<receiver>s.unpack(%<format>s)%<method>s`.'
         | 
| 26 | 
            +
                    MSG = 'Use `unpack1(%<format>s)` instead of `%<current>s`.'
         | 
| 28 27 | 
             
                    RESTRICT_ON_SEND = %i[first [] slice at].freeze
         | 
| 29 28 |  | 
| 30 29 | 
             
                    # @!method unpack_and_first_element?(node)
         | 
| 31 30 | 
             
                    def_node_matcher :unpack_and_first_element?, <<~PATTERN
         | 
| 32 31 | 
             
                      {
         | 
| 33 | 
            -
                        ( | 
| 34 | 
            -
                        ( | 
| 32 | 
            +
                        (call $(call (...) :unpack $(...)) :first)
         | 
| 33 | 
            +
                        (call $(call (...) :unpack $(...)) {:[] :slice :at} (int 0))
         | 
| 35 34 | 
             
                      }
         | 
| 36 35 | 
             
                    PATTERN
         | 
| 37 36 |  | 
| 38 37 | 
             
                    def on_send(node)
         | 
| 39 38 | 
             
                      unpack_and_first_element?(node) do |unpack_call, unpack_arg|
         | 
| 40 | 
            -
                         | 
| 41 | 
            -
                         | 
| 42 | 
            -
             | 
| 43 | 
            -
             | 
| 44 | 
            -
             | 
| 45 | 
            -
             | 
| 46 | 
            -
                          corrector.remove(first_element_range(node, unpack_call))
         | 
| 39 | 
            +
                        first_element_range = first_element_range(node, unpack_call)
         | 
| 40 | 
            +
                        offense_range = unpack_call.loc.selector.join(node.source_range.end)
         | 
| 41 | 
            +
                        message = format(MSG, format: unpack_arg.source, current: offense_range.source)
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                        add_offense(offense_range, message: message) do |corrector|
         | 
| 44 | 
            +
                          corrector.remove(first_element_range)
         | 
| 47 45 | 
             
                          corrector.replace(unpack_call.loc.selector, 'unpack1')
         | 
| 48 46 | 
             
                        end
         | 
| 49 47 | 
             
                      end
         | 
| 50 48 | 
             
                    end
         | 
| 49 | 
            +
                    alias on_csend on_send
         | 
| 51 50 |  | 
| 52 51 | 
             
                    private
         | 
| 53 52 |  | 
| 54 53 | 
             
                    def first_element_range(node, unpack_call)
         | 
| 55 | 
            -
                       | 
| 56 | 
            -
                                                unpack_call.source_range.end_pos,
         | 
| 57 | 
            -
                                                node.source_range.end_pos)
         | 
| 54 | 
            +
                      unpack_call.source_range.end.join(node.source_range.end)
         | 
| 58 55 | 
             
                    end
         | 
| 59 56 | 
             
                  end
         | 
| 60 57 | 
             
                end
         | 
| @@ -251,9 +251,18 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength | |
| 251 251 | 
             
                "\ninclude::../partials/#{filename}[]\n"
         | 
| 252 252 | 
             
              end
         | 
| 253 253 |  | 
| 254 | 
            +
              # rubocop:disable Metrics/MethodLength
         | 
| 254 255 | 
             
              def print_cops_of_department(department)
         | 
| 255 256 | 
             
                selected_cops = cops_of_department(department)
         | 
| 256 | 
            -
                content =  | 
| 257 | 
            +
                content = +<<~HEADER
         | 
| 258 | 
            +
                  ////
         | 
| 259 | 
            +
                    Do NOT edit this file by hand directly, as it is automatically generated.
         | 
| 260 | 
            +
             | 
| 261 | 
            +
                    Please make any necessary changes to the cop documentation within the source files themselves.
         | 
| 262 | 
            +
                  ////
         | 
| 263 | 
            +
             | 
| 264 | 
            +
                  = #{department}
         | 
| 265 | 
            +
                HEADER
         | 
| 257 266 | 
             
                selected_cops.each { |cop| content << print_cop_with_doc(cop) }
         | 
| 258 267 | 
             
                content << footer_for_department(department)
         | 
| 259 268 | 
             
                file_name = "#{docs_path}/#{department_to_basename(department)}.adoc"
         | 
| @@ -262,6 +271,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength | |
| 262 271 | 
             
                  file.write("#{content.strip}\n")
         | 
| 263 272 | 
             
                end
         | 
| 264 273 | 
             
              end
         | 
| 274 | 
            +
              # rubocop:enable Metrics/MethodLength
         | 
| 265 275 |  | 
| 266 276 | 
             
              def print_cop_with_doc(cop) # rubocop:todo Metrics/AbcSize, Metrics/MethodLength
         | 
| 267 277 | 
             
                cop_config = config.for_cop(cop)
         | 
| @@ -54,10 +54,7 @@ module RuboCop | |
| 54 54 | 
             
                    return enum_for(__method__, named: named) unless block_given?
         | 
| 55 55 |  | 
| 56 56 | 
             
                    parsed_tree&.traverse do |event, exp, _index|
         | 
| 57 | 
            -
                      yield(exp) if event  | 
| 58 | 
            -
                                    named == exp.respond_to?(:name) &&
         | 
| 59 | 
            -
                                    exp.respond_to?(:capturing?) &&
         | 
| 60 | 
            -
                                    exp.capturing?
         | 
| 57 | 
            +
                      yield(exp) if named_capturing?(exp, event, named)
         | 
| 61 58 | 
             
                    end
         | 
| 62 59 |  | 
| 63 60 | 
             
                    self
         | 
| @@ -65,6 +62,14 @@ module RuboCop | |
| 65 62 |  | 
| 66 63 | 
             
                  private
         | 
| 67 64 |  | 
| 65 | 
            +
                  def named_capturing?(exp, event, named)
         | 
| 66 | 
            +
                    event == :enter &&
         | 
| 67 | 
            +
                      named == exp.respond_to?(:name) &&
         | 
| 68 | 
            +
                      !exp.text.start_with?('(?<=') &&
         | 
| 69 | 
            +
                      exp.respond_to?(:capturing?) &&
         | 
| 70 | 
            +
                      exp.capturing?
         | 
| 71 | 
            +
                  end
         | 
| 72 | 
            +
             | 
| 68 73 | 
             
                  def with_interpolations_blanked
         | 
| 69 74 | 
             
                    # Ignore the trailing regopt node
         | 
| 70 75 | 
             
                    children[0...-1].map do |child|
         |