Skip to content

Commit 56c33b2

Browse files
authored
Various fixes for K"parens" nodes (#227)
* Fix `:kw` conversion in dubious constructs like `f(((a=1)))` * Fix Expr conversion for parens which contain an error * Allow more parens in import paths like `import A.:(==)`
1 parent 7482d9c commit 56c33b2

File tree

4 files changed

+47
-7
lines changed

4 files changed

+47
-7
lines changed

src/expr.jl

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,8 @@ function _to_expr(node::SyntaxNode; iteration_spec=false, need_linenodes=true,
138138
eq_to_kw_in_call =
139139
((headsym === :call || headsym === :dotcall) && is_prefix_call(node)) ||
140140
headsym === :ref
141-
eq_to_kw_all = headsym === :parameters && !map_kw_in_params
141+
eq_to_kw_all = (headsym === :parameters && !map_kw_in_params) ||
142+
(headsym === :parens && eq_to_kw)
142143
in_vcbr = headsym === :vect || headsym === :curly || headsym === :braces || headsym === :ref
143144
if insert_linenums && isempty(node_args)
144145
push!(args, source_location(LineNumberNode, node.source, node.position))
@@ -205,7 +206,15 @@ function _to_expr(node::SyntaxNode; iteration_spec=false, need_linenodes=true,
205206
reorder_parameters!(args, 2)
206207
elseif headsym === :parens
207208
# parens are used for grouping and don't appear in the Expr AST
208-
return only(args)
209+
if length(args) == 1
210+
return args[1]
211+
else
212+
# This case should only occur when there's an error inside the
213+
# parens, and we've passed ignore_errors=true to the parser.
214+
# Wrap in a block to preserve both the value and the error.
215+
@check all(Meta.isexpr(args[j], :error) for j in 2:length(args))
216+
return Expr(:block, args...)
217+
end
209218
elseif headsym in (:try, :try_finally_catch)
210219
# Try children in source order:
211220
# try_block catch_var catch_block else_block finally_block
@@ -329,9 +338,10 @@ function _to_expr(node::SyntaxNode; iteration_spec=false, need_linenodes=true,
329338
imports = args
330339
end
331340
for imp in imports
332-
for i = 1:length(imp.args)
333-
if imp.args[i] isa QuoteNode
334-
imp.args[i] = imp.args[i].value
341+
imp_path = Meta.isexpr(imp, :as) ? imp.args[1].args : imp.args
342+
for i = 1:length(imp_path)
343+
if imp_path[i] isa QuoteNode
344+
imp_path[i] = imp_path[i].value
335345
end
336346
end
337347
end

src/parser.jl

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2387,13 +2387,22 @@ function parse_atsym(ps::ParseState, allow_quotes=true)
23872387
end
23882388
parse_unary_prefix(ps)
23892389
pos = position(ps)
2390-
while peek_behind(ps, pos).kind == K"parens"
2390+
warn_parens = false
2391+
if peek_behind(ps, pos).kind == K"parens"
23912392
# import A.(:+) ==> (import (. A (parens (quote +))))
23922393
pos = first_child_position(ps, pos)
2393-
emit_diagnostic(ps, mark, warning="parentheses are not required here")
2394+
warn_parens = true
23942395
end
23952396
if allow_quotes && peek_behind(ps, pos).kind == K"quote"
23962397
pos = first_child_position(ps, pos)
2398+
if peek_behind(ps, pos).kind == K"parens"
2399+
# import A.:(+) ==> (import (. A (quote (parens +))))
2400+
pos = first_child_position(ps, pos)
2401+
warn_parens = true
2402+
end
2403+
end
2404+
if warn_parens
2405+
emit_diagnostic(ps, mark, warning="parentheses are not required here")
23972406
end
23982407
b = peek_behind(ps, pos)
23992408
ok = (b.is_leaf && (b.kind == K"Identifier" || is_operator(b.kind))) ||

test/expr.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,15 @@
230230
# dotted = is not :kw
231231
@test parse(Expr, "f(a .= 1)") ==
232232
Expr(:call, :f, Expr(:.=, :a, 1))
233+
234+
# = inside parens in calls and tuples
235+
# (TODO: we should warn for these cases.)
236+
@test parse(Expr, "f(((a = 1)))") ==
237+
Expr(:call, :f, Expr(:kw, :a, 1))
238+
@test parse(Expr, "(((a = 1)),)") ==
239+
Expr(:tuple, Expr(:(=), :a, 1))
240+
@test parse(Expr, "(;((a = 1)),)") ==
241+
Expr(:tuple, Expr(:parameters, Expr(:kw, :a, 1)))
233242
end
234243

235244
@testset "dotcall" begin
@@ -334,10 +343,21 @@
334343
@test parseall(Expr, "a b", ignore_errors=true) ==
335344
Expr(:toplevel, LineNumberNode(1), :a,
336345
LineNumberNode(1), Expr(:error, :b))
346+
@test parse(Expr, "(x", ignore_errors=true) ==
347+
Expr(:block, :x, Expr(:error))
337348
end
338349

339350
@testset "import" begin
340351
@test parse(Expr, "import A.(:b).:c: x.:z", ignore_warnings=true) ==
341352
Expr(:import, Expr(Symbol(":"), Expr(:., :A, :b, :c), Expr(:., :x, :z)))
353+
# Stupid parens and quotes in import paths
354+
@test parse(Expr, "import A.:+", ignore_warnings=true) ==
355+
Expr(:import, Expr(:., :A, :+))
356+
@test parse(Expr, "import A.(:+)", ignore_warnings=true) ==
357+
Expr(:import, Expr(:., :A, :+))
358+
@test parse(Expr, "import A.:(+)", ignore_warnings=true) ==
359+
Expr(:import, Expr(:., :A, :+))
360+
@test parse(Expr, "import A.:(+) as y", ignore_warnings=true, version=v"1.6") ==
361+
Expr(:import, Expr(:as, Expr(:., :A, :+), :y))
342362
end
343363
end

test/parser.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,7 @@ tests = [
651651
"import A.B.C" => "(import (. A B C))"
652652
"import A.:+" => "(import (. A (quote +)))"
653653
"import A.(:+)"=> "(import (. A (parens (quote +))))"
654+
"import A.:(+)" => "(import (. A (quote (parens +))))"
654655
"import A.==" => "(import (. A ==))"
655656
"import A.⋆.f" => "(import (. A ⋆ f))"
656657
"import A..." => "(import (. A ..))"

0 commit comments

Comments
 (0)