From c1333cec8bad56e0b6a59daf36bc40278ed03a2a Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Mon, 17 Feb 2020 07:17:33 -0600 Subject: [PATCH] Improve line-number detection in parse errors (#424) Fixes #421 --- src/parsing.jl | 9 ++++---- test/runtests.jl | 59 ++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 58 insertions(+), 10 deletions(-) diff --git a/src/parsing.jl b/src/parsing.jl index c37adcee..fe842e08 100644 --- a/src/parsing.jl +++ b/src/parsing.jl @@ -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 @@ -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 diff --git a/test/runtests.jl b/test/runtests.jl index 80ceb340..3cad1a9d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -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 @@ -1498,6 +1502,10 @@ 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 @@ -1505,19 +1513,19 @@ end 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 @@ -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 @@ -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 """)