Class: RuboCop::Socketry::Style::GlobalExceptionVariables

Inherits:
Cop::Base
  • Object
show all
Defined in:
lib/rubocop/socketry/style/global_exception_variables.rb

Overview

A RuboCop cop that warns against using global exception variables in unsafe contexts.

This cop discourages the use of:

  • ‘$!` (last exception)

  • ‘$@` (backtrace of last exception)

  • ‘$ERROR_INFO` (English name for `$!`)

  • ‘$ERROR_POSITION` (English name for `$@`)

These global variables are implicit and can make code harder to understand.

However, this cop allows their use in safe contexts where the scope is well-defined:

  • Inside rescue blocks (well-defined scope)

  • In rescue modifiers (‘expression rescue $!`)

  • In method parameter defaults (‘def foo(error = $!)`, `def bar(error: $!)`)

This cop specifically flags their use in unsafe contexts:

  • Inside ensure blocks (extremely unsafe - exception state is unpredictable)

  • Outside of exception handling contexts

Examples:

# bad - unsafe in ensure block
begin
  risky_operation
ensure
  log($!.message) if $!  # unsafe!
end

# bad - outside exception handling
def process
  puts $!.message
end

# good - explicit exception handling
begin
  risky_operation
rescue => error
  puts error.message
end

# allowed - inside rescue block (well-defined scope)
begin
  risky_operation
rescue
  puts $!.message
end

# allowed - rescue modifier
result = risky_operation rescue $!

# allowed - parameter defaults
def foo(error = $!)
def bar(error: $!)

Constant Summary collapse

MSG =
"Avoid using global exception variable `%<variable>s` in %<context>s. Use explicit exception handling with `rescue => error` instead."
ENSURE_MSG =
"Using global exception variable `%<variable>s` in an ensure block is extremely unsafe."
EXCEPTION_VARIABLES =
%i[$! $@ $ERROR_INFO $ERROR_POSITION].freeze

Instance Method Summary collapse

Instance Method Details

#on_gvar(node) ⇒ Object



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
97
# File 'lib/rubocop/socketry/style/global_exception_variables.rb', line 69

def on_gvar(node)
	variable_name = node.children.first
	
	return unless EXCEPTION_VARIABLES.include?(variable_name)
	
	# Allow in parameter defaults (explicitly opting in)
	return if in_parameter_default?(node)
	
	# Allow in rescue modifier (well-defined scope)
	return if in_rescue_modifier?(node)
	
	# Allow in rescue block (well-defined scope)
	return if in_rescue_block?(node)
	
	# Flag if in ensure block (extremely unsafe)
	if in_ensure_block?(node)
		add_offense(
			node,
			message: format(ENSURE_MSG, variable: variable_name)
		)
		return
	end
	
	# Flag in all other contexts
	add_offense(
		node,
		message: format(MSG, variable: variable_name, context: "this context")
	)
end