Skip to content

Commit

Permalink
special case splat call printing, add basic tests for printing
Browse files Browse the repository at this point in the history
  • Loading branch information
aviatesk committed Sep 18, 2020
1 parent d4997d2 commit 6f62c7a
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 23 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ You can have TypeProfiler.jl detect possible errors:
```julia
julia> using TypeProfiler

julia> profile_and_watch_file("demo.jl")
julia> profile_and_watch_file("demo.jl"; annotate_types = true)

profiling from demo.jl (finished in 2.745 sec)
════ 4 possible errors found ═════
Expand Down
16 changes: 8 additions & 8 deletions src/print.jl
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ function print_reports(io::IO,
print_inference_sucess::Bool = true,
color::Bool = get(io, :color, false),
fullpath::Bool = false,
dont_annotate_types::Bool = false,
annotate_types::Bool = false,
__kwargs...)
uniquify_reports!(reports)

Expand All @@ -134,7 +134,7 @@ function print_reports(io::IO,
toplevel_linfo_hash = new_toplevel_linfo_hash
wrote_linfos = Set{UInt64}()
end
print_report(io, report, wrote_linfos; fullpath, dont_annotate_types)
print_report(io, report, wrote_linfos; fullpath, annotate_types)
end
end |> postprocess |> Fix1(print, io)

Expand Down Expand Up @@ -184,7 +184,7 @@ function print_error_frame(io, report, depth; kwargs...)
print_rails(io, depth-1)
printstyled(io, "", report.msg; color)
print_signature(io, report.sig;
dont_annotate_types = false, # always don't suppress annotations for errored signatures
annotate_types = true, # always annotate types for errored signatures
bold = true,
)

Expand All @@ -194,14 +194,14 @@ end

function print_frame(io, (file, line, sig), depth, is_err;
fullpath = false,
dont_annotate_types = false,
annotate_types = false,
)
print_rails(io, depth-1)

color = is_err ? ERROR_COLOR : RAIL_COLORS[(depth)%N_RAILS+1]
s = string("┌ @ ", (fullpath ? tofullpath : identity)(string(file)), ':', line)
printstyled(io, s, ' '; color)
print_signature(io, sig; dont_annotate_types)
print_signature(io, sig; annotate_types)

return length(s) # the length of frame info string
end
Expand All @@ -213,11 +213,11 @@ function print_signature(io, sig; kwargs...)
println(io)
end
_print_signature(io, a::Union{AbstractChar,AbstractString};
dont_annotate_types = false,
annotate_types = false,
kwargs...) =
printstyled(io, a; kwargs...)
function _print_signature(io, @nospecialize(typ); dont_annotate_types = false, kwargs...)
dont_annotate_types && return
function _print_signature(io, @nospecialize(typ); annotate_types = false, kwargs...)
annotate_types || return

printstyled(io, "::", string(typ); color = TYPE_ANNOTATION_COLOR, kwargs...)
end
46 changes: 33 additions & 13 deletions src/reports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -128,12 +128,12 @@ macro reportdef(ex, kwargs...)
isroot($(sv)) ? st : track_abstract_call_stack!(cache_report!, $(sv).parent, st) # postwalk
end)

function strip_type_annotations(x)
isexpr(x, :escape) && return Expr(:escape, strip_type_annotations(first(x.args))) # keep escape
function strip_type_decls(x)
isexpr(x, :escape) && return Expr(:escape, strip_type_decls(first(x.args))) # keep escape
return isexpr(x, :(::)) ? first(x.args) : x
end
args′ = strip_type_annotations.(args)
spec_args′ = strip_type_annotations.(spec_args)
args′ = strip_type_decls.(args)
spec_args′ = strip_type_decls.(spec_args)
constructor_body = quote
msg = get_msg(#= T, interp, sv, ... =# $(args′...))
sig = get_sig(#= T, interp, sv, ... =# $(args′...))
Expand Down Expand Up @@ -291,16 +291,36 @@ function _get_sig_type(sv::InferenceState, expr::Expr)
return if head === :call
f = first(expr.args)
args = expr.args[2:end]
nargs = length(args)

sig = _get_sig(sv, f)
push!(sig, '(')
for (i, arg) in enumerate(args)
arg_sig = _get_sig(sv, arg)
append!(sig, arg_sig)
i nargs && push!(sig, ", ")

# special case splat call signature
if isa(f, GlobalRef) && f.name === :_apply_iterate && begin
itf = first(args)
isa(itf, GlobalRef) && itf.name === :iterate
end
f = args[2]
args = args[3:end]

sig = _get_sig(sv, f)
push!(sig, '(')

nargs = length(args)
for (i, arg) in enumerate(args)
arg_sig = _get_sig(sv, arg)
append!(sig, arg_sig)
i nargs ? push!(sig, ", ") : push!(sig, "...)")
end
else
sig = _get_sig(sv, f)
push!(sig, '(')

nargs = length(args)
for (i, arg) in enumerate(args)
arg_sig = _get_sig(sv, arg)
append!(sig, arg_sig)
i nargs && push!(sig, ", ")
end
push!(sig, ')')
end
push!(sig, ')')

sig, nothing
elseif head === :(=)
Expand Down
7 changes: 6 additions & 1 deletion test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import Core.Compiler:
import TypeProfiler:
TPInterpreter, profile_call, get_result,
virtual_process!, report_errors, get_virtual_globalvar,
ToplevelErrorReport, InferenceErrorReport
ToplevelErrorReport, InferenceErrorReport,
print_reports

for sym in Symbol.(last.(Base.Fix2(split, '.').(string.(vcat(subtypes(TypeProfiler, ToplevelErrorReport),
subtypes(TypeProfiler, InferenceErrorReport),
Expand Down Expand Up @@ -98,6 +99,10 @@ end
include("test_tfuncs.jl")
end

@testset "print" begin
include("test_print.jl")
end

@testset "is it truly necessary ?" begin
let
# constant propagation can help to exclude false positive alerts;
Expand Down
70 changes: 70 additions & 0 deletions test/test_print.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# NOTE:
# tests in this file may not be so robust against the future changes in TypeProfiler.jl or
# even those in julia itself

@testset "print toplevel errors" begin
let
io = IOBuffer()
s = """
a = begin
b =
end
"""

res, interp = profile_toplevel(s; filename = @__FILE__)
print_reports(io, res.toplevel_error_reports)
let s = String(take!(io))
@test occursin("1 toplevel error found", s)
@test occursin(Regex("@ $(@__FILE__):\\d"), s)
@test occursin("syntax: unexpected \"end\"", s)
end

res, interp = profile_toplevel(s; filename = "foo")
print_reports(io, res.toplevel_error_reports)
let s = String(take!(io))
@test occursin("1 toplevel error found", s)
@test occursin(r"@ foo:\d", s)
@test occursin("syntax: unexpected \"end\"", s)
end
end
end

# tests with Windows-paths is just an hell
@static Sys.iswindows() || begin

@testset "print inference errors" begin
let
res, interp = @profile_toplevel begin
s = "julia"
sum(s)
end

io = IOBuffer()
@test print_reports(io, res.inference_error_reports)
let s = String(take!(io))
@test occursin("2 possible errors found", s)
@test occursin(Regex("@ $(escape_string(@__FILE__)):$((@__LINE__)-7)"), s) # toplevel call signature
end
end

@testset "special case splat call signature" begin
let
vmod = gen_virtualmod()
res, interp = @profile_toplevel vmod begin
foo(args...) = sum(args)
foo(rand(Char, 1000000000)...)
end

io = IOBuffer()
postprocess = TypeProfiler.gen_postprocess(vmod, Main)
@test print_reports(io, res.inference_error_reports, postprocess)
let s = String(take!(io))
@test occursin("1 possible error found", s)
@test occursin(Regex("@ $(escape_string(@__FILE__)):$((@__LINE__)-8)"), s) # toplevel call signature
@test occursin("foo(rand(Char, 1000000000)...)", s)
end
end
end
end

end # @static Sys.iswindows ||

0 comments on commit 6f62c7a

Please sign in to comment.