Repository: scalameta/scalameta (scala)

Issue:
[dialect scala3] outdent expected but } found
## Configuration (required) ##


Please paste the contents of your `.scalafmt.conf` file here:
```
version = "3.7.3"
runner.dialect = scala3
```

## Command-line parameters (required) ##

I ran scalafmt via CLI like this: `./scalafmt-native`

## Steps

Given code like this:
```scala
object Hello {
  val fun = () =>
    if (true) {
      new Object { obj =>
        println(toString)
      }
    }

  def main(args: Array[String]): Unit = fun()
}
```
in file `Hello.scala`.

## Problem

Scalafmt fails with `Hello.scala:10: error: [dialect scala3] outdent expected but } found` on the last `}`.

## Expectation

It should work as this code compiles and run fine.

## Workaround

Put `println(toString)` in a block like: `{ println(toString) }` or put it on the same line as `obj`.

## Notes

The error does not happen if we comment `obj =>` or if we remove the block of the `if` or if we do not put it in `fun`.

Pull request:
Instead of `next()`, use underlying `getNextTokenRef()`. Fixes #3113.

Patch:
diff --git a/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/LazyTokenIterator.scala b/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/LazyTokenIterator.scala
index 07c0e7ecc4..5c421cb249 100644
--- a/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/LazyTokenIterator.scala
+++ b/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/LazyTokenIterator.scala
@@ -61,8 +61,8 @@ private[parsers] class LazyTokenIterator private (
   def undoIndent(): Unit = {
     curr.regions match {
       case (region: SepRegionIndented) :: others if curr.token.is[Indentation.Indent] =>
-        next()
-        resetCurr(curr.withRegions(curr.regions match {
+        val ref = getNextTokenRef()
+        resetCurr(ref.withRegions(ref.regions match {
           // deal with  region added by `case` in enum after self type
           case RegionArrow :: _ => others
           // if no region was added
diff --git a/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/TokenRef.scala b/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/TokenRef.scala
index fc5c65de7b..55d3435b04 100644
--- a/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/TokenRef.scala
+++ b/scalameta/parsers/shared/src/main/scala/scala/meta/internal/parsers/TokenRef.scala
@@ -11,7 +11,8 @@ private[parsers] class TokenRef private (
     var next: TokenRef = null
 ) {
   def withRegions(regions: List[SepRegion]): TokenRef =
-    new TokenRef(regions, token, pos, nextPos, pointPos)
+    if (regions eq this.regions) this
+    else new TokenRef(regions, token, pos, nextPos, pointPos)
 }
 
 private[parsers] object TokenRef {


Test patch:
diff --git a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/BaseDottySuite.scala b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/BaseDottySuite.scala
index 894112f494..b32352c4e0 100644
--- a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/BaseDottySuite.scala
+++ b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/BaseDottySuite.scala
@@ -21,8 +21,12 @@ trait BaseDottySuite extends ParseSuite {
   final def tpl(stats: List[Stat]): Template = Template(Nil, Nil, slf, stats)
   final def tparamval(name: String, tpe: String) =
     Term.Param(List(Mod.ValParam()), Term.Name(name), Some(pname(tpe)), None)
-  final def tparam(name: String, tpe: String = null) =
-    Term.Param(Nil, Term.Name(name), Option(tpe).map(pname), None)
+  final def tparam(name: String, tpe: Option[Type] = None): Term.Param =
+    Term.Param(Nil, Term.Name(name), tpe, None)
+  final def tparam(name: String, tpe: Type): Term.Param =
+    tparam(name, Option(tpe))
+  final def tparam(name: String, tpe: String): Term.Param =
+    tparam(name, Option(tpe).map(pname))
   final def tparamInline(name: String, tpe: String) =
     Term.Param(List(Mod.Inline()), Term.Name(name), Some(pname(tpe)), None)
 
diff --git a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/SignificantIndentationSuite.scala b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/SignificantIndentationSuite.scala
index 37d9d30019..a706ee2c82 100644
--- a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/SignificantIndentationSuite.scala
+++ b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/SignificantIndentationSuite.scala
@@ -2486,4 +2486,76 @@ class SignificantIndentationSuite extends BaseDottySuite {
     )
 
   }
+
+  test("#3113") {
+    runTestAssert[Source](
+      """|object Hello {
+         |  val fun = () =>
+         |    if (true) {
+         |      new Object { obj =>
+         |        println(toString)
+         |      }
+         |    }
+         |
+         |  def main(args: Array[String]): Unit = fun()
+         |}""".stripMargin,
+      assertLayout = Some(
+        """|object Hello {
+           |  val fun = () => if (true) {
+           |    new Object { obj => println(toString) }
+           |  }
+           |  def main(args: Array[String]): Unit = fun()
+           |}
+           |""".stripMargin
+      )
+    )(
+      Source(
+        Defn.Object(
+          Nil,
+          tname("Hello"),
+          Template(
+            Nil,
+            Nil,
+            Self(Name(""), None),
+            List(
+              Defn.Val(
+                Nil,
+                List(Pat.Var(tname("fun"))),
+                None,
+                Term.Function(
+                  Nil,
+                  Term.If(
+                    bool(true),
+                    Term.Block(
+                      Term.NewAnonymous(
+                        Template(
+                          Nil,
+                          List(Init(pname("Object"), Name(""), Nil)),
+                          Self(tname("obj"), None),
+                          List(Term.Apply(tname("println"), List(tname("toString")))),
+                          Nil
+                        )
+                      ) :: Nil
+                    ),
+                    Lit.Unit(),
+                    Nil
+                  )
+                )
+              ),
+              Defn.Def(
+                Nil,
+                tname("main"),
+                Nil,
+                List(List(tparam("args", Type.Apply(pname("Array"), List(pname("String")))))),
+                Some(pname("Unit")),
+                Term.Apply(tname("fun"), Nil)
+              )
+            ),
+            Nil
+          )
+        ) :: Nil
+      )
+    )
+  }
+
 }