Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 17 additions & 12 deletions base/experimental.jl
Original file line number Diff line number Diff line change
Expand Up @@ -295,18 +295,18 @@ Closest candidates are:
`if isdefined(Base.Experimental, :register_error_hint) ... end` block.
"""
function register_error_hint(@nospecialize(handler), @nospecialize(exct::Type))
list = get!(Vector{Any}, _hint_handlers, exct)
push!(list, handler)
list = get!(Vector{Any}, _hint_handlers, Core.typename(exct))
push!(list, (exct, handler))
return nothing
end

const _hint_handlers = IdDict{Type,Vector{Any}}()
const _hint_handlers = IdDict{Core.TypeName,Vector{Any}}()

"""
Experimental.show_error_hints(io, ex, args...)

Invoke all handlers from [`Experimental.register_error_hint`](@ref) for the particular
exception type `typeof(ex)`. `args` must contain any other arguments expected by
exception type `typeof(ex)` and all of its supertypes. `args` must contain any other arguments expected by
the handler for that type.

!!! compat "Julia 1.5"
Expand All @@ -316,15 +316,20 @@ the handler for that type.
"""
function show_error_hints(io, ex, args...)
@nospecialize
hinters = get(_hint_handlers, typeof(ex), nothing)
isnothing(hinters) && return
for handler in hinters
try
@invokelatest handler(io, ex, args...)
catch
tn = typeof(handler).name
@error "Hint-handler $handler for $(typeof(ex)) in $(tn.module) caused an error" exception=current_exceptions()
ex_supertype = typeof(ex)
while ex_supertype != Any
hinters = get(_hint_handlers, Core.typename(ex_supertype), Any[])
for (exct, handler) in hinters
ex isa exct || continue
try
# TODO: deal with handlers accepting different signatures?
@invokelatest handler(io, ex, args...)
catch
tn = typeof(handler).name
@error "Hint-handler $handler for $(ex_supertype) in $(tn.module) caused an error" exception=current_exceptions()
end
end
ex_supertype = supertype(ex_supertype)
end
end

Expand Down
39 changes: 37 additions & 2 deletions test/errorshow.jl
Original file line number Diff line number Diff line change
Expand Up @@ -740,7 +740,7 @@ let err_str
@test occursin(Regex("MethodError: no method matching one\\(::.*HasNoOne; value::$(Int)\\)"), err_str)
@test occursin("`one` doesn't take keyword arguments, that would be silly", err_str)
end
pop!(Base.Experimental._hint_handlers[MethodError]) # order is undefined, don't copy this
pop!(Base.Experimental._hint_handlers[Core.typename(MethodError)]) # order is undefined, don't copy this

function busted_hint(io, exc, notarg) # wrong number of args
print(io, "\nI don't have a hint for you, sorry")
Expand All @@ -752,7 +752,7 @@ catch ex
io = IOBuffer()
@test_logs (:error, "Hint-handler busted_hint for DomainError in $(@__MODULE__) caused an error") showerror(io, ex)
end
pop!(Base.Experimental._hint_handlers[DomainError]) # order is undefined, don't copy this
pop!(Base.Experimental._hint_handlers[Core.typename(DomainError)]) # order is undefined, don't copy this

struct ANumber <: Number end
let err_str = @except_str ANumber()(3 + 4) MethodError
Expand Down Expand Up @@ -1476,3 +1476,38 @@ let err_str
err_str = @except_str f56325(1,2) MethodError
@test occursin("The anonymous function", err_str)
end

# Test that error hints catch abstract exception supertypes (issue #58367)

module Hinterland

abstract type AbstractHintableException <: Exception end
struct ConcreteHintableException <: AbstractHintableException end
gonnathrow() = throw(ConcreteHintableException())

function Base.showerror(io::IO, exc::ConcreteHintableException)
print(io, "This is my exception")
Base.Experimental.show_error_hints(io, exc)
end

function __init__()
Base.Experimental.register_error_hint(ConcreteHintableException) do io, exc
print(io, "\nThis hint caught my concrete exception type")
end
Base.Experimental.register_error_hint(AbstractHintableException) do io, exc
print(io, "\nThis other hint caught my abstract exception supertype")
end
end

end

@testset "Hints for abstract exception supertypes" begin
exc = try
Hinterland.gonnathrow()
catch e
e
end
exc_print = sprint(Base.showerror, exc)
@test occursin("This hint caught my concrete exception type", exc_print)
@test occursin("This other hint caught my abstract exception supertype", exc_print)
end