65 lines
1.6 KiB
Ruby
65 lines
1.6 KiB
Ruby
|
module RuboCop
|
||
|
module Cop
|
||
|
# This cop makes sure that custom error classes, when empty, are declared
|
||
|
# with Class.new.
|
||
|
#
|
||
|
# @example
|
||
|
# # bad
|
||
|
# class FooError < StandardError
|
||
|
# end
|
||
|
#
|
||
|
# # okish
|
||
|
# class FooError < StandardError; end
|
||
|
#
|
||
|
# # good
|
||
|
# FooError = Class.new(StandardError)
|
||
|
class CustomErrorClass < RuboCop::Cop::Cop
|
||
|
MSG = 'Use `Class.new(SuperClass)` to define an empty custom error class.'.freeze
|
||
|
|
||
|
def on_class(node)
|
||
|
_klass, parent, body = node.children
|
||
|
|
||
|
return if body
|
||
|
|
||
|
parent_klass = class_name_from_node(parent)
|
||
|
|
||
|
return unless parent_klass && parent_klass.to_s.end_with?('Error')
|
||
|
|
||
|
add_offense(node, :expression)
|
||
|
end
|
||
|
|
||
|
def autocorrect(node)
|
||
|
klass, parent, _body = node.children
|
||
|
replacement = "#{class_name_from_node(klass)} = Class.new(#{class_name_from_node(parent)})"
|
||
|
|
||
|
lambda do |corrector|
|
||
|
corrector.replace(node.source_range, replacement)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
private
|
||
|
|
||
|
# The nested constant `Foo::Bar::Baz` looks like:
|
||
|
#
|
||
|
# s(:const,
|
||
|
# s(:const,
|
||
|
# s(:const, nil, :Foo), :Bar), :Baz)
|
||
|
#
|
||
|
# So recurse through that to get the name as written in the source.
|
||
|
#
|
||
|
def class_name_from_node(node, suffix = nil)
|
||
|
return unless node&.type == :const
|
||
|
|
||
|
name = node.children[1].to_s
|
||
|
name = "#{name}::#{suffix}" if suffix
|
||
|
|
||
|
if node.children[0]
|
||
|
class_name_from_node(node.children[0], name)
|
||
|
else
|
||
|
name
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|