rubocop 1.72.2 → 1.74.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/README.md +3 -3
- data/config/default.yml +56 -15
- data/config/internal_affairs.yml +20 -0
- data/lib/rubocop/config_loader.rb +0 -1
- data/lib/rubocop/config_loader_resolver.rb +4 -3
- data/lib/rubocop/config_obsoletion/extracted_cop.rb +4 -3
- data/lib/rubocop/config_obsoletion.rb +1 -1
- data/lib/rubocop/config_validator.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/example_description.rb +3 -1
- data/lib/rubocop/cop/internal_affairs/node_type_group.rb +91 -0
- data/lib/rubocop/cop/internal_affairs.rb +1 -0
- data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +4 -4
- data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +26 -1
- data/lib/rubocop/cop/layout/line_length.rb +3 -3
- data/lib/rubocop/cop/lint/duplicate_methods.rb +0 -14
- data/lib/rubocop/cop/lint/empty_conditional_body.rb +14 -64
- data/lib/rubocop/cop/lint/erb_new_arguments.rb +0 -6
- data/lib/rubocop/cop/lint/float_comparison.rb +1 -6
- data/lib/rubocop/cop/lint/literal_as_condition.rb +103 -9
- data/lib/rubocop/cop/lint/mixed_case_range.rb +2 -2
- data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +2 -2
- data/lib/rubocop/cop/lint/redundant_require_statement.rb +0 -21
- data/lib/rubocop/cop/lint/redundant_type_conversion.rb +32 -5
- data/lib/rubocop/cop/lint/return_in_void_context.rb +4 -11
- data/lib/rubocop/cop/lint/shared_mutable_default.rb +12 -1
- data/lib/rubocop/cop/lint/useless_constant_scoping.rb +2 -11
- data/lib/rubocop/cop/lint/void.rb +6 -0
- data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +1 -1
- data/lib/rubocop/cop/mixin/hash_subset.rb +19 -4
- data/lib/rubocop/cop/mixin/range_help.rb +12 -0
- data/lib/rubocop/cop/mixin/target_ruby_version.rb +1 -1
- data/lib/rubocop/cop/mixin/trailing_comma.rb +12 -0
- data/lib/rubocop/cop/naming/variable_name.rb +64 -6
- data/lib/rubocop/cop/style/accessor_grouping.rb +19 -5
- data/lib/rubocop/cop/style/class_and_module_children.rb +29 -7
- data/lib/rubocop/cop/style/commented_keyword.rb +10 -3
- data/lib/rubocop/cop/style/comparable_between.rb +75 -0
- data/lib/rubocop/cop/style/double_negation.rb +1 -1
- data/lib/rubocop/cop/style/endless_method.rb +163 -18
- data/lib/rubocop/cop/style/expand_path_arguments.rb +2 -7
- data/lib/rubocop/cop/style/exponential_notation.rb +2 -2
- data/lib/rubocop/cop/style/format_string_token.rb +38 -11
- data/lib/rubocop/cop/style/if_unless_modifier.rb +2 -2
- data/lib/rubocop/cop/style/inverse_methods.rb +8 -5
- data/lib/rubocop/cop/style/keyword_parameters_order.rb +13 -7
- data/lib/rubocop/cop/style/line_end_concatenation.rb +10 -4
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +3 -3
- data/lib/rubocop/cop/style/method_called_on_do_end_block.rb +1 -1
- data/lib/rubocop/cop/style/multiline_block_chain.rb +1 -1
- data/lib/rubocop/cop/style/multiline_method_signature.rb +1 -9
- data/lib/rubocop/cop/style/redundant_condition.rb +45 -0
- data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +14 -4
- data/lib/rubocop/cop/style/redundant_format.rb +23 -11
- data/lib/rubocop/cop/style/redundant_freeze.rb +1 -1
- data/lib/rubocop/cop/style/redundant_self_assignment.rb +1 -1
- data/lib/rubocop/cop/style/rescue_modifier.rb +3 -0
- data/lib/rubocop/cop/style/single_line_methods.rb +3 -3
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +0 -6
- data/lib/rubocop/cop/style/string_concatenation.rb +1 -1
- data/lib/rubocop/cop/style/trailing_comma_in_array_literal.rb +47 -6
- data/lib/rubocop/cop/style/trailing_comma_in_hash_literal.rb +48 -6
- data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
- data/lib/rubocop/cop/utils/format_string.rb +5 -2
- data/lib/rubocop/cops_documentation_generator.rb +12 -1
- data/lib/rubocop/directive_comment.rb +1 -1
- data/lib/rubocop/ext/regexp_node.rb +0 -1
- data/lib/rubocop/plugin/load_error.rb +1 -1
- data/lib/rubocop/plugin.rb +9 -2
- data/lib/rubocop/rspec/shared_contexts.rb +15 -0
- data/lib/rubocop/rspec/support.rb +1 -0
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +1 -1
- metadata +6 -5
- data/lib/rubocop/cop/utils/regexp_ranges.rb +0 -113
| @@ -5,8 +5,11 @@ module RuboCop | |
| 5 5 | 
             
                module Style
         | 
| 6 6 | 
             
                  # Checks for endless methods.
         | 
| 7 7 | 
             
                  #
         | 
| 8 | 
            -
                  # It can enforce  | 
| 9 | 
            -
                  #  | 
| 8 | 
            +
                  # It can enforce endless method definitions whenever possible or with single line methods.
         | 
| 9 | 
            +
                  # It can also disallow multiline endless method definitions or all endless definitions.
         | 
| 10 | 
            +
                  #
         | 
| 11 | 
            +
                  # `require_single_line` style enforces endless method definitions for single line methods.
         | 
| 12 | 
            +
                  # `require_always` style enforces endless method definitions for single statement methods.
         | 
| 10 13 | 
             
                  #
         | 
| 11 14 | 
             
                  # Other method definition types are not considered by this cop.
         | 
