class RuboCop::Cop::Lint::DuplicateMethods

This cop checks for duplicated instance (or singleton) method definitions.

@example

# bad

def foo
  1
end

def foo
  2
end

@example

# bad

def foo
  1
end

alias foo bar

@example

# good

def foo
  1
end

def bar
  2
end

@example

# good

def foo
  1
end

alias bar foo

Constants

MSG

Public Class Methods

new(config = nil, options = nil) click to toggle source
Calls superclass method RuboCop::Cop::Cop::new
# File lib/rubocop/cop/lint/duplicate_methods.rb, line 56
def initialize(config = nil, options = nil)
  super
  @definitions = {}
end

Public Instance Methods

on_alias(node) click to toggle source
# File lib/rubocop/cop/lint/duplicate_methods.rb, line 86
def on_alias(node)
  return unless (name = method_alias?(node))
  return if node.ancestors.any?(&:if_type?)
  return if possible_dsl?(node)

  found_instance_method(node, name)
end
on_def(node) click to toggle source
# File lib/rubocop/cop/lint/duplicate_methods.rb, line 61
def on_def(node)
  # if a method definition is inside an if, it is very likely
  # that a different definition is used depending on platform, etc.
  return if node.ancestors.any?(&:if_type?)
  return if possible_dsl?(node)

  found_instance_method(node, node.method_name)
end
on_defs(node) click to toggle source
# File lib/rubocop/cop/lint/duplicate_methods.rb, line 70
def on_defs(node)
  return if node.ancestors.any?(&:if_type?)
  return if possible_dsl?(node)

  if node.receiver.const_type?
    _, const_name = *node.receiver
    check_const_receiver(node, node.method_name, const_name)
  elsif node.receiver.self_type?
    check_self_receiver(node, node.method_name)
  end
end
on_send(node) click to toggle source
# File lib/rubocop/cop/lint/duplicate_methods.rb, line 104
def on_send(node)
  if (name = alias_method?(node))
    return unless name
    return if node.ancestors.any?(&:if_type?)
    return if possible_dsl?(node)

    found_instance_method(node, name)
  elsif (attr = attr?(node))
    on_attr(node, *attr)
  end
end

Private Instance Methods

check_const_receiver(node, name, const_name) click to toggle source
# File lib/rubocop/cop/lint/duplicate_methods.rb, line 118
def check_const_receiver(node, name, const_name)
  qualified = lookup_constant(node, const_name)
  return unless qualified

  found_method(node, "#{qualified}.#{name}")
end
check_self_receiver(node, name) click to toggle source
# File lib/rubocop/cop/lint/duplicate_methods.rb, line 125
def check_self_receiver(node, name)
  enclosing = node.parent_module_name
  return unless enclosing

  found_method(node, "#{enclosing}.#{name}")
end
found_attr(node, args, readable: false, writable: false) click to toggle source
# File lib/rubocop/cop/lint/duplicate_methods.rb, line 177
def found_attr(node, args, readable: false, writable: false)
  args.each do |arg|
    name = sym_name(arg)
    next unless name

    found_instance_method(node, name) if readable
    found_instance_method(node, "#{name}=") if writable
  end
end
found_instance_method(node, name) click to toggle source
# File lib/rubocop/cop/lint/duplicate_methods.rb, line 137
def found_instance_method(node, name)
  return unless (scope = node.parent_module_name)

  if scope =~ /\A#<Class:(.*)>\Z/
    found_method(node, "#{Regexp.last_match(1)}.#{name}")
  else
    found_method(node, "#{scope}##{name}")
  end
end
found_method(node, method_name) click to toggle source
# File lib/rubocop/cop/lint/duplicate_methods.rb, line 147
def found_method(node, method_name)
  if @definitions.key?(method_name)
    loc = case node.type
          when :def, :defs
            node.loc.keyword.join(node.loc.name)
          else
            node.loc.expression
          end
    message = message_for_dup(node, method_name)

    add_offense(node, location: loc, message: message)
  else
    @definitions[method_name] = source_location(node)
  end
end
lookup_constant(node, const_name) click to toggle source
# File lib/rubocop/cop/lint/duplicate_methods.rb, line 187
def lookup_constant(node, const_name)
  # this method is quite imperfect and can be fooled
  # to do much better, we would need to do global analysis of the whole
  # codebase
  node.each_ancestor(:class, :module, :casgn) do |ancestor|
    namespace, mod_name = *ancestor.defined_module
    loop do
      if mod_name == const_name
        return qualified_name(ancestor.parent_module_name,
                              namespace,
                              mod_name)
      end

      break if namespace.nil?

      namespace, mod_name = *namespace
    end
  end
end
message_for_dup(node, method_name) click to toggle source
# File lib/rubocop/cop/lint/duplicate_methods.rb, line 132
def message_for_dup(node, method_name)
  format(MSG, method: method_name, defined: @definitions[method_name],
              current: source_location(node))
end
on_attr(node, attr_name, args) click to toggle source
# File lib/rubocop/cop/lint/duplicate_methods.rb, line 163
def on_attr(node, attr_name, args)
  case attr_name
  when :attr
    writable = args.size == 2 && args.last.true_type?
    found_attr(node, [args.first], readable: true, writable: writable)
  when :attr_reader
    found_attr(node, args, readable: true)
  when :attr_writer
    found_attr(node, args, writable: true)
  when :attr_accessor
    found_attr(node, args, readable: true, writable: true)
  end
end
possible_dsl?(node) click to toggle source
# File lib/rubocop/cop/lint/duplicate_methods.rb, line 221
def possible_dsl?(node)
  # DSL methods may evaluate a block in the context of a newly created
  # class or module
  # Assume that if a method definition is inside any block call which
  # we can't identify, it could be a DSL
  node.each_ancestor(:block).any? do |ancestor|
    ancestor.method_name != :class_eval && !ancestor.class_constructor?
  end
end
qualified_name(enclosing, namespace, mod_name) click to toggle source
# File lib/rubocop/cop/lint/duplicate_methods.rb, line 207
def qualified_name(enclosing, namespace, mod_name)
  if enclosing != 'Object'
    if namespace
      "#{enclosing}::#{namespace.const_name}::#{mod_name}"
    else
      "#{enclosing}::#{mod_name}"
    end
  elsif namespace
    "#{namespace.const_name}::#{mod_name}"
  else
    mod_name
  end
end
source_location(node) click to toggle source
# File lib/rubocop/cop/lint/duplicate_methods.rb, line 231
def source_location(node)
  range = node.location.expression
  path = smart_path(range.source_buffer.name)
  "#{path}:#{range.line}"
end