Repository: scalameta/scalameta (scala)

Issue:
ScalametaParser: pos for trailing comments on multi-line expression inconsistent from one dialect to another
```scala
import scala.meta._

val program =
"""
object Main extends App {
  val a = 1 // comment attached to Defn.Val on all dialects
  val b =
    1 // comment attached to Defn.Val on Scala 3 dialect only
}
"""

{
  println("Scala213")
  implicit val scala213: Dialect = dialects.Scala213
  println(program.parse[Stat].get.collect { case v: Defn.Val => v }.map(_.pos))
  println()
}

{
  println("Scala3")
  implicit val scala3: Dialect = dialects.Scala3
  println(program.parse[Stat].get.collect { case v: Defn.Val => v }.map(_.pos))
  println()
}
```

```diff
---Scala213
+++Scala3
 List([29..38) in Input.String("
 object Main extends App {
   val a = 1 // comment attached to Defn.Val on all dialects
   val b =
     1 // comment attached to Defn.Val on Scala 3 dialect only
 }
-"), [89..102) in Input.String("
+"), [89..158) in Input.String("
 object Main extends App {
   val a = 1 // comment attached to Defn.Val on all dialects
   val b =
     1 // comment attached to Defn.Val on Scala 3 dialect only
 }
 "))
```

This makes cross-dialect usage of `AssociatedComments` hard.

I don't have any opinion on what is the right behavior. FWIW,
- the behavior for the Scala213 dialect hasn't changed since at least 4.2.0
- the behavior for the Scala3 dialect hasn't changed since [4.4.33](https://github.com/scalameta/scalameta/releases/tag/v4.4.33) (before that, Scala3 had the same behavior as Scala213)

I found this inconsistency while trying to port a Scala2 Scalafix test to Scala3. I am not aware of any reported problem in production, so "won't fix" is definitely a reasonable outcome for this issue.

