Skip to content

Commit

Permalink
Try make REPL error easier to visually parse (#19569)
Browse files Browse the repository at this point in the history
  • Loading branch information
KristofferC authored and StefanKarpinski committed Dec 29, 2016
1 parent b561cfb commit dc7b4fb
Show file tree
Hide file tree
Showing 13 changed files with 87 additions and 57 deletions.
14 changes: 7 additions & 7 deletions base/REPL.jl
Original file line number Diff line number Diff line change
Expand Up @@ -112,14 +112,12 @@ end

function display_error(io::IO, er, bt)
print_with_color(Base.error_color(), io, "ERROR: "; bold = true)
Base.with_output_color(Base.error_color(), io) do io
# remove REPL-related frames from interactive printing
eval_ind = findlast(addr->ip_matches_func(addr, :eval), bt)
if eval_ind != 0
bt = bt[1:eval_ind-1]
end
Base.showerror(IOContext(io, :limit => true), er, bt)
# remove REPL-related frames from interactive printing
eval_ind = findlast(addr->Base.REPL.ip_matches_func(addr, :eval), bt)
if eval_ind != 0
bt = bt[1:eval_ind-1]
end
showerror(IOContext(io, :limit => true), er, bt)
end

immutable REPLDisplay{R<:AbstractREPL} <: Display
Expand Down Expand Up @@ -167,6 +165,8 @@ function print_response(errio::IO, val::ANY, bt, show_value::Bool, have_color::B
catch err
if bt !== nothing
println(errio, "SYSTEM: show(lasterr) caused an error")
println(errio, err)
Base.show_backtrace(errio, bt)
break
end
val = err
Expand Down
3 changes: 2 additions & 1 deletion base/array.jl
Original file line number Diff line number Diff line change
Expand Up @@ -737,7 +737,8 @@ julia> deleteat!([6, 5, 4, 3, 2, 1], 1:2:5)
julia> deleteat!([6, 5, 4, 3, 2, 1], (2, 2))
ERROR: ArgumentError: indices must be unique and sorted
in deleteat!(::Array{Int64,1}, ::Tuple{Int64,Int64}) at ./array.jl:753
Stacktrace:
[1] deleteat!(::Array{Int64,1}, ::Tuple{Int64,Int64}) at ./array.jl:748
```
"""
function deleteat!(a::Vector, inds)
Expand Down
13 changes: 9 additions & 4 deletions base/client.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,19 @@ const text_colors = AnyDict(
:normal => "\033[0m",
:default => "\033[39m",
:bold => "\033[1m",
:nothing => "",
)

for i in 0:255
text_colors[i] = "\033[38;5;$(i)m"
end

const disable_text_style = AnyDict(
:bold => "\033[22m",
:normal => "",
:default => "",
)

for i in 0:255
text_colors[i] = "\033[38;5;$(i)m"
end

# Create a docstring with an automatically generated list
# of colors.
available_text_colors = collect(Iterators.filter(x -> !isa(x, Integer), keys(text_colors)))
Expand All @@ -52,6 +53,7 @@ Available colors are: $available_text_colors_docstring as well as the integers 0
The color `:default` will print text in the default color while the color `:normal`
will print text with all text properties (like boldness) reset.
Printing with the color `:nothing` will print the string without modifications.
"""
text_colors

Expand Down Expand Up @@ -83,6 +85,9 @@ info_color() = repl_color("JULIA_INFO_COLOR" , default_color_info)
input_color() = text_colors[:bold] * text_colors[repl_color("JULIA_INPUT_COLOR", default_color_input)]
answer_color() = text_colors[:bold] * text_colors[repl_color("JULIA_ANSWER_COLOR", default_color_answer)]

stackframe_lineinfo_color() = repl_color("JULIA_STACKFRAME_LINEINFO_COLOR", :bold)
stackframe_function_color() = repl_color("JULIA_STACKFRAME_FUNCTION_COLOR", :bold)

function repl_cmd(cmd, out)
shell = shell_split(get(ENV,"JULIA_SHELL",get(ENV,"SHELL","/bin/sh")))
# Note that we can't support the fish shell due to its lack of subshells
Expand Down
6 changes: 4 additions & 2 deletions base/docs/helpdb/Base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,8 @@ If `T` is not a bitstype, an error is thrown.
```jldoctest
julia> sizeof(Base.LinAlg.LU)
ERROR: argument is an abstract type; size is indeterminate
in sizeof(::Type{T}) at ./essentials.jl:99
Stacktrace:
[1] sizeof(::Type{T}) at ./essentials.jl:99
```
"""
sizeof(::Type)
Expand Down Expand Up @@ -2532,7 +2533,8 @@ julia> convert(Int, 3.0)
julia> convert(Int, 3.5)
ERROR: InexactError()
in convert(::Type{Int64}, ::Float64) at ./float.jl:656
Stacktrace:
[1] convert(::Type{Int64}, ::Float64) at ./float.jl:656
```
If `T` is a `AbstractFloat` or `Rational` type,
Expand Down
10 changes: 6 additions & 4 deletions base/linalg/eigen.jl
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,9 @@ julia> A = [0 im; -1 0]
julia> eigmax(A)
ERROR: DomainError:
in #eigmax#36(::Bool, ::Bool, ::Function, ::Array{Complex{Int64},2}) at ./linalg/eigen.jl:218
in eigmax(::Array{Complex{Int64},2}) at ./linalg/eigen.jl:216
Stacktrace:
[1] #eigmax#30(::Bool, ::Bool, ::Function, ::Array{Complex{Int64},2}) at ./linalg/eigen.jl:219
[2] eigmax(::Array{Complex{Int64},2}) at ./linalg/eigen.jl:217
```
"""
function eigmax(A::Union{Number, StridedMatrix}; permute::Bool=true, scale::Bool=true)
Expand Down Expand Up @@ -249,8 +250,9 @@ julia> A = [0 im; -1 0]
julia> eigmin(A)
ERROR: DomainError:
in #eigmin#37(::Bool, ::Bool, ::Function, ::Array{Complex{Int64},2}) at ./linalg/eigen.jl:259
in eigmin(::Array{Complex{Int64},2}) at ./linalg/eigen.jl:257
Stacktrace:
[1] #eigmin#31(::Bool, ::Bool, ::Function, ::Array{Complex{Int64},2}) at ./linalg/eigen.jl:261
[2] eigmin(::Array{Complex{Int64},2}) at ./linalg/eigen.jl:259
```
"""
function eigmin(A::Union{Number, StridedMatrix}; permute::Bool=true, scale::Bool=true)
Expand Down
3 changes: 2 additions & 1 deletion base/nullable.jl
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@ Nullable{String}()
julia> unsafe_get(x)
ERROR: UndefRefError: access to undefined reference
in unsafe_get(::Nullable{String}) at ./nullable.jl:123
Stacktrace:
[1] unsafe_get(::Nullable{String}) at ./nullable.jl:124
julia> x = 1
1
Expand Down
18 changes: 13 additions & 5 deletions base/replutil.jl
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,9 @@ end

function showerror(io::IO, ex, bt; backtrace=true)
try
showerror(io, ex)
with_output_color(have_color ? error_color() : :nothing, io) do io
showerror(io, ex)
end
finally
backtrace && show_backtrace(io, bt)
end
Expand Down Expand Up @@ -577,15 +579,21 @@ function show_method_candidates(io::IO, ex::MethodError, kwargs::Vector=Any[])
end
end

function show_trace_entry(io, frame, n)
function show_trace_entry(io, frame, n; prefix = " in ")
print(io, "\n")
show(io, frame, full_path=true)
show(io, frame, full_path=true; prefix = prefix)
n > 1 && print(io, " (repeats ", n, " times)")
end

function show_backtrace(io::IO, t::Vector)
process_entry(last_frame, n) =
show_trace_entry(io, last_frame, n)
n_frames = 0
frame_counter = 0
process_backtrace((a,b) -> n_frames += 1, t)
n_frames != 0 && print(io, "\nStacktrace:")
process_entry = (last_frame, n) -> begin
frame_counter += 1
show_trace_entry(io, last_frame, n, prefix = string(" [", frame_counter, "] "))
end
process_backtrace(process_entry, t)
end

Expand Down
42 changes: 24 additions & 18 deletions base/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1031,31 +1031,37 @@ end

function show_lambda_types(io::IO, li::Core.MethodInstance)
# print a method signature tuple for a lambda definition
if li.specTypes === Tuple
print(io, li.def.name, "(...)")
return
end

sig = li.specTypes.parameters
ft = sig[1]
if ft <: Function && isempty(ft.parameters) &&
isdefined(ft.name.module, ft.name.mt.name) &&
ft == typeof(getfield(ft.name.module, ft.name.mt.name))
print(io, ft.name.mt.name)
elseif isa(ft, DataType) && ft.name === Type.name && isleaftype(ft)
f = ft.parameters[1]
print(io, f)
else
print(io, "(::", ft, ")")
local sig
returned_from_do = false
Base.with_output_color(have_color ? stackframe_function_color() : :nothing, io) do io
if li.specTypes === Tuple
print(io, li.def.name, "(...)")
returned_from_do = true
return
end
sig = li.specTypes.parameters
ft = sig[1]
if ft <: Function && isempty(ft.parameters) &&
isdefined(ft.name.module, ft.name.mt.name) &&
ft == typeof(getfield(ft.name.module, ft.name.mt.name))
print(io, ft.name.mt.name)
elseif isa(ft, DataType) && ft.name === Type.name && isleaftype(ft)
f = ft.parameters[1]
print(io, f)
else
print(io, "(::", ft, ")")
end
end
returned_from_do && return
first = true
print(io, '(')
print_style = have_color ? :bold : :nothing
print_with_color(print_style, io, "(")
for i = 2:length(sig) # fixme (iter): `eachindex` with offset?
first || print(io, ", ")
first = false
print(io, "::", sig[i])
end
print(io, ')')
print_with_color(print_style, io, ")")
nothing
end

Expand Down
20 changes: 12 additions & 8 deletions base/stacktraces.jl
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ function show_spec_linfo(io::IO, frame::StackFrame)
if frame.func === empty_sym
@printf(io, "ip:%#x", frame.pointer)
else
print(io, frame.func)
print_with_color(Base.have_color ? Base.stackframe_function_color() : :nothing, io, string(frame.func))
end
else
linfo = get(frame.linfo)
Expand All @@ -200,16 +200,20 @@ function show_spec_linfo(io::IO, frame::StackFrame)
end
end

function show(io::IO, frame::StackFrame; full_path::Bool=false)
print(io, " in ")
function show(io::IO, frame::StackFrame; full_path::Bool=false,
prefix = " in ")
print(io, prefix)
show_spec_linfo(io, frame)
if frame.file !== empty_sym
file_info = full_path ? string(frame.file) : basename(string(frame.file))
print(io, " at ", file_info, ":")
if frame.line >= 0
print(io, frame.line)
else
print(io, "?")
print(io, " at ")
Base.with_output_color(Base.have_color ? Base.stackframe_lineinfo_color() : :nothing, io) do io
print(io, file_info, ":")
if frame.line >= 0
print(io, frame.line)
else
print(io, "?")
end
end
end
if frame.inlined
Expand Down
3 changes: 2 additions & 1 deletion base/test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -972,7 +972,8 @@ Body:
julia> @inferred f(1,2,3)
ERROR: return type Int64 does not match inferred return type Union{Float64,Int64}
in error(::String) at ./error.jl:21
Stacktrace:
[1] error(::String) at ./error.jl:21
julia> @inferred max(1,2)
2
Expand Down
2 changes: 1 addition & 1 deletion base/util.jl
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ function with_output_color(f::Function, color::Union{Int, Symbol}, io::IO, args.
have_color && print(buf, get(text_colors, color, color_normal))
try f(IOContext(buf, io), args...)
finally
have_color && print(buf, get(disable_text_style, color, text_colors[:default]))
have_color && color != :nothing && print(buf, get(disable_text_style, color, text_colors[:default]))
have_color && (bold || color == :bold) && print(buf, disable_text_style[:bold])
print(io, String(take!(buf)))
end
Expand Down
6 changes: 3 additions & 3 deletions test/cmdlineargs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -366,12 +366,12 @@ end
for precomp in ("yes", "no")
bt = readstring(pipeline(ignorestatus(`$(Base.julia_cmd()) --startup-file=no --precompiled=$precomp
-E 'include("____nonexistent_file")'`), stderr=catcmd))
@test contains(bt, "in include_from_node1")
@test contains(bt, "include_from_node1")
if is_windows() && Sys.WORD_SIZE == 32 && precomp == "yes"
# fixme, issue #17251
@test_broken contains(bt, "in include_from_node1(::String) at $(joinpath(".","loading.jl"))")
@test_broken contains(bt, "include_from_node1(::String) at $(joinpath(".","loading.jl"))")
else
@test contains(bt, "in include_from_node1(::String) at $(joinpath(".","loading.jl"))")
@test contains(bt, "include_from_node1(::String) at $(joinpath(".","loading.jl"))")
end
lno = match(r"at \.[\/\\]loading\.jl:(\d+)", bt)
@test length(lno.captures) == 1
Expand Down
4 changes: 2 additions & 2 deletions test/compile.jl
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ try
end
""")

t = redirected_stderr("ERROR: LoadError: Declaring __precompile__(false) is not allowed in files that are being precompiled.\n in __precompile__")
t = redirected_stderr("ERROR: LoadError: Declaring __precompile__(false) is not allowed in files that are being precompiled.\nStacktrace:\n [1] __precompile__")
try
Base.compilecache("Baz") # from __precompile__(false)
error("__precompile__ disabled test failed")
Expand Down Expand Up @@ -314,7 +314,7 @@ try
error("break me")
end
""")
t = redirected_stderr("ERROR: LoadError: break me\n in error")
t = redirected_stderr("ERROR: LoadError: break me\nStacktrace:\n [1] error")
try
Base.require(:FooBar)
error("\"LoadError: break me\" test failed")
Expand Down

0 comments on commit dc7b4fb

Please sign in to comment.