Skip to content

Commit cde579f

Browse files
committed
Add parens meta to stab operator and empty block
1 parent 01474e0 commit cde579f

File tree

2 files changed

+111
-9
lines changed

2 files changed

+111
-9
lines changed

lib/elixir/src/elixir_parser.yrl

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ access_expr -> fn_eoe stab_eoe 'end' : build_fn('$1', '$2', '$3').
267267
access_expr -> open_paren stab_eoe ')' : build_paren_stab('$1', '$2', '$3').
268268
access_expr -> open_paren ';' stab_eoe ')' : build_paren_stab('$1', '$3', '$4').
269269
access_expr -> open_paren ';' close_paren : build_paren_stab('$1', [], '$3').
270-
access_expr -> empty_paren : warn_empty_paren('$1'), {'__block__', [], []}.
270+
access_expr -> empty_paren : warn_empty_paren('$1'), {'__block__', parens_meta('$1'), []}.
271271
access_expr -> int : handle_number(number_value('$1'), '$1', ?exprs('$1')).
272272
access_expr -> flt : handle_number(number_value('$1'), '$1', ?exprs('$1')).
273273
access_expr -> char : handle_number(?exprs('$1'), '$1', number_value('$1')).
@@ -342,15 +342,15 @@ stab_expr -> expr :
342342
stab_expr -> stab_op_eol_and_expr :
343343
build_op([], '$1').
344344
stab_expr -> empty_paren stab_op_eol_and_expr :
345-
build_op([], '$2').
345+
build_op_with_meta([], '$2', parens_meta('$1')).
346346
stab_expr -> empty_paren when_op expr stab_op_eol_and_expr :
347-
build_op([{'when', meta_from_token('$2'), ['$3']}], '$4').
347+
build_op_with_meta([{'when', meta_from_token('$2'), ['$3']}], '$4', parens_meta('$1')).
348348
stab_expr -> call_args_no_parens_all stab_op_eol_and_expr :
349349
build_op(unwrap_when(unwrap_splice('$1')), '$2').
350350
stab_expr -> stab_parens_many stab_op_eol_and_expr :
351-
build_op(unwrap_splice('$1'), '$2').
351+
build_op_with_meta(unwrap_splice(element(2, '$1')), '$2', parens_meta('$1')).
352352
stab_expr -> stab_parens_many when_op expr stab_op_eol_and_expr :
353-
build_op([{'when', meta_from_token('$2'), unwrap_splice('$1') ++ ['$3']}], '$4').
353+
build_op_with_meta([{'when', meta_from_token('$2'), unwrap_splice(element(2, '$1')) ++ ['$3']}], '$4', parens_meta('$1')).
354354

355355
stab_op_eol_and_expr -> stab_op_eol expr : {'$1', '$2'}.
356356
stab_op_eol_and_expr -> stab_op_eol : warn_empty_stab_clause('$1'), {'$1', handle_literal(nil, '$1')}.
@@ -370,7 +370,7 @@ open_paren -> '(' eol : next_is_eol('$1', '$2').
370370
close_paren -> ')' : '$1'.
371371
close_paren -> eol ')' : '$2'.
372372

373-
empty_paren -> open_paren ')' : '$1'.
373+
empty_paren -> open_paren ')' : {'$1', '$2'}.
374374

375375
open_bracket -> '[' : '$1'.
376376
open_bracket -> '[' eol : next_is_eol('$1', '$2').
@@ -515,8 +515,8 @@ call_args_no_parens_many_strict -> call_args_no_parens_many : '$1'.
515515
call_args_no_parens_many_strict -> open_paren call_args_no_parens_kw close_paren : error_no_parens_strict('$1').
516516
call_args_no_parens_many_strict -> open_paren call_args_no_parens_many close_paren : error_no_parens_strict('$1').
517517

518-
stab_parens_many -> open_paren call_args_no_parens_kw close_paren : ['$2'].
519-
stab_parens_many -> open_paren call_args_no_parens_many close_paren : '$2'.
518+
stab_parens_many -> open_paren call_args_no_parens_kw close_paren : {'$1', ['$2'], '$3'}.
519+
stab_parens_many -> open_paren call_args_no_parens_many close_paren : {'$1', '$2', '$3'}.
520520

521521
% Containers
522522

@@ -719,6 +719,10 @@ number_value({_, {_, _, Value}, _}) ->
719719

720720
%% Operators
721721

722+
build_op_with_meta(Left, {Op, Right}, Meta) ->
723+
{Op1, OpMeta, Args} = build_op(Left, Op, Right),
724+
{Op1, Meta ++ OpMeta, Args}.
725+
722726
build_op(Left, {Op, Right}) ->
723727
build_op(Left, Op, Right).
724728

