class RuboCop::Cop::Performance::RedundantMerge
hash = 2<br>hash = 1
# good
hash.merge!(a: 1, b: 2)
# bad
@example MaxKeyValuePairs: 2 (default)<br><br>hash = ‘value’<br>hash = 1
# good
hash.merge!({‘key’ => ‘value’})
hash.merge!(a: 1)
# bad
@example
receiver of ‘merge!` is actually a hash or not.
This cop is unsafe because RuboCop cannot determine if the
@safety
an offense with `MaxKeyValuePairs`.
You can set the maximum number of key-value pairs to consider
Identifies places where `Hash#merge!` can be replaced by `Hash#[]=`.
def correct_multiple_elements(corrector, node, parent, new_source)
def correct_multiple_elements(corrector, 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.replace(node, new_source) end
def correct_single_element(corrector, node, new_source)
def correct_single_element(corrector, node, new_source) corrector.replace(node, new_source) end
def each_redundant_merge(node)
def each_redundant_merge(node) redundant_merge_candidate(node) do |receiver, pairs| next if non_redundant_merge?(node, receiver, pairs) yield node end end
def kwsplat_used?(pairs)
def kwsplat_used?(pairs) pairs.any?(&:kwsplat_type?) end
def leading_spaces(node)
def leading_spaces(node) node.source_range.source_line[/\A\s*/] end
def max_key_value_pairs
def max_key_value_pairs Integer(cop_config['MaxKeyValuePairs'] || 2) end
def message(node)
def message(node) redundant_merge_candidate(node) do |receiver, pairs| assignments = to_assignments(receiver, pairs).join('; ') format(MSG, prefer: assignments, current: node.source) end end
def non_redundant_merge?(node, receiver, pairs)
def non_redundant_merge?(node, receiver, pairs) pairs.empty? || non_redundant_pairs?(receiver, pairs) || kwsplat_used?(pairs) || non_redundant_value_used?(receiver, node) end
def non_redundant_pairs?(receiver, pairs)
def non_redundant_pairs?(receiver, pairs) (pairs.size > 1 && !receiver.pure?) || pairs.size > max_key_value_pairs end
def non_redundant_value_used?(receiver, node)
def non_redundant_value_used?(receiver, node) node.value_used? && !EachWithObjectInspector.new(node, receiver).value_used? end
def on_send(node)
def on_send(node) each_redundant_merge(node) do |redundant_merge_node| message = message(node) add_offense(redundant_merge_node, message: message) do |corrector| redundant_merge_candidate(node) do |receiver, pairs| new_source = to_assignments(receiver, pairs).join("\n") if node.parent && pairs.size > 1 correct_multiple_elements(corrector, node, node.parent, new_source) else correct_single_element(corrector, node, new_source) end end end end end
def rewrite_with_modifier(node, parent, new_source)
def rewrite_with_modifier(node, parent, new_source) indent = ' ' * configured_indentation_width padding = "\n#{indent + leading_spaces(node)}" new_source.gsub!("\n", padding) format(WITH_MODIFIER_CORRECTION, keyword: parent.loc.keyword.source, condition: parent.condition.source, leading_space: leading_spaces(node), indent: indent, body: new_source).chomp end
def to_assignments(receiver, pairs)
def to_assignments(receiver, pairs) pairs.map do |pair| key, value = *pair key = key.sym_type? && pair.colon? ? ":#{key.source}" : key.source format(AREF_ASGN, receiver: receiver.source, key: key, value: value.source) end end