| 12 15 | 
             
                  #
         | 
| @@ -15,36 +18,116 @@ module RuboCop | |
| 15 18 | 
             
                  # * allow_single_line (default) - only single line endless method definitions are allowed.
         | 
| 16 19 | 
             
                  # * allow_always - all endless method definitions are allowed.
         | 
| 17 20 | 
             
                  # * disallow - all endless method definitions are disallowed.
         | 
| 21 | 
            +
                  # * require_single_line - endless method definitions are required for single line methods.
         | 
| 22 | 
            +
                  # * require_always - all endless method definitions are required.
         | 
| 18 23 | 
             
                  #
         | 
| 19 24 | 
             
                  # NOTE: Incorrect endless method definitions will always be
         | 
| 20 25 | 
             
                  # corrected to a multi-line definition.
         | 
| 21 26 | 
             
                  #
         | 
| 22 27 | 
             
                  # @example EnforcedStyle: allow_single_line (default)
         | 
| 28 | 
            +
                  #   # bad, multi-line endless method
         | 
| 29 | 
            +
                  #   def my_method = x.foo
         | 
| 30 | 
            +
                  #                    .bar
         | 
| 31 | 
            +
                  #                    .baz
         | 
| 32 | 
            +
                  #
         | 
| 23 33 | 
             
                  #   # good
         | 
| 24 | 
            -
                  #   def my_method | 
| 34 | 
            +
                  #   def my_method
         | 
| 35 | 
            +
                  #     x
         | 
| 36 | 
            +
                  #   end
         | 
| 25 37 | 
             
                  #
         | 
| 26 | 
            -
                  #   #  | 
| 27 | 
            -
                  #   def my_method | 
| 28 | 
            -
                  # | 
| 29 | 
            -
                  # | 
| 38 | 
            +
                  #   # good
         | 
| 39 | 
            +
                  #   def my_method = x
         | 
| 40 | 
            +
                  #
         | 
| 41 | 
            +
                  #   # good
         | 
| 42 | 
            +
                  #   def my_method
         | 
| 43 | 
            +
                  #     x.foo
         | 
| 44 | 
            +
                  #      .bar
         | 
| 45 | 
            +
                  #      .baz
         | 
| 46 | 
            +
                  #   end
         | 
| 30 47 | 
             
                  #
         | 
| 31 48 | 
             
                  # @example EnforcedStyle: allow_always
         | 
| 32 49 | 
             
                  #   # good
         | 
| 33 | 
            -
                  #   def my_method | 
| 50 | 
            +
                  #   def my_method
         | 
| 51 | 
            +
                  #     x
         | 
| 52 | 
            +
                  #   end
         | 
| 34 53 | 
             
                  #
         | 
| 35 54 | 
             
                  #   # good
         | 
| 36 | 
            -
                  #   def my_method | 
| 37 | 
            -
                  # | 
| 38 | 
            -
                  # | 
| 55 | 
            +
                  #   def my_method = x
         | 
| 56 | 
            +
                  #
         | 
| 57 | 
            +
                  #   # good
         | 
| 58 | 
            +
                  #   def my_method = x.foo
         | 
| 59 | 
            +
                  #                    .bar
         | 
| 60 | 
            +
                  #                    .baz
         | 
| 61 | 
            +
                  #
         | 
| 62 | 
            +
                  #   # good
         | 
| 63 | 
            +
                  #   def my_method
         | 
| 64 | 
            +
                  #     x.foo
         | 
| 65 | 
            +
                  #      .bar
         | 
| 66 | 
            +
                  #      .baz
         | 
| 67 | 
            +
                  #   end
         | 
| 39 68 | 
             
                  #
         | 
| 40 69 | 
             
                  # @example EnforcedStyle: disallow
         | 
| 41 70 | 
             
                  #   # bad
         | 
| 42 | 
            -
                  #   def my_method | 
| 71 | 
            +
                  #   def my_method = x
         | 
| 72 | 
            +
                  #
         | 
| 73 | 
            +
                  #   # bad
         | 
| 74 | 
            +
                  #   def my_method = x.foo
         | 
| 75 | 
            +
                  #                    .bar
         | 
| 76 | 
            +
                  #                    .baz
         | 
| 77 | 
            +
                  #
         | 
| 78 | 
            +
                  #   # good
         | 
| 79 | 
            +
                  #   def my_method
         | 
| 80 | 
            +
                  #     x
         | 
| 81 | 
            +
                  #   end
         | 
| 82 | 
            +
                  #
         | 
| 83 | 
            +
                  #   # good
         | 
| 84 | 
            +
                  #   def my_method
         | 
| 85 | 
            +
                  #     x.foo
         | 
| 86 | 
            +
                  #      .bar
         | 
| 87 | 
            +
                  #      .baz
         | 
| 88 | 
            +
                  #   end
         | 
| 89 | 
            +
                  #
         | 
| 90 | 
            +
                  # @example EnforcedStyle: require_single_line
         | 
| 91 | 
            +
                  #   # bad
         | 
| 92 | 
            +
                  #   def my_method
         | 
| 93 | 
            +
                  #     x
         | 
| 94 | 
            +
                  #   end
         | 
| 95 | 
            +
                  #
         | 
| 96 | 
            +
                  #   # bad
         | 
| 97 | 
            +
                  #   def my_method = x.foo
         | 
| 98 | 
            +
                  #                    .bar
         | 
| 99 | 
            +
                  #                    .baz
         | 
| 100 | 
            +
                  #
         | 
| 101 | 
            +
                  #   # good
         | 
| 102 | 
            +
                  #   def my_method = x
         | 
| 103 | 
            +
                  #
         | 
| 104 | 
            +
                  #   # good
         | 
| 105 | 
            +
                  #   def my_method
         | 
| 106 | 
            +
                  #     x.foo
         | 
| 107 | 
            +
                  #      .bar
         | 