@@ -1139,6 +1143,18 @@ unwrap_when(Args) ->
11391143
Args
11401144
end.
11411145

1146+
parens_meta({Open, Close}) ->
1147+
case ?token_metadata() of
1148+
true ->
1149+
ParensEntry = meta_from_token(Open) ++ [{closing, meta_from_token(Close)}],
1150+
[{parens, [ParensEntry]}];
1151+
false ->
1152+
[]
1153+
end;
1154+
parens_meta({Open, _Args, Close}) ->
1155+
parens_meta({Open, Close}).
1156+
1157+
11421158
%% Warnings and errors
11431159

11441160
return_error({Line, Column, _}, ErrorMessage, ErrorToken) ->
@@ -1290,7 +1306,7 @@ warn_nested_no_parens_keyword(Key, Value) when is_atom(Key) ->
12901306
warn_nested_no_parens_keyword(_Key, _Value) ->
12911307
ok.
12921308

1293-
warn_empty_paren({_, {Line, Column, _}}) ->
1309+
warn_empty_paren({{_, {Line, Column, _}}, _}) ->
12941310
warn(
12951311
{Line, Column},
12961312
"invalid expression (). "

lib/elixir/test/elixir/kernel/parser_test.exs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,92 @@ defmodule Kernel.ParserTest do
681681
]}
682682
end
683683

684+
test "adds opening and closing information for empty block" do
685+
string_to_quoted =
686+
&Code.string_to_quoted!(&1, token_metadata: true, columns: true, emit_warnings: false)
687+
688+
file = "()"
689+
690+
assert string_to_quoted.(file) ==
691+
{:__block__, [parens: [[line: 1, column: 1, closing: [line: 1, column: 2]]]], []}
692+
693+
file = "(())"
694+
695+
assert string_to_quoted.(file) ==
696+
{:__block__,
697+
[
698+
parens: [
699+
[line: 1, column: 1, closing: [line: 1, column: 4]],
700+
[line: 1, column: 2, closing: [line: 1, column: 3]]
701+
]
702+
], []}
703+
end
704+
705+
test "adds opening and closing information for stab arguments" do
706+
file = "fn () -> x end "
707+
708+
assert Code.string_to_quoted!(file, token_metadata: true, columns: true) ==
709+
{:fn, [closing: [line: 1, column: 12], line: 1, column: 1],
710+
[
711+
{:->,
712+
[
713+
parens: [[line: 1, column: 4, closing: [line: 1, column: 5]]],
714+
line: 1,
715+
column: 7
716+
], [[], {:x, [line: 1, column: 10], nil}]}
717+
]}
718+
719+
file = "fn (x, y) -> x end "
720+
721+
assert Code.string_to_quoted!(file, token_metadata: true, columns: true) ==
722+
{
723+
:fn,
724+
[{:closing, [line: 1, column: 16]}, {:line, 1}, {:column, 1}],
725+
[
726+
{:->,
727+
[
728+
parens: [[line: 1, column: 4, closing: [line: 1, column: 9]]],
729+
line: 1,
730+
column: 11
731+
],
732+
[
733+
[{:x, [line: 1, column: 5], nil}, {:y, [line: 1, column: 8], nil}],
734+
{:x, [line: 1, column: 14], nil}
735+
]}
736+
]
737+
}
738+
739+
file = "if true do (x, y) -> x end"
740+
741+
assert Code.string_to_quoted!(file, token_metadata: true, columns: true) ==
742+
{
743+
:if,
744+
[
745+
{:do, [line: 1, column: 9]},
746+
{:end, [line: 1, column: 24]},
747+
{:line, 1},
748+
{:column, 1}
749+
],
750+
[
751+
true,
752+
[
753+
do: [
754+
{:->,
755+
[
756+
parens: [[line: 1, column: 12, closing: [line: 1, column: 17]]],
757+
line: 1,
758+
column: 19
759+
],
760+
[
761+
[{:x, [line: 1, column: 13], nil}, {:y, [line: 1, column: 16], nil}],
762+
{:x, [line: 1, column: 22], nil}
763+
]}
764+
]
765+
]
766+
]
767+
}
768+
end
769+
684770
test "with :literal_encoder" do
685771
opts = [literal_encoder: &{:ok, {:__block__, &2, [&1]}}, token_metadata: true]
686772
string_to_quoted = &Code.string_to_quoted!(&1, opts)

0 commit comments

Comments
 (0)