rubocop 1.66.0 → 1.67.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 +1 -1
- data/config/default.yml +15 -6
- data/config/internal_affairs.yml +11 -0
- data/lib/rubocop/cli/command/auto_generate_config.rb +6 -7
- data/lib/rubocop/cli/command/lsp.rb +2 -2
- data/lib/rubocop/comment_config.rb +4 -8
- data/lib/rubocop/config.rb +4 -16
- data/lib/rubocop/config_loader.rb +14 -8
- data/lib/rubocop/config_loader_resolver.rb +3 -3
- data/lib/rubocop/config_validator.rb +7 -10
- data/lib/rubocop/cop/base.rb +6 -2
- data/lib/rubocop/cop/bundler/gem_version.rb +1 -0
- data/lib/rubocop/cop/cop.rb +8 -0
- data/lib/rubocop/cop/correctors/parentheses_corrector.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/cop_description.rb +0 -4
- data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/redundant_message_argument.rb +6 -21
- data/lib/rubocop/cop/internal_affairs/redundant_source_range.rb +8 -1
- data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +0 -5
- data/lib/rubocop/cop/internal_affairs.rb +16 -0
- data/lib/rubocop/cop/layout/access_modifier_indentation.rb +5 -1
- data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -1
- data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +8 -0
- data/lib/rubocop/cop/layout/indentation_width.rb +4 -5
- data/lib/rubocop/cop/layout/leading_comment_space.rb +28 -1
- data/lib/rubocop/cop/lint/ambiguous_range.rb +4 -1
- data/lib/rubocop/cop/lint/big_decimal_new.rb +4 -7
- data/lib/rubocop/cop/lint/boolean_symbol.rb +1 -1
- data/lib/rubocop/cop/lint/duplicate_set_element.rb +74 -0
- data/lib/rubocop/cop/lint/ensure_return.rb +0 -3
- data/lib/rubocop/cop/lint/erb_new_arguments.rb +1 -1
- data/lib/rubocop/cop/lint/float_comparison.rb +1 -1
- data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +10 -4
- data/lib/rubocop/cop/lint/it_without_arguments_in_block.rb +5 -14
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +25 -2
- data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +5 -6
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +1 -1
- data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +105 -41
- data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -1
- data/lib/rubocop/cop/lint/uri_regexp.rb +25 -7
- data/lib/rubocop/cop/mixin/percent_array.rb +1 -1
- data/lib/rubocop/cop/mixin/statement_modifier.rb +3 -2
- data/lib/rubocop/cop/naming/inclusive_language.rb +12 -3
- data/lib/rubocop/cop/naming/predicate_name.rb +1 -1
- data/lib/rubocop/cop/offense.rb +2 -2
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +12 -2
- data/lib/rubocop/cop/style/accessor_grouping.rb +10 -2
- data/lib/rubocop/cop/style/arguments_forwarding.rb +46 -6
- data/lib/rubocop/cop/style/block_delimiters.rb +14 -1
- data/lib/rubocop/cop/style/collection_compact.rb +10 -10
- data/lib/rubocop/cop/style/combinable_loops.rb +7 -0
- data/lib/rubocop/cop/style/commented_keyword.rb +7 -1
- data/lib/rubocop/cop/style/conditional_assignment.rb +1 -1
- data/lib/rubocop/cop/style/data_inheritance.rb +1 -1
- data/lib/rubocop/cop/style/empty_else.rb +1 -0
- data/lib/rubocop/cop/style/empty_literal.rb +1 -1
- data/lib/rubocop/cop/style/eval_with_location.rb +1 -1
- data/lib/rubocop/cop/style/guard_clause.rb +1 -1
- data/lib/rubocop/cop/style/hash_each_methods.rb +6 -0
- data/lib/rubocop/cop/style/hash_syntax.rb +2 -2
- data/lib/rubocop/cop/style/if_inside_else.rb +1 -1
- data/lib/rubocop/cop/style/if_with_semicolon.rb +16 -5
- data/lib/rubocop/cop/style/lambda.rb +1 -1
- data/lib/rubocop/cop/style/magic_comment_format.rb +3 -8
- data/lib/rubocop/cop/style/map_into_array.rb +54 -10
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +12 -7
- data/lib/rubocop/cop/style/multiline_memoization.rb +1 -1
- data/lib/rubocop/cop/style/nested_modifier.rb +1 -1
- data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +1 -1
- data/lib/rubocop/cop/style/one_line_conditional.rb +4 -0
- data/lib/rubocop/cop/style/operator_method_call.rb +25 -6
- data/lib/rubocop/cop/style/redundant_begin.rb +4 -0
- data/lib/rubocop/cop/style/redundant_condition.rb +1 -1
- data/lib/rubocop/cop/style/redundant_interpolation_unfreeze.rb +1 -1
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +3 -3
- data/lib/rubocop/cop/style/redundant_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/require_order.rb +1 -1
- data/lib/rubocop/cop/style/rescue_modifier.rb +13 -1
- data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +54 -12
- data/lib/rubocop/cop/style/safe_navigation.rb +92 -50
- data/lib/rubocop/cop/style/select_by_regexp.rb +9 -6
- data/lib/rubocop/cop/style/semicolon.rb +1 -1
- data/lib/rubocop/cop/style/struct_inheritance.rb +1 -1
- data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
- data/lib/rubocop/cop/team.rb +8 -1
- data/lib/rubocop/cop/util.rb +1 -1
- data/lib/rubocop/cops_documentation_generator.rb +73 -34
- data/lib/rubocop/file_finder.rb +9 -4
- data/lib/rubocop/lsp/runtime.rb +2 -0
- data/lib/rubocop/lsp/server.rb +0 -1
- data/lib/rubocop/rspec/expect_offense.rb +1 -0
- data/lib/rubocop/runner.rb +3 -0
- data/lib/rubocop/server/cache.rb +6 -1
- data/lib/rubocop/server/core.rb +1 -0
- data/lib/rubocop/target_ruby.rb +12 -12
- data/lib/rubocop/version.rb +3 -1
- data/lib/rubocop/yaml_duplication_checker.rb +20 -26
- data/lib/rubocop.rb +2 -0
- metadata +12 -10
| @@ -81,7 +81,7 @@ module RuboCop | |
| 81 81 | 
             
                  #   foo.baz = bar if foo
         | 