| 108 | 
            +
                  #      .baz
         | 
| 109 | 
            +
                  #   end
         | 
| 110 | 
            +
                  #
         | 
| 111 | 
            +
                  # @example EnforcedStyle: require_always
         | 
| 112 | 
            +
                  #   # bad
         | 
| 113 | 
            +
                  #   def my_method
         | 
| 114 | 
            +
                  #     x
         | 
| 115 | 
            +
                  #   end
         | 
| 43 116 | 
             
                  #
         | 
| 44 117 | 
             
                  #   # bad
         | 
| 45 | 
            -
                  #   def my_method | 
| 46 | 
            -
                  # | 
| 47 | 
            -
                  # | 
| 118 | 
            +
                  #   def my_method
         | 
| 119 | 
            +
                  #     x.foo
         | 
| 120 | 
            +
                  #      .bar
         | 
| 121 | 
            +
                  #      .baz
         | 
| 122 | 
            +
                  #   end
         | 
| 123 | 
            +
                  #
         | 
| 124 | 
            +
                  #   # good
         | 
| 125 | 
            +
                  #   def my_method = x
         | 
| 126 | 
            +
                  #
         | 
| 127 | 
            +
                  #   # good
         | 
| 128 | 
            +
                  #   def my_method = x.foo
         | 
| 129 | 
            +
                  #                    .bar
         | 
| 130 | 
            +
                  #                    .baz
         | 
| 48 131 | 
             
                  #
         | 
| 49 132 | 
             
                  class EndlessMethod < Base
         | 
| 50 133 | 
             
                    include ConfigurableEnforcedStyle
         | 
| @@ -57,12 +140,21 @@ module RuboCop | |
| 57 140 | 
             
                    CORRECTION_STYLES = %w[multiline single_line].freeze
         | 
| 58 141 | 
             
                    MSG = 'Avoid endless method definitions.'
         | 
| 59 142 | 
             
                    MSG_MULTI_LINE = 'Avoid endless method definitions with multiple lines.'
         | 
| 143 | 
            +
                    MSG_REQUIRE_SINGLE = 'Use endless method definitions for single line methods.'
         | 
| 144 | 
            +
                    MSG_REQUIRE_ALWAYS = 'Use endless method definitions.'
         | 
| 60 145 |  | 
| 61 146 | 
             
                    def on_def(node)
         | 
| 62 | 
            -
                      if  | 
| 63 | 
            -
             | 
| 64 | 
            -
                       | 
| 147 | 
            +
                      return if node.assignment_method?
         | 
| 148 | 
            +
             | 
| 149 | 
            +
                      case style
         | 
| 150 | 
            +
                      when :allow_single_line, :allow_always
         | 
| 65 151 | 
             
                        handle_allow_style(node)
         | 
| 152 | 
            +
                      when :disallow
         | 
| 153 | 
            +
                        handle_disallow_style(node)
         | 
| 154 | 
            +
                      when :require_single_line
         | 
| 155 | 
            +
                        handle_require_single_line_style(node)
         | 
| 156 | 
            +
                      when :require_always
         | 
| 157 | 
            +
                        handle_require_always_style(node)
         | 
| 66 158 | 
             
                      end
         | 
| 67 159 | 
             
                    end
         | 
| 68 160 |  | 
| @@ -77,11 +169,64 @@ module RuboCop | |
| 77 169 | 
             
                      end
         | 
| 78 170 | 
             
                    end
         | 
| 79 171 |  | 
| 172 | 
            +
                    def handle_require_single_line_style(node)
         | 
| 173 | 
            +
                      if node.endless? && !node.single_line?
         | 
| 174 | 
            +
                        add_offense(node, message: MSG_MULTI_LINE) do |corrector|
         | 
| 175 | 
            +
                          correct_to_multiline(corrector, node)
         | 
| 176 | 
            +
                        end
         | 
| 177 | 
            +
                      elsif !node.endless? && can_be_made_endless?(node) && node.body.single_line?
         | 
| 178 | 
            +
                        return if too_long_when_made_endless?(node)
         | 
| 179 | 
            +
             | 
| 180 | 
            +
                        add_offense(node, message: MSG_REQUIRE_SINGLE) do |corrector|
         | 
| 181 | 
            +
                          corrector.replace(node, endless_replacement(node))
         | 
| 182 | 
            +
                        end
         | 
| 183 | 
            +
                      end
         | 
| 184 | 
            +
                    end
         | 
| 185 | 
            +
             | 
| 186 | 
            +
                    def handle_require_always_style(node)
         | 
| 187 | 
            +
                      return if node.endless? || !can_be_made_endless?(node)
         | 
| 188 | 
            +
                      return if too_long_when_made_endless?(node)
         | 
| 189 | 
            +
             | 
| 190 | 
            +
                      add_offense(node, message: MSG_REQUIRE_ALWAYS) do |corrector|
         | 
| 191 | 
            +
                        corrector.replace(node, endless_replacement(node))
         | 
| 192 | 
            +
                      end
         | 
| 193 | 
            +
                    end
         | 
| 194 | 
            +
             | 
| 80 195 | 
             
                    def handle_disallow_style(node)
         | 
| 81 196 | 
             
                      return unless node.endless?
         | 
| 82 197 |  | 
| 83 198 | 
             
                      add_offense(node) { |corrector| correct_to_multiline(corrector, node) }
         | 
| 84 199 | 
             
                    end
         | 
| 200 | 
            +
             | 
| 201 | 
            +
                    def correct_to_multiline(corrector, node)
         | 
| 202 | 
            +
                      replacement = <<~RUBY.strip
         | 
| 203 | 
            +
                        def #{node.method_name}#{arguments(node)}
         | 
| 204 | 
            +
                          #{node.body.source}
         | 
| 205 | 
            +
                        end
         | 
| 206 | 
            +
                      RUBY
         | 
| 207 | 
            +
             | 
