Skip to content

Commit 662426c

Browse files
committed
handle stab clauses without right-hand-sides
Currently a stab clause without a right-hand-side is parsed as an error: ```elixir Enum.map(xs, fn x -> end) ``` And the `end` token ends up not being highlighted as a keyword. The compiler gives a warning about this syntax but it comes up pretty often when editing (writing a `case` block for example). Implementation-wise, this might be a bug in tree-sitter? `prec.right` seems to fight with error recovery when the rightmost token(s) are `optional`. ```elixir fn -> end ``` gets parsed as ```scm (anonymous_function (stab_clause right: (body (identifier))) (MISSING "end")) ``` although the `optional` should allow this case. I've seen this in other grammars and it seems like the way around it is to replace the `prec.right` with a conflict.
1 parent c68c4ad commit 662426c

File tree

3 files changed

+52
-7
lines changed

3 files changed

+52
-7
lines changed

grammar.js

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,16 @@ module.exports = grammar({
159159
// * stab arguments item in `arg1, left when right ->`
160160
[$.binary_operator, $._stab_clause_arguments_without_parentheses],
161161

162+
// Given `( -> • \n`, the newline could be either:
163+
// * stab clause without a body
164+
// * stab clause with a body
165+
[$._stab_clause_without_body, $._stab_clause_with_body],
166+
167+
// Given `( -> • /`, `/` token could be either:
168+
// * stab clause with a body
169+
// * -> as an operator followed by `/`
170+
[$._stab_clause_with_body, $.operator_identifier],
171+
162172
// Given `((arg1, arg2 • ,`, `arg3` expression can be either:
163173
// * stab parenthesised arguments item in `((arg1, arg2, arg3) ->)`
164174
// * stab non-parenthesised arguments item in `((arg1, arg2, arg3 ->))`
@@ -729,13 +739,20 @@ module.exports = grammar({
729739
),
730740

731741
stab_clause: ($) =>
732-
// Right precedence, because we want to consume body if any
733-
prec.right(
734-
seq(
735-
optional(field("left", $._stab_clause_left)),
736-
field("operator", "->"),
737-
optional(field("right", $.body))
738-
)
742+
choice($._stab_clause_with_body, $._stab_clause_without_body),
743+
744+
_stab_clause_with_body: ($) =>
745+
seq(
746+
optional(field("left", $._stab_clause_left)),
747+
field("operator", "->"),
748+
field("right", $.body)
749+
),
750+
751+
_stab_clause_without_body: ($) =>
752+
seq(
753+
optional(field("left", $._stab_clause_left)),
754+
field("operator", "->"),
755+
optional($._terminator)
739756
),
740757

741758
_stab_clause_left: ($) =>

test/corpus/do_end.txt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -683,6 +683,28 @@ end
683683
(body
684684
(integer))))))
685685

686+
=====================================
687+
stab clause / edge cases / empty right-hand-sides
688+
=====================================
689+
690+
fun do
691+
x ->
692+
y ->
693+
end
694+
695+
---
696+
697+
(source
698+
(call
699+
(identifier)
700+
(do_block
701+
(stab_clause
702+
(arguments
703+
(identifier)))
704+
(stab_clause
705+
(arguments
706+
(identifier))))))
707+
686708
=====================================
687709
pattern matching
688710
=====================================

test/highlight/anonymous.ex

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ fn x, y, z ->
4141
end
4242
end
4343

44+
fn ->
45+
# <- keyword
46+
# ^ operator
47+
end
48+
# <- keyword
49+
4450
&Set.put(&1, &2)
4551
# <- operator
4652
# ^ module

0 commit comments

Comments
 (0)