rubocop 1.34.0 → 1.35.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 +1 -1
- data/config/default.yml +22 -0
- data/lib/rubocop/cli/command/{auto_genenerate_config.rb → auto_generate_config.rb} +0 -0
- data/lib/rubocop/config.rb +1 -1
- data/lib/rubocop/cop/correctors/parentheses_corrector.rb +32 -2
- data/lib/rubocop/cop/gemspec/require_mfa.rb +1 -1
- data/lib/rubocop/cop/generator/require_file_injector.rb +2 -2
- data/lib/rubocop/cop/internal_affairs/numblock_handler.rb +69 -0
- data/lib/rubocop/cop/internal_affairs/single_line_comparison.rb +5 -4
- data/lib/rubocop/cop/internal_affairs.rb +1 -0
- data/lib/rubocop/cop/layout/block_alignment.rb +2 -0
- data/lib/rubocop/cop/layout/block_end_newline.rb +2 -0
- data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +5 -2
- data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +2 -0
- data/lib/rubocop/cop/layout/end_of_line.rb +4 -4
- data/lib/rubocop/cop/layout/first_array_element_indentation.rb +2 -2
- data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +2 -2
- data/lib/rubocop/cop/layout/indentation_width.rb +2 -0
- data/lib/rubocop/cop/layout/line_length.rb +4 -1
- data/lib/rubocop/cop/layout/multiline_block_layout.rb +2 -0
- 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 +1 -1
- data/lib/rubocop/cop/layout/space_before_block_braces.rb +2 -0
- data/lib/rubocop/cop/lint/deprecated_class_methods.rb +4 -4
- data/lib/rubocop/cop/lint/empty_block.rb +1 -1
- data/lib/rubocop/cop/lint/erb_new_arguments.rb +9 -9
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +4 -0
- data/lib/rubocop/cop/lint/next_without_accumulator.rb +25 -6
- data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +6 -6
- data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +12 -0
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +9 -3
- data/lib/rubocop/cop/lint/redundant_with_index.rb +13 -10
- data/lib/rubocop/cop/lint/redundant_with_object.rb +12 -11
- data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +1 -0
- data/lib/rubocop/cop/lint/unreachable_loop.rb +7 -1
- data/lib/rubocop/cop/lint/useless_access_modifier.rb +6 -4
- data/lib/rubocop/cop/lint/void.rb +2 -0
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
- data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +76 -1
- data/lib/rubocop/cop/mixin/hash_transform_method.rb +1 -1
- data/lib/rubocop/cop/mixin/method_complexity.rb +4 -4
- data/lib/rubocop/cop/mixin/multiline_element_indentation.rb +1 -1
- data/lib/rubocop/cop/mixin/range_help.rb +2 -3
- data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
- data/lib/rubocop/cop/naming/constant_name.rb +2 -2
- data/lib/rubocop/cop/style/arguments_forwarding.rb +2 -2
- data/lib/rubocop/cop/style/class_methods_definitions.rb +2 -1
- data/lib/rubocop/cop/style/collection_methods.rb +2 -0
- data/lib/rubocop/cop/style/combinable_loops.rb +3 -1
- data/lib/rubocop/cop/style/each_for_simple_loop.rb +1 -1
- data/lib/rubocop/cop/style/each_with_object.rb +39 -8
- data/lib/rubocop/cop/style/empty_block_parameter.rb +1 -1
- data/lib/rubocop/cop/style/empty_lambda_parameter.rb +1 -1
- data/lib/rubocop/cop/style/for.rb +2 -0
- data/lib/rubocop/cop/style/guard_clause.rb +27 -16
- data/lib/rubocop/cop/style/hash_each_methods.rb +3 -1
- data/lib/rubocop/cop/style/hash_syntax.rb +17 -0
- data/lib/rubocop/cop/style/inverse_methods.rb +8 -6
- data/lib/rubocop/cop/style/magic_comment_format.rb +307 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -2
- data/lib/rubocop/cop/style/method_called_on_do_end_block.rb +4 -1
- data/lib/rubocop/cop/style/multiline_block_chain.rb +3 -1
- data/lib/rubocop/cop/style/multiline_in_pattern_then.rb +1 -1
- data/lib/rubocop/cop/style/next.rb +3 -5
- data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
- data/lib/rubocop/cop/style/object_then.rb +2 -0
- data/lib/rubocop/cop/style/proc.rb +4 -1
- data/lib/rubocop/cop/style/redundant_begin.rb +2 -0
- data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -1
- data/lib/rubocop/cop/style/redundant_self.rb +2 -0
- data/lib/rubocop/cop/style/redundant_sort_by.rb +24 -8
- data/lib/rubocop/cop/style/safe_navigation.rb +4 -2
- data/lib/rubocop/cop/style/single_line_block_params.rb +1 -1
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +0 -2
- data/lib/rubocop/cop/style/symbol_array.rb +1 -1
- data/lib/rubocop/cop/style/top_level_method_definition.rb +3 -1
- data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
- data/lib/rubocop/cop/style/word_array.rb +1 -1
- data/lib/rubocop/cop/util.rb +1 -1
- data/lib/rubocop/feature_loader.rb +9 -3
- data/lib/rubocop/formatter/html_formatter.rb +2 -2
- data/lib/rubocop/server/cache.rb +11 -8
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +3 -2
- metadata +9 -7
| @@ -28,6 +28,7 @@ module RuboCop | |
| 28 28 | 
             
                  # * always - forces use of the 3.1 syntax (e.g. {foo:})
         | 