| 208 | 
            +
                      corrector.replace(node, replacement)
         | 
| 209 | 
            +
                    end
         | 
| 210 | 
            +
             | 
| 211 | 
            +
                    def endless_replacement(node)
         | 
| 212 | 
            +
                      <<~RUBY.strip
         | 
| 213 | 
            +
                        def #{node.method_name}#{arguments(node)} = #{node.body.source}
         | 
| 214 | 
            +
                      RUBY
         | 
| 215 | 
            +
                    end
         | 
| 216 | 
            +
             | 
| 217 | 
            +
                    def arguments(node, missing = '')
         | 
| 218 | 
            +
                      node.arguments.any? ? node.arguments.source : missing
         | 
| 219 | 
            +
                    end
         | 
| 220 | 
            +
             | 
| 221 | 
            +
                    def can_be_made_endless?(node)
         | 
| 222 | 
            +
                      node.body && !node.body.begin_type? && !node.body.kwbegin_type?
         | 
| 223 | 
            +
                    end
         | 
| 224 | 
            +
             | 
| 225 | 
            +
                    def too_long_when_made_endless?(node)
         | 
| 226 | 
            +
                      return false unless config.cop_enabled?('Layout/LineLength')
         | 
| 227 | 
            +
             | 
| 228 | 
            +
                      endless_replacement(node).length > config.for_cop('Layout/LineLength')['Max']
         | 
| 229 | 
            +
                    end
         | 
| 85 230 | 
             
                  end
         | 
| 86 231 | 
             
                end
         | 
| 87 232 | 
             
              end
         | 
| @@ -137,11 +137,11 @@ module RuboCop | |
| 137 137 |  | 
| 138 138 | 
             
                      case depth(stripped_current_path)
         | 
| 139 139 | 
             
                      when 0
         | 
| 140 | 
            -
                        range = arguments_range(current_path)
         | 
| 140 | 
            +
                        range = arguments_range(current_path.parent)
         | 
| 141 141 |  | 
| 142 142 | 
             
                        corrector.replace(range, '__FILE__')
         | 
| 143 143 | 
             
                      when 1
         | 
| 144 | 
            -
                        range = arguments_range(current_path)
         | 
| 144 | 
            +
                        range = arguments_range(current_path.parent)
         | 
| 145 145 |  | 
| 146 146 | 
             
                        corrector.replace(range, '__dir__')
         | 
| 147 147 | 
             
                      else
         | 
| @@ -185,11 +185,6 @@ module RuboCop | |
| 185 185 | 
             
                      corrector.remove(node.loc.dot)
         | 
| 186 186 | 
             
                      corrector.remove(node.loc.selector)
         | 
| 187 187 | 
             
                    end
         | 
| 188 | 
            -
             | 
| 189 | 
            -
                    def arguments_range(node)
         | 
| 190 | 
            -
                      range_between(node.parent.first_argument.source_range.begin_pos,
         | 
| 191 | 
            -
                                    node.parent.last_argument.source_range.end_pos)
         | 
| 192 | 
            -
                    end
         | 
| 193 188 | 
             
                  end
         | 
| 194 189 | 
             
                end
         | 
| 195 190 | 
             
              end
         | 
| @@ -60,8 +60,8 @@ module RuboCop | |
| 60 60 | 
             
                  class ExponentialNotation < Base
         | 
| 61 61 | 
             
                    include ConfigurableEnforcedStyle
         | 
| 62 62 | 
             
                    MESSAGES = {
         | 
| 63 | 
            -
                      scientific: 'Use a mantissa  | 
| 64 | 
            -
                      engineering: 'Use an exponent divisible by 3 and a mantissa  | 
| 63 | 
            +
                      scientific: 'Use a mantissa >= 1 and < 10.',
         | 
| 64 | 
            +
                      engineering: 'Use an exponent divisible by 3 and a mantissa >= 0.1 and < 1000.',
         | 
| 65 65 | 
             
                      integral: 'Use an integer as mantissa, without trailing zero.'
         | 
| 66 66 | 
             
                    }.freeze
         | 
| 67 67 |  | 
| @@ -3,16 +3,24 @@ | |
| 3 3 | 
             
            module RuboCop
         | 
| 4 4 | 
             
              module Cop
         | 
| 5 5 | 
             
                module Style
         | 
| 6 | 
            -
                  # Use a consistent style for  | 
| 6 | 
            +
                  # Use a consistent style for tokens within a format string.
         | 
| 7 7 | 
             
                  #
         | 
| 8 | 
            -
                  #  | 
| 9 | 
            -
                  #  | 
| 10 | 
            -
                  #  | 
| 11 | 
            -
                  # The reason is that _unannotated_ format is very similar
         | 
| 12 | 
            -
                  # to encoded URLs or Date/Time formatting strings.
         | 
| 8 | 
            +
                  # By default, all strings are evaluated. In some cases, this may be undesirable,
         | 
| 9 | 
            +
                  # as they could be used as arguments to a method that does not consider
         | 
| 10 | 
            +
                  # them to be tokens, but rather other identifiers or just part of the string.
         | 
| 13 11 | 
             
                  #
         | 
| 14 | 
            -
                  #  | 
| 15 | 
            -
                  #  | 
| 12 | 
            +
                  # `AllowedMethods` or `AllowedPatterns` can be configured with in order to mark specific
         | 
| 13 | 
            +
                  # methods as always allowed, thereby avoiding an offense from the cop. By default, there
         | 
| 14 | 
            +
                  # are no allowed methods.
         | 
| 15 | 
            +
                  #
         | 
| 16 | 
            +
                  # Additionally, the cop can be made conservative by configuring it with
         | 
| 17 | 
            +
                  # `Mode: conservative` (default `aggressive`). In this mode, tokens (regardless
         | 
| 18 | 
            +
                  # of `EnforcedStyle`) are only considered if used in the format string argument to the
         | 
