Repository: scalameta/scalameta (scala)

Issue:
error: [dialect scala3] `;` expected but `new` found
## Configuration (required) ##

```
version = "3.8.3"
runner.dialect = scala3
```

## Command-line parameters (required) ##

```
./scalafmt-linux-glibc scalafmt-report.sc
```

## Steps

Given code like this in file `scalafmt-report.sc`:
```scala
import scala.collection.mutable.Buffer

object Hello {
  val buffer = Buffer[Object]()
  buffer
    += new Object()
    += new Object
}

println(Hello.buffer)
```

## Problem

Scalafmt fails with:
```scala
<input>:7: error: [dialect scala3] `;` expected but `new` found
    += new Object
       ^
```

While `scala-cli scalafmt-report.sc` gives:
```
Compiling project (Scala 3.5.0, JVM (17))
Compiled project (Scala 3.5.0, JVM (17))
ArrayBuffer(java.lang.Object@2f7a2457, java.lang.Object@566776ad)
```

## Notes

If we modify the code to be (remove the second new):
```scala
import scala.collection.mutable.Buffer

object Hello {
  val buffer = Buffer[Object]()
  buffer
    += new Object()
    += Object()
}

println(Hello.buffer)
```

then it is formated like:
```scala
import scala.collection.mutable.Buffer

object Hello {
  val buffer = Buffer[Object]()
  buffer
    += new Object()
  += Object ()
}

println(Hello.buffer)
```

Pull request:
Fixes #3948.

Patch:
diff --git a/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/SepRegion.scala b/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/SepRegion.scala
index 15dea95277..1640b3141c 100644
--- a/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/SepRegion.scala
+++ b/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/SepRegion.scala
@@ -47,7 +47,7 @@ case object RegionDefType extends RegionDefDecl
 sealed trait RegionTemplateDecl extends RegionNonDelimNonIndented
 
 /** the initial part of declaration, before the template */
-case object RegionTemplateMark extends RegionTemplateDecl
+case object RegionTemplateMark extends RegionTemplateDecl with CanProduceLF
 
 /** the initial part of the template, containing any inherit clauses */
 case object RegionTemplateInherit extends RegionTemplateDecl


Test patch:
diff --git a/tests/shared/src/test/scala-2.13/scala/meta/tests/parsers/dotty/Scala3PositionSuite.scala b/tests/shared/src/test/scala-2.13/scala/meta/tests/parsers/dotty/Scala3PositionSuite.scala
index 3cce082bf1..4f2e296935 100644
--- a/tests/shared/src/test/scala-2.13/scala/meta/tests/parsers/dotty/Scala3PositionSuite.scala
+++ b/tests/shared/src/test/scala-2.13/scala/meta/tests/parsers/dotty/Scala3PositionSuite.scala
@@ -2978,4 +2978,55 @@ class Scala3PositionSuite extends BasePositionSuite(dialects.Scala3) {
     showFieldName = true
   )
 
+  checkPositions[Stat](
+    """|object Hello {
+       |  buffer
+       |    += new Object()
+       |    += new Object
+       |}
+       |""".stripMargin,
+    """|<templ>Template {
+       |  buffer
+       |    += new Object()
+       |    += new Object
+       |}</templ>
+       |<body>Template.Body {
+       |  buffer
+       |    += new Object()
+       |    += new Object
+       |}</body>
+       |<stats0>Term.ApplyInfix buffer
+       |    += new Object()
+       |    += new Object</stats0>
+       |<lhs>Term.ApplyInfix buffer
+       |    += new Object()</lhs>
+       |<targClause>Type.ArgClause     += @@new Object()</targClause>
+       |<values0>Term.New new Object()</values0>
+       |<init>Init Object()</init>
+       |<argClauses0>Term.ArgClause ()</argClauses0>
+       |<targClause>Type.ArgClause     += @@new Object</targClause>
+       |<values0>Term.New new Object</values0>
+       |""".stripMargin,
+    """|BOF [0..0)
+       |KwObject [0..6)
+       |Ident(Hello) [7..12)
+       |LeftBrace [13..14)
+       |Ident(buffer) [17..23)
+       |InfixLF [23..24)
+       |Ident(+=) [28..30)
+       |KwNew [31..34)
+       |Ident(Object) [35..41)
+       |LeftParen [41..42)
+       |RightParen [42..43)
+       |InfixLF [43..44)
+       |Ident(+=) [48..50)
+       |KwNew [51..54)
+       |Ident(Object) [55..61)
+       |LF [61..62)
+       |RightBrace [62..63)
+       |EOF [64..64)
+       |""".stripMargin,
+    showFieldName = true
+  )
+
 }
