Repository: scalameta/scalameta (scala)

Issue:
3.8.0 breaks code when the next line starts with a parenthesis
## Configuration (required) ##

```
version = 3.8.0

runner.dialect = "scala3"
align.preset = more
docstrings.wrap = no
maxColumn = 120
```

## Command-line parameters (required) ##

When I run scalafmt via CLI like this: `scalafmt .`

## Steps

Given code like this:
```scala
val srcRect = stackalloc[SDL_Rect]()
(!srcRect).x = cx
(!srcRect).y = cy
(!srcRect).w = cw
(!srcRect).h = ch
val dstRect = stackalloc[SDL_Rect]()
(!dstRect).x = x
(!dstRect).y = y
(!dstRect).w = cw
(!dstRect).h = ch
```

## Problem

Scalafmt formats code like this:
```scala
val srcRect = stackalloc[SDL_Rect]()(!srcRect).x = cx(!srcRect).y = cy(!srcRect).w = cw(!srcRect).h = ch
val dstRect = stackalloc[SDL_Rect]()(!dstRect).x = x(!dstRect).y = y(!dstRect).w = cw(!dstRect).h = ch
```

## Expectation

I would like the formatted output to look like this:
```scala
val srcRect = stackalloc[SDL_Rect]()
(!srcRect).x = cx
(!srcRect).y = cy
(!srcRect).w = cw
(!srcRect).h = ch
val dstRect = stackalloc[SDL_Rect]()
(!dstRect).x = x
(!dstRect).y = y
(!dstRect).w = cw
(!dstRect).h = ch
```

Pull request:
- Remove wrong LF check for `case`
- Detect `case class` in `case` body
Fixes scalameta/scalafmt#3790. Fixes scalameta/scalafmt#3789.

Patch:
diff --git a/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScannerTokens.scala b/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScannerTokens.scala
index d84159c1cf..c146049cb9 100644
--- a/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScannerTokens.scala
+++ b/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/ScannerTokens.scala
@@ -724,9 +724,6 @@ final class ScannerTokens(val tokens: Tokens)(implicit dialect: Dialect) {
           case RegionTry :: xs
               if !next.isAny[KwCatch, KwFinally] && isLeadingInfix != LeadingInfix.Yes =>
             strip(xs)
-          case (_: RegionCaseBody) :: xs
-              if !next.isAny[KwCase, KwFinally] && isLeadingInfix != LeadingInfix.Yes =>
-            strip(xs)
           case Nil | (_: CanProduceLF) :: _ => Some(rs)
           case _ => None
         }
