forked from rubocop/rubocop
/
inherit_exception.rb
96 lines (82 loc) · 2.53 KB
/
inherit_exception.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# frozen_string_literal: true
module RuboCop
module Cop
module Lint
# This cop looks for error classes inheriting from `Exception`.
# It is configurable to suggest using either `StandardError` (default) or
# `RuntimeError` instead.
#
# @safety
# This cop's autocorrection is unsafe because `rescue` that omit
# exception class handle `StandardError` and its subclasses,
# but not `Exception` and its subclasses.
#
# @example EnforcedStyle: standard_error (default)
# # bad
#
# class C < Exception; end
#
# C = Class.new(Exception)
#
# # good
#
# class C < StandardError; end
#
# C = Class.new(StandardError)
#
# @example EnforcedStyle: runtime_error
# # bad
#
# class C < Exception; end
#
# C = Class.new(Exception)
#
# # good
#
# class C < RuntimeError; end
#
# C = Class.new(RuntimeError)
class InheritException < Base
include ConfigurableEnforcedStyle
extend AutoCorrector
MSG = 'Inherit from `%<prefer>s` instead of `Exception`.'
PREFERRED_BASE_CLASS = {
runtime_error: 'RuntimeError',
standard_error: 'StandardError'
}.freeze
RESTRICT_ON_SEND = %i[new].freeze
# @!method class_new_call?(node)
def_node_matcher :class_new_call?, <<~PATTERN
(send
(const {cbase nil?} :Class) :new
$(const {cbase nil?} _))
PATTERN
def on_class(node)
return unless node.parent_class && exception_class?(node.parent_class)
message = message(node.parent_class)
add_offense(node.parent_class, message: message) do |corrector|
corrector.replace(node.parent_class, preferred_base_class)
end
end
def on_send(node)
constant = class_new_call?(node)
return unless constant && exception_class?(constant)
message = message(constant)
add_offense(constant, message: message) do |corrector|
corrector.replace(constant, preferred_base_class)
end
end
private
def message(node)
format(MSG, prefer: preferred_base_class, current: node.const_name)
end
def exception_class?(class_node)
class_node.const_name == 'Exception'
end
def preferred_base_class
PREFERRED_BASE_CLASS[style]
end
end
end
end
end