Skip to content

Commit 3922789

Browse files
Add parsing of match sub cases
1 parent 17ca7fc commit 3922789

File tree

2 files changed

+68
-12
lines changed

2 files changed

+68
-12
lines changed

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2592,6 +2592,13 @@ object Parsers {
25922592
Match(t, inBracesOrIndented(caseClauses(() => caseClause())))
25932593
}
25942594

2595+
/** SubMatchClause ::= `match' `{' CaseClauses `}'
2596+
*/
2597+
def subMatchClause(t: Tree): SubMatch = atSpan(startOffset(t), accept(MATCH)):
2598+
val cases = inBracesOrIndented(caseClauses(() => caseClause()))
2599+
if !in.isNestedEnd then acceptStatSep() // else is sub sub match
2600+
SubMatch(t, cases)
2601+
25952602
/** `match' <<< TypeCaseClauses >>>
25962603
*/
25972604
def matchType(t: Tree): MatchTypeTree =
@@ -3092,24 +3099,27 @@ object Parsers {
30923099
buf.toList
30933100
}
30943101

3095-
/** CaseClause ::= ‘case’ Pattern [Guard] `=>' Block
3096-
* ExprCaseClause ::= ‘case’ Pattern [Guard] ‘=>’ Expr
3102+
/** CaseClause ::= ‘case’ Pattern [Guard] (‘with’ SimpleExpr SubMatchClause | `=>' Block)
3103+
* ExprCaseClause ::= ‘case’ Pattern [Guard] (‘with’ SimpleExpr SubMatchClause | `=>' Expr)
30973104
*/
30983105
def caseClause(exprOnly: Boolean = false): CaseDef = atSpan(in.offset) {
30993106
val (pat, grd) = inSepRegion(InCase) {
31003107
accept(CASE)
31013108
(withinMatchPattern(pattern()), guard())
31023109
}
3103-
CaseDef(pat, grd, atSpan(accept(ARROW)) {
3104-
if exprOnly then
3105-
if in.indentSyntax && in.isAfterLineEnd && in.token != INDENT then
3106-
warning(em"""Misleading indentation: this expression forms part of the preceding catch case.
3107-
|If this is intended, it should be indented for clarity.
3108-
|Otherwise, if the handler is intended to be empty, use a multi-line catch with
3109-
|an indented case.""")
3110-
expr()
3111-
else block()
3112-
})
3110+
val body =
3111+
if in.token == WITH && in.featureEnabled(Feature.matchWithSubCases) then atSpan(in.skipToken()):
3112+
subMatchClause(simpleExpr(Location.ElseWhere))
3113+
else atSpan(accept(ARROW)):
3114+
if exprOnly then
3115+
if in.indentSyntax && in.isAfterLineEnd && in.token != INDENT then
3116+
warning(em"""Misleading indentation: this expression forms part of the preceding catch case.
3117+
|If this is intended, it should be indented for clarity.
3118+
|Otherwise, if the handler is intended to be empty, use a multi-line catch with
3119+
|an indented case.""")
3120+
expr()
3121+
else block()
3122+
CaseDef(pat, grd, body)
31133123
}
31143124

31153125
/** TypeCaseClause ::= ‘case’ (InfixType | ‘_’) ‘=>’ Type [semi]

tests/run/sub-cases.scala

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import scala.language.experimental.matchWithSubCases
2+
3+
enum E:
4+
case A(e: E)
5+
case B(e: E)
6+
case C
7+
8+
def f: Option[E] = this match
9+
case A(e) => Some(e)
10+
case B(e) => Some(e)
11+
case C => None
12+
13+
end E
14+
import E.*
15+
16+
17+
@main def Test =
18+
19+
def test(e: E): Int = e match
20+
case A(B(e1)) if true with e1.f match
21+
case Some(x) with x match
22+
case A(_) => 11
23+
case C => 12
24+
case B(A(e1)) with e1.f match
25+
case Some(C) => 21
26+
case None => 22
27+
case _ => 3
28+
end test
29+
30+
def check(e: E, r: Int): Unit = assert(test(e) == r)
31+
32+
check(A(A(C)), 3)
33+
34+
val x1 = B(A(C))
35+
check(A(B(x1)), 11)
36+
37+
val x2 = B(C)
38+
check(A(B(x2)), 12)
39+
40+
check(A(B(C)), 3)
41+
check(A(B(B(B(C)))), 3)
42+
43+
check(B(A(x2)), 21)
44+
check(B(A(C)), 22)
45+
46+
check(A(A(C)), 3)

0 commit comments

Comments
 (0)