Skip to content

Commit 2e39f64

Browse files
Kenoclaude
andauthored
Add world age hint for UndefVarError (#58572)
Similar to the existing world age hint for MethodError, this adds a helpful message when an UndefVarError occurs because a binding was defined in a newer world age than the code trying to access it. The implementation checks if a binding that was undefined at the error's world age is now defined in the current world, and displays: "The binding may be too new: running in world age X, while current world is Y." Additionally, for all binding kinds, the error hint now notes when the binding state has changed between the error's world and the current world. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-authored-by: Claude <noreply@anthropic.com>
1 parent 8275173 commit 2e39f64

File tree

2 files changed

+47
-9
lines changed

2 files changed

+47
-9
lines changed

base/errorshow.jl

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1154,22 +1154,42 @@ function UndefVarError_hint(io::IO, ex::UndefVarError)
11541154
if isdefined(ex, :scope)
11551155
scope = ex.scope
11561156
if scope isa Module
1157-
bpart = Base.lookup_binding_partition(ex.world, GlobalRef(scope, var))
1158-
kind = Base.binding_kind(bpart)
1159-
if kind === Base.PARTITION_KIND_GLOBAL || kind === Base.PARTITION_KIND_UNDEF_CONST || kind == Base.PARTITION_KIND_DECLARED
1157+
bpart = lookup_binding_partition(ex.world, GlobalRef(scope, var))
1158+
kind = binding_kind(bpart)
1159+
1160+
# Get the current world's binding partition for comparison
1161+
curworld = tls_world_age()
1162+
cur_bpart = lookup_binding_partition(curworld, GlobalRef(scope, var))
1163+
cur_kind = binding_kind(cur_bpart)
1164+
1165+
# Track if we printed the "too new" message
1166+
printed_too_new = false
1167+
1168+
# Check if the binding exists in the current world but was undefined in the error's world
1169+
if kind === PARTITION_KIND_GUARD
1170+
if isdefinedglobal(scope, var)
1171+
print(io, "\nThe binding may be too new: running in world age $(ex.world), while current world is $(curworld).")
1172+
printed_too_new = true
1173+
else
1174+
print(io, "\nSuggestion: check for spelling errors or missing imports.")
1175+
end
1176+
elseif kind === PARTITION_KIND_GLOBAL || kind === PARTITION_KIND_UNDEF_CONST || kind == PARTITION_KIND_DECLARED
11601177
print(io, "\nSuggestion: add an appropriate import or assignment. This global was declared but not assigned.")
1161-
elseif kind === Base.PARTITION_KIND_FAILED
1178+
elseif kind === PARTITION_KIND_FAILED
11621179
print(io, "\nHint: It looks like two or more modules export different ",
11631180
"bindings with this name, resulting in ambiguity. Try explicitly ",
11641181
"importing it from a particular module, or qualifying the name ",
11651182
"with the module it should come from.")
1166-
elseif kind === Base.PARTITION_KIND_GUARD
1167-
print(io, "\nSuggestion: check for spelling errors or missing imports.")
1168-
elseif Base.is_some_explicit_imported(kind)
1169-
print(io, "\nSuggestion: this global was defined as `$(Base.partition_restriction(bpart).globalref)` but not assigned a value.")
1170-
elseif kind === Base.PARTITION_KIND_BACKDATED_CONST
1183+
elseif is_some_explicit_imported(kind)
1184+
print(io, "\nSuggestion: this global was defined as `$(partition_restriction(bpart).globalref)` but not assigned a value.")
1185+
elseif kind === PARTITION_KIND_BACKDATED_CONST
11711186
print(io, "\nSuggestion: define the const at top-level before running function that uses it (stricter Julia v1.12+ rule).")
11721187
end
1188+
1189+
# Check if binding kind changed between the error's world and current world
1190+
if !printed_too_new && kind !== cur_kind
1191+
print(io, "\nNote: the binding state changed since the error occurred (was: $(kind), now: $(cur_kind)).")
1192+
end
11731193
elseif scope === :static_parameter
11741194
print(io, "\nSuggestion: run Test.detect_unbound_args to detect method arguments that do not fully constrain a type parameter.")
11751195
elseif scope === :local

test/errorshow.jl

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -943,6 +943,24 @@ end
943943
@test_throws expected_message X.x
944944
end
945945

946+
# Module for UndefVarError world age testing
947+
module TestWorldAgeUndef end
948+
949+
@testset "UndefVarError world age hint" begin
950+
ex = try
951+
TestWorldAgeUndef.newvar
952+
catch e
953+
e
954+
end
955+
@test ex isa UndefVarError
956+
957+
Core.eval(TestWorldAgeUndef, :(newvar = 42))
958+
959+
err_str = sprint(Base.showerror, ex)
960+
@test occursin("The binding may be too new: running in world age", err_str)
961+
@test occursin("while current world is", err_str)
962+
end
963+
946964
# test showing MethodError with type argument
947965
struct NoMethodsDefinedHere; end
948966
let buf = IOBuffer()

0 commit comments

Comments
 (0)