scalameta / scalafmt

Code formatter for Scala
http://scalameta.org/scalafmt
Apache License 2.0
1.42k stars 276 forks source link

Scalafmt inserts the wrong endmarker, creating invalid code (Scala3) #4098

Closed blandflakes closed 1 month ago

blandflakes commented 1 month ago

Configuration (required)

Please paste the smallest possible set of .scalafmt.conf configuration parameters that reproduces the problem:

runner.dialect = "Scala3"
version = "3.8.2"
rewrite.scala3.insertEndMarkerMinLines = 10

Command-line parameters (required)

When I run scalafmt via CLI like this: scalafmt -c .scalafmt.conf

Steps

Given code like this:

class FmtTest:

  private val const = 3

  def testBrokenMatch(s: String) =
    s match
      case "hello" =>
        val a = 1
        val b = 1
        val c = 1
        val d = 1
        val e = 1
        val f = 1
        val g = 1
        val h = 1
        val i = 1
        val j = 1
        val k = 1
        val l = 1
    end match

end FmtTest

Problem

Scalafmt formats code like this:

class FmtTest:

  private val const = 3

  def testBrokenMatch(s: String) =
    s match
      case "hello" =>
        val a = 1
        val b = 1
        val c = 1
        val d = 1
        val e = 1
        val f = 1
        val g = 1
        val h = 1
        val i = 1
        val j = 1
        val k = 1
        val l = 1
    end match

  end FmtTest
end FmtTest

The extra FmtTest of course then fails fmt in future calls as it is not valid.

Expectation

I would like the formatted output to look like this:

class FmtTest:

  private val const = 3

  def testBrokenMatch(s: String) =
    s match
      case "hello" =>
        val a = 1
        val b = 1
        val c = 1
        val d = 1
        val e = 1
        val f = 1
        val g = 1
        val h = 1
        val i = 1
        val j = 1
        val k = 1
        val l = 1
    end match
  end testBrokenMatch // Or nothing
end FmtTest

Workaround

I've found that by adding the end testBrokenMatch marker myself, scalafmt does not add an extra end FmtTest anymore.

Additionally, if I don't include include end match, scalafmt doesn't alter the code at all.

Notes

The private val or an import seems required to trigger the formatting threshold of trying to insert the extra marker.

I see a similar situation with simply calling a method and passing a body for which an end marker can't be labeled, such as munit's test:

class MyTest extends munit.FunSuite:
  import com.github.plokhotnyuk.jsoniter_scala.core.*
  test("some test name"):
    // body here, ending with an `end match`

end MyTest

In this case, scalafmt will insert an extra end MyTest.