Skip to content

Commit 5add326

Browse files
committed
In errorshow UI, filter out frames for _include implementation
This simplifies stack traces for include() which were made much more complex by moving jl_parse_eval_all to the Julia layer.
1 parent f0f535f commit 5add326

File tree

3 files changed

+52
-11
lines changed

3 files changed

+52
-11
lines changed

base/errorshow.jl

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,39 @@ function is_kw_sorter_name(name::Symbol)
750750
return !startswith(sn, '#') && endswith(sn, "##kw")
751751
end
752752

753+
# For improved user experience, filter out frames for include() implementation
754+
# - see #33065. See also #35371 for extended discussion of internal frames.
755+
function _simplify_include_frames(trace)
756+
kept_frames = trues(length(trace))
757+
i = length(trace)
758+
first_ignored = nothing
759+
while i >= 1
760+
frame,_ = trace[i]
761+
mod = parentmodule(frame)
762+
if isnothing(first_ignored)
763+
if mod in (Base,Core) && frame.func === :_include
764+
# Hide include() machinery by default
765+
first_ignored = i
766+
end
767+
else
768+
# Hack: allow `mod==nothing` as a workaround for inlined functions.
769+
# TODO: Fix this by improving debug info.
770+
if mod in (Base,Core,nothing) && 1+first_ignored-i <= 5
771+
if frame.func == :eval
772+
@debug "Ignoring frames" removed=trace[i:first_ignored]
773+
kept_frames[i:first_ignored] .= false
774+
first_ignored = nothing
775+
end
776+
else
777+
# Bail out
778+
first_ignored = nothing
779+
end
780+
end
781+
i -= 1
782+
end
783+
trace[kept_frames]
784+
end
785+
753786
function process_backtrace(t::Vector, limit::Int=typemax(Int); skipC = true)
754787
n = 0
755788
last_frame = StackTraces.UNKNOWN
@@ -790,7 +823,7 @@ function process_backtrace(t::Vector, limit::Int=typemax(Int); skipC = true)
790823
if n > 0
791824
push!(ret, (last_frame, n))
792825
end
793-
return ret
826+
return _simplify_include_frames(ret)
794827
end
795828

796829
function show_exception_stack(io::IO, stack::Vector)

base/loading.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1149,6 +1149,7 @@ Base.include # defined in Base.jl
11491149

11501150
# Full include() implementation which is used after bootstrap
11511151
function _include(mapexpr::Function, mod::Module, _path::AbstractString)
1152+
@_noinline_meta # Workaround for module availability in _simplify_include_frames
11521153
path, prev = _include_dependency(mod, _path)
11531154
for callback in include_callbacks # to preserve order, must come before Core.include
11541155
invokelatest(callback, mod, path)

base/stacktraces.jl

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -268,22 +268,29 @@ function show(io::IO, frame::StackFrame; full_path::Bool=false)
268268
end
269269
end
270270

271+
function Base.parentmodule(frame::StackFrame)
272+
if frame.linfo isa MethodInstance
273+
def = frame.linfo.def
274+
if def isa Module
275+
return def
276+
else
277+
@assert def isa Method
278+
return def.module
279+
end
280+
else
281+
# Bug: currently the module is not available for inlined frames and
282+
# frames arising from the interpreter.
283+
nothing
284+
end
285+
end
286+
271287
"""
272288
from(frame::StackFrame, filter_mod::Module) -> Bool
273289
274290
Returns whether the `frame` is from the provided `Module`
275291
"""
276292
function from(frame::StackFrame, m::Module)
277-
finfo = frame.linfo
278-
result = false
279-
280-
if finfo isa MethodInstance
281-
frame_m = finfo.def
282-
isa(frame_m, Method) && (frame_m = frame_m.module)
283-
result = nameof(frame_m) === nameof(m)
284-
end
285-
286-
return result
293+
nameof(parentmodule(frame)) == nameof(m)
287294
end
288295

289296
end

0 commit comments

Comments
 (0)