Repository: scalameta/scalameta (scala)

Issue:
Formatter errors with trait mixin, enum and extension declarations
- **Version**: 2.4.3
- **Integration**: None, reproduces in VSCode, and IntelliJ
- **Configuration**:
```
version = "3.1.1"
runner.dialect = scala3
```

## Steps

Given code like this:
```scala
trait T
trait T2 { self: T =>
  enum T2Enum:
    case EnumCase
  
  extension (n: Int) def negate: Int = -n
}
```

When I run scalafmt like this (in sbt console):

```bash
scalafmt
```

## Problem

Scalafmt errors saying a `;` is required, even though `compile` passes:
```scala
sbt:scalafmt-bug-reports> scalafmt
[info] Formatting 1 Scala sources...
[error] S:\programming\personal\scalafmt-bug-reports\src\main\scala\io\github\kory33\bugreports\scalafmt\Test.scala:8: error: [dialect scala3] 
; expected but identifier found
[error]   extension (n: Int)
[error]   ^: S:\programming\personal\scalafmt-bug-reports\src\main\scala\io\github\kory33\bugreports\scalafmt\Test.scala
[error] (Compile / scalafmt) S:\programming\personal\scalafmt-bug-reports\src\main\scala\io\github\kory33\bugreports\scalafmt\Test.scala:8: error: [dialect scala3] ; expected but identifier found
[error]   extension (n: Int)
[error]   ^: S:\programming\personal\scalafmt-bug-reports\src\main\scala\io\github\kory33\bugreports\scalafmt\Test.scala
[error] Total time: 0 s, completed 2021/11/13 11:05:42
sbt:scalafmt-bug-reports> compile
[info] compiling 1 Scala source to S:\programming\personal\scalafmt-bug-reports\target\scala-3.1.0\classes ...
[success] Total time: 4 s, completed 2021/11/13 11:06:28
```

## Expectation

I would like the formatter to format the code in some way without outputting an error.

## Workaround

I've found that by bringing `enum` declaration below `extension` the formatter works as expected.

```Scala
trait T
trait T2 { self: T =>
  extension (n: Int) def negate: Int = -n

  enum T2Enum:
    case EnumCase  
}
```

## Notes

The example is minimized. Removing any one of trait mixin, extension or enum solves the issue. Please see https://github.com/kory33/scalafmt-bug-reports/tree/072c26b778a2baf10f8af1bf43ab9605e084c8b2 for the full project example.

Pull request:
Fixes https://github.com/scalameta/scalameta/issues/2505

PReviously, we would try to undo the indentation, but isntead we can just not introduce indentation on `=>` inside enums.

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 d4097d4033..ac37cdcaee 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
@@ -310,12 +310,23 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
       }
     }
 
+    /**
+     * Deals with different rules for indentation after self type arrow.
+     */
     def undoIndent(): Unit = {
-      sepRegions = sepRegions match {
+      sepRegions match {
         case region :: others if region.isIndented && curr.token.is[Indentation.Indent] =>
           next()
-          others
-        case regions => regions
+          sepRegions = sepRegions match {
+            // deal with  region added by `case` in enum after self type
+            case RegionArrow :: _ => others
+            // if no region was added
+            case `region` :: _ => others
+            // keep any added region in `next()`
+            case head :: _ => head :: others
+            case _ => sepRegions
+          }
+        case _ =>
       }
     }
 


Test patch:
diff --git a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/EnumSuite.scala b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/EnumSuite.scala
index 7bcf29b07e..ca61d946a6 100644
--- a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/EnumSuite.scala
+++ b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/EnumSuite.scala
@@ -603,6 +603,51 @@ class EnumSuite extends BaseDottySuite {
     )
   }
 
+  test("enum-arrow-toplevel") {
+    val code =
+      """|enum T2Enum:
+         |  case Hmm
+         |  val a = () =>  
+         |    fx()
+         |    gx()
+    """.stripMargin
+    val expected =
+      """|enum T2Enum {
+         |  case Hmm
+         |  val a = () => {
+         |    fx()
+         |    gx()
+         |  }
+         |}
+         |""".stripMargin
+    runTestAssert[Stat](code, assertLayout = Some(expected))(
+      Defn.Enum(
+        Nil,
+        Type.Name("T2Enum"),
+        Nil,
+        Ctor.Primary(Nil, Name(""), Nil),
+        Template(
+          Nil,
+          Nil,
+          Self(Name(""), None),
+          List(
+            Defn.EnumCase(Nil, Term.Name("Hmm"), Nil, Ctor.Primary(Nil, Name(""), Nil), Nil),
+            Defn.Val(
+              Nil,
+              List(Pat.Var(Term.Name("a"))),
+              None,
+              Term.Function(
+                Nil,
+                Term.Block(List(Term.Apply(Term.Name("fx"), Nil), Term.Apply(Term.Name("gx"), Nil)))
+              )
+            )
+          ),
+          Nil
+        )
+      )
+    )
+  }
+
   private def enumWithCase(name: String, enumCase: Stat) = Defn.Enum(
     Nil,
     Type.Name(name),
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 c57c03a18d..d46f88249c 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
@@ -2240,4 +2240,74 @@ class SignificantIndentationSuite extends BaseDottySuite {
     )
   }
 
+  test("i2505") {
+    runTestAssert[Source](
+      """|trait T2 { self: T =>
+         |  enum T2Enum:
+         |    case EnumCase
+         |  
+         |  extension (n: Int) def negate: Int = -n
+         |}""".stripMargin,
+      assertLayout = Some(
+        """|trait T2 { self: T =>
+           |  enum T2Enum { case EnumCase }
+           |  extension (n: Int) def negate: Int = -n
+           |}
+           |""".stripMargin
+      )
+    )(
+      Source(
+        List(
+          Defn.Trait(
+            Nil,
+            Type.Name("T2"),
+            Nil,
+            Ctor.Primary(Nil, Name(""), Nil),
+            Template(
+              Nil,
+              Nil,
+              Self(Term.Name("self"), Some(Type.Name("T"))),
+              List(
+                Defn.Enum(
+                  Nil,
+                  Type.Name("T2Enum"),
+                  Nil,
+                  Ctor.Primary(Nil, Name(""), Nil),
+                  Template(
+                    Nil,
+                    Nil,
+                    Self(Name(""), None),
+                    List(
+                      Defn.EnumCase(
+                        Nil,
+                        Term.Name("EnumCase"),
+                        Nil,
+                        Ctor.Primary(Nil, Name(""), Nil),
+                        Nil
+                      )
+                    ),
+                    Nil
+                  )
+                ),
+                Defn.ExtensionGroup(
+                  Nil,
+                  List(List(Term.Param(Nil, Term.Name("n"), Some(Type.Name("Int")), None))),
+                  Defn.Def(
+                    Nil,
+                    Term.Name("negate"),
+                    Nil,
+                    Nil,
+                    Some(Type.Name("Int")),
+                    Term.ApplyUnary(Term.Name("-"), Term.Name("n"))
+                  )
+                )
+              ),
+              Nil
+            )
+          )
+        )
+      )
+    )
+
+  }
 }