Skip to content

Commit

Permalink
process invoked error location signature (#193)
Browse files Browse the repository at this point in the history
Before `@report_call rand(String)`:
```julia
┌ @ /Users/aviatesk/julia/julia/usr/share/julia/stdlib/v1.7/Random/src/Random.jl:259 Random.rand(Random.default_rng(), _)
│┌ @ /Users/aviatesk/julia/julia/usr/share/julia/stdlib/v1.7/Random/src/Random.jl:256 Random.rand(rng, Random.Sampler(rng, _, Random.Val(1)))
││┌ @ /Users/aviatesk/julia/julia/usr/share/julia/stdlib/v1.7/Random/src/Random.jl:253 Random.Sampler(rng, X, Random.Val(1))
│││┌ @ /Users/aviatesk/julia/julia/usr/share/julia/stdlib/v1.7/Random/src/Random.jl:139 Random.Sampler(Random.typeof_rng(rng), x, r)
││││┌ @ /Users/aviatesk/julia/julia/usr/share/julia/stdlib/v1.7/Random/src/Random.jl:145 Random.Sampler(::Type{Random.MersenneTwister}, ::Random.SamplerType{String}, ::Val{1})
│││││ may throw: Random.throw($(Expr(:invoke, MethodInstance for ArgumentError(::String), :(Random.ArgumentError), "Sampler for this object is not defined"))::ArgumentError)
││││└────────────────────────────────────────────────────────────────────────────────────
Union{}
```

After `@report_call rand(String)`:
```julia
═════ 1 possible error found ═════
┌ @ /Users/aviatesk/julia/julia/usr/share/julia/stdlib/v1.7/Random/src/Random.jl:259 Random.rand(Random.default_rng(), _)
│┌ @ /Users/aviatesk/julia/julia/usr/share/julia/stdlib/v1.7/Random/src/Random.jl:256 Random.rand(rng, Random.Sampler(rng, _, Random.Val(1)))
││┌ @ /Users/aviatesk/julia/julia/usr/share/julia/stdlib/v1.7/Random/src/Random.jl:253 Random.Sampler(rng, X, Random.Val(1))
│││┌ @ /Users/aviatesk/julia/julia/usr/share/julia/stdlib/v1.7/Random/src/Random.jl:139 Random.Sampler(Random.typeof_rng(rng), x, r)
││││┌ @ /Users/aviatesk/julia/julia/usr/share/julia/stdlib/v1.7/Random/src/Random.jl:145 Random.Sampler(::Type{Random.MersenneTwister}, ::Random.SamplerType{String}, ::Val{1})
│││││ may throw: Random.throw(Random.ArgumentError("Sampler for this object is not defined")::ArgumentError)
││││└────────────────────────────────────────────────────────────────────────────────────
Union{}
```
  • Loading branch information
aviatesk authored May 7, 2021
1 parent 46545da commit 5c57f32
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 22 deletions.
1 change: 1 addition & 0 deletions src/JET.jl
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ import Core:
Const,
SSAValue,
SlotNumber,
Argument,
Slot,
ReturnNode,
SimpleVector,
Expand Down
55 changes: 33 additions & 22 deletions src/reports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,26 @@ get_sig(interp, sv::InferenceState, @nospecialize(x = get_stmt(sv))) = _get_sig(

_get_sig(args...) = first(_get_sig_type(args...))::Vector{Any}

function _get_callsig(interp, sv::InferenceState, @nospecialize(f), args::Vector{Any};
splat::Bool = false)
sig = _get_sig(interp, sv, f)
push!(sig, '(')

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

return sig
end

function _get_sig_type(interp, sv::InferenceState, expr::Expr)
head = expr.head
if head === :call
Expand All @@ -273,30 +293,14 @@ function _get_sig_type(interp, sv::InferenceState, expr::Expr)
end
f = args[2]
args = args[3:end]

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

nargs = length(args)
for (i, arg) in enumerate(args)
arg_sig = _get_sig(interp, sv, arg)
append!(sig, arg_sig)
i nargs ? push!(sig, ", ") : push!(sig, "...)")
end
return _get_callsig(interp, sv, f, args; splat = true), nothing
else
sig = _get_sig(interp, sv, f)
push!(sig, '(')

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

return sig, nothing
elseif head === :invoke
f = expr.args[2]
args = expr.args[3:end]
return _get_callsig(interp, sv, f, args), nothing
elseif head === :(=)
return _get_sig_type(interp, sv, last(expr.args))
elseif head === :static_parameter
Expand Down Expand Up @@ -326,6 +330,13 @@ function _get_sig_type(interp, sv::InferenceState, slot::SlotNumber)
return Any[sig, typ], typ
end
end
function _get_sig_type(interp, sv::InferenceState, arg::Argument)
name = get_slotname(sv, arg.n)
sig = string(name)
# NOTE top-level frame isn't optimized and so we don't need to handle abstract global variable here
typ = widenconst(ignorelimited(get_slottype(sv, arg.n)))
return Any[sig, typ], typ
end
_get_sig_type(interp, ::InferenceState, gr::GlobalRef) = Any[string(gr.mod, '.', gr.name)], nothing
function _get_sig_type(interp, sv::InferenceState, s::Symbol)
if istoplevel(interp, sv)
Expand Down
4 changes: 4 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ include("setup.jl")
include("test_jetcache.jl")
end

@testset "reports.jl" begin
include("test_reports.jl")
end

# tests with Windows-paths is just an hell
@static Sys.iswindows() || @testset "print.jl" begin
include("test_print.jl")
Expand Down
40 changes: 40 additions & 0 deletions test/test_reports.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# similar to ⊆, but respect the sequence, ignore char vs. string difference
function (a, b)
normalize(@nospecialize(x)) = x
normalize(c::AbstractChar) = string(c)
a = normalize.(a)
b = normalize.(b)

n = length(a)
i = 1
for bi in b
if a[i] == bi
i += 1
else
i = 1
end
i == n && return true
end
return false
end

@testset "error location signature" begin
interp, frame = analyze_call((Char,Char)) do a, b
a + b
end
@test length(interp.reports) == 1
r = first(interp.reports)
@test isa(r, NoMethodErrorReport)
@test Any['(', 'a', Char, ", ", 'b', Char, ')'] r.sig
end

@testset ":invoke signature" begin
m = @fixturedef begin
foo(s::AbstractString) = throw(ArgumentError(s))
end
interp, frame = analyze_call(m.foo, (String,))
@test length(interp.reports) == 1
r = first(interp.reports)
@test isa(r, UncaughtExceptionReport)
@test Any['(', 's', String, ')', ArgumentError] r.sig
end

0 comments on commit 5c57f32

Please sign in to comment.