| 19 | 
            +
                  # methods `printf`, `sprintf`, `format` and `%`.
         | 
| 20 | 
            +
                  #
         | 
| 21 | 
            +
                  # NOTE: Tokens in the `unannotated` style (eg. `%s`) are always treated as if
         | 
| 22 | 
            +
                  # configured with `Conservative: true`. This is done in order to prevent false positives,
         | 
| 23 | 
            +
                  # because this format is very similar to encoded URLs or Date/Time formatting strings.
         | 
| 16 24 | 
             
                  #
         | 
| 17 25 | 
             
                  # @example EnforcedStyle: annotated (default)
         | 
| 18 26 | 
             
                  #
         | 
| @@ -82,6 +90,20 @@ module RuboCop | |
| 82 90 | 
             
                  #   # good
         | 
| 83 91 | 
             
                  #   redirect('foo/%{bar_id}')
         | 
| 84 92 | 
             
                  #
         | 
| 93 | 
            +
                  # @example Mode: conservative, EnforcedStyle: annotated
         | 
| 94 | 
            +
                  #   # In `conservative` mode, offenses are only registered for strings
         | 
| 95 | 
            +
                  #   # given to a known formatting method.
         | 
| 96 | 
            +
                  #
         | 
| 97 | 
            +
                  #   # good
         | 
| 98 | 
            +
                  #   "%{greeting}"
         | 
| 99 | 
            +
                  #   foo("%{greeting}")
         | 
| 100 | 
            +
                  #
         | 
| 101 | 
            +
                  #   # bad
         | 
| 102 | 
            +
                  #   format("%{greeting}", greeting: 'Hello')
         | 
| 103 | 
            +
                  #   printf("%{greeting}", greeting: 'Hello')
         | 
| 104 | 
            +
                  #   sprintf("%{greeting}", greeting: 'Hello')
         | 
| 105 | 
            +
                  #   "%{greeting}" % { greeting: 'Hello' }
         | 
| 106 | 
            +
                  #
         | 
| 85 107 | 
             
                  class FormatStringToken < Base
         | 
| 86 108 | 
             
                    include ConfigurableEnforcedStyle
         | 
| 87 109 | 
             
                    include AllowedMethods
         | 
| @@ -153,8 +175,9 @@ module RuboCop | |
| 153 175 | 
             
                      corrector.replace(token_range, correction)
         | 
| 154 176 | 
             
                    end
         | 
| 155 177 |  | 
| 156 | 
            -
                    def  | 
| 157 | 
            -
                      detected_style == :unannotated  | 
| 178 | 
            +
                    def allowed_string?(node, detected_style)
         | 
| 179 | 
            +
                      (detected_style == :unannotated || conservative?) &&
         | 
| 180 | 
            +
                        !format_string_in_typical_context?(node)
         | 
| 158 181 | 
             
                    end
         | 
| 159 182 |  | 
| 160 183 | 
             
                    def message(detected_style)
         | 
| @@ -203,7 +226,7 @@ module RuboCop | |
| 203 226 | 
             
                    def collect_detections(node)
         | 
| 204 227 | 
             
                      detections = []
         | 
| 205 228 | 
             
                      tokens(node) do |detected_sequence, token_range|
         | 
| 206 | 
            -
                        unless  | 
| 229 | 
            +
                        unless allowed_string?(node, detected_sequence.style)
         | 
| 207 230 | 
             
                          detections << [detected_sequence, token_range]
         | 
| 208 231 | 
             
                        end
         | 
| 209 232 | 
             
                      end
         | 
| @@ -222,6 +245,10 @@ module RuboCop | |
| 222 245 | 
             
                    def max_unannotated_placeholders_allowed
         | 
| 223 246 | 
             
                      cop_config['MaxUnannotatedPlaceholdersAllowed']
         | 
| 224 247 | 
             
                    end
         | 
| 248 | 
            +
             | 
| 249 | 
            +
                    def conservative?
         | 
| 250 | 
            +
                      cop_config.fetch('Mode', :aggressive).to_sym == :conservative
         | 
| 251 | 
            +
                    end
         | 
| 225 252 | 
             
                  end
         | 
| 226 253 | 
             
                end
         | 
| 227 254 | 
             
              end
         | 
| @@ -164,8 +164,8 @@ module RuboCop | |
| 164 164 |  | 
| 165 165 | 
             
                    def too_long_due_to_comment_after_modifier?(node, comment)
         | 
| 166 166 | 
             
                      source_length = processed_source.lines[node.first_line - 1].length
         | 
| 167 | 
            -
             | 
| 168 | 
            -
             | 
| 167 | 
            +
             | 
| 168 | 
            +
                      max_line_length.between?(source_length - comment.source_range.length, source_length)
         | 
| 169 169 | 
             
                    end
         | 
| 170 170 |  | 
| 171 171 | 
             
                    def allowed_patterns
         | 
| @@ -46,6 +46,7 @@ module RuboCop | |
| 46 46 |  | 
| 47 47 | 
             
                    MSG = 'Use `%<inverse>s` instead of inverting `%<method>s`.'
         | 
| 48 48 | 
             
                    CLASS_COMPARISON_METHODS = %i[<= >= < >].freeze
         | 
| 49 | 
            +
                    SAFE_NAVIGATION_INCOMPATIBLE_METHODS = (CLASS_COMPARISON_METHODS + %i[any? none?]).freeze
         | 
| 49 50 | 
             
                    EQUALITY_METHODS = %i[== != =~ !~ <= >= < >].freeze
         | 
| 50 51 | 
             
                    NEGATED_EQUALITY_METHODS = %i[!= !~].freeze
         | 
| 51 52 | 
             
                    CAMEL_CASE = /[A-Z]+[a-z]+/.freeze
         | 
| @@ -77,7 +78,7 @@ module RuboCop | |
| 77 78 | 
             
                    def on_send(node)
         | 
| 78 79 | 
             
                      inverse_candidate?(node) do |method_call, lhs, method, rhs|
         | 
