diff --git a/Library/Homebrew/debrew.rb b/Library/Homebrew/debrew.rb index 82407ecbc2..670446d3d2 100644 --- a/Library/Homebrew/debrew.rb +++ b/Library/Homebrew/debrew.rb @@ -3,10 +3,7 @@ require "mutex_m" require "debrew/irb" -require "warnings" -Warnings.ignore(/warning: callcc is obsolete; use Fiber instead/) do - require "continuation" -end +require "ignorable" # Helper module for debugging formulae. # @@ -14,31 +11,6 @@ end module Debrew extend Mutex_m - # Marks exceptions which can be ignored and provides - # the ability to jump back to where it was raised. - module Ignorable - attr_accessor :continuation - - def ignore - continuation.call - end - end - - # Module for allowing to ignore exceptions. - module Raise - def raise(*) - callcc do |continuation| - super - rescue Exception => e # rubocop:disable Lint/RescueException - e.extend(Ignorable) - e.continuation = continuation - super(e) - end - end - - alias fail raise - end - # Module for allowing to debug formulae. module Formula def install @@ -106,28 +78,28 @@ module Debrew class << self extend Predicable - alias original_raise raise attr_predicate :active? attr_reader :debugged_exceptions end def self.debrew @active = true - Object.include Raise + Ignorable.hook_raise begin yield rescue SystemExit - original_raise + raise rescue Exception => e # rubocop:disable Lint/RescueException e.ignore if debug(e) == :ignore # execution jumps back to where the exception was thrown ensure + Ignorable.unhook_raise @active = false end end def self.debug(e) - original_raise(e) if !active? || !debugged_exceptions.add?(e) || !try_lock + raise(e) if !active? || !debugged_exceptions.add?(e) || !try_lock begin puts e.backtrace.first.to_s @@ -137,15 +109,15 @@ module Debrew Menu.choose do |menu| menu.prompt = "Choose an action: " - menu.choice(:raise) { original_raise(e) } - menu.choice(:ignore) { return :ignore } if e.is_a?(Ignorable) + menu.choice(:raise) { raise(e) } + menu.choice(:ignore) { return :ignore } if e.is_a?(Ignorable::ExceptionMixin) menu.choice(:backtrace) { puts e.backtrace } - if e.is_a?(Ignorable) + if e.is_a?(Ignorable::ExceptionMixin) menu.choice(:irb) do puts "When you exit this IRB session, execution will continue." set_trace_func proc { |event, _, _, id, binding, klass| - if klass == Raise && id == :raise && event == "return" + if klass == Object && id == :raise && event == "return" set_trace_func(nil) synchronize { IRB.start_within(binding) } end diff --git a/Library/Homebrew/ignorable.rb b/Library/Homebrew/ignorable.rb new file mode 100644 index 0000000000..4ede76f392 --- /dev/null +++ b/Library/Homebrew/ignorable.rb @@ -0,0 +1,58 @@ +# typed: false +# frozen_string_literal: true + +require "warnings" +Warnings.ignore(/warning: callcc is obsolete; use Fiber instead/) do + require "continuation" +end + +# Provides the ability to optionally ignore errors raised and continue execution. +# +# @api private +module Ignorable + # Marks exceptions which can be ignored and provides + # the ability to jump back to where it was raised. + module ExceptionMixin + attr_accessor :continuation + + def ignore + continuation.call + end + end + + def self.hook_raise + Object.class_eval do + alias_method :original_raise, :raise + + def raise(*) + callcc do |continuation| + super + rescue Exception => e # rubocop:disable Lint/RescueException + unless e.is_a?(ScriptError) + e.extend(ExceptionMixin) + e.continuation = continuation + end + super(e) + end + end + + alias_method :fail, :raise + end + + return unless block_given? + + yield + unhook_raise + end + + def self.unhook_raise + Object.class_eval do + # False positive - https://github.com/rubocop/rubocop/issues/5022 + # rubocop:disable Lint/DuplicateMethods + alias_method :raise, :original_raise + alias_method :fail, :original_raise + # rubocop:enable Lint/DuplicateMethods + undef :original_raise + end + end +end