| 82 82 | 
             
                  #   foo.baz + bar if foo
         | 
| 83 83 | 
             
                  #   foo.bar > 2 if foo
         | 
| 84 | 
            -
                  class SafeNavigation < Base
         | 
| 84 | 
            +
                  class SafeNavigation < Base # rubocop:disable Metrics/ClassLength
         | 
| 85 85 | 
             
                    include NilMethods
         | 
| 86 86 | 
             
                    include RangeHelp
         | 
| 87 87 | 
             
                    extend AutoCorrector
         | 
| @@ -124,47 +124,112 @@ module RuboCop | |
| 124 124 | 
             
                    # @!method not_nil_check?(node)
         | 
| 125 125 | 
             
                    def_node_matcher :not_nil_check?, '(send (send $_ :nil?) :!)'
         | 
| 126 126 |  | 
| 127 | 
            +
                    # @!method and_inside_begin?(node)
         | 
| 128 | 
            +
                    def_node_matcher :and_inside_begin?, '`(begin and ...)'
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                    # @!method strip_begin(node)
         | 
| 131 | 
            +
                    def_node_matcher :strip_begin, '{ (begin $!begin) $!(begin) }'
         | 
| 132 | 
            +
             | 
| 127 133 | 
             
                    def on_if(node)
         | 
| 128 134 | 
             
                      return if allowed_if_condition?(node)
         | 
| 129 135 |  | 
| 130 | 
            -
                       | 
| 136 | 
            +
                      checked_variable, receiver, method_chain, _method = extract_parts_from_if(node)
         | 
| 137 | 
            +
                      return unless offending_node?(node, checked_variable, method_chain, receiver)
         | 
| 138 | 
            +
             | 
| 139 | 
            +
                      body = extract_if_body(node)
         | 
| 140 | 
            +
                      method_call = receiver.parent
         | 
| 141 | 
            +
             | 
| 142 | 
            +
                      removal_ranges = [begin_range(node, body), end_range(node, body)]
         | 
| 143 | 
            +
             | 
| 144 | 
            +
                      report_offense(node, method_chain, method_call, *removal_ranges) do |corrector|
         | 
| 145 | 
            +
                        corrector.insert_before(method_call.loc.dot, '&') unless method_call.safe_navigation?
         | 
| 146 | 
            +
                      end
         | 
| 147 | 
            +
                    end
         | 
| 148 | 
            +
             | 
| 149 | 
            +
                    def on_and(node) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
         | 
| 150 | 
            +
                      collect_and_clauses(node).each do |(lhs, lhs_operator_range), (rhs, _rhs_operator_range)|
         | 
| 151 | 
            +
                        lhs_not_nil_check = not_nil_check?(lhs)
         | 
| 152 | 
            +
                        lhs_receiver = lhs_not_nil_check || lhs
         | 
| 153 | 
            +
                        rhs_receiver = find_matching_receiver_invocation(strip_begin(rhs), lhs_receiver)
         | 
| 154 | 
            +
             | 
| 155 | 
            +
                        next if !cop_config['ConvertCodeThatCanStartToReturnNil'] && lhs_not_nil_check
         | 
| 156 | 
            +
                        next unless offending_node?(node, lhs_receiver, rhs, rhs_receiver)
         | 
| 157 | 
            +
             | 
| 158 | 
            +
                        # Since we are evaluating every clause in potentially a complex chain of `and` nodes,
         | 
| 159 | 
            +
                        # we need to ensure that there isn't an object check happening
         | 
| 160 | 
            +
                        lhs_method_chain = find_method_chain(lhs_receiver)
         | 
| 161 | 
            +
                        next unless lhs_method_chain == lhs_receiver || lhs_not_nil_check
         | 
| 162 | 
            +
             | 
| 163 | 
            +
                        report_offense(
         | 
| 164 | 
            +
                          node,
         | 
| 165 | 
            +
                          rhs, rhs_receiver,
         | 
| 166 | 
            +
                          range_with_surrounding_space(range: lhs.source_range, side: :right),
         | 
| 167 | 
            +
                          range_with_surrounding_space(range: lhs_operator_range, side: :right),
         | 
| 168 | 
            +
                          offense_range: range_between(lhs.source_range.begin_pos, rhs.source_range.end_pos)
         | 
| 169 | 
            +
                        )
         | 
| 170 | 
            +
                      end
         | 
| 131 171 | 
             
                    end
         | 
| 132 172 |  | 
| 133 | 
            -
                    def  | 
| 134 | 
            -
                       | 
| 173 | 
            +
                    def report_offense(node, rhs, rhs_receiver, *removal_ranges, offense_range: node)
         | 
| 174 | 
            +
                      add_offense(offense_range) do |corrector|
         | 
| 175 | 
            +
                        removal_ranges.each { |range| corrector.remove(range) }
         | 
| 176 | 
            +
                        yield corrector if block_given?
         | 
| 177 | 
            +
             | 
| 178 | 
            +
                        handle_comments(corrector, node, rhs)
         | 
| 179 | 
            +
             | 