Pull request:
Fixes #3689.

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 75bdc43be1..73b5591b02 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
@@ -343,7 +343,9 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) {
       if (nonSpaceEnd < startPos) return (startPos, if (endPos == startPos) endPos else endExcl)
       val start = tokens.skipIf(_.is[Trivia], startPos, endExcl)
       if (start > endPos) return (startPos, nonSpaceEnd + 1)
-      (start, nonSpaceEnd + 1)
+      if (!tokens(nonSpaceEnd).is[Comment]) return (start, nonSpaceEnd + 1)
+      val end = tokens.rskipIf(_.is[HTrivia], nonSpaceEnd - 1, start)
+      (start, (if (tokens(end).is[AtEOLorF]) nonSpaceEnd else end) + 1)
     }
     val (start, endExcl) = getPosRange()
     body.withOrigin(Origin.Parsed(originSource, start, endExcl))


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 32b905484e..39d038a50b 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
@@ -947,9 +947,9 @@ class Scala3PositionSuite extends BasePositionSuite(dialects.Scala3) {
        |  case (false, false) => (baz, qux) // c1""".stripMargin,
     """|Pat.Tuple (x, y)
        |Term.Match (foo, bar) match
-       |  case (false, false) => (baz, qux) // c1
+       |  case (false, false) => (baz, qux)
        |Term.Tuple (foo, bar)
-       |Case case (false, false) => (baz, qux) // c1
+       |Case case (false, false) => (baz, qux)
        |Pat.Tuple (false, false)
        |Term.Tuple (baz, qux)
        |""".stripMargin
@@ -961,9 +961,9 @@ class Scala3PositionSuite extends BasePositionSuite(dialects.Scala3) {
        |""".stripMargin,
     """|Pat.Tuple (x, y)
        |Term.Match (foo, bar) match
-       |  case (false, false) => (baz, qux) // c1
+       |  case (false, false) => (baz, qux)
        |Term.Tuple (foo, bar)
-       |Case case (false, false) => (baz, qux) // c1
+       |Case case (false, false) => (baz, qux)
        |Pat.Tuple (false, false)
        |Term.Tuple (baz, qux)
        |""".stripMargin
@@ -977,12 +977,12 @@ class Scala3PositionSuite extends BasePositionSuite(dialects.Scala3) {
     """|Pat.Tuple (x, y)
        |Term.Match (foo, bar) match
        |  case (true, false) => (baz, qux) // c1
-       |  case (false, true) => (baz, qux) // c2
+       |  case (false, true) => (baz, qux)
        |Term.Tuple (foo, bar)
-       |Case case (true, false) => (baz, qux) // c1
+       |Case case (true, false) => (baz, qux)
        |Pat.Tuple (true, false)
        |Term.Tuple (baz, qux)
-       |Case case (false, true) => (baz, qux) // c2
+       |Case case (false, true) => (baz, qux)
        |Pat.Tuple (false, true)
        |Term.Tuple (baz, qux)
        |""".stripMargin
@@ -995,12 +995,12 @@ class Scala3PositionSuite extends BasePositionSuite(dialects.Scala3) {
     """|Pat.Tuple (x, y)
        |Term.Match (foo, bar) match
        |  case (true, false) => (baz, qux) // c1
-       |  case (false, true) => (baz, qux) // c2
+       |  case (false, true) => (baz, qux)
        |Term.Tuple (foo, bar)
-       |Case case (true, false) => (baz, qux) // c1
+       |Case case (true, false) => (baz, qux)
        |Pat.Tuple (true, false)
        |Term.Tuple (baz, qux)
-       |Case case (false, true) => (baz, qux) // c2
+       |Case case (false, true) => (baz, qux)
        |Pat.Tuple (false, true)
        |Term.Tuple (baz, qux)
        |""".stripMargin
@@ -1011,10 +1011,10 @@ class Scala3PositionSuite extends BasePositionSuite(dialects.Scala3) {
        |  case (true, false) => (baz, qux) // c1
        |  case (false, true) => (baz, qux) // c2""".stripMargin,
     """|Term.Tuple (foo, bar)
-       |Case case (true, false) => (baz, qux) // c1
+       |Case case (true, false) => (baz, qux)
        |Pat.Tuple (true, false)
        |Term.Tuple (baz, qux)
-       |Case case (false, true) => (baz, qux) // c2
+       |Case case (false, true) => (baz, qux)
        |Pat.Tuple (false, true)
        |Term.Tuple (baz, qux)
        |""".stripMargin
@@ -1025,10 +1025,10 @@ class Scala3PositionSuite extends BasePositionSuite(dialects.Scala3) {
        |case (true, false) => (baz, qux) // c1
        |case (false, true) => (baz, qux) // c2""".stripMargin,
     """|Term.Tuple (foo, bar)
-       |Case case (true, false) => (baz, qux) // c1
+       |Case case (true, false) => (baz, qux)
        |Pat.Tuple (true, false)
        |Term.Tuple (baz, qux)
-       |Case case (false, true) => (baz, qux) // c2
+       |Case case (false, true) => (baz, qux)
        |Pat.Tuple (false, true)
        |Term.Tuple (baz, qux)
        |""".stripMargin
@@ -1040,10 +1040,10 @@ class Scala3PositionSuite extends BasePositionSuite(dialects.Scala3) {
        |case (false, true) => (baz, qux) // c2
        |""".stripMargin,
     """|Term.Tuple (foo, bar)
-       |Case case (true, false) => (baz, qux) // c1
+       |Case case (true, false) => (baz, qux)
        |Pat.Tuple (true, false)
        |Term.Tuple (baz, qux)
-       |Case case (false, true) => (baz, qux) // c2
+       |Case case (false, true) => (baz, qux)
        |Pat.Tuple (false, true)
        |Term.Tuple (baz, qux)
        |""".stripMargin
@@ -1166,7 +1166,7 @@ class Scala3PositionSuite extends BasePositionSuite(dialects.Scala3) {
        |}
        |""".stripMargin,
     """|Defn.Val val b =
-       |    1 // comment
+       |    1
        |""".stripMargin
   )
 
@@ -1195,7 +1195,7 @@ class Scala3PositionSuite extends BasePositionSuite(dialects.Scala3) {
        |}
        |""".stripMargin,
     """|Defn.Val val b = // comment1
-       |    1 // comment2
+       |    1
        |""".stripMargin
   )
 
@@ -1226,7 +1226,7 @@ class Scala3PositionSuite extends BasePositionSuite(dialects.Scala3) {
        |""".stripMargin,
     """|Defn.Val val b =
        |    // comment1
-       |    1 // comment2
+       |    1
        |""".stripMargin
   )