| 79 80 | 
             
                        return unless inverse_methods.key?(method)
         | 
| 80 | 
            -
                        return if negated?(node) ||  | 
| 81 | 
            +
                        return if negated?(node) || safe_navigation_incompatible?(method_call)
         | 
| 81 82 | 
             
                        return if part_of_ignored_node?(node)
         | 
| 82 83 | 
             
                        return if possible_class_hierarchy_check?(lhs, rhs, method)
         | 
| 83 84 |  | 
| @@ -154,10 +155,6 @@ module RuboCop | |
| 154 155 | 
             
                      node.parent.respond_to?(:method?) && node.parent.method?(:!)
         | 
| 155 156 | 
             
                    end
         | 
| 156 157 |  | 
| 157 | 
            -
                    def relational_comparison_with_safe_navigation?(node)
         | 
| 158 | 
            -
                      node.csend_type? && CLASS_COMPARISON_METHODS.include?(node.method_name)
         | 
| 159 | 
            -
                    end
         | 
| 160 | 
            -
             | 
| 161 158 | 
             
                    def not_to_receiver(node, method_call)
         | 
| 162 159 | 
             
                      node.loc.selector.begin.join(method_call.source_range.begin)
         | 
| 163 160 | 
             
                    end
         | 
| @@ -166,6 +163,12 @@ module RuboCop | |
| 166 163 | 
             
                      method_call.source_range.end.join(node.source_range.end)
         | 
| 167 164 | 
             
                    end
         | 
| 168 165 |  | 
| 166 | 
            +
                    def safe_navigation_incompatible?(node)
         | 
| 167 | 
            +
                      return false unless node.csend_type?
         | 
| 168 | 
            +
             | 
| 169 | 
            +
                      SAFE_NAVIGATION_INCOMPATIBLE_METHODS.include?(node.method_name)
         | 
| 170 | 
            +
                    end
         | 
| 171 | 
            +
             | 
| 169 172 | 
             
                    # When comparing classes, `!(Integer < Numeric)` is not the same as
         | 
| 170 173 | 
             
                    # `Integer > Numeric`.
         | 
| 171 174 | 
             
                    def possible_class_hierarchy_check?(lhs, rhs, method)
         | 
| @@ -42,19 +42,25 @@ module RuboCop | |
| 42 42 | 
             
                      return if kwarg_nodes.empty?
         | 
| 43 43 |  | 
| 44 44 | 
             
                      add_offense(node) do |corrector|
         | 
| 45 | 
            -
                         | 
| 46 | 
            -
             | 
| 45 | 
            +
                        defining_node = node.each_ancestor(:def, :defs, :block).first
         | 
| 46 | 
            +
                        next if processed_source.contains_comment?(arguments_range(defining_node))
         | 
| 47 | 
            +
                        next unless node.parent.find(&:kwoptarg_type?) == node
         | 
| 47 48 |  | 
| 48 | 
            -
             | 
| 49 | 
            -
                          append_newline_to_last_kwoptarg(arguments, corrector) unless parentheses?(arguments)
         | 
| 50 | 
            -
             | 
| 51 | 
            -
                          remove_kwargs(kwarg_nodes, corrector)
         | 
| 52 | 
            -
                        end
         | 
| 49 | 
            +
                        autocorrect(corrector, node, defining_node, kwarg_nodes)
         | 
| 53 50 | 
             
                      end
         | 
| 54 51 | 
             
                    end
         | 
| 55 52 |  | 
| 56 53 | 
             
                    private
         | 
| 57 54 |  | 
| 55 | 
            +
                    def autocorrect(corrector, node, defining_node, kwarg_nodes)
         | 
| 56 | 
            +
                      corrector.insert_before(node, "#{kwarg_nodes.map(&:source).join(', ')}, ")
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                      arguments = defining_node.arguments
         | 
| 59 | 
            +
                      append_newline_to_last_kwoptarg(arguments, corrector) unless parentheses?(arguments)
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                      remove_kwargs(kwarg_nodes, corrector)
         | 
| 62 | 
            +
                    end
         | 
| 63 | 
            +
             | 
| 58 64 | 
             
                    def append_newline_to_last_kwoptarg(arguments, corrector)
         | 
| 59 65 | 
             
                      last_argument = arguments.last
         | 
| 60 66 | 
             
                      return if last_argument.type?(:kwrestarg, :blockarg)
         | 
| @@ -36,7 +36,7 @@ module RuboCop | |
| 36 36 | 
             
                    include RangeHelp
         | 
| 37 37 | 
             
                    extend AutoCorrector
         | 
| 38 38 |  | 
| 39 | 
            -
                    MSG = 'Use `\\` instead of  | 
| 39 | 
            +
                    MSG = 'Use `\\` instead of `%<operator>s` to concatenate multiline strings.'
         | 
| 40 40 | 
             
                    CONCAT_TOKEN_TYPES = %i[tPLUS tLSHFT].freeze
         | 
| 41 41 | 
             
                    SIMPLE_STRING_TOKEN_TYPE = :tSTRING
         | 
| 42 42 | 
             
                    COMPLEX_STRING_BEGIN_TOKEN = :tSTRING_BEG
         | 
| @@ -61,14 +61,20 @@ module RuboCop | |
| 61 61 | 
             
                      successor = tokens[index + 2]
         | 
| 62 62 |  | 
| 63 63 | 
             
                      return unless eligible_token_set?(predecessor, operator, successor)
         | 
| 64 | 
            -
             | 
| 65 64 | 
             
                      return if same_line?(operator, successor)
         | 
| 66 65 |  | 
| 67 66 | 
             
                      next_successor = token_after_last_string(successor, index)
         | 
| 68 | 
            -
             | 
| 69 67 | 
             
                      return unless eligible_next_successor?(next_successor)
         | 
| 70 68 |  | 
| 71 | 
            -
                       | 