@@ -788,7 +785,8 @@ final class ScannerTokens(val tokens: Tokens)(implicit dialect: Dialect) {
               case (rc: RegionCaseBody) :: (r: RegionIndent) :: rs =>
                 if (nextIndent > r.indent) null
                 else if (next.is[KwFinally]) OutdentInfo(r, rs, noOutdent(rs))
-                else if (nextIndent < r.indent || rc.arrow.ne(prev) && !next.is[KwCase])
+                else if (nextIndent < r.indent || rc.arrow.ne(prev) &&
+                  (!next.is[KwCase]) || getNextToken(nextPos).isClassOrObject)
                   OutdentInfo(r, rs)
                 else null
               case (r: RegionIndent) :: (rs @ RegionTry :: xs) =>


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 669b0815eb..86ca19874b 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
@@ -4038,4 +4038,202 @@ class ControlSyntaxSuite extends BaseDottySuite {
     assertNoDiff(parseStat(code2, dialect).reprint, layout)
   }
 
+  test("scalafmt #3790 case") {
+    val code =
+      """|case 1 =>
+         |  println(2)
+         |  (3, 4)
+         |""".stripMargin
+    val layout =
+      """|case 1 =>
+         |  println(2)
+         |  (3, 4)
+         |""".stripMargin
+    val tree = Case(
+      int(1),
+      None,
+      blk(
+        Term.Apply(tname("println"), List(int(2))),
+        Term.Tuple(List(int(3), int(4)))
+      )
+    )
+    runTestAssert[Case](code, layout)(tree)
+  }
+
+  test("scalafmt #3790 match optional braces") {
+    val code =
+      """|def foo =
+         |  bar match
+         |  case 1 =>
+         |    println(2)
+         |    (3, 4)
+         |  baz
+         |""".stripMargin
+    val layout =
+      """|def foo = {
+         |  bar match {
+         |    case 1 =>
+         |      println(2)
+         |      (3, 4)
+         |  }
+         |  baz
+         |}
+         |""".stripMargin
+    val tree = Defn.Def(
+      Nil,
+      tname("foo"),
+      Nil,
+      None,
+      blk(
+        Term.Match(
+          tname("bar"),
+          Case(
+            int(1),
+            None,
+            blk(
+              Term.Apply(tname("println"), List(int(2))),
+              Term.Tuple(List(int(3), int(4)))
+            )
+          ) :: Nil,
+          Nil
+        ),
+        tname("baz")
+      )
+    )
+    runTestAssert[Stat](code, layout)(tree)
+  }
+
+  test("scalafmt #3790 match optional braces, and case class following in case body") {
+    val code =
+      """|def foo =
+         |  bar match
+         |  case 1 =>
+         |    println(2)
+         |    (3, 4)
+         |    case class A(a: Int)
+         |""".stripMargin
+    val layout =
+      """|def foo = bar match {
+         |  case 1 =>
+         |    println(2)
+         |    (3, 4)
+         |    case class A(a: Int)
+         |}
+         |""".stripMargin
+    val tree = Defn.Def(
+      Nil,
+      tname("foo"),
+      Nil,
+      None,
+      Term.Match(
+        tname("bar"),
+        Case(
+          int(1),
+          None,
+          blk(
+            Term.Apply(tname("println"), List(int(2))),
+            Term.Tuple(List(int(3), int(4))),
+            Defn.Class(
+              List(Mod.Case()),
+              pname("A"),
+              Nil,
+              ctorp(tparam("a", "Int")),
+              tpl()
+            )
+          )
+        ) :: Nil,
+        Nil
+      )
+    )
+    runTestAssert[Stat](code, layout)(tree)
+  }
+
+  test("scalafmt #3790 match optional braces, and case class following after case body") {
+    val code =
+      """|def foo =
+         |  bar match
+         |  case 1 =>
+         |    println(2)
+         |    (3, 4)
+         |  case class A(a: Int)
+         |""".stripMargin
+    val layout =
+      """|def foo = {
+         |  bar match {
+         |    case 1 =>
+         |      println(2)
+         |      (3, 4)
+         |  }
+         |  case class A(a: Int)
+         |}
+         |""".stripMargin
+    val tree = Defn.Def(
+      Nil,
+      tname("foo"),
+      Nil,
+      None,
+      blk(
+        Term.Match(
+          tname("bar"),
+          Case(
+            int(1),
+            None,
+            blk(
+              Term.Apply(tname("println"), List(int(2))),
+              Term.Tuple(List(int(3), int(4)))
+            )
+          ) :: Nil,
+          Nil
+        ),
+        Defn.Class(
+          List(Mod.Case()),
+          pname("A"),
+          Nil,
+          ctorp(List(tparam("a", "Int"))),
+          tpl()
+        )
+      )
+    )
+    runTestAssert[Stat](code, layout)(tree)
+  }
+
+  test("scalafmt #3790 match actual braces") {
+    val code =
+      """|def foo =
+         |  bar match {
+         |  case 1 =>
+         |    println(2)
+         |    (3, 4)
+         |  baz}
+         |""".stripMargin
+    val layout =
+      """|def foo = bar match {
+         |  case 1 =>
+         |    println(2)
+         |    (3, 4)
+         |    baz
+         |}
+         |""".stripMargin
+    val tree = Defn.Def(
+      Nil,
+      tname("foo"),
+      Nil,
+      None,
+      Term.Match(
+        tname("bar"),
+        Case(
+          int(1),
+          None,
+          blk(
+            Term.Apply(tname("println"), List(int(2))),
+            Term.Tuple(List(int(3), int(4))),
+            tname("baz")
+          )
+        ) :: Nil,
+        Nil
+      )
+    )
+    runTestAssert[Stat](code, layout)(tree)
+  }
+
 }