diff --git a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/InfixSuite.scala b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/InfixSuite.scala
index 61b457f0fa..5ba15d63e8 100644
--- a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/InfixSuite.scala
+++ b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/InfixSuite.scala
@@ -697,4 +697,109 @@ class InfixSuite extends BaseDottySuite {
     runTestAssert[Stat](code, layout)(tree)
   }
 
+  test("#3948 1") {
+    val code = """|object Hello {
+                  |  buffer
+                  |    += new Object()
+                  |    += new Object
+                  |}
+                  |""".stripMargin
+    val tokenized = """|BOF [0..0)
+                       |KwObject [0..6)
+                       |Ident(Hello) [7..12)
+                       |LeftBrace [13..14)
+                       |Ident(buffer) [17..23)
+                       |InfixLF [23..24)
+                       |Ident(+=) [28..30)
+                       |KwNew [31..34)
+                       |Ident(Object) [35..41)
+                       |LeftParen [41..42)
+                       |RightParen [42..43)
+                       |InfixLF [43..44)
+                       |Ident(+=) [48..50)
+                       |KwNew [51..54)
+                       |Ident(Object) [55..61)
+                       |LF [61..62)
+                       |RightBrace [62..63)
+                       |EOF [64..64)
+                       |""".stripMargin
+    assertTokenizedAsStructureLines(code, tokenized)
+    val layout = "object Hello { buffer += (new Object()) += (new Object) }"
+    val tree = Defn.Object(
+      Nil,
+      tname("Hello"),
+      tpl(Term.ApplyInfix(
+        Term
+          .ApplyInfix(tname("buffer"), tname("+="), Nil, List(Term.New(init("Object", List(Nil))))),
+        tname("+="),
+        Nil,
+        List(Term.New(init("Object")))
+      ))
+    )
+    runTestAssert[Stat](code, layout)(tree)
+  }
+
+  test("#3948 2") {
+    val code = """|object Hello {
+                  |  buffer
+                  |    += new Object() with Foo {
+                  |      def toString() = "foo"
+                  |    }
+                  |    += new Object
+                  |}
+                  |""".stripMargin
+    val tokenized = """|BOF [0..0)
+                       |KwObject [0..6)
+                       |Ident(Hello) [7..12)
+                       |LeftBrace [13..14)
+                       |Ident(buffer) [17..23)
+                       |InfixLF [23..24)
+                       |Ident(+=) [28..30)
+                       |KwNew [31..34)
+                       |Ident(Object) [35..41)
+                       |LeftParen [41..42)
+                       |RightParen [42..43)
+                       |KwWith [44..48)
+                       |Ident(Foo) [49..52)
+                       |LeftBrace [53..54)
+                       |KwDef [61..64)
+                       |Ident(toString) [65..73)
+                       |LeftParen [73..74)
+                       |RightParen [74..75)
+                       |Equals [76..77)
+                       |Constant.String(foo) [78..83)
+                       |LF [83..84)
+                       |RightBrace [88..89)
+                       |InfixLF [89..90)
+                       |Ident(+=) [94..96)
+                       |KwNew [97..100)
+                       |Ident(Object) [101..107)
+                       |LF [107..108)
+                       |RightBrace [108..109)
+                       |EOF [110..110)
+                       |""".stripMargin
+    assertTokenizedAsStructureLines(code, tokenized)
+    val layout =
+      """object Hello { buffer += (new Object() with Foo { def toString() = "foo" }) += (new Object) }"""
+    val tree = Defn.Object(
+      Nil,
+      tname("Hello"),
+      tpl(Term.ApplyInfix(
+        Term.ApplyInfix(
+          tname("buffer"),
+          tname("+="),
+          Nil,
+          List(Term.NewAnonymous(tpl(
+            List(init("Object", List(Nil)), init("Foo")),
+            List(Defn.Def(Nil, tname("toString"), Nil, List(Nil), None, lit("foo")))
+          )))
+        ),
+        tname("+="),
+        Nil,
+        List(Term.New(init("Object")))
+      ))
+    )
+    runTestAssert[Stat](code, layout)(tree)
+  }
+
 }