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

Reference parser compat: Make toplevel error Exprs more compatible #280

Merged
merged 1 commit into from
May 13, 2023
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
35 changes: 33 additions & 2 deletions src/hooks.jl
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,18 @@ function core_parser_hook(code, filename, offset, options)
core_parser_hook(code, filename, 1, offset, options)
end

function _has_nested_error(ex)
if ex isa Expr
if ex.head == :error
return true
else
return any(_has_nested_error(e) for e in ex.args)
end
else
return false
end
end

# Debug log file for dumping parsed code
const _debug_log = Ref{Union{Nothing,IO}}(nothing)

Expand Down Expand Up @@ -166,7 +178,8 @@ function _core_parser_hook(code, filename::String, lineno::Int, offset::Int, opt

if any_error(stream)
tree = build_tree(SyntaxNode, stream,
wrap_toplevel_as_kind=K"None", first_line=lineno)
wrap_toplevel_as_kind=K"None", first_line=lineno,
filename=filename)
tag = _incomplete_tag(tree, lastindex(code))
if tag !== :none
# Here we replicate the particular messages
Expand All @@ -184,7 +197,25 @@ function _core_parser_hook(code, filename::String, lineno::Int, offset::Int, opt
# we can do fancy error reporting instead.
error_ex = Expr(:error, ParseError(stream, filename=filename, first_line=lineno))
end
ex = options === :all ? Expr(:toplevel, error_ex) : error_ex
ex = if options === :all
# When encountering a toplevel error, the reference parser
# * truncates the top level expression arg list before that error
# * includes the last line number
# * appends the error message
topex = Expr(tree)
@assert topex.head == :toplevel
i = findfirst(_has_nested_error, topex.args)
if i > 1 && topex.args[i-1] isa LineNumberNode
i -= 1
end
resize!(topex.args, i-1)
_,errort = _first_error(tree)
push!(topex.args, LineNumberNode(source_line(errort), filename))
push!(topex.args, error_ex)
topex
else
error_ex
end
else
# TODO: Figure out a way to show warnings. Meta.parse() has no API
# to communicate this, and we also can't show them to stdout as
Expand Down
13 changes: 13 additions & 0 deletions test/hooks.jl
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,19 @@
@test err.source.first_line == 2
end

@testset "toplevel errors" begin
ex = JuliaSyntax._core_parser_hook("a\nb\n[x,\ny)", "somefile", 1, 0, :all)[1]
@test ex.head == :toplevel
@test ex.args[1:5] == [
LineNumberNode(1, "somefile"),
:a,
LineNumberNode(2, "somefile"),
:b,
LineNumberNode(4, "somefile"),
]
@test Meta.isexpr(ex.args[6], :error)
end

@testset "enable_in_core!" begin
JuliaSyntax.enable_in_core!()

Expand Down