Repository: scalameta/scalameta (scala)

Issue:
scala3: fails to parse TryWithHandler if catch followed by NL
The following snippet fails parsing with scala3 dialect:
```
  try
    a
  catch
    bar
  finally
    a
```
according to https://docs.scala-lang.org/scala3/reference/syntax.html#expressions, it looks like `catch` can be followed by any expression (not just `case`) and doesn't prohibit newline.

Pull request description:
Fixes #2513.

Patch:
diff --git a/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScalametaParser.scala b/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScalametaParser.scala
index e39d00d88a..fb303aa6d6 100644
--- a/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScalametaParser.scala
+++ b/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScalametaParser.scala
@@ -2427,17 +2427,15 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
           case LeftBrace() | Indentation.Indent() => block()
           case _ => expr()
         }
+        def caseClausesOrExpr = caseClausesIfAny().getOrElse(expr())
         val catchopt =
-          if (tryAcceptWithOptLF[KwCatch]) {
-            if (token.is[CaseIntro]) { accept[KwCase]; Some(caseClause(true)) }
-            else if (token.is[Indentation.Indent]) Some(indented(caseClauses()))
-            else if (token.isNot[LeftBrace]) Some(expr())
-            else
-              inBraces {
-                if (token.is[CaseIntro] || token.is[Ellipsis]) Some(caseClauses())
-                else Some(expr())
-              }
-          } else { None }
+          if (tryAcceptWithOptLF[KwCatch]) Some {
+            if (token.is[CaseIntro]) { accept[KwCase]; caseClause(true) }
+            else if (token.is[Indentation.Indent]) indented(caseClausesOrExpr)
+            else if (token.is[LeftBrace]) inBraces(caseClausesOrExpr)
+            else expr()
+          }
+          else { None }
 
         val finallyopt =
           if (tryAcceptWithOptLF[KwFinally]) {
@@ -3268,7 +3266,10 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
     caseClause()
   }
 
-  def caseClauses(): List[Case] = {
+  def caseClauses(): List[Case] =
+    caseClausesIfAny().getOrElse(syntaxErrorExpected[KwCase])
+
+  def caseClausesIfAny(): Option[List[Case]] = {
     val cases = new ListBuffer[Case]
     while (token.is[CaseIntro] || token.is[Ellipsis]) {
       if (token.is[Ellipsis]) {
@@ -3284,9 +3285,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
       }
       if (token.is[StatSep] && ahead(token.is[CaseIntro])) acceptStatSep()
     }
-    if (cases.isEmpty) // trigger error if there are no cases
-      accept[KwCase]
-    cases.toList
+    if (cases.isEmpty) None else Some(cases.toList)
   }
 
   def guard(): Option[Term] =


Test patch:
diff --git a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/ControlSyntaxSuite.scala b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/ControlSyntaxSuite.scala
index 6e8d53ecd7..c2ad1b57ad 100644
--- a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/ControlSyntaxSuite.scala
+++ b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/ControlSyntaxSuite.scala
@@ -468,6 +468,32 @@ class ControlSyntaxSuite extends BaseDottySuite {
     )
   }
 
+  test("new-catch-handler-nl") {
+    val code = """|try
+                  |  foo
+                  |catch
+                  |  bar
+                  |""".stripMargin
+    val output = "try foo catch bar"
+    runTestAssert[Stat](code, assertLayout = Some(output))(
+      Term.TryWithHandler(Term.Name("foo"), Term.Name("bar"), None)
+    )
+  }
+
+  test("new-catch-handler-finally-nl") {
+    val code = """|try
+                  |  foo
+                  |catch
+                  |  bar
+                  |finally
+                  |  baz
+                  |""".stripMargin
+    val output = "try foo catch bar finally baz"
+    runTestAssert[Stat](code, assertLayout = Some(output))(
+      Term.TryWithHandler(Term.Name("foo"), Term.Name("bar"), Some(Term.Name("baz")))
+    )
+  }
+
   test("new-catch-finally-single") {
     val code = """|try fx
                   |catch case x =>