Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Render diagnostics only for actual syntax error #2820

Merged
merged 3 commits into from
Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/analysis/Parse.jl
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ Make some small adjustments to the `expr` to make it work nicely inside a timed,
1. If `expr` is a `:toplevel` expression (this is the case iff the expression is a combination of expressions using semicolons, like `a = 1; b` or `123;`), then it gets turned into a `:block` expression. The reason for this transformation is that `:toplevel` does not return/relay the output of its last argument, unlike `begin`, `let`, `if`, etc. (But we want it!)
2. If `expr` is a `:module` expression, wrap it in a `:toplevel` block - module creation needs to be at toplevel. Rule 1. is not applied.
3. If `expr` is a `:(=)` expression with a curly assignment, wrap it in a `:const` to allow execution - see https://github.com/fonsp/Pluto.jl/issues/517
4. If `expr` failed to parse, it has head in (:incomplete, :error) and is replaced with a call to PlutoRunner.throw_syntax_error which will render diagnostics in a frontend compatible manner (Pluto.jl#2526).
"""
function preprocess_expr(expr::Expr)
if expr.head === :toplevel
Expand All @@ -107,7 +108,7 @@ function preprocess_expr(expr::Expr)
Expr(:toplevel, expr)
elseif expr.head === :(=) && (expr.args[1] isa Expr && expr.args[1].head == :curly)
Expr(:const, expr)
elseif expr.head === :incomplete
elseif expr.head === :incomplete || expr.head === :error
Expr(:call, :(PlutoRunner.throw_syntax_error), expr.args...)
else
expr
Expand Down
17 changes: 15 additions & 2 deletions src/runner/PlutoRunner/src/PlutoRunner.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1210,9 +1210,22 @@ function convert_parse_error_to_dict(ex)
)
end

"""
*Internal* wrapper for syntax errors which have diagnostics.
Thrown through PlutoRunner.throw_syntax_error
"""
struct PrettySyntaxError <: Exception
ex::Any
end

function throw_syntax_error(@nospecialize(syntax_err))
syntax_err isa String && (syntax_err = "syntax: $syntax_err")
syntax_err isa Exception || (syntax_err = ErrorException(syntax_err))

if has_julia_syntax && syntax_err isa Base.Meta.ParseError && syntax_err.detail isa Base.JuliaSyntax.ParseError
syntax_err = PrettySyntaxError(syntax_err)
end

throw(syntax_err)
end

Expand All @@ -1239,8 +1252,8 @@ function frame_url(frame::Base.StackTraces.StackFrame)
end

function format_output(val::CapturedException; context=default_iocontext)
if has_julia_syntax && val.ex isa Base.Meta.ParseError && val.ex.detail isa Base.JuliaSyntax.ParseError
dict = convert_parse_error_to_dict(val.ex.detail)
if has_julia_syntax && val.ex isa PrettySyntaxError
dict = convert_parse_error_to_dict(val.ex.ex.detail)
return dict, MIME"application/vnd.pluto.parseerror+object"()
end

Expand Down
10 changes: 10 additions & 0 deletions test/React.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1024,16 +1024,26 @@ import Pluto.Configuration: Options, EvaluationOptions
notebook = Notebook(Cell.([
"begin",
"\n\nend",
"throw(Meta.parse(\"begin\"; raise=false).args[end])",
]))
update_run!(🍭, notebook, notebook.cells)
@test Pluto.is_just_text(notebook.topology, notebook.cells[1])
@test Pluto.is_just_text(notebook.topology, notebook.cells[2])
@static if VERSION >= v"1.10.0-DEV.1548" # ~JuliaSyntax PR Pluto.jl#2526 julia#46372
@test notebook.cells[1].errored
@test notebook.cells[2].errored
@test notebook.cells[3].errored

@test haskey(notebook.cells[1].output.body, :source)
@test haskey(notebook.cells[1].output.body, :diagnostics)

@test haskey(notebook.cells[2].output.body, :source)
@test haskey(notebook.cells[2].output.body, :diagnostics)

# not literal syntax error
@test haskey(notebook.cells[3].output.body, :msg)
@test !haskey(notebook.cells[3].output.body, :source)
@test !haskey(notebook.cells[3].output.body, :diagnostics)
else
@test !occursinerror("(incomplete ", notebook.cells[1])
@test !occursinerror("(incomplete ", notebook.cells[2])
Expand Down
Loading