| 69 | 
            +
                      register_offense(operator)
         | 
| 70 | 
            +
                    end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                    def register_offense(operator)
         | 
| 73 | 
            +
                      message = format(MSG, operator: operator.text)
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                      add_offense(operator.pos, message: message) do |corrector|
         | 
| 76 | 
            +
                        autocorrect(corrector, operator.pos)
         | 
| 77 | 
            +
                      end
         | 
| 72 78 | 
             
                    end
         | 
| 73 79 |  | 
| 74 80 | 
             
                    def autocorrect(corrector, operator_range)
         | 
| @@ -108,7 +108,7 @@ module RuboCop | |
| 108 108 | 
             
                      end
         | 
| 109 109 |  | 
| 110 110 | 
             
                      def call_in_literals?(node)
         | 
| 111 | 
            -
                        parent = node.parent&. | 
| 111 | 
            +
                        parent = node.parent&.any_block_type? ? node.parent.parent : node.parent
         | 
| 112 112 | 
             
                        return false unless parent
         | 
| 113 113 |  | 
| 114 114 | 
             
                        parent.type?(:pair, :array, :range) ||
         | 
| @@ -117,7 +117,7 @@ module RuboCop | |
| 117 117 | 
             
                      end
         | 
| 118 118 |  | 
| 119 119 | 
             
                      def call_in_logical_operators?(node)
         | 
| 120 | 
            -
                        parent = node.parent&. | 
| 120 | 
            +
                        parent = node.parent&.any_block_type? ? node.parent.parent : node.parent
         | 
| 121 121 | 
             
                        return false unless parent
         | 
| 122 122 |  | 
| 123 123 | 
             
                        logical_operator?(parent) ||
         | 
| @@ -153,7 +153,7 @@ module RuboCop | |
| 153 153 | 
             
                      end
         | 
| 154 154 |  | 
| 155 155 | 
             
                      def call_in_argument_with_block?(node)
         | 
| 156 | 
            -
                        parent = node.parent&. | 
| 156 | 
            +
                        parent = node.parent&.any_block_type? && node.parent.parent
         | 
| 157 157 | 
             
                        return false unless parent
         | 
| 158 158 |  | 
| 159 159 | 
             
                        parent.type?(:call, :super, :yield)
         | 
| @@ -41,7 +41,7 @@ module RuboCop | |
| 41 41 | 
             
                      return if ignored_node?(node)
         | 
| 42 42 |  | 
| 43 43 | 
             
                      return unless (receiver = node.receiver)
         | 
| 44 | 
            -
                      return unless receiver.any_block_type? && receiver. | 
| 44 | 
            +
                      return unless receiver.any_block_type? && receiver.keywords?
         | 
| 45 45 |  | 
| 46 46 | 
             
                      range = range_between(receiver.loc.end.begin_pos, node.source_range.end_pos)
         | 
| 47 47 |  | 
| @@ -28,7 +28,7 @@ module RuboCop | |
| 28 28 | 
             
                    MSG = 'Avoid multi-line chains of blocks.'
         | 
| 29 29 |  | 
| 30 30 | 
             
                    def on_block(node)
         | 
