Improving error messages for the scoping problem


#1

There is a problem with beginners and the global scope https://discourse.julialang.org/t/another-possible-solution-to-the-global-scope-debacle/15894/223. Regardless of the eventual solution, it would be desirable to backport better error messages. A more primitive change would be https://github.com/JuliaLang/julia/pull/29585.

Below is a pretty rube-goldberg attempt that only works on debug builds of master (otherwise the relevant symbols from interpreter.c for scanning the stacktrace are missing):

julia> function Base.showerror(io::IO, ex::UndefVarError, bt)
                  if ex.var in [:UTF16String, :UTF32String, :WString, :utf16, :utf32, :wstring, :RepString]
                      return showerror(io, ErrorException("""
                      `$(ex.var)` has been moved to the package LegacyStrings.jl:
                      Run Pkg.add("LegacyStrings") to install LegacyStrings on Julia v0.5-;
                      Then do `using LegacyStrings` to get `$(ex.var)`.
                      """))
                  end
                  print(io, "UndefVarError: $(ex.var) not defined")
                  #check whether we have tried to find a global
                  global last_bt = bt
                  check_bt_for_errtype(io, ex, bt)
                  end

julia> function check_bt_for_errtype(io, ex, bt)
           st = stacktrace(bt, true)
           for sf in st
               if sf.func == :jl_get_binding_or_error || sf.func == :jl_eval_global_var
                   print(io, "\nthe global variable $(ex.var) was not found. Did you mistype or forget a `where {$(ex.var)}` clause?")
                   return
               end
           end
           
           print(io, "\nthe local variable $(ex.var) was accessed before assignment. Did you mean `global $(ex.var)`?")
           nothing
       end

With this we display in whether the failed resolution of the symbol was in the global scope or in a local scope:

julia> xxx
ERROR: UndefVarError: xxx not defined
the global variable xxx was not found. Did you mistype or forget a `where {xxx}` clause?
julia> f(x::T) = x
ERROR: UndefVarError: T not defined
the global variable T was not found. Did you mistype or forget a `where {T}` clause?
julia> struct flub
       x::T
       end
ERROR: UndefVarError: T not defined
the global variable T was not found. Did you mistype or forget a `where {T}` clause?
julia> f()=xxx
f (generic function with 1 method)

julia> f()
ERROR: UndefVarError: xxx not defined
the global variable xxx was not found. Did you mistype or forget a `where {xxx}` clause?

On the other hand:

julia> N=10;
julia> for i=1:N
       N -= 1
       end
ERROR: UndefVarError: N not defined
the local variable N was accessed before assignment. Did you mean `global N`?
julia> function ff()
       return N
       N=0
       end
ff (generic function with 1 method)

julia> ff()
ERROR: UndefVarError: N not defined
the local variable N was accessed before assignment. Did you mean `global N`?

What do you all think? Is this the missing information for confused people?

It would be nice if we could remove the “or forget where {N}” clause in some contexts where we know that this definitely is not the case. It would be nice to also show in which module we tried to resolve a global symbol, and when offering " Did you mean global N" it may be nice to check whether a global of that name actually exists.


New scope solution
#2

In the last 2 examples ( for and ff() ), at “Did you mean global N”, it would help to indicate in which line it refers to.


#3

Right. I’ll see whether I can get that info from the interpreter backtrace; for the function, this is already part of the backtrace.