| 180 | 
            +
                        add_safe_nav_to_all_methods_in_chain(corrector, rhs_receiver, rhs)
         | 
| 181 | 
            +
                      end
         | 
| 135 182 | 
             
                    end
         | 
| 136 183 |  | 
| 137 184 | 
             
                    private
         | 
| 138 185 |  | 
| 139 | 
            -
                    def  | 
| 140 | 
            -
                       | 
| 141 | 
            -
                      return if receiver != checked_variable || receiver.nil?
         | 
| 142 | 
            -
                      return if use_var_only_in_unless_modifier?(node, checked_variable)
         | 
| 143 | 
            -
                      return if chain_length(method_chain, method) > max_chain_length
         | 
| 144 | 
            -
                      return if unsafe_method_used?(method_chain, method)
         | 
| 145 | 
            -
                      return if method_chain.method?(:empty?)
         | 
| 186 | 
            +
                    def find_method_chain(node)
         | 
| 187 | 
            +
                      return node unless node&.parent&.call_type?
         | 
| 146 188 |  | 
| 147 | 
            -
                       | 
| 189 | 
            +
                      find_method_chain(node.parent)
         | 
| 148 190 | 
             
                    end
         | 
| 149 191 |  | 
| 150 | 
            -
                    def  | 
| 151 | 
            -
                       | 
| 192 | 
            +
                    def collect_and_clauses(node)
         | 
| 193 | 
            +
                      # Collect the lhs, operator and rhs of all `and` nodes
         | 
| 194 | 
            +
                      # `and` nodes can be nested and can contain `begin` nodes
         | 
| 195 | 
            +
                      # This gives us a source-ordered list of clauses that is then used to look
         | 
| 196 | 
            +
                      # for matching receivers as well as operator locations for offense and corrections
         | 
| 197 | 
            +
                      node.each_descendant(:and)
         | 
| 198 | 
            +
                          .inject(and_parts(node)) { |nodes, and_node| concat_nodes(nodes, and_node) }
         | 
| 199 | 
            +
                          .sort_by { |a| a.is_a?(RuboCop::AST::Node) ? a.source_range.begin_pos : a.begin_pos }
         | 
| 200 | 
            +
                          .each_slice(2)
         | 
| 201 | 
            +
                          .each_cons(2)
         | 
| 152 202 | 
             
                    end
         | 
| 153 203 |  | 
| 154 | 
            -
                    def  | 
| 155 | 
            -
                       | 
| 156 | 
            -
                      method_call = method_call(node)
         | 
| 204 | 
            +
                    def concat_nodes(nodes, and_node)
         | 
| 205 | 
            +
                      return nodes if and_node.each_ancestor(:block).any?
         | 
| 157 206 |  | 
| 158 | 
            -
                       | 
| 159 | 
            -
             | 
| 160 | 
            -
                      corrector.insert_before(method_call.loc.dot, '&') unless method_call.safe_navigation?
         | 
| 161 | 
            -
                      handle_comments(corrector, node, method_call)
         | 
| 207 | 
            +
                      nodes.concat(and_parts(and_node))
         | 
| 208 | 
            +
                    end
         | 
| 162 209 |  | 
| 163 | 
            -
             | 
| 210 | 
            +
                    def and_parts(node)
         | 
| 211 | 
            +
                      parts = [node.loc.operator]
         | 
| 212 | 
            +
                      parts << node.rhs unless and_inside_begin?(node.rhs)
         | 
| 213 | 
            +
                      parts << node.lhs unless node.lhs.and_type? || and_inside_begin?(node.lhs)
         | 
| 214 | 
            +
                      parts
         | 
| 164 215 | 
             
                    end
         | 
| 165 216 |  | 
| 166 | 
            -
                    def  | 
| 167 | 
            -
                      if  | 
| 217 | 
            +
                    def offending_node?(node, lhs_receiver, rhs, rhs_receiver) # rubocop:disable Metrics/CyclomaticComplexity
         | 
| 218 | 
            +
                      return false if lhs_receiver != rhs_receiver || rhs_receiver.nil?
         | 
| 219 | 
            +
                      return false if use_var_only_in_unless_modifier?(node, lhs_receiver)
         | 
| 220 | 
            +
                      return false if chain_length(rhs, rhs_receiver) > max_chain_length
         | 
| 221 | 
            +
                      return false if unsafe_method_used?(rhs, rhs_receiver.parent)
         | 
| 222 | 
            +
                      return false if rhs.send_type? && rhs.method?(:empty?)
         | 
| 223 | 
            +
             | 
| 224 | 
            +
                      true
         | 
| 225 | 
            +
                    end
         | 
| 226 | 
            +
             | 
| 227 | 
            +
                    def use_var_only_in_unless_modifier?(node, variable)
         | 
| 228 | 
            +
                      node.if_type? && node.unless? && !method_called?(variable)
         | 
| 229 | 
            +
                    end
         | 
| 230 | 
            +
             | 
| 231 | 
            +
                    def extract_if_body(node)
         | 
| 232 | 
            +
                      if node.ternary?
         | 
| 168 233 | 
             
                        node.branches.find { |branch| !branch.nil_type? }
         | 
| 169 234 | 
             
                      else
         | 
| 170 235 | 
             
                        node.node_parts[1]
         | 
| @@ -201,20 +266,6 @@ module RuboCop | |
| 201 266 | 
             
                      node.else? || node.elsif?
         | 
| 202 267 | 
             
                    end
         | 
| 203 268 |  | 
| 204 | 
            -
                    def method_call(node)
         | 
| 205 | 
            -
                      _checked_variable, matching_receiver, = extract_parts(node)
         | 
| 206 | 
            -
                      matching_receiver.parent
         | 
