lib/rubocop/cop/performance/hash_each_methods.rb in rubocop-0.49.1 vs lib/rubocop/cop/performance/hash_each_methods.rb in rubocop-0.50.0

- old
+ new

@@ -18,64 +18,105 @@ class HashEachMethods < Cop include Lint::UnusedArgument MSG = 'Use `%s` instead of `%s`.'.freeze - def_node_matcher :plain_each, <<-END - (block $(send (send _ :hash) :each) (args (arg $_k) (arg $_v)) ...) - END + def_node_matcher :plain_each, <<-PATTERN + (block $(send _ :each) (args (arg $_k) (arg $_v)) ...) + PATTERN - def_node_matcher :kv_each, <<-END - (block $(send (send (send _ :hash) ${:keys :values}) :each) ...) - END + def_node_matcher :kv_each, <<-PATTERN + (block $(send (send _ ${:keys :values}) :each) ...) + PATTERN def on_block(node) + register_each_offense(node) + register_kv_offense(node) + end + + private + + def register_each_offense(node) plain_each(node) do |target, k, v| return if @args[k] && @args[v] used = @args[k] ? :key : :value - add_offense(target, range(target), format(message, - "each_#{used}", - :each)) + add_offense( + target, plain_range(target), format(message, + "each_#{used}", + :each) + ) end + end + + def register_kv_offense(node) kv_each(node) do |target, method| - add_offense(target, range(target), format(message, - "each_#{method[0..-2]}", - "#{method}.each")) + add_offense( + target, kv_range(target), format(message, + "each_#{method[0..-2]}", + "#{method}.each") + ) end end def check_argument(variable) return unless variable.block_argument? (@args ||= {})[variable.name] = variable.used? end def autocorrect(node) receiver, _second_method = *node - caller, first_method = *receiver + _caller, first_method = *receiver + lambda do |corrector| - if first_method == :hash - method = @args.values.first ? :key : :value - new_source = receiver.source + ".each_#{method}" - corrector.replace(node.loc.expression, new_source) - correct_args(node, corrector) + case first_method + when :keys, :values + return correct_implicit(node, corrector) if receiver.receiver.nil? + + correct_key_value_each(node, corrector) else - new_source = caller.source + ".each_#{first_method[0..-2]}" - corrector.replace(node.loc.expression, new_source) + return correct_implicit(node, corrector) if receiver.nil? + + correct_plain_each(node, corrector) end end end - private + def correct_implicit(node, corrector) + method = @args.include?(:k) ? :key : :value + new_source = "each_#{method}" + corrector.replace(node.loc.expression, new_source) + correct_args(node, corrector) + end + + def correct_key_value_each(node, corrector) + receiver = node.receiver + + new_source = receiver.receiver.source + + ".each_#{receiver.method_name[0..-2]}" + corrector.replace(node.loc.expression, new_source) + end + + def correct_plain_each(node, corrector) + method = @args.include?(:k) ? :key : :value + new_source = node.receiver.source + ".each_#{method}" + + corrector.replace(node.loc.expression, new_source) + correct_args(node, corrector) + end + def correct_args(node, corrector) args = node.parent.children[1] used_arg = "|#{@args.detect { |_k, v| v }.first}|" - args_range = range_between(args.loc.begin.begin_pos, - args.loc.end.end_pos) - corrector.replace(args_range, used_arg) + + corrector.replace(args.source_range, used_arg) end - def range(outer_node) + def plain_range(outer_node) + outer_node.loc.selector + end + + def kv_range(outer_node) inner_node = outer_node.children.first inner_node.loc.selector.join(outer_node.loc.selector) end end end