Skip to content

Commit 9fd0ba2

Browse files
Kenoclaude
authored andcommitted
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> (cherry picked from commit 2e39f64)
1 parent 505789c commit 9fd0ba2

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
@@ -1144,22 +1144,42 @@ function UndefVarError_hint(io::IO, ex::UndefVarError)
11441144
if isdefined(ex, :scope)
11451145
scope = ex.scope
11461146
if scope isa Module
1147-
bpart = Base.lookup_binding_partition(ex.world, GlobalRef(scope, var))
1148-
kind = Base.binding_kind(bpart)
1149-
if kind === Base.PARTITION_KIND_GLOBAL || kind === Base.PARTITION_KIND_UNDEF_CONST || kind == Base.PARTITION_KIND_DECLARED
1147+
bpart = lookup_binding_partition(ex.world, GlobalRef(scope, var))
1148+
kind = binding_kind(bpart)
1149+
1150+
# Get the current world's binding partition for comparison
1151+
curworld = tls_world_age()
1152+
cur_bpart = lookup_binding_partition(curworld, GlobalRef(scope, var))
1153+
cur_kind = binding_kind(cur_bpart)
1154+
1155+
# Track if we printed the "too new" message
1156+
printed_too_new = false
1157+
1158+
# Check if the binding exists in the current world but was undefined in the error's world
1159+
if kind === PARTITION_KIND_GUARD
1160+
if isdefinedglobal(scope, var)
1161+
print(io, "\nThe binding may be too new: running in world age $(ex.world), while current world is $(curworld).")
1162+
printed_too_new = true
1163+
else
1164+
print(io, "\nSuggestion: check for spelling errors or missing imports.")
1165+
end
1166+
elseif kind === PARTITION_KIND_GLOBAL || kind === PARTITION_KIND_UNDEF_CONST || kind == PARTITION_KIND_DECLARED
11501167
print(io, "\nSuggestion: add an appropriate import or assignment. This global was declared but not assigned.")
1151-
elseif kind === Base.PARTITION_KIND_FAILED
1168+
elseif kind === PARTITION_KIND_FAILED
11521169
print(io, "\nHint: It looks like two or more modules export different ",
11531170
"bindings with this name, resulting in ambiguity. Try explicitly ",
11541171
"importing it from a particular module, or qualifying the name ",
11551172
"with the module it should come from.")
1156-
elseif kind === Base.PARTITION_KIND_GUARD
1157-
print(io, "\nSuggestion: check for spelling errors or missing imports.")
1158-
elseif Base.is_some_explicit_imported(kind)
1159-
print(io, "\nSuggestion: this global was defined as `$(Base.partition_restriction(bpart).globalref)` but not assigned a value.")
1160-
elseif kind === Base.PARTITION_KIND_BACKDATED_CONST
1173+
elseif is_some_explicit_imported(kind)
1174+
print(io, "\nSuggestion: this global was defined as `$(partition_restriction(bpart).globalref)` but not assigned a value.")
1175+
elseif kind === PARTITION_KIND_BACKDATED_CONST
11611176
print(io, "\nSuggestion: define the const at top-level before running function that uses it (stricter Julia v1.12+ rule).")
11621177
end
1178+
1179+
# Check if binding kind changed between the error's world and current world
1180+
if !printed_too_new && kind !== cur_kind
1181+
print(io, "\nNote: the binding state changed since the error occurred (was: $(kind), now: $(cur_kind)).")
1182+
end
11631183
elseif scope === :static_parameter
11641184
print(io, "\nSuggestion: run Test.detect_unbound_args to detect method arguments that do not fully constrain a type parameter.")
11651185
elseif scope === :local

test/errorshow.jl

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -938,6 +938,24 @@ end
938938
@test_throws expected_message X.x
939939
end
940940

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

0 commit comments

Comments
 (0)