Repository: scalameta/scalameta
Pull request #2736 (2022-04-25 03:55:29)

fix: exclude parens tokens in Term.ApplyInfix.args
fix: https://github.com/scalacenter/scalafix/issues/1594
related: https://github.com/scalameta/scalameta/pull/1489

Previously, for the following code

```scala
trait SymbolTest {
  def shouldBe(right: Any): Unit
  def arg = 1
  this shouldBe (arg)
}
```

Scala2 (semanticdb-scalac) generates:

```scala
trait SymbolTest/*<=_empty_.SymbolTest#*/ { 
  ...
  this shouldBe/*=>_empty_.SymbolTest#shouldBe().*/ (arg)/*=>_empty_.SymbolTest#arg().*/ 
} 
```

`[3:16..3:21): (arg) => _empty_/SymbolTest#arg().`

parens tokens are included in the `Term.ApplyInfix.args`.

This PR exclude the parens tokens from `args` and now scalameta generates:

```scala
trait SymbolTest/*<=_empty_.SymbolTest#*/ { 
  ...
  this shouldBe/*=>_empty_.SymbolTest#shouldBe().*/ (arg/*=>_empty_.SymbolTest#arg().*/)
} 
```
`[3:17..3:20): arg -> _empty_/SymbolTest#arg().`

which is consistent with Scala3 semanticdb-generator, see: https://github.com/scalacenter/scalafix/issues/1594#issuecomment-1106146379


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 763967a69..b71f5c357 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
@@ -2198,7 +2198,17 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
       val lpPos = auto.startTokenPos
       val args = checkNoTripleDots(argumentExprs(location))
       val rpPos = auto.endTokenPos