| 29 29 | 
             
                  # * never - forces use of explicit hash literal value
         | 
| 30 30 | 
             
                  # * either - accepts both shorthand and explicit use of hash literal value
         | 
| 31 | 
            +
                  # * consistent - like "always", but will avoid mixing styles in a single hash
         | 
| 31 32 | 
             
                  #
         | 
| 32 33 | 
             
                  # @example EnforcedStyle: ruby19 (default)
         | 
| 33 34 | 
             
                  #   # bad
         | 
| @@ -89,6 +90,20 @@ module RuboCop | |
| 89 90 | 
             
                  #   # good
         | 
| 90 91 | 
             
                  #   {foo:, bar:}
         | 
| 91 92 | 
             
                  #
         | 
| 93 | 
            +
                  # @example EnforcedShorthandSyntax: consistent
         | 
| 94 | 
            +
                  #
         | 
| 95 | 
            +
                  #   # bad
         | 
| 96 | 
            +
                  #   {foo: , bar: bar}
         | 
| 97 | 
            +
                  #
         | 
| 98 | 
            +
                  #   # good
         | 
| 99 | 
            +
                  #   {foo:, bar:}
         | 
| 100 | 
            +
                  #
         | 
| 101 | 
            +
                  #   # bad
         | 
| 102 | 
            +
                  #   {foo: , bar: baz}
         | 
| 103 | 
            +
                  #
         | 
| 104 | 
            +
                  #   # good
         | 
| 105 | 
            +
                  #   {foo: foo, bar: baz}
         | 
| 106 | 
            +
                  #
         | 
| 92 107 | 
             
                  class HashSyntax < Base
         | 
| 93 108 | 
             
                    include ConfigurableEnforcedStyle
         | 
| 94 109 | 
             
                    include HashShorthandSyntax
         | 
| @@ -104,6 +119,8 @@ module RuboCop | |
| 104 119 |  | 
| 105 120 | 
             
                      return if pairs.empty?
         | 
| 106 121 |  | 
| 122 | 
            +
                      on_hash_for_mixed_shorthand(node)
         | 
| 123 | 
            +
             | 
| 107 124 | 
             
                      if style == :hash_rockets || force_hash_rockets?(pairs)
         | 
| 108 125 | 
             
                        hash_rockets_check(pairs)
         | 
| 109 126 | 
             
                      elsif style == :ruby19_no_mixed_keys
         | 
| @@ -59,18 +59,18 @@ module RuboCop | |
| 59 59 | 
             
                    def_node_matcher :inverse_candidate?, <<~PATTERN
         | 
| 60 60 | 
             
                      {
         | 
| 61 61 | 
             
                        (send $(send $(...) $_ $...) :!)
         | 
| 62 | 
            -
                        (send (block $(send $(...) $_) $...) :!)
         | 
| 62 | 
            +
                        (send ({block numblock} $(send $(...) $_) $...) :!)
         | 
| 63 63 | 
             
                        (send (begin $(send $(...) $_ $...)) :!)
         | 
| 64 64 | 
             
                      }
         | 
| 65 65 | 
             
                    PATTERN
         | 
| 66 66 |  | 
| 67 67 | 
             
                    # @!method inverse_block?(node)
         | 
| 68 68 | 
             
                    def_node_matcher :inverse_block?, <<~PATTERN
         | 