| 207 | 
            -
                    end
         | 
| 208 | 
            -
             | 
| 209 | 
            -
                    def extract_parts(node)
         | 
| 210 | 
            -
                      case node.type
         | 
| 211 | 
            -
                      when :if
         | 
| 212 | 
            -
                        extract_parts_from_if(node)
         | 
| 213 | 
            -
                      when :and
         | 
| 214 | 
            -
                        extract_parts_from_and(node)
         | 
| 215 | 
            -
                      end
         | 
| 216 | 
            -
                    end
         | 
| 217 | 
            -
             | 
| 218 269 | 
             
                    def extract_parts_from_if(node)
         | 
| 219 270 | 
             
                      variable, receiver =
         | 
| 220 271 | 
             
                        if node.ternary?
         | 
| @@ -230,16 +281,6 @@ module RuboCop | |
| 230 281 | 
             
                      [checked_variable, matching_receiver, receiver, method]
         | 
| 231 282 | 
             
                    end
         | 
| 232 283 |  | 
| 233 | 
            -
                    def extract_parts_from_and(node)
         | 
| 234 | 
            -
                      checked_variable, rhs = *node
         | 
| 235 | 
            -
                      if cop_config['ConvertCodeThatCanStartToReturnNil']
         | 
| 236 | 
            -
                        checked_variable = not_nil_check?(checked_variable) || checked_variable
         | 
| 237 | 
            -
                      end
         | 
| 238 | 
            -
             | 
| 239 | 
            -
                      checked_variable, matching_receiver, method = extract_common_parts(rhs, checked_variable)
         | 
| 240 | 
            -
                      [checked_variable, matching_receiver, rhs, method]
         | 
| 241 | 
            -
                    end
         | 
| 242 | 
            -
             | 
| 243 284 | 
             
                    def extract_common_parts(method_chain, checked_variable)
         | 
| 244 285 | 
             
                      matching_receiver = find_matching_receiver_invocation(method_chain, checked_variable)
         | 
| 245 286 |  | 
| @@ -249,7 +290,7 @@ module RuboCop | |
| 249 290 | 
             
                    end
         | 
| 250 291 |  | 
| 251 292 | 
             
                    def find_matching_receiver_invocation(method_chain, checked_variable)
         | 
| 252 | 
            -
                      return nil unless method_chain
         | 
| 293 | 
            +
                      return nil unless method_chain.respond_to?(:receiver)
         | 
| 253 294 |  | 
| 254 295 | 
             
                      receiver = method_chain.receiver
         | 
| 255 296 |  | 
| @@ -259,7 +300,7 @@ module RuboCop | |
| 259 300 | 
             
                    end
         | 
| 260 301 |  | 
| 261 302 | 
             
                    def chain_length(method_chain, method)
         | 
