/
raise_exception.rb
83 lines (69 loc) · 2.41 KB
/
raise_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
# frozen_string_literal: true
module RuboCop
module Cop
module Lint
# This cop checks for `raise` or `fail` statements which are
# raising `Exception` class.
#
# You can specify a module name that will be an implicit namespace
# using `AllowedImplicitNamespaces` option. The cop cause a false positive
# for namespaced `Exception` when a namespace is omitted. This option can
# prevent the false positive by specifying a namespace to be omitted for
# `Exception`. Alternatively, make `Exception` a fully qualified class
# name with an explicit namespace.
#
# @example
# # bad
# raise Exception, 'Error message here'
#
# # good
# raise StandardError, 'Error message here'
#
# @example AllowedImplicitNamespaces: ['Gem']
# # good
# module Gem
# def self.foo
# raise Exception # This exception means `Gem::Exception`.
# end
# end
class RaiseException < Cop
MSG = 'Use `StandardError` over `Exception`.'
def_node_matcher :exception?, <<~PATTERN
(send nil? {:raise :fail} $(const ${cbase nil?} :Exception) ... )
PATTERN
def_node_matcher :exception_new_with_message?, <<~PATTERN
(send nil? {:raise :fail}
(send $(const ${cbase nil?} :Exception) :new ... ))
PATTERN
def on_send(node)
exception?(node, &check(node)) ||
exception_new_with_message?(node, &check(node))
end
def autocorrect(node)
lambda do |corrector|
exception_class = node.children.first&.cbase_type? ? '::StandardError' : 'StandardError'
corrector.replace(node, exception_class)
end
end
private
def check(node)
lambda do |exception_class, cbase|
return if cbase.nil? && implicit_namespace?(node)
add_offense(exception_class)
end
end
def implicit_namespace?(node)
return false unless (parent = node.parent)
if parent.module_type?
namespace = parent.identifier.source
return allow_implicit_namespaces.include?(namespace)
end
implicit_namespace?(parent)
end
def allow_implicit_namespaces
cop_config['AllowedImplicitNamespaces'] || []
end
end
end
end
end