Skip to content

Commit

Permalink
Improve line-number detection in parse errors (#424)
Browse files Browse the repository at this point in the history
Fixes #421
  • Loading branch information
timholy authored Feb 17, 2020
1 parent edc1c97 commit c1333ce
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 10 deletions.
9 changes: 4 additions & 5 deletions src/parsing.jl
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ function parse_source!(mod_exprs_sigs::ModuleExprsSigs, src::AbstractString, fil
ex = Base.parse_input_line(src; filename=filename)
ex === nothing && return mod_exprs_sigs
if isexpr(ex, :error) || isexpr(ex, :incomplete)
prevex, pos = last_good_position(src)
prevex, pos = first_bad_position(src)
ln = count(isequal('\n'), SubString(src, 1, pos)) + 1
throw(LoadError(filename, ln, ex.args[1]))
end
Expand Down Expand Up @@ -69,12 +69,11 @@ function parse_source!(mod_exprs_sigs::ModuleExprsSigs, src::AbstractString, fil
return mod_exprs_sigs
end

function last_good_position(str)
function first_bad_position(str)
ex, pos, n = nothing, 1, length(str)
while pos < n
try
ex, pos = Meta.parse(str, pos; greedy=false)
catch
ex, pos = Meta.parse(str, pos; greedy=true, raise=false)
if isexpr(ex, :error) || isexpr(ex, :incomplete)
return ex, pos
end
end
Expand Down
59 changes: 54 additions & 5 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1487,6 +1487,10 @@ end
println(io, """
module RevisionErrors
f(x) = 1
struct Vec{N, T <: Union{Float32,Float64}}
data::NTuple{N, T}
end
g(x) = 1
end
""")
end
Expand All @@ -1498,26 +1502,30 @@ end
println(io, """
module RevisionErrors
f{x) = 2
struct Vec{N, T <: Union{Float32,Float64}}
data::NTuple{N, T}
end
g(x) = 1
end
""")
end
logs, _ = Test.collect_test_logs() do
yry()
end

function check_revision_error(rec)
function check_revision_error(rec, msg, line)
@test rec.message == "Failed to revise $fn"
exc, bt = rec.kwargs[:exception]
@test exc isa LoadError
@test exc.file == fn
@test exc.line == 2
@test occursin("missing comma or }", exc.error)
@test exc.line == line
@test occursin(msg, exc.error)
st = stacktrace(bt)
@test length(st) == 1
end

# test errors are reported the the first time
check_revision_error(logs[1])
check_revision_error(logs[1], "missing comma or }", 2)
logs, _ = Test.collect_test_logs() do
yry()
end
Expand All @@ -1535,12 +1543,16 @@ end
logs,_ = Test.collect_test_logs() do
Revise.errors()
end
check_revision_error(logs[1])
check_revision_error(logs[1], "missing comma or }", 2)

open(joinpath(dn, "RevisionErrors.jl"), "w") do io
println(io, """
module RevisionErrors
f(x) = 2
struct Vec{N, T <: Union{Float32,Float64}}
data::NTuple{N, T}
end
g(x) = 1
end
""")
end
Expand All @@ -1550,11 +1562,48 @@ end
@test isempty(logs)
@test RevisionErrors.f(0) == 2

# issue #421
open(joinpath(dn, "RevisionErrors.jl"), "w") do io
println(io, """
module RevisionErrors
f(x) = 2
struct Vec{N, T <: Union{Float32,Float64}}
data::NTuple{N, T}
end
function g(x) = 1
end
""")
end
logs, _ = Test.collect_test_logs() do
yry()
end
check_revision_error(logs[1], "unexpected \"=\"", 6)

open(joinpath(dn, "RevisionErrors.jl"), "w") do io
println(io, """
module RevisionErrors
f(x) = 2
struct Vec{N, T <: Union{Float32,Float64}}
data::NTuple{N, T}
end
g(x) = 1
end
""")
end
logs, _ = Test.collect_test_logs() do
yry()
end
@test isempty(logs)

# Also test that it ends up being reported to the user (issue #281)
open(joinpath(dn, "RevisionErrors.jl"), "w") do io
println(io, """
module RevisionErrors
f(x) = 2
struct Vec{N, T <: Union{Float32,Float64}}
data::NTuple{N, T}
end
g(x) = 1
foo(::Vector{T}) = 3
end
""")
Expand Down

0 comments on commit c1333ce

Please sign in to comment.