lib/rubocop/cop/style/redundant_initialize.rb
# frozen_string_literal: true
module RuboCop
module Cop
module Style
# Checks for `initialize` methods that are redundant.
#
# An initializer is redundant if it does not do anything, or if it only
# calls `super` with the same arguments given to it. If the initializer takes
# an argument that accepts multiple values (`restarg`, `kwrestarg`, etc.) it
# will not register an offense, because it allows the initializer to take a different
# number of arguments as its superclass potentially does.
#
# NOTE: If an initializer argument has a default value, RuboCop assumes it
# to *not* be redundant.
#
# NOTE: Empty initializers are registered as offenses, but it is possible
# to purposely create an empty `initialize` method to override a superclass's
# initializer.
#
# @safety
# This cop is unsafe because if subclass overrides `initialize` method with
# a different arity than superclass.
#
# @example
# # bad
# def initialize
# end
#
# # bad
# def initialize
# super
# end
#
# # bad
# def initialize(a, b)
# super
# end
#
# # bad
# def initialize(a, b)
# super(a, b)
# end
#
# # good
# def initialize
# do_something
# end
#
# # good
# def initialize
# do_something
# super
# end
#
# # good (different number of parameters)
# def initialize(a, b)
# super(a)
# end
#
# # good (default value)
# def initialize(a, b = 5)
# super
# end
#
# # good (default value)
# def initialize(a, b: 5)
# super
# end
#
# # good (changes the parameter requirements)
# def initialize(*)
# end
#
# # good (changes the parameter requirements)
# def initialize(**)
# end
#
# # good (changes the parameter requirements)
# def initialize(...)
# end
#
# @example AllowComments: true (default)
#
# # good
# def initialize
# # Overriding to negate superclass `initialize` method.
# end
#
# @example AllowComments: false
#
# # bad
# def initialize
# # Overriding to negate superclass `initialize` method.
# end
#
class RedundantInitialize < Base
include CommentsHelp
include RangeHelp
extend AutoCorrector
MSG = 'Remove unnecessary `initialize` method.'
MSG_EMPTY = 'Remove unnecessary empty `initialize` method.'
# @!method initialize_forwards?(node)
def_node_matcher :initialize_forwards?, <<~PATTERN
(def _ (args $arg*) $({super zsuper} ...))
PATTERN
def on_def(node)
return if acceptable?(node)
if node.body.nil?
register_offense(node, MSG_EMPTY)
else
return if node.body.begin_type?
if (args, super_node = initialize_forwards?(node))
return unless same_args?(super_node, args)
register_offense(node, MSG)
end
end
end
private
def register_offense(node, message)
add_offense(node, message: message) do |corrector|
corrector.remove(range_by_whole_lines(node.source_range, include_final_newline: true))
end
end
def acceptable?(node)
!node.method?(:initialize) || forwards?(node) || allow_comments?(node)
end
def forwards?(node)
node.arguments.each_child_node(:restarg, :kwrestarg, :forward_args, :forward_arg).any?
end
def allow_comments?(node)
return false unless cop_config['AllowComments']
contains_comments?(node) && !comments_contain_disables?(node, name)
end
def same_args?(super_node, args)
return true if super_node.zsuper_type?
args.map(&:name) == super_node.arguments.map { |a| a.children[0] }
end
end
end
end
end