| 69 | 
            -
                      (block $(send (...) $_) ... { $(send ... :!)
         | 
| 70 | 
            -
             | 
| 71 | 
            -
             | 
| 72 | 
            -
             | 
| 73 | 
            -
             | 
| 69 | 
            +
                      ({block numblock} $(send (...) $_) ... { $(send ... :!)
         | 
| 70 | 
            +
                                                               $(send (...) {:!= :!~} ...)
         | 
| 71 | 
            +
                                                               (begin ... $(send ... :!))
         | 
| 72 | 
            +
                                                               (begin ... $(send (...) {:!= :!~} ...))
         | 
| 73 | 
            +
                                                             })
         | 
| 74 74 | 
             
                    PATTERN
         | 
| 75 75 |  | 
| 76 76 | 
             
                    def on_send(node)
         | 
| @@ -102,6 +102,8 @@ module RuboCop | |
| 102 102 | 
             
                      end
         | 
| 103 103 | 
             
                    end
         | 
| 104 104 |  | 
| 105 | 
            +
                    alias on_numblock on_block
         | 
| 106 | 
            +
             | 
| 105 107 | 
             
                    private
         | 
| 106 108 |  | 
| 107 109 | 
             
                    def correct_inverse_method(corrector, node)
         | 
| @@ -0,0 +1,307 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module RuboCop
         | 
| 4 | 
            +
              module Cop
         | 
| 5 | 
            +
                module Style
         | 
| 6 | 
            +
                  # Ensures magic comments are written consistently throughout your code base.
         | 
| 7 | 
            +
                  # Looks for discrepancies in separators (`-` vs `_`) and capitalization for
         | 
| 8 | 
            +
                  # both magic comment directives and values.
         | 
| 9 | 
            +
                  #
         | 
| 10 | 
            +
                  # Required capitalization can be set with the `DirectiveCapitalization` and
         | 
| 11 | 
            +
                  # `ValueCapitalization` configuration keys.
         | 
| 12 | 
            +
                  #
         | 
| 13 | 
            +
                  # NOTE: If one of these configuration is set to nil, any capitalization is allowed.
         | 
| 14 | 
            +
                  #
         | 
| 15 | 
            +
                  # @example EnforcedStyle: snake_case (default)
         | 
| 16 | 
            +
                  #   # The `snake_case` style will enforce that the frozen string literal
         | 
| 17 | 
            +
                  #   # comment is written in snake case. (Words separated by underscores)
         | 
| 18 | 
            +
                  #   # bad
         | 
| 19 | 
            +
                  #   # frozen-string-literal: true
         | 
| 20 | 
            +
                  #
         | 
| 21 | 
            +
                  #   module Bar
         | 
| 22 | 
            +
                  #     # ...
         | 
| 23 | 
            +
                  #   end
         | 
| 24 | 
            +
                  #
         | 
| 25 | 
            +
                  #   # good
         | 
| 26 | 
            +
                  #   # frozen_string_literal: false
         | 
| 27 | 
            +
                  #
         | 
| 28 | 
            +
                  #   module Bar
         | 
| 29 | 
            +
                  #     # ...
         | 
| 30 | 
            +
                  #   end
         | 
| 31 | 
            +
                  #
         | 
| 32 | 
            +
                  # @example EnforcedStyle: kebab_case
         | 
| 33 | 
            +
                  #   # The `kebab_case` style will enforce that the frozen string literal
         | 
| 34 | 
            +
                  #   # comment is written in kebab case. (Words separated by hyphens)
         | 
| 35 | 
            +
                  #   # bad
         | 
| 36 | 
            +
                  #   # frozen_string_literal: true
         | 
| 37 | 
            +
                  #
         | 
| 38 | 
            +
                  #   module Baz
         | 
| 39 | 
            +
                  #     # ...
         | 
| 40 | 
            +
                  #   end
         | 
| 41 | 
            +
                  #
         | 
| 42 | 
            +
                  #   # good
         | 
| 43 | 
            +
                  #   # frozen-string-literal: true
         | 
| 44 | 
            +
                  #
         | 
| 45 | 
            +
                  #   module Baz
         | 
| 46 | 
            +
                  #     # ...
         | 
| 47 | 
            +
                  #   end
         | 
| 48 | 
            +
                  #
         | 
| 49 | 
            +
                  # @example DirectiveCapitalization: lowercase (default)
         | 
| 50 | 
            +
                  #   # bad
         | 
| 51 | 
            +
                  #   # FROZEN-STRING-LITERAL: true
         | 
| 52 | 
            +
                  #
         | 
| 53 | 
            +
                  #   # good
         | 
| 54 | 
            +
                  #   # frozen-string-literal: true
         | 
| 55 | 
            +
                  #
         | 
| 56 | 
            +
                  # @example DirectiveCapitalization: uppercase
         | 
| 57 | 
            +
                  #   # bad
         | 
| 58 | 
            +
                  #   # frozen-string-literal: true
         | 
| 59 | 
            +
                  #
         | 
| 60 | 
            +
                  #   # good
         | 
| 61 | 
            +
                  #   # FROZEN-STRING-LITERAL: true
         | 
| 62 | 
            +
                  #
         | 
| 63 | 
            +
                  # @example DirectiveCapitalization: nil
         | 
| 64 | 
            +
                  #   # any capitalization is accepted
         | 
| 65 | 
            +
                  #
         | 
| 66 | 
            +
                  #   # good
         | 
| 67 | 
            +
                  #   # frozen-string-literal: true
         | 
| 68 | 
            +
                  #
         | 
| 69 | 
            +
                  #   # good
         | 
| 70 | 
            +
                  #   # FROZEN-STRING-LITERAL: true
         | 
| 71 | 
            +
                  #
         | 
| 72 | 
            +
                  # @example ValueCapitalization: nil (default)
         | 
| 73 | 
            +
                  #   # any capitalization is accepted
         | 
| 74 | 
            +
                  #
         | 
| 75 | 
            +
                  #   # good
         | 
| 76 | 
            +
                  #   # frozen-string-literal: true
         | 
| 77 | 
            +
                  #
         | 
| 78 | 
            +
                  #   # good
         | 
| 79 | 
            +
                  #   # frozen-string-literal: TRUE
         | 
| 80 | 
            +
                  #
         | 
| 81 | 
            +
                  # @example ValueCapitalization: lowercase
         | 
| 82 | 
            +
                  #   # when a value is not given, any capitalization is accepted
         | 
| 83 | 
            +
                  #
         | 
| 84 | 
            +
                  #   # bad
         | 
| 85 | 
            +
                  #   # frozen-string-literal: TRUE
         | 
| 86 | 
            +
                  #
         | 
| 87 | 
            +
                  #   # good
         | 
| 88 | 
            +
                  #   # frozen-string-literal: TRUE
         | 
| 89 | 
            +
                  #
         | 
| 90 | 
            +
                  # @example ValueCapitalization: uppercase
         | 
| 91 | 
            +
                  #   # bad
         | 
| 92 | 
            +
                  #   # frozen-string-literal: true
         | 
| 93 | 
            +
                  #
         | 
| 94 | 
            +
                  #   # good
         | 
| 95 | 
            +
                  #   # frozen-string-literal: TRUE
         | 
| 96 | 
            +
                  #
         | 
| 97 | 
            +
                  class MagicCommentFormat < Base
         | 
| 98 | 
            +
                    include ConfigurableEnforcedStyle
         | 
| 99 | 
            +
                    extend AutoCorrector
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                    SNAKE_SEPARATOR = '_'
         | 
| 102 | 
            +
                    KEBAB_SEPARATOR = '-'
         | 
| 103 | 
            +
                    MSG = 'Prefer %<style>s case for magic comments.'
         | 
| 104 | 
            +
                    MSG_VALUE = 'Prefer %<case>s for magic comment values.'
         | 
| 105 | 
            +
             | 
| 106 | 
            +
                    # Value object to extract source ranges for the different parts of a magic comment
         | 
| 107 | 
            +
                    class CommentRange
         | 
| 108 | 
            +
                      extend Forwardable
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                      DIRECTIVE_REGEXP = Regexp.union(MagicComment::KEYWORDS.map do |_, v|
         | 
| 111 | 
            +
                        Regexp.new(v, Regexp::IGNORECASE)
         | 
| 112 | 
            +
                      end).freeze
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                      VALUE_REGEXP = Regexp.new("(?:#{DIRECTIVE_REGEXP}:\s*)(.*?)(?=;|$)")
         | 
| 115 | 
            +
             | 
| 116 | 
            +
                      def_delegators :@comment, :text, :loc
         | 
| 117 | 
            +
                      attr_reader :comment
         | 
| 118 | 
            +
             | 
| 119 | 
            +
                      def initialize(comment)
         | 
| 120 | 
            +
                        @comment = comment
         | 
| 121 | 
            +
                      end
         | 
| 122 | 
            +
             | 
| 123 | 
            +
                      # A magic comment can contain one directive (normal style) or
         | 
| 124 | 
            +
                      # multiple directives (emacs style)
         | 
| 125 | 
            +
                      def directives
         | 
| 126 | 
            +
                        @directives ||= begin
         | 
| 127 | 
            +
                          matches = []
         | 
| 128 | 
            +
             | 
| 129 | 
            +
                          text.scan(DIRECTIVE_REGEXP) do
         | 
| 130 | 
            +
                            offset = Regexp.last_match.offset(0)
         | 
| 131 | 
            +
                            matches << loc.expression.adjust(begin_pos: offset.first)
         | 
| 132 | 
            +
                                          .with(end_pos: loc.expression.begin_pos + offset.last)
         | 
| 133 | 
            +
                          end
         | 
| 134 | 
            +
             | 
| 135 | 
            +
                          matches
         | 
| 136 | 
            +
                        end
         | 
| 137 | 
            +
                      end
         | 
| 138 | 
            +
             | 
| 139 | 
            +
                      # A magic comment can contain one value (normal style) or
         | 
| 140 | 
            +
                      # multiple directives (emacs style)
         | 
| 141 | 
            +
                      def values
         | 
| 142 | 
            +
                        @values ||= begin
         | 
| 143 | 
            +
                          matches = []
         | 
| 144 | 
            +
             | 
| 145 | 
            +
                          text.scan(VALUE_REGEXP) do
         | 
| 146 | 
            +
                            offset = Regexp.last_match.offset(1)
         | 
| 147 | 
            +
                            matches << loc.expression.adjust(begin_pos: offset.first)
         | 
| 148 | 
            +
                                          .with(end_pos: loc.expression.begin_pos + offset.last)
         | 
| 149 | 
            +
                          end
         | 
| 150 | 
            +
             | 
| 151 | 
            +
                          matches
         | 
| 152 | 
            +
                        end
         | 
| 153 | 
            +
                      end
         | 
| 154 | 
            +
                    end
         | 
| 155 | 
            +
             | 
| 156 | 
            +
                    def on_new_investigation
         | 
| 157 | 
            +
                      return unless processed_source.ast
         | 
| 158 | 
            +
             | 
| 159 | 
            +
                      magic_comments.each do |comment|
         | 
| 160 | 
            +
                        issues = find_issues(comment)
         | 
| 161 | 
            +
                        register_offenses(issues) if issues.any?
         | 
| 162 | 
            +
                      end
         | 
| 163 | 
            +
                    end
         | 
| 164 | 
            +
             | 
| 165 | 
            +
                    private
         | 
| 166 | 
            +
             | 
| 167 | 
            +
                    def magic_comments
         | 
| 168 | 
            +
                      processed_source.each_comment_in_lines(leading_comment_lines)
         | 
| 169 | 
            +
                                      .select { |comment| MagicComment.parse(comment.text).valid? }
         | 
| 170 | 
            +
                                      .map { |comment| CommentRange.new(comment) }
         | 
| 171 | 
            +
                    end
         | 
| 172 | 
            +
             | 
| 173 | 
            +
                    def leading_comment_lines
         | 
| 174 | 
            +
                      first_non_comment_token = processed_source.tokens.find { |token| !token.comment? }
         | 
| 175 | 
            +
             | 
| 176 | 
            +
                      if first_non_comment_token
         | 
| 177 | 
            +
                        0...first_non_comment_token.line
         | 
| 178 | 
            +
                      else
         | 
| 179 | 
            +
                        (0..)
         | 
| 180 | 
            +
                      end
         | 
| 181 | 
            +
                    end
         | 
| 182 | 
            +
             | 
| 183 | 
            +
                    def find_issues(comment)
         | 
| 184 | 
            +
                      issues = { directives: [], values: [] }
         | 
| 185 | 
            +
             | 
| 186 | 
            +
                      comment.directives.each do |directive|
         | 
| 187 | 
            +
                        issues[:directives] << directive if directive_offends?(directive)
         | 
| 188 | 
            +
                      end
         | 
| 189 | 
            +
             | 
| 190 | 
            +
                      comment.values.each do |value| # rubocop:disable Style/HashEachMethods
         | 
| 191 | 
            +
                        issues[:values] << value if wrong_capitalization?(value.source, value_capitalization)
         | 
| 192 | 
            +
                      end
         | 
| 193 | 
            +
             | 
| 194 | 
            +
                      issues
         | 
| 195 | 
            +
                    end
         | 
| 196 | 
            +
             | 
| 197 | 
            +
                    def directive_offends?(directive)
         | 
| 198 | 
            +
                      incorrect_separator?(directive.source) ||
         | 
| 199 | 
            +
                        wrong_capitalization?(directive.source, directive_capitalization)
         | 
| 200 | 
            +
                    end
         | 
| 201 | 
            +
             | 
| 202 | 
            +
                    def register_offenses(issues)
         | 
| 203 | 
            +
                      fix_directives(issues[:directives])
         | 
| 204 | 
            +
                      fix_values(issues[:values])
         | 
| 205 | 
            +
                    end
         | 
| 206 | 
            +
             | 
| 207 | 
            +
                    def fix_directives(issues)
         | 
| 208 | 
            +
                      return if issues.empty?
         | 
| 209 | 
            +
             | 
| 210 | 
            +
                      msg = format(MSG, style: expected_style)
         | 
| 211 | 
            +
             | 
| 212 | 
            +
                      issues.each do |directive|
         | 
| 213 | 
            +
                        add_offense(directive, message: msg) do |corrector|
         | 
| 214 | 
            +
                          replacement = replace_separator(replace_capitalization(directive.source,
         | 
| 215 | 
            +
                                                                                 directive_capitalization))
         | 
| 216 | 
            +
                          corrector.replace(directive, replacement)
         | 
| 217 | 
            +
                        end
         | 
| 218 | 
            +
                      end
         | 
| 219 | 
            +
                    end
         | 
| 220 | 
            +
             | 
| 221 | 
            +
                    def fix_values(issues)
         | 
| 222 | 
            +
                      return if issues.empty?
         | 
| 223 | 
            +
             | 
| 224 | 
            +
                      msg = format(MSG_VALUE, case: value_capitalization)
         | 
| 225 | 
            +
             | 
| 226 | 
            +
                      issues.each do |value|
         | 
| 227 | 
            +
                        add_offense(value, message: msg) do |corrector|
         | 
| 228 | 
            +
                          corrector.replace(value, replace_capitalization(value.source, value_capitalization))
         | 
| 229 | 
            +
                        end
         | 
| 230 | 
            +
                      end
         | 
| 231 | 
            +
                    end
         | 
| 232 | 
            +
             | 
| 233 | 
            +
                    def expected_style
         | 
| 234 | 
            +
                      [directive_capitalization, style].compact.join(' ').gsub(/_?case\b/, '')
         | 
| 235 | 
            +
                    end
         | 
| 236 | 
            +
             | 
| 237 | 
            +
                    def wrong_separator
         | 
| 238 | 
            +
                      style == :snake_case ? KEBAB_SEPARATOR : SNAKE_SEPARATOR
         | 
| 239 | 
            +
                    end
         | 
| 240 | 
            +
             | 
| 241 | 
            +
                    def correct_separator
         | 
| 242 | 
            +
                      style == :snake_case ? SNAKE_SEPARATOR : KEBAB_SEPARATOR
         | 
| 243 | 
            +
                    end
         | 
| 244 | 
            +
             | 
| 245 | 
            +
                    def incorrect_separator?(text)
         | 
| 246 | 
            +
                      text[wrong_separator]
         | 
| 247 | 
            +
                    end
         | 
| 248 | 
            +
             | 
| 249 | 
            +
                    def wrong_capitalization?(text, expected_case)
         | 
| 250 | 
            +
                      return false unless expected_case
         | 
| 251 | 
            +
             | 
| 252 | 
            +
                      case expected_case
         | 
| 253 | 
            +
                      when :lowercase
         | 
| 254 | 
            +
                        text != text.downcase
         | 
| 255 | 
            +
                      when :uppercase
         | 
| 256 | 
            +
                        text != text.upcase
         | 
| 257 | 
            +
                      end
         | 
| 258 | 
            +
                    end
         | 
| 259 | 
            +
             | 
| 260 | 
            +
                    def replace_separator(text)
         | 
| 261 | 
            +
                      text.tr(wrong_separator, correct_separator)
         | 
| 262 | 
            +
                    end
         | 
| 263 | 
            +
             | 
| 264 | 
            +
                    def replace_capitalization(text, style)
         | 
| 265 | 
            +
                      return text unless style
         | 
| 266 | 
            +
             | 
| 267 | 
            +
                      case style
         | 
| 268 | 
            +
                      when :lowercase
         | 
| 269 | 
            +
                        text.downcase
         | 
| 270 | 
            +
                      when :uppercase
         | 
| 271 | 
            +
                        text.upcase
         | 
| 272 | 
            +
                      end
         | 
| 273 | 
            +
                    end
         | 
| 274 | 
            +
             | 
| 275 | 
            +
                    def line_range(line)
         | 
| 276 | 
            +
                      processed_source.buffer.line_range(line)
         | 
| 277 | 
            +
                    end
         | 
| 278 | 
            +
             | 
| 279 | 
            +
                    def directive_capitalization
         | 
| 280 | 
            +
                      cop_config['DirectiveCapitalization']&.to_sym.tap do |style|
         | 
| 281 | 
            +
                        unless valid_capitalization?(style)
         | 
| 282 | 
            +
                          raise "Unknown `DirectiveCapitalization` #{style} selected!"
         | 
| 283 | 
            +
                        end
         | 
| 284 | 
            +
                      end
         | 
| 285 | 
            +
                    end
         | 
| 286 | 
            +
             | 
| 287 | 
            +
                    def value_capitalization
         | 
| 288 | 
            +
                      cop_config['ValueCapitalization']&.to_sym.tap do |style|
         | 
| 289 | 
            +
                        unless valid_capitalization?(style)
         | 
| 290 | 
            +
                          raise "Unknown `ValueCapitalization` #{style} selected!"
         | 
| 291 | 
            +
                        end
         | 
| 292 | 
            +
                      end
         | 
| 293 | 
            +
                    end
         | 
| 294 | 
            +
             | 
| 295 | 
            +
                    def valid_capitalization?(style)
         | 
| 296 | 
            +
                      return true unless style
         | 
| 297 | 
            +
             | 
| 298 | 
            +
                      supported_capitalizations.include?(style)
         | 
| 299 | 
            +
                    end
         | 
| 300 | 
            +
             | 
| 301 | 
            +
                    def supported_capitalizations
         | 
| 302 | 
            +
                      cop_config['SupportedCapitalizations'].map(&:to_sym)
         | 
| 303 | 
            +
                    end
         | 
| 304 | 
            +
                  end
         | 
| 305 | 
            +
                end
         | 
| 306 | 
            +
              end
         | 
| 307 | 
            +
            end
         | 
| @@ -130,7 +130,7 @@ module RuboCop | |
| 130 130 | 
             
                          call_as_argument_or_chain?(node) ||
         | 
| 131 131 | 
             
                          hash_literal_in_arguments?(node) ||
         | 
| 132 132 | 
             
                          node.descendants.any? do |n|
         | 
| 133 | 
            -
                            n.forwarded_args_type? ||  | 
| 133 | 
            +
                            n.forwarded_args_type? || ambiguous_literal?(n) || logical_operator?(n) ||
         | 
| 134 134 | 
             
                              call_with_braced_block?(n)
         | 
| 135 135 | 
             
                          end
         | 
| 136 136 | 
             
                      end
         | 
| @@ -166,7 +166,7 @@ module RuboCop | |
| 166 166 | 
             
                        previous.parenthesized? || allowed_chained_call_with_parentheses?(previous)
         | 
| 167 167 | 
             
                      end
         | 
| 168 168 |  | 
| 169 | 
            -
                      def  | 
| 169 | 
            +
                      def ambiguous_literal?(node)
         | 
| 170 170 | 
             
                        splat?(node) || ternary_if?(node) || regexp_slash_literal?(node) || unary_literal?(node)
         | 
| 171 171 | 
             
                      end
         | 
| 172 172 |  | 
| @@ -35,12 +35,15 @@ module RuboCop | |
| 35 35 | 
             
                      ignore_node(node.send_node)
         | 
| 36 36 | 
             
                    end
         | 
| 37 37 |  | 
| 38 | 
            +
                    alias on_numblock on_block
         | 
| 39 | 
            +
             | 
| 38 40 | 
             
                    def on_send(node)
         | 
| 39 41 | 
             
                      return if ignored_node?(node)
         | 
| 40 42 |  | 
| 41 43 | 
             
                      receiver = node.receiver
         | 
| 42 44 |  | 
| 43 | 
            -
                      return unless receiver&.block_type?  | 
| 45 | 
            +
                      return unless (receiver&.block_type? || receiver&.numblock_type?) &&
         | 
| 46 | 
            +
                                    receiver.loc.end.is?('end')
         | 
| 44 47 |  | 
| 45 48 | 
             
                      range = range_between(receiver.loc.end.begin_pos, node.source_range.end_pos)
         | 
| 46 49 |  | 
| @@ -31,7 +31,7 @@ module RuboCop | |
| 31 31 | 
             
                      node.send_node.each_node(:send) do |send_node|
         | 
| 32 32 | 
             
                        receiver = send_node.receiver
         | 
| 33 33 |  | 
| 34 | 
            -
                        next unless receiver&.block_type? && receiver&.multiline?
         | 
| 34 | 
            +
                        next unless (receiver&.block_type? || receiver&.numblock_type?) && receiver&.multiline?
         | 
| 35 35 |  | 
| 36 36 | 
             
                        range = range_between(receiver.loc.end.begin_pos, node.send_node.source_range.end_pos)
         | 
| 37 37 |  | 
| @@ -42,6 +42,8 @@ module RuboCop | |
| 42 42 | 
             
                        break
         | 
| 43 43 | 
             
                      end
         | 
| 44 44 | 
             
                    end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                    alias on_numblock on_block
         | 
| 45 47 | 
             
                  end
         | 
| 46 48 | 
             
                end
         | 
| 47 49 | 
             
              end
         | 
| @@ -49,7 +49,7 @@ module RuboCop | |
| 49 49 |  | 
| 50 50 | 
             
                    # Requires `then` for write `in` and its body on the same line.
         | 
| 51 51 | 
             
                    def require_then?(in_pattern_node)
         | 
| 52 | 
            -
                      return true  | 
| 52 | 
            +
                      return true unless in_pattern_node.pattern.single_line?
         | 
| 53 53 | 
             
                      return false unless in_pattern_node.body
         | 
| 54 54 |  | 
| 55 55 | 
             
                      same_line?(in_pattern_node, in_pattern_node.body)
         | 
| @@ -71,6 +71,8 @@ module RuboCop | |
| 71 71 | 
             
                      check(node)
         | 
| 72 72 | 
             
                    end
         | 
| 73 73 |  | 
| 74 | 
            +
                    alias on_numblock on_block
         | 
| 75 | 
            +
             | 
| 74 76 | 
             
                    def on_while(node)
         | 
| 75 77 | 
             
                      check(node)
         | 
| 76 78 | 
             
                    end
         | 
| @@ -223,11 +225,7 @@ module RuboCop | |
| 223 225 | 
             
                      adjustment = delta + @reindented_lines[lineno]
         | 
| 224 226 | 
             
                      @reindented_lines[lineno] = adjustment
         | 
| 225 227 |  | 
| 226 | 
            -
                      if adjustment.positive?
         | 
| 227 | 
            -
                        corrector.remove_leading(buffer.line_range(lineno), adjustment)
         | 
| 228 | 
            -
                      elsif adjustment.negative?
         | 
| 229 | 
            -
                        corrector.insert_before(buffer.line_range(lineno), ' ' * -adjustment)
         | 
| 230 | 
            -
                      end
         | 
| 228 | 
            +
                      corrector.remove_leading(buffer.line_range(lineno), adjustment) if adjustment.positive?
         | 
| 231 229 | 
             
                    end
         | 
| 232 230 | 
             
                  end
         | 
| 233 231 | 
             
                end
         | 
| @@ -19,7 +19,8 @@ module RuboCop | |
| 19 19 | 
             
                    MSG = 'Use `proc` instead of `Proc.new`.'
         | 
| 20 20 |  | 
| 21 21 | 
             
                    # @!method proc_new?(node)
         | 
| 22 | 
            -
                    def_node_matcher :proc_new?, | 
| 22 | 
            +
                    def_node_matcher :proc_new?,
         | 
| 23 | 
            +
                                     '({block numblock} $(send (const {nil? cbase} :Proc) :new) ...)'
         | 
| 23 24 |  | 
| 24 25 | 
             
                    def on_block(node)
         | 
| 25 26 | 
             
                      proc_new?(node) do |block_method|
         | 
| @@ -28,6 +29,8 @@ module RuboCop | |
| 28 29 | 
             
                        end
         | 
| 29 30 | 
             
                      end
         | 
| 30 31 | 
             
                    end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                    alias on_numblock on_block
         | 
| 31 34 | 
             
                  end
         | 
| 32 35 | 
             
                end
         | 
| 33 36 | 
             
              end
         | 
| @@ -50,7 +50,7 @@ module RuboCop | |
| 50 50 | 
             
                        ${nil? #basic_literal? #const_type?})
         | 
| 51 51 | 
             
                    PATTERN
         | 
| 52 52 |  | 
| 53 | 
            -
                    def on_block(node)
         | 
| 53 | 
            +
                    def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
         | 
| 54 54 | 
             
                      redundant_fetch_block_candidate?(node) do |send, body|
         | 
| 55 55 | 
             
                        return if should_not_check?(send, body)
         | 
| 56 56 |  | 
| @@ -120,6 +120,8 @@ module RuboCop | |
| 120 120 | 
             
                      add_scope(node, @local_variables_scopes[node])
         | 
| 121 121 | 
             
                    end
         | 
| 122 122 |  | 
| 123 | 
            +
                    alias on_numblock on_block
         | 
| 124 | 
            +
             | 
| 123 125 | 
             
                    def on_if(node)
         | 
| 124 126 | 
             
                      # Allow conditional nodes to use `self` in the condition if that variable
         | 
| 125 127 | 
             
                      # name is used in an `lvasgn` or `masgn` within the `if`.
         | 
| @@ -19,18 +19,24 @@ module RuboCop | |
| 19 19 | 
             
                    include RangeHelp
         | 
| 20 20 | 
             
                    extend AutoCorrector
         | 
| 21 21 |  | 
| 22 | 
            -
                     | 
| 23 | 
            -
             | 
| 24 | 
            -
                    # @!method redundant_sort_by(node)
         | 
| 25 | 
            -
                    def_node_matcher :redundant_sort_by, <<~PATTERN
         | 
| 26 | 
            -
                      (block $(send _ :sort_by) (args (arg $_x)) (lvar _x))
         | 
| 27 | 
            -
                    PATTERN
         | 
| 22 | 
            +
                    MSG_BLOCK = 'Use `sort` instead of `sort_by { |%<var>s| %<var>s }`.'
         | 
| 23 | 
            +
                    MSG_NUMBLOCK = 'Use `sort` instead of `sort_by { _1 }`.'
         | 
| 28 24 |  | 
| 29 25 | 
             
                    def on_block(node)
         | 
| 30 | 
            -
                       | 
| 26 | 
            +
                      redundant_sort_by_block(node) do |send, var_name|
         | 
| 31 27 | 
             
                        range = sort_by_range(send, node)
         | 
| 32 28 |  | 
| 33 | 
            -
                        add_offense(range, message: format( | 
| 29 | 
            +
                        add_offense(range, message: format(MSG_BLOCK, var: var_name)) do |corrector|
         | 
| 30 | 
            +
                          corrector.replace(range, 'sort')
         | 
| 31 | 
            +
                        end
         | 
| 32 | 
            +
                      end
         | 
| 33 | 
            +
                    end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                    def on_numblock(node)
         | 
| 36 | 
            +
                      redundant_sort_by_numblock(node) do |send|
         | 
| 37 | 
            +
                        range = sort_by_range(send, node)
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                        add_offense(range, message: format(MSG_NUMBLOCK)) do |corrector|
         | 
| 34 40 | 
             
                          corrector.replace(range, 'sort')
         | 
| 35 41 | 
             
                        end
         | 
| 36 42 | 
             
                      end
         | 
| @@ -38,6 +44,16 @@ module RuboCop | |
| 38 44 |  | 
| 39 45 | 
             
                    private
         | 
| 40 46 |  | 
| 47 | 
            +
                    # @!method redundant_sort_by_block(node)
         | 
| 48 | 
            +
                    def_node_matcher :redundant_sort_by_block, <<~PATTERN
         | 
| 49 | 
            +
                      (block $(send _ :sort_by) (args (arg $_x)) (lvar _x))
         | 
| 50 | 
            +
                    PATTERN
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                    # @!method redundant_sort_by_numblock(node)
         | 
| 53 | 
            +
                    def_node_matcher :redundant_sort_by_numblock, <<~PATTERN
         | 
| 54 | 
            +
                      (numblock $(send _ :sort_by) 1 (lvar :_1))
         | 
| 55 | 
            +
                    PATTERN
         | 
| 56 | 
            +
             | 
| 41 57 | 
             
                    def sort_by_range(send, node)
         | 
| 42 58 | 
             
                      range_between(send.loc.selector.begin_pos, node.loc.end.end_pos)
         | 
| 43 59 | 
             
                    end
         | 
| @@ -141,7 +141,7 @@ module RuboCop | |
| 141 141 |  | 
| 142 142 | 
             
                      corrector.remove(begin_range(node, body))
         | 
| 143 143 | 
             
                      corrector.remove(end_range(node, body))
         | 
| 144 | 
            -
                      corrector.insert_before(method_call.loc.dot, '&')
         | 
| 144 | 
            +
                      corrector.insert_before(method_call.loc.dot, '&') unless method_call.safe_navigation?
         | 
| 145 145 | 
             
                      handle_comments(corrector, node, method_call)
         | 
| 146 146 |  | 
| 147 147 | 
             
                      add_safe_nav_to_all_methods_in_chain(corrector, method_call, body)
         | 
| @@ -250,7 +250,9 @@ module RuboCop | |
| 250 250 | 
             
                    end
         | 
| 251 251 |  | 
| 252 252 | 
             
                    def unsafe_method?(send_node)
         | 
| 253 | 
            -
                      negated?(send_node) || | 
| 253 | 
            +
                      negated?(send_node) ||
         | 
| 254 | 
            +
                        send_node.assignment? ||
         | 
| 255 | 
            +
                        (!send_node.dot? && !send_node.safe_navigation?)
         | 
| 254 256 | 
             
                    end
         | 
| 255 257 |  | 
| 256 258 | 
             
                    def negated?(send_node)
         | 
| @@ -173,8 +173,6 @@ module RuboCop | |
| 173 173 | 
             
                    end
         | 
| 174 174 |  | 
| 175 175 | 
             
                    def correct_for_comment(corrector, node, if_branch)
         | 
| 176 | 
            -
                      return if config.for_cop('Style/IfUnlessModifier')['Enabled']
         | 
| 177 | 
            -
             | 
| 178 176 | 
             
                      comments = processed_source.ast_with_comments[if_branch]
         | 
| 179 177 | 
             
                      comment_text = comments.map(&:text).join("\n") << "\n"
         | 
| 180 178 |  | 
| @@ -69,7 +69,7 @@ module RuboCop | |
| 69 69 | 
             
                        if c.dsym_type?
         | 
| 70 70 | 
             
                          string_literal = to_string_literal(c.source)
         | 
| 71 71 |  | 
| 72 | 
            -
                          ":#{ | 
| 72 | 
            +
                          ":#{trim_string_interpolation_escape_character(string_literal)}"
         | 
| 73 73 | 
             
                        else
         | 
| 74 74 | 
             
                          to_symbol_literal(c.value.to_s)
         | 
| 75 75 | 
             
                        end
         | 
| @@ -63,6 +63,8 @@ module RuboCop | |
| 63 63 | 
             
                      add_offense(node)
         | 
| 64 64 | 
             
                    end
         | 
| 65 65 |  | 
| 66 | 
            +
                    alias on_numblock on_block
         | 
| 67 | 
            +
             | 
| 66 68 | 
             
                    private
         | 
| 67 69 |  | 
| 68 70 | 
             
                    def top_level_method_definition?(node)
         | 
| @@ -75,7 +77,7 @@ module RuboCop | |
| 75 77 |  | 
| 76 78 | 
             
                    # @!method define_method_block?(node)
         | 
| 77 79 | 
             
                    def_node_matcher :define_method_block?, <<~PATTERN
         | 
| 78 | 
            -
                      (block (send _ {:define_method} _) ...)
         | 
| 80 | 
            +
                      ({block numblock} (send _ {:define_method} _) ...)
         | 
| 79 81 | 
             
                    PATTERN
         | 
| 80 82 | 
             
                  end
         | 
| 81 83 | 
             
                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)
         | 
| 67 | 
            +
                    def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
         | 
| 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)
         | 
| @@ -87,7 +87,7 @@ module RuboCop | |
| 87 87 | 
             
                        if word.dstr_type?
         | 
| 88 88 | 
             
                          string_literal = to_string_literal(word.source)
         | 
| 89 89 |  | 
| 90 | 
            -
                           | 
| 90 | 
            +
                          trim_string_interpolation_escape_character(string_literal)
         | 
| 91 91 | 
             
                        else
         | 
| 92 92 | 
             
                          to_string_literal(word.children[0])
         | 
| 93 93 | 
             
                        end
         | 
    
        data/lib/rubocop/cop/util.rb
    CHANGED