| 262 | 
            -
                      method.each_ancestor(:send).inject( | 
| 303 | 
            +
                      method.each_ancestor(:send).inject(0) do |total, ancestor|
         | 
| 263 304 | 
             
                        break total + 1 if ancestor == method_chain
         | 
| 264 305 |  | 
| 265 306 | 
             
                        total + 1
         | 
| @@ -310,6 +351,7 @@ module RuboCop | |
| 310 351 | 
             
                      start_method.each_ancestor do |ancestor|
         | 
| 311 352 | 
             
                        break unless %i[send block].include?(ancestor.type)
         | 
| 312 353 | 
             
                        next unless ancestor.send_type?
         | 
| 354 | 
            +
                        next if ancestor.safe_navigation?
         | 
| 313 355 |  | 
| 314 356 | 
             
                        corrector.insert_before(ancestor.loc.dot, '&')
         | 
| 315 357 |  | 
| @@ -27,7 +27,7 @@ module RuboCop | |
| 27 27 | 
             
                  #   so the correction may not be actually equivalent.
         | 
| 28 28 | 
             
                  #
         | 
| 29 29 | 
             
                  # @example
         | 
| 30 | 
            -
                  #   # bad (select or find_all)
         | 
| 30 | 
            +
                  #   # bad (select, filter, or find_all)
         | 
| 31 31 | 
             
                  #   array.select { |x| x.match? /regexp/ }
         | 
| 32 32 | 
             
                  #   array.select { |x| /regexp/.match?(x) }
         | 
| 33 33 | 
             
                  #   array.select { |x| x =~ /regexp/ }
         | 
| @@ -47,9 +47,11 @@ module RuboCop | |
| 47 47 | 
             
                    include RangeHelp
         | 
| 48 48 |  | 
| 49 49 | 
             
                    MSG = 'Prefer `%<replacement>s` to `%<original_method>s` with a regexp match.'
         | 
| 50 | 
            -
                    RESTRICT_ON_SEND = %i[select find_all reject].freeze
         | 
| 51 | 
            -
                    REPLACEMENTS = { select: 'grep', find_all: 'grep', reject: 'grep_v' }.freeze
         | 
| 52 | 
            -
                    OPPOSITE_REPLACEMENTS = { | 
| 50 | 
            +
                    RESTRICT_ON_SEND = %i[select filter find_all reject].freeze
         | 
| 51 | 
            +
                    REPLACEMENTS = { select: 'grep', filter: 'grep', find_all: 'grep', reject: 'grep_v' }.freeze
         | 
| 52 | 
            +
                    OPPOSITE_REPLACEMENTS = {
         | 
| 53 | 
            +
                      select: 'grep_v', filter: 'grep_v', find_all: 'grep_v', reject: 'grep'
         | 
| 54 | 
            +
                    }.freeze
         | 
| 53 55 | 
             
                    REGEXP_METHODS = %i[match? =~ !~].to_set.freeze
         | 
| 54 56 |  | 
| 55 57 | 
             
                    # @!method regexp_match?(node)
         | 
| @@ -84,8 +86,9 @@ module RuboCop | |
| 84 86 | 
             
                      }
         | 
| 85 87 | 
             
                    PATTERN
         | 
| 86 88 |  | 
| 87 | 
            -
                    # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
         | 
| 89 | 
            +
                    # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
         | 
| 88 90 | 
             
                    def on_send(node)
         | 
| 91 | 
            +
                      return if target_ruby_version < 2.6 && node.method?(:filter)
         | 
| 89 92 | 
             
                      return unless (block_node = node.block_node)
         | 
| 90 93 | 
             
                      return if block_node.body&.begin_type?
         | 
| 91 94 | 
             
                      return if receiver_allowed?(block_node.receiver)
         | 
| @@ -99,7 +102,7 @@ module RuboCop | |
| 99 102 |  | 
| 100 103 | 
             
                      register_offense(node, block_node, regexp, replacement)
         | 
| 101 104 | 
             
                    end
         | 
| 102 | 
            -
                    # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
         | 
| 105 | 
            +
                    # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
         | 
| 103 106 | 
             
                    alias on_csend on_send
         | 
| 104 107 |  | 
| 105 108 | 
             
                    private
         | 
| @@ -141,7 +141,7 @@ module RuboCop | |
| 141 141 |  | 
| 142 142 | 
             
                    def expressions_per_line(exprs)
         | 
| 143 143 | 
             
                      # create a map matching lines to the number of expressions on them
         | 
| 144 | 
            -
                      exprs_lines = exprs.map(&: | 
| 144 | 
            +
                      exprs_lines = exprs.map(&:last_line)
         | 
| 145 145 | 
             
                      exprs_lines.group_by(&:itself)
         | 
| 146 146 | 
             
                    end
         | 
| 147 147 |  | 
| @@ -33,7 +33,7 @@ module RuboCop | |
| 33 33 | 
             
                    def on_class(node)
         | 
| 34 34 | 
             
                      return unless struct_constructor?(node.parent_class)
         | 
| 35 35 |  | 
| 36 | 
            -
                      add_offense(node.parent_class | 
| 36 | 
            +
                      add_offense(node.parent_class) do |corrector|
         | 
| 37 37 | 
             
                        corrector.remove(range_with_surrounding_space(node.loc.keyword, newlines: false))
         | 
| 38 38 | 
             
                        corrector.replace(node.loc.operator, '=')
         | 
| 39 39 |  | 
    
        data/lib/rubocop/cop/team.rb
    CHANGED
    
    | @@ -9,11 +9,17 @@ module RuboCop | |
| 9 9 | 
             
                # For performance reasons, Team will first dispatch cops & forces in two groups,
         | 
| 10 10 | 
             
                # first the ones needed for autocorrection (if any), then the rest
         | 
| 11 11 | 
             
                # (unless autocorrections happened).
         | 
| 12 | 
            +
                # rubocop:disable Metrics/ClassLength
         | 
| 12 13 | 
             
                class Team
         | 
| 13 14 | 
             
                  # @return [Team]
         | 
| 14 15 | 
             
                  def self.new(cop_or_classes, config, options = {})
         | 
| 15 16 | 
             
                    # Support v0 api:
         | 
| 16 | 
            -
                     | 
| 17 | 
            +
                    if cop_or_classes.first.is_a?(Class)
         | 
| 18 | 
            +
                      warn Rainbow(<<~WARNING).yellow, uplevel: 1
         | 
| 19 | 
            +
                        `Team.new` with cop classes is deprecated. Use `Team.mobilize` instead.
         | 
| 20 | 
            +
                      WARNING
         | 
| 21 | 
            +
                      return mobilize(cop_or_classes, config, options)
         | 
| 22 | 
            +
                    end
         | 
| 17 23 |  | 
| 18 24 | 
             
                    super
         | 
| 19 25 | 
             
                  end
         | 
| @@ -279,5 +285,6 @@ module RuboCop | |
| 279 285 | 
             
                    end
         | 
| 280 286 | 
             
                  end
         | 
| 281 287 | 
             
                end
         | 
| 288 | 
            +
                # rubocop:enable Metrics/ClassLength
         | 
| 282 289 | 
             
              end
         | 
| 283 290 | 
             
            end
         | 
    
        data/lib/rubocop/cop/util.rb
    CHANGED
    
    
| @@ -6,14 +6,38 @@ require 'fileutils' | |
| 6 6 | 
             
            # @api private
         | 
| 7 7 | 
             
            class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
         | 
| 8 8 | 
             
              include ::RuboCop::Cop::Documentation
         | 
| 9 | 
            +
              CopData = Struct.new(
         | 
| 10 | 
            +
                :cop, :description, :example_objects, :safety_objects, :see_objects, :config, keyword_init: true
         | 
| 11 | 
            +
              )
         | 
| 12 | 
            +
             | 
| 13 | 
            +
              STRUCTURE = {
         | 
| 14 | 
            +
                name:                  ->(data) { cop_header(data.cop) },
         | 
| 15 | 
            +
                required_ruby_version: ->(data) { required_ruby_version(data.cop) },
         | 
| 16 | 
            +
                properties:            ->(data) { properties(data.cop) },
         | 
| 17 | 
            +
                description:           ->(data) { "#{data.description}\n" },
         | 
| 18 | 
            +
                safety:                ->(data) { safety_object(data.safety_objects, data.cop) },
         | 
| 19 | 
            +
                examples:              ->(data) { examples(data.example_objects, data.cop) },
         | 
| 20 | 
            +
                configuration:         ->(data) { configurations(data.cop.department, data.config, data.cop) },
         | 
| 21 | 
            +
                references:            ->(data) { references(data.cop, data.see_objects) }
         | 
| 22 | 
            +
              }.freeze
         | 
| 23 | 
            +
             | 
| 9 24 | 
             
              # This class will only generate documentation for cops that belong to one of
         | 
| 10 25 | 
             
              # the departments given in the `departments` array. E.g. if we only wanted
         | 
| 11 26 | 
             
              # documentation for Lint cops:
         | 
| 12 27 | 
             
              #
         | 
| 13 28 | 
             
              #   CopsDocumentationGenerator.new(departments: ['Lint']).call
         | 
| 14 29 | 
             
              #
         | 
| 15 | 
            -
               | 
| 30 | 
            +
              # You can append additional information:
         | 
| 31 | 
            +
              #
         | 
| 32 | 
            +
              #   callback = ->(data) { required_rails_version(data.cop) }
         | 
| 33 | 
            +
              #   CopsDocumentationGenerator.new(extra_info: { ruby_version: callback }).call
         | 
| 34 | 
            +
              #
         | 
| 35 | 
            +
              # This will insert the string returned from the lambda _after_ the section from RuboCop itself.
         | 
| 36 | 
            +
              # See `CopsDocumentationGenerator::STRUCTURE` for available sections.
         | 
| 37 | 
            +
              #
         | 
| 38 | 
            +
              def initialize(departments: [], extra_info: {})
         | 
| 16 39 | 
             
                @departments = departments.map(&:to_sym).sort!
         | 
| 40 | 
            +
                @extra_info = extra_info
         | 
| 17 41 | 
             
                @cops = RuboCop::Cop::Registry.global
         | 
| 18 42 | 
             
                @config = RuboCop::ConfigLoader.default_configuration
         | 
| 19 43 | 
             
                @docs_path = "#{Dir.pwd}/docs/modules/ROOT/pages/"
         | 
| @@ -37,24 +61,21 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength | |
| 37 61 | 
             
                cops.with_department(department).sort!
         | 
| 38 62 | 
             
              end
         | 
| 39 63 |  | 
| 40 | 
            -
              def cops_body( | 
| 41 | 
            -
                check_examples_to_have_the_default_enforced_style!( | 
| 42 | 
            -
             | 
| 43 | 
            -
                content =  | 
| 44 | 
            -
                 | 
| 45 | 
            -
             | 
| 46 | 
            -
             | 
| 47 | 
            -
                 | 
| 48 | 
            -
                content << examples(examples_objects) if examples_objects.any?
         | 
| 49 | 
            -
                content << configurations(cop.department, pars)
         | 
| 50 | 
            -
                content << references(cop, see_objects)
         | 
| 64 | 
            +
              def cops_body(data)
         | 
| 65 | 
            +
                check_examples_to_have_the_default_enforced_style!(data.example_objects, data.cop)
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                content = +''
         | 
| 68 | 
            +
                STRUCTURE.each do |section, block|
         | 
| 69 | 
            +
                  content << instance_exec(data, &block)
         | 
| 70 | 
            +
                  content << @extra_info[section].call(data) if @extra_info[section]
         | 
| 71 | 
            +
                end
         | 
| 51 72 | 
             
                content
         | 
| 52 73 | 
             
              end
         | 
| 53 74 |  | 
| 54 | 
            -
              def check_examples_to_have_the_default_enforced_style!( | 
| 55 | 
            -
                return if  | 
| 75 | 
            +
              def check_examples_to_have_the_default_enforced_style!(example_objects, cop)
         | 
| 76 | 
            +
                return if example_objects.none?
         | 
| 56 77 |  | 
| 57 | 
            -
                examples_describing_enforced_style =  | 
| 78 | 
            +
                examples_describing_enforced_style = example_objects.map(&:name).grep(/EnforcedStyle:/)
         | 
| 58 79 | 
             
                return if examples_describing_enforced_style.none?
         | 
| 59 80 |  | 
| 60 81 | 
             
                if examples_describing_enforced_style.index { |name| name.match?('default') }.nonzero?
         | 
| @@ -66,16 +87,20 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength | |
| 66 87 | 
             
                raise "Specify the default EnforcedStyle for #{cop.cop_name}"
         | 
| 67 88 | 
             
              end
         | 
| 68 89 |  | 
| 69 | 
            -
              def examples( | 
| 70 | 
            -
                 | 
| 90 | 
            +
              def examples(example_objects, cop)
         | 
| 91 | 
            +
                return '' if example_objects.none?
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                example_objects.each_with_object(cop_subsection('Examples', cop).dup) do |example, content|
         | 
| 71 94 | 
             
                  content << "\n" unless content.end_with?("\n\n")
         | 
| 72 | 
            -
                  content <<  | 
| 95 | 
            +
                  content << example_header(example.name, cop) unless example.name == ''
         | 
| 73 96 | 
             
                  content << code_example(example)
         | 
| 74 97 | 
             
                end
         | 
| 75 98 | 
             
              end
         | 
| 76 99 |  | 
| 77 | 
            -
              def safety_object( | 
| 78 | 
            -
                 | 
| 100 | 
            +
              def safety_object(safety_objects, cop)
         | 
| 101 | 
            +
                return '' if safety_objects.all? { |s| s.text.blank? }
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                safety_objects.each_with_object(cop_subsection('Safety', cop).dup) do |safety_object, content|
         | 
| 79 104 | 
             
                  next if safety_object.text.blank?
         | 
| 80 105 |  | 
| 81 106 | 
             
                  content << "\n" unless content.end_with?("\n\n")
         | 
| @@ -115,22 +140,25 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength | |
| 115 140 | 
             
              end
         | 
| 116 141 | 
             
              # rubocop:enable Metrics/MethodLength
         | 
| 117 142 |  | 
| 118 | 
            -
              def  | 
| 143 | 
            +
              def cop_header(cop)
         | 
| 119 144 | 
             
                content = +"\n"
         | 
| 120 | 
            -
                content << " | 
| 145 | 
            +
                content << "[##{to_anchor(cop.cop_name)}]\n"
         | 
| 146 | 
            +
                content << "== #{cop.cop_name}\n"
         | 
| 121 147 | 
             
                content << "\n"
         | 
| 122 148 | 
             
                content
         | 
| 123 149 | 
             
              end
         | 
| 124 150 |  | 
| 125 | 
            -
              def  | 
| 151 | 
            +
              def cop_subsection(title, cop)
         | 
| 126 152 | 
             
                content = +"\n"
         | 
| 153 | 
            +
                content << "[##{to_anchor(title)}-#{to_anchor(cop.cop_name)}]\n"
         | 
| 127 154 | 
             
                content << "=== #{title}\n"
         | 
| 128 155 | 
             
                content << "\n"
         | 
| 129 156 | 
             
                content
         | 
| 130 157 | 
             
              end
         | 
| 131 158 |  | 
| 132 | 
            -
              def  | 
| 133 | 
            -
                content =  | 
| 159 | 
            +
              def example_header(title, cop)
         | 
| 160 | 
            +
                content = "[##{to_anchor(title)}-#{to_anchor(cop.cop_name)}]\n"
         | 
| 161 | 
            +
                content << +"==== #{title}\n"
         | 
| 134 162 | 
             
                content << "\n"
         | 
| 135 163 | 
             
                content
         | 
| 136 164 | 
             
              end
         | 
| @@ -142,7 +170,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength | |
| 142 170 | 
             
                content
         | 
| 143 171 | 
             
              end
         | 
| 144 172 |  | 
| 145 | 
            -
              def configurations(department, pars)
         | 
| 173 | 
            +
              def configurations(department, pars, cop)
         | 
| 146 174 | 
             
                return '' if pars.empty?
         | 
| 147 175 |  | 
| 148 176 | 
             
                header = ['Name', 'Default value', 'Configurable values']
         | 
| @@ -157,7 +185,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength | |
| 157 185 | 
             
                  [configuration_name(department, name), default, configurable]
         | 
| 158 186 | 
             
                end
         | 
| 159 187 |  | 
| 160 | 
            -
                 | 
| 188 | 
            +
                cop_subsection('Configurable attributes', cop) + to_table(header, content)
         | 
| 161 189 | 
             
              end
         | 
| 162 190 |  | 
| 163 191 | 
             
              def configuration_name(department, name)
         | 
| @@ -235,7 +263,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength | |
| 235 263 | 
             
                urls = RuboCop::Cop::MessageAnnotator.new(config, cop.name, cop_config, {}).urls
         | 
| 236 264 | 
             
                return '' if urls.empty? && see_objects.empty?
         | 
| 237 265 |  | 
| 238 | 
            -
                content =  | 
| 266 | 
            +
                content = cop_subsection('References', cop)
         | 
| 239 267 | 
             
                content << urls.map { |url| "* #{url}" }.join("\n")
         | 
| 240 268 | 
             
                content << "\n" unless urls.empty?
         | 
| 241 269 | 
             
                content << see_objects.map { |see| "* #{see.name}" }.join("\n")
         | 
| @@ -283,14 +311,16 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength | |
| 283 311 | 
             
                ]
         | 
| 284 312 | 
             
                pars = cop_config.reject { |k| non_display_keys.include? k }
         | 
| 285 313 | 
             
                description = 'No documentation'
         | 
| 286 | 
            -
                 | 
| 314 | 
            +
                example_objects = safety_objects = see_objects = []
         | 
| 287 315 | 
             
                cop_code(cop) do |code_object|
         | 
| 288 316 | 
             
                  description = code_object.docstring unless code_object.docstring.blank?
         | 
| 289 | 
            -
                   | 
| 290 | 
            -
                   | 
| 291 | 
            -
                   | 
| 317 | 
            +
                  example_objects = code_object.tags('example')
         | 
| 318 | 
            +
                  safety_objects = code_object.tags('safety')
         | 
| 319 | 
            +
                  see_objects = code_object.tags('see')
         | 
| 292 320 | 
             
                end
         | 
| 293 | 
            -
                 | 
| 321 | 
            +
                data = CopData.new(cop: cop, description: description, example_objects: example_objects,
         | 
| 322 | 
            +
                                   safety_objects: safety_objects, see_objects: see_objects, config: pars)
         | 
| 323 | 
            +
                cops_body(data)
         | 
| 294 324 | 
             
              end
         | 
| 295 325 |  | 
| 296 326 | 
             
              def cop_code(cop)
         | 
| @@ -306,7 +336,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength | |
| 306 336 | 
             
                filename = "#{department_to_basename(department)}.adoc"
         | 
| 307 337 | 
             
                content = +"=== Department xref:#{filename}[#{type_title}]\n\n"
         | 
| 308 338 | 
             
                cops_of_department(department).each do |cop|
         | 
| 309 | 
            -
                  anchor = cop.cop_name | 
| 339 | 
            +
                  anchor = to_anchor(cop.cop_name)
         | 
| 310 340 | 
             
                  content << "* xref:#{filename}##{anchor}[#{cop.cop_name}]\n"
         | 
| 311 341 | 
             
                end
         | 
| 312 342 |  | 
| @@ -338,4 +368,13 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength | |
| 338 368 |  | 
| 339 369 | 
             
                status == 'pending' ? 'Pending' : 'Enabled'
         | 
| 340 370 | 
             
              end
         | 
| 371 | 
            +
             | 
| 372 | 
            +
              # HTML anchor are somewhat limited in what characters they can contain, just
         | 
| 373 | 
            +
              # accept a known-good subset. As long as it's consistent it doesn't matter.
         | 
| 374 | 
            +
              #
         | 
| 375 | 
            +
              # Style/AccessModifierDeclarations => styleaccessmodifierdeclarations
         | 
| 376 | 
            +
              # OnlyFor: [] (default) => onlyfor_-__-_default_
         | 
| 377 | 
            +
              def to_anchor(title)
         | 
| 378 | 
            +
                title.delete('/').tr(' ', '-').gsub(/[^a-zA-Z0-9-]/, '_').downcase
         | 
| 379 | 
            +
              end
         | 
| 341 380 | 
             
            end
         | 
    
        data/lib/rubocop/file_finder.rb
    CHANGED
    
    | @@ -23,15 +23,20 @@ module RuboCop | |
| 23 23 | 
             
                  last_file
         | 
| 24 24 | 
             
                end
         | 
| 25 25 |  | 
| 26 | 
            +
                def traverse_directories_upwards(start_dir, stop_dir = nil)
         | 
| 27 | 
            +
                  Pathname.new(start_dir).expand_path.ascend do |dir|
         | 
| 28 | 
            +
                    yield(dir)
         | 
| 29 | 
            +
                    dir = dir.to_s
         | 
| 30 | 
            +
                    break if dir == stop_dir || dir == FileFinder.root_level
         | 
| 31 | 
            +
                  end
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
             | 
| 26 34 | 
             
                private
         | 
| 27 35 |  | 
| 28 36 | 
             
                def traverse_files_upwards(filename, start_dir, stop_dir)
         | 
| 29 | 
            -
                   | 
| 37 | 
            +
                  traverse_directories_upwards(start_dir, stop_dir) do |dir|
         | 
| 30 38 | 
             
                    file = dir + filename
         | 
| 31 39 | 
             
                    yield(file.to_s) if file.exist?
         | 
| 32 | 
            -
             | 
| 33 | 
            -
                    dir = dir.to_s
         | 
| 34 | 
            -
                    break if dir == stop_dir || dir == FileFinder.root_level
         | 
| 35 40 | 
             
                  end
         | 
| 36 41 | 
             
                end
         | 
| 37 42 | 
             
              end
         | 
    
        data/lib/rubocop/lsp/runtime.rb
    CHANGED
    
    
    
        data/lib/rubocop/lsp/server.rb
    CHANGED
    
    
| @@ -169,6 +169,7 @@ module RuboCop | |
| 169 169 | 
             
                    raise 'Expected correction but no corrections were made' if new_source == source
         | 
| 170 170 |  | 
| 171 171 | 
             
                    expect(new_source).to eq(correction)
         | 
| 172 | 
            +
                    expect(@processed_source).to be_valid_syntax, 'Expected correction to be valid syntax'
         | 
| 172 173 | 
             
                  end
         | 
| 173 174 | 
             
                  # rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity
         | 
| 174 175 |  | 
    
        data/lib/rubocop/runner.rb
    CHANGED
    
    | @@ -362,6 +362,9 @@ module RuboCop | |
| 362 362 | 
             
                  self.class.ruby_extractors.find do |ruby_extractor|
         | 
| 363 363 | 
             
                    result = ruby_extractor.call(processed_source)
         | 
| 364 364 | 
             
                    break result if result
         | 
| 365 | 
            +
                  rescue StandardError
         | 
| 366 | 
            +
                    raise Error, "Ruby extractor #{ruby_extractor.source_location[0]} failed to process " \
         | 
| 367 | 
            +
                                 "#{processed_source.path}."
         | 
| 365 368 | 
             
                  end
         | 
| 366 369 | 
             
                end
         | 
| 367 370 |  | 
    
        data/lib/rubocop/server/cache.rb
    CHANGED
    
    | @@ -43,13 +43,18 @@ module RuboCop | |
| 43 43 | 
             
                      @project_dir_cache_key ||= project_dir[1..].tr('/', '+')
         | 
| 44 44 | 
             
                    end
         | 
| 45 45 |  | 
| 46 | 
            +
                    # rubocop:disable Metrics/AbcSize
         | 
| 46 47 | 
             
                    def restart_key
         | 
| 47 48 | 
             
                      lockfile_path = LOCKFILE_NAMES.map do |lockfile_name|
         | 
| 48 49 | 
             
                        Pathname(project_dir).join(lockfile_name)
         | 
| 49 50 | 
             
                      end.find(&:exist?)
         | 
| 51 | 
            +
                      version_data = lockfile_path&.read || RuboCop::Version::STRING
         | 
| 52 | 
            +
                      config_data = Pathname(ConfigFinder.find_config_path(Dir.pwd)).read
         | 
| 53 | 
            +
                      todo_data = (rubocop_todo = Pathname('.rubocop_todo.yml')).exist? ? rubocop_todo.read : ''
         | 
| 50 54 |  | 
| 51 | 
            -
                      Digest::SHA1.hexdigest( | 
| 55 | 
            +
                      Digest::SHA1.hexdigest(version_data + config_data + todo_data)
         | 
| 52 56 | 
             
                    end
         | 
| 57 | 
            +
                    # rubocop:enable Metrics/AbcSize
         | 
| 53 58 |  | 
| 54 59 | 
             
                    def dir
         | 
| 55 60 | 
             
                      Pathname.new(File.join(cache_path, project_dir_cache_key)).tap do |d|
         |