<commit_before># encoding: utf-8
# frozen_string_literal: true

module RuboCop
  module Cop
    module Performance
      # This cop identifies places where `Hash#merge!` can be replaced by
      # `Hash#[]=`.
      #
      # @example
      #   hash.merge!(a: 1)
      #   hash.merge!({'key' => 'value'})
      #   hash.merge!(a: 1, b: 2)
      class RedundantMerge < Cop
        AREF_ASGN = '%s[%s] = %s'.freeze
        MSG = 'Use `%s` instead of `%s`.'.freeze

        def_node_matcher :redundant_merge, '(send $_ :merge! (hash $...))'
        def_node_matcher :modifier_flow_control, <<-END
          [{if while until} modifier_form?]
        END

        def on_send(node)
          each_redundant_merge(node) do |receiver, pairs|
            assignments = to_assignments(receiver, pairs).join('; ')
            message = format(MSG, assignments, node.source)
            add_offense(node, :expression, message)
          end
        end

        def autocorrect(node)
          redundant_merge(node) do |receiver, pairs|
            lambda do |corrector|
              new_source = to_assignments(receiver, pairs).join("\n")

              parent = node.parent
              if parent && pairs.size > 1
                if modifier_flow_control(parent)
                  new_source = rewrite_with_modifier(node, parent, new_source)
                  node = parent
                else
                  padding = "\n#{leading_spaces(node)}"
                  new_source.gsub!(/\n/, padding)
                end
              end

              corrector.replace(node.source_range, new_source)
            end
          end
        end

        private

        def each_redundant_merge(node)
          redundant_merge(node) do |receiver, pairs|
            next if node.value_used? &&
                    !EachWithObjectInspector.new(node, receiver).value_used?
            next if pairs.size > 1 && !receiver.pure?
            next if pairs.size > max_key_value_pairs

            yield receiver, pairs
          end
        end

        def to_assignments(receiver, pairs)
          pairs.map do |pair|
            key, value = *pair
            key_src = if key.sym_type? && !key.source.start_with?(':')
                        ":#{key.source}"
                      else
                        key.source
                      end

            format(AREF_ASGN, receiver.source, key_src, value.source)
          end
        end

        def rewrite_with_modifier(node, parent, new_source)
          cond, = *parent
          padding = "\n#{(' ' * indent_width) + leading_spaces(node)}"
          new_source.gsub!(/\n/, padding)

          parent.loc.keyword.source << ' ' << cond.source << padding <<
            new_source << "\n" << leading_spaces(node) << 'end'
        end

        def leading_spaces(node)
          node.source_range.source_line[/\A\s*/]
        end

        def indent_width
          @config.for_cop('IndentationWidth')['Width'] || 2<commit_msg>Reduce complexity in RedundantMerge<commit_after># encoding: utf-8
# frozen_string_literal: true

module RuboCop
  module Cop
    module Performance
      # This cop identifies places where `Hash#merge!` can be replaced by
      # `Hash#[]=`.
      #
      # @example
      #   hash.merge!(a: 1)
      #   hash.merge!({'key' => 'value'})
      #   hash.merge!(a: 1, b: 2)
      class RedundantMerge < Cop
        AREF_ASGN = '%s[%s] = %s'.freeze
        MSG = 'Use `%s` instead of `%s`.'.freeze

        def_node_matcher :redundant_merge, '(send $_ :merge! (hash $...))'
        def_node_matcher :modifier_flow_control?, <<-END
          [{if while until} modifier_form?]
        END

        def on_send(node)
          each_redundant_merge(node) do |receiver, pairs|
            assignments = to_assignments(receiver, pairs).join('; ')
            message = format(MSG, assignments, node.source)
            add_offense(node, :expression, message)
          end
        end

        def autocorrect(node)
          redundant_merge(node) do |receiver, pairs|
            new_source = to_assignments(receiver, pairs).join("\n")

            parent = node.parent
            if parent && pairs.size > 1
              correct_multiple_elements(node, parent, new_source)
            else
              correct_single_element(node, new_source)
            end
          end
        end

        private

        def each_redundant_merge(node)
          redundant_merge(node) do |receiver, pairs|
            next if node.value_used? &&
                    !EachWithObjectInspector.new(node, receiver).value_used?
            next if pairs.size > 1 && !receiver.pure?
            next if pairs.size > max_key_value_pairs

            yield receiver, pairs
          end
        end

        def correct_multiple_elements(node, parent, new_source)
          if modifier_flow_control?(parent)
            new_source = rewrite_with_modifier(node, parent, new_source)
            node = parent
          else
            padding = "\n#{leading_spaces(node)}"
            new_source.gsub!(/\n/, padding)
          end

          ->(corrector) { corrector.replace(node.source_range, new_source) }
        end

        def correct_single_element(node, new_source)
          ->(corrector) { corrector.replace(node.source_range, new_source) }
        end

        def to_assignments(receiver, pairs)
          pairs.map do |pair|
            key, value = *pair
            key_src = if key.sym_type? && !key.source.start_with?(':')
                        ":#{key.source}"
                      else
                        key.source
                      end

            format(AREF_ASGN, receiver.source, key_src, value.source)
          end
        end

        def rewrite_with_modifier(node, parent, new_source)
          cond, = *parent
          padding = "\n#{(' ' * indent_width) + leading_spaces(node)}"
          new_source.gsub!(/\n/, padding)

          parent.loc.keyword.source << ' ' << cond.source << padding <<
            new_source << "\n" << leading_spaces(node) << 'end'
        end

        def leading_spaces(node)
          node.source_range.source_line[/\A\s*/]
        end

        def indent_width
          @config.for_cop('IndentationWidth')['Width'] || 2
{'fraction_non_alphanumeric': 0.1096, 'fraction_numerical': 0.00224, 'mean_word_length': 1.7356673960612692, 'pattern_counts': {'":': 2, '<': 37, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5992479', 'n_tokens_mistral': 1879, 'n_tokens_neox': 1807, 'n_words': 395}