Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP/RFC: print stacktrace when eval fails #160

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
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
58 changes: 50 additions & 8 deletions src/Literate.jl
Original file line number Diff line number Diff line change
Expand Up @@ -782,16 +782,58 @@ function execute_block(sb::Module, block::String; inputfile::String="<unknown>")
end
popdisplay(disp) # IOCapture.capture has a try-catch so should always end up here
if c.error
error("""
$(sprint(showerror, c.value))
when executing the following code block in file `$(Base.contractuser(inputfile))`

```julia
$block
```
""")
# if c.value isa LoadError
# @info "..." c.value.file c.value.line
# bt = remove_common_backtrace(c.backtrace, backtrace())
# st = stacktrace(bt)
# @assert length(bt) >= length(st)
# idx = findlast(sf -> sf.func === :eval, st)
# display(st)
# @show idx st[idx]
# bt_ptr = Ptr{Nothing}(st[idx-1].pointer)
# idx = findfirst(isequal(bt_ptr), bt)
# @show idx
# display(stacktrace(bt[1:idx]))
# throw(EvalException(Base.contractuser(inputfile), block, c.value.error, bt[1:idx]))
# else
# throw(EvalException(Base.contractuser(inputfile), block, c.value, remove_common_backtrace(c.backtrace, backtrace())))
# end
Comment on lines +785 to +800
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a naive attempt the get rid of the stackframes introduced by include_string, but it didn't quite work right.

throw(EvalException(Base.contractuser(inputfile), block, c.value, remove_common_backtrace(c.backtrace, backtrace())))
end
return c.value, c.output, disp.data
end

struct EvalException <: Exception
file :: AbstractString
codeblock :: AbstractString
error :: Any
backtrace :: Any
end

function Base.showerror(io::IO, e::EvalException)
println(io, "Literate.EvalException: $(typeof(e.error)) when executing code in: $(e.file)")
println(io, "While executing the following code block:")
println(io, "```")
println(io, e.codeblock)
println(io, "```")
showerror(io, e.error, e.backtrace)
end

# Stolen from Documenter
function remove_common_backtrace(bt, reference_bt)
cutoff = nothing
# We'll start from the top of the backtrace (end of the array) and go down, checking
# if the backtraces agree
for ridx in 1:length(bt)
# Cancel search if we run out the reference BT or find a non-matching one frames:
if ridx > length(reference_bt) || bt[length(bt) - ridx + 1] != reference_bt[length(reference_bt) - ridx + 1]
cutoff = length(bt) - ridx + 1
break
end
end
# It's possible that the loop does not find anything, i.e. that all BT elements are in
# the reference_BT too. In that case we'll just return an empty BT.
bt[1:(cutoff === nothing ? 0 : cutoff)]
end

end # module