diff --git a/src/toplevel/virtualprocess.jl b/src/toplevel/virtualprocess.jl index 2165d26d7..eb450b7ad 100644 --- a/src/toplevel/virtualprocess.jl +++ b/src/toplevel/virtualprocess.jl @@ -21,18 +21,19 @@ function Base.getproperty(er::ToplevelErrorReport, sym::Symbol) end end -struct SyntaxErrorReport <: ToplevelErrorReport - err::JuliaSyntax.ParseError +struct ParseErrorReport <: ToplevelErrorReport + diagnostic::JuliaSyntax.Diagnostic + source::JuliaSyntax.SourceFile file::String line::Int - function SyntaxErrorReport(err::JuliaSyntax.ParseError) - lnn = JuliaSyntax.source_location(LineNumberNode, err.source, - JuliaSyntax.first_byte(first(err.diagnostics))) - return new(err, String(lnn.file::Symbol), lnn.line) + function ParseErrorReport(diagnostic::JuliaSyntax.Diagnostic, source::JuliaSyntax.SourceFile) + line = JuliaSyntax.source_line(source, JuliaSyntax.first_byte(diagnostic)) + return new(diagnostic, source, source.filename::String, line) end end # don't show stacktrace for syntax errors -print_report(io::IO, report::SyntaxErrorReport) = showerror(io, report.err) +print_report(io::IO, report::ParseErrorReport) = + JuliaSyntax.show_diagnostic(io, report.diagnostic, report.source) # TODO Use JuliaLowering.jl here struct LoweringErrorReport <: ToplevelErrorReport @@ -586,19 +587,17 @@ function _virtual_process!(res::VirtualProcessResult, end s = String(s)::String - parsed = try - JuliaSyntax.parseall(Expr, s; filename) - catch err - err isa JuliaSyntax.ParseError || rethrow(err) - err - end - - if parsed isa JuliaSyntax.ParseError - # if there's any syntax error, try to identify all the syntax error location - push!(res.toplevel_error_reports, SyntaxErrorReport(parsed)) - else + stream = JuliaSyntax.ParseStream(s) + JuliaSyntax.parse!(stream; rule=:all) + if isempty(stream.diagnostics) + parsed = JuliaSyntax.build_tree(Expr, stream; filename) @assert isexpr(parsed, :toplevel) _virtual_process!(res, parsed, filename, analyzer, config, context, pkg_mod_depth) + else + source = JuliaSyntax.SourceFile(stream; filename) + for diagnostic in stream.diagnostics + push!(res.toplevel_error_reports, ParseErrorReport(diagnostic, source)) + end end with_toplevel_logger(config) do @nospecialize(io) diff --git a/test/toplevel/test_virtualprocess.jl b/test/toplevel/test_virtualprocess.jl index e45ca2785..305c4c406 100644 --- a/test/toplevel/test_virtualprocess.jl +++ b/test/toplevel/test_virtualprocess.jl @@ -13,9 +13,27 @@ include("../setup.jl") res = report_text(s) report = only(res.res.toplevel_error_reports) - @test report isa SyntaxErrorReport + @test report isa ParseErrorReport @test report.line == 5 end + + # report multiple syntax errors if exist + let s = """ + function f(W,X,Y) + s = 0 + for i = 1:10 + s += g(W[i]*f(X[end-1] + Y[end÷2+]), + W[i+1]*f(X[end-2] + Y[end÷2]) +, + W[i+2]*f(X[end-3] + Y[end÷2-3])) + end + return s + end + """ |> strip + + res = report_text(s) + @test length(res.res.toplevel_error_reports) == 2 + @test all(r -> r isa ParseErrorReport, res.res.toplevel_error_reports) + end end @testset "virtualize module context" begin diff --git a/test/ui/test_print.jl b/test/ui/test_print.jl index a4b00a6e2..bf1360b41 100644 --- a/test/ui/test_print.jl +++ b/test/ui/test_print.jl @@ -19,17 +19,19 @@ end res = report_text(src, @__FILE__) print_reports(io, res.res.toplevel_error_reports) let s = String(take!(io)) - @test occursin("1 toplevel error found", s) + @test occursin("2 toplevel errors found", s) @test occursin(Regex("@ $(@__FILE__):\\d"), s) @test occursin("invalid identifier", s) + @test occursin("Expected `end`", s) end res = report_text(src, "foo") print_reports(io, res.res.toplevel_error_reports) let s = String(take!(io)) - @test occursin("1 toplevel error found", s) + @test occursin("2 toplevel errors found", s) @test occursin(r"@ foo:\d", s) @test occursin("invalid identifier", s) + @test occursin("Expected `end`", s) end end end