| 31 | 
            -
                      node.send_node.each_node(: | 
| 31 | 
            +
                      node.send_node.each_node(:call) do |send_node|
         | 
| 32 32 | 
             
                        receiver = send_node.receiver
         | 
| 33 33 |  | 
| 34 34 | 
             
                        next unless receiver&.any_block_type? && receiver.multiline?
         | 
| @@ -50,7 +50,7 @@ module RuboCop | |
| 50 50 | 
             
                        corrector.remove(range_by_whole_lines(arguments.loc.end, include_final_newline: true))
         | 
| 51 51 | 
             
                      end
         | 
| 52 52 |  | 
| 53 | 
            -
                      arguments_range = arguments_range(node)
         | 
| 53 | 
            +
                      arguments_range = range_with_surrounding_space(arguments_range(node), side: :left)
         | 
| 54 54 | 
             
                      # If the method name isn't on the same line as def, move it directly after def
         | 
| 55 55 | 
             
                      if arguments_range.first_line != opening_line(node)
         | 
| 56 56 | 
             
                        corrector.remove(node.loc.name)
         | 
| @@ -66,14 +66,6 @@ module RuboCop | |
| 66 66 | 
             
                      processed_source[arguments.last_line - 1].strip
         | 
| 67 67 | 
             
                    end
         | 
| 68 68 |  | 
| 69 | 
            -
                    def arguments_range(node)
         | 
| 70 | 
            -
                      range = range_between(
         | 
| 71 | 
            -
                        node.first_argument.source_range.begin_pos, node.last_argument.source_range.end_pos
         | 
| 72 | 
            -
                      )
         | 
| 73 | 
            -
             | 
| 74 | 
            -
                      range_with_surrounding_space(range, side: :left)
         | 
| 75 | 
            -
                    end
         | 
| 76 | 
            -
             | 
| 77 69 | 
             
                    def opening_line(node)
         | 
| 78 70 | 
             
                      node.first_line
         | 
| 79 71 | 
             
                    end
         | 
| @@ -42,7 +42,28 @@ module RuboCop | |
| 42 42 | 
             
                  #     c
         | 
| 43 43 | 
             
                  #   end
         | 
| 44 44 | 
             
                  #
         | 
| 45 | 
            +
                  #   # bad
         | 
| 46 | 
            +
                  #   a.nil? ? true : a
         | 
| 47 | 
            +
                  #
         | 
| 48 | 
            +
                  #   # good
         | 
| 49 | 
            +
                  #   a.nil? || a
         | 
| 50 | 
            +
                  #
         | 
| 51 | 
            +
                  #   # bad
         | 
| 52 | 
            +
                  #   if a.nil?
         | 
| 53 | 
            +
                  #     true
         | 
| 54 | 
            +
                  #   else
         | 
| 55 | 
            +
                  #     a
         | 
| 56 | 
            +
                  #   end
         | 
| 57 | 
            +
                  #
         | 
| 58 | 
            +
                  #   # good
         | 
| 59 | 
            +
                  #   a.nil? || a
         | 
| 60 | 
            +
                  #
         | 
| 61 | 
            +
                  # @example AllowedMethods: ['nonzero?'] (default)
         | 
| 62 | 
            +
                  #   # good
         | 
| 63 | 
            +
                  #   num.nonzero? ? true : false
         | 
| 64 | 
            +
                  #
         | 
| 45 65 | 
             
                  class RedundantCondition < Base
         | 
| 66 | 
            +
                    include AllowedMethods
         | 
| 46 67 | 
             
                    include CommentsHelp
         | 
| 47 68 | 
             
                    include RangeHelp
         | 
| 48 69 | 
             
                    extend AutoCorrector
         | 
| @@ -128,6 +149,16 @@ module RuboCop | |
| 128 149 | 
             
                      #   end
         | 
| 129 150 | 
             
                      return true if condition == if_branch
         | 
| 130 151 |  | 
| 152 | 
            +
                      # e.g.
         | 
| 153 | 
            +
                      #   a.nil? ? true : a
         | 
| 154 | 
            +
                      # or
         | 
| 155 | 
            +
                      #   if a.nil?
         | 
| 156 | 
            +
                      #     true
         | 
| 157 | 
            +
                      #   else
         | 
| 158 | 
            +
                      #     a
         | 
| 159 | 
            +
                      #   end
         | 
| 160 | 
            +
                      return true if if_branch_is_true_type_and_else_is_not?(node)
         | 
| 161 | 
            +
             | 
| 131 162 | 
             
                      # e.g.
         | 
| 132 163 | 
             
                      #   if foo
         | 
| 133 164 | 
             
                      #     @value = foo
         | 
| @@ -146,6 +177,18 @@ module RuboCop | |
| 146 177 | 
             
                        !use_hash_key_access?(if_branch)
         | 
| 147 178 | 
             
                    end
         | 
| 148 179 |  | 
| 180 | 
            +
                    # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
         | 
| 181 | 
            +
                    def if_branch_is_true_type_and_else_is_not?(node)
         | 
| 182 | 
            +
                      return false unless node.ternary? || node.if?
         | 
| 183 | 
            +
             | 
| 184 | 
            +
                      cond = node.condition
         | 
| 185 | 
            +
                      return false unless cond.call_type?
         | 
| 186 | 
            +
                      return false if !cond.predicate_method? || allowed_method?(cond.method_name)
         | 
| 187 | 
            +
             | 
| 188 | 
            +
                      node.if_branch&.true_type? && node.else_branch && !node.else_branch.true_type?
         | 
| 189 | 
            +
                    end
         | 
| 190 | 
            +
                    # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
         | 
| 191 | 
            +
             | 
| 149 192 | 
             
                    def branches_have_assignment?(node)
         | 
| 150 193 | 
             
                      _condition, if_branch, else_branch = *node # rubocop:disable InternalAffairs/NodeDestructuring
         | 
| 151 194 |  | 
| @@ -194,6 +237,8 @@ module RuboCop | |
| 194 237 | 
             
                        argument_source = if_branch.first_argument.source
         | 
| 195 238 |  | 
| 196 239 | 
             
                        "#{if_branch.receiver.source} #{if_branch.method_name} (#{argument_source}"
         | 
| 240 | 
            +
                      elsif if_branch.true_type?
         | 
| 241 | 
            +
                        if_branch.parent.condition.source
         | 
| 197 242 | 
             
                      else
         | 
| 198 243 | 
             
                        if_branch.source
         | 
| 199 244 | 
             
                      end
         | 
| @@ -20,20 +20,30 @@ module RuboCop | |
| 20 20 |  | 
| 21 21 | 
             
                    MSG = 'Remove the redundant current directory path.'
         | 
| 22 22 | 
             
                    RESTRICT_ON_SEND = %i[require_relative].freeze
         | 
| 23 | 
            -
                     | 
| 23 | 
            +
                    CURRENT_DIRECTORY_PREFIX = %r{./+}.freeze
         | 
| 24 | 
            +
                    REDUNDANT_CURRENT_DIRECTORY_PREFIX = /\A#{CURRENT_DIRECTORY_PREFIX}/.freeze
         | 
| 24 25 |  | 
| 25 26 | 
             
                    def on_send(node)
         | 
| 26 27 | 
             
                      return unless (first_argument = node.first_argument)
         | 
| 27 | 
            -
                      return unless first_argument. | 
| 28 | 
            -
                      return unless ( | 
| 28 | 
            +
                      return unless (index = first_argument.source.index(CURRENT_DIRECTORY_PREFIX))
         | 
| 29 | 
            +
                      return unless (redundant_length = redundant_path_length(first_argument.str_content))
         | 
| 29 30 |  | 
| 30 31 | 
             
                      begin_pos = first_argument.source_range.begin.begin_pos + index
         | 
| 31 | 
            -
                       | 
| 32 | 
            +
                      end_pos = begin_pos + redundant_length
         | 
| 33 | 
            +
                      range = range_between(begin_pos, end_pos)
         | 
| 32 34 |  | 
| 33 35 | 
             
                      add_offense(range) do |corrector|
         | 
| 34 36 | 
             
                        corrector.remove(range)
         | 
| 35 37 | 
             
                      end
         | 
| 36 38 | 
             
                    end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                    private
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                    def redundant_path_length(path)
         | 
| 43 | 
            +
                      return unless (match = path&.match(REDUNDANT_CURRENT_DIRECTORY_PREFIX))
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                      match[0].length
         | 
| 46 | 
            +
                    end
         | 
| 37 47 | 
             
                  end
         | 
| 38 48 | 
             
                end
         | 
| 39 49 | 
             
              end
         |