-      @inline def addPos(body: Term): Term = atPosWithBody(lpPos, body, rpPos)
+      @inline def addPos(body: Term): Term = {
+        body match {
+          // For `Term.Ref`, avoid using autoPos to exclude parens tokens in Term.ApplyInfix.args.
+          // For `a f (b)`, autoPos include `(b)` for ApplyInfix.args position.
+          // https://github.com/scalacenter/scalafix/issues/1594
+          case _: Term.Ref =>
+            body
+          case _ =>
+            atPosWithBody(lpPos, body, rpPos)
+        }
+      }
       token match {
         case Dot() | LeftBracket() | LeftParen() | LeftBrace() | Underscore() =>
           findRep(args).foreach(x => syntaxError("repeated argument not allowed here", at = x))
diff --git a/semanticdb/integration/src/main/scala/example/Infix.scala b/semanticdb/integration/src/main/scala/example/Infix.scala
new file mode 100644
index 000000000..5c232eda6
--- /dev/null
+++ b/semanticdb/integration/src/main/scala/example/Infix.scala
@@ -0,0 +1,7 @@
+package example
+
+trait SymbolTest {
+  def shouldBe(right: Any): Unit
+  def arg = 1
+  this shouldBe (arg) // see: https://github.com/scalacenter/scalafix/issues/1594
+}


Test patch:
diff --git a/tests/jvm/src/test/resources/example/Infix.scala b/tests/jvm/src/test/resources/example/Infix.scala
new file mode 100644
index 000000000..2c40a9300
--- /dev/null
+++ b/tests/jvm/src/test/resources/example/Infix.scala
@@ -0,0 +1,7 @@
+package example
+
+trait SymbolTest/*<=example.SymbolTest#*/ {
+  def shouldBe/*<=example.SymbolTest#shouldBe().*/(right/*<=example.SymbolTest#shouldBe().(right)*/: Any/*=>scala.Any#*/): Unit/*=>scala.Unit#*/
+  def arg/*<=example.SymbolTest#arg().*/ = 1
+  this shouldBe/*=>example.SymbolTest#shouldBe().*/ (arg/*=>example.SymbolTest#arg().*/) // see: https://github.com/scalacenter/scalafix/issues/1594
+}
diff --git a/tests/jvm/src/test/resources/metac.expect b/tests/jvm/src/test/resources/metac.expect
index 0bebf76c6..d29f11c62 100644
--- a/tests/jvm/src/test/resources/metac.expect
+++ b/tests/jvm/src/test/resources/metac.expect
@@ -1834,6 +1834,40 @@ Occurrences:
 Diagnostics:
 [0:26..0:34) [warning] Unused import
 
+semanticdb/integration/src/main/scala/example/Infix.scala
+---------------------------------------------------------
+
+Summary:
+Schema => SemanticDB v4
+Uri => semanticdb/integration/src/main/scala/example/Infix.scala
+Text => non-empty
+Language => Scala
+Symbols => 4 entries
+Occurrences => 9 entries
+
+Symbols:
+example/SymbolTest# => trait SymbolTest extends AnyRef { +2 decls }
+  AnyRef => scala/AnyRef#
+example/SymbolTest#arg(). => method arg: Int
+  Int => scala/Int#
+example/SymbolTest#shouldBe(). => abstract method shouldBe(right: Any): Unit
+  right => example/SymbolTest#shouldBe().(right)
+  Any => scala/Any#
+  Unit => scala/Unit#
+example/SymbolTest#shouldBe().(right) => param right: Any
+  Any => scala/Any#
+
+Occurrences:
+[0:8..0:15): example <= example/
+[2:6..2:16): SymbolTest <= example/SymbolTest#
+[3:6..3:14): shouldBe <= example/SymbolTest#shouldBe().
+[3:15..3:20): right <= example/SymbolTest#shouldBe().(right)
+[3:22..3:25): Any => scala/Any#
+[3:28..3:32): Unit => scala/Unit#
+[4:6..4:9): arg <= example/SymbolTest#arg().
+[5:7..5:15): shouldBe => example/SymbolTest#shouldBe().
+[5:17..5:20): arg => example/SymbolTest#arg().
+
 semanticdb/integration/src/main/scala/example/InstrumentTyper.scala
 -------------------------------------------------------------------
 
diff --git a/tests/jvm/src/test/resources/metacp.expect b/tests/jvm/src/test/resources/metacp.expect
index 912135494..b25a0d260 100644
--- a/tests/jvm/src/test/resources/metacp.expect
+++ b/tests/jvm/src/test/resources/metacp.expect
@@ -2607,6 +2607,28 @@ example/NamedArguments#User.unapply().(x$0) => param x$0: User
   User => example/NamedArguments#User#
 example/NamedArguments#`<init>`(). => primary ctor <init>()
 
+example/SymbolTest.class
+------------------------
+
+Summary:
+Schema => SemanticDB v4
+Uri => example/SymbolTest.class
+Text => empty
+Language => Scala
+Symbols => 4 entries
+
+Symbols:
+example/SymbolTest# => trait SymbolTest extends AnyRef { +2 decls }
+  AnyRef => scala/AnyRef#
+example/SymbolTest#arg(). => method arg: Int
+  Int => scala/Int#
+example/SymbolTest#shouldBe(). => abstract method shouldBe(right: Any): Unit
+  right => example/SymbolTest#shouldBe().(right)
+  Any => scala/Any#
+  Unit => scala/Unit#
+example/SymbolTest#shouldBe().(right) => param right: Any
+  Any => scala/Any#
+
 example/Synthetic.class
 -----------------------
 
diff --git a/tests/shared/src/test/scala-2.13/scala/meta/tests/tokenizers/TokensPositionSuite.scala b/tests/shared/src/test/scala-2.13/scala/meta/tests/tokenizers/TokensPositionSuite.scala
index 129eb736b..de71f7527 100644
--- a/tests/shared/src/test/scala-2.13/scala/meta/tests/tokenizers/TokensPositionSuite.scala
+++ b/tests/shared/src/test/scala-2.13/scala/meta/tests/tokenizers/TokensPositionSuite.scala
@@ -171,7 +171,6 @@ class TokensPositionSuite extends BasePositionSuite(dialects.Scala213) {
   checkPositions[Stat](
     "(a) op (b)",
     """|Term.Name (a)
-       |Term.Name (b)
        |""".stripMargin
   )
   checkPositions[Stat](
@@ -188,9 +187,7 @@ class TokensPositionSuite extends BasePositionSuite(dialects.Scala213) {
   checkPositions[Stat]("1 + 1")
   checkPositions[Stat]("a f ()")
   checkPositions[Stat](
-    "a f (b)",
-    """|Term.Name (b)
-       |""".stripMargin
+    "a f (b)"
   )
   checkPositions[Stat](
     "(f) [A,B]",
diff --git a/tests/shared/src/test/scala/scala/meta/tests/parsers/BasicPositionSuite.scala b/tests/shared/src/test/scala/scala/meta/tests/parsers/BasicPositionSuite.scala
index 32974e744..405a54458 100644
--- a/tests/shared/src/test/scala/scala/meta/tests/parsers/BasicPositionSuite.scala
+++ b/tests/shared/src/test/scala/scala/meta/tests/parsers/BasicPositionSuite.scala
@@ -12,6 +12,11 @@ class BasicPositionSuite extends BasePositionSuite(dialects.Scala213) {
        |Term.ApplyInfix (2 / 3)
        |""".stripMargin
   )
+
+  checkPositions[Term](
+    "a f (b)"
+  )
+
   checkPositions[Term](
     "(1 + 2).foo",
     """|Term.ApplyInfix (1 + 2)