Repository: scalameta/scalameta (scala)

Issue:
Parsing issue in ApplyInfix with a tuple and braces in a new line
Using:
  - Scalameta version 4.7.7
  - Scala 2.13.10

Steps to reproduce:
```scala
import scala.meta._

val term = 
  q"""
    Foo bar (2, 3)
    { a }
  """

term.structure
```

The above expression should be parsed has an Term.ApplyInfix, but it is being parsed as a Term.Block.
It should be equivalent to parsing `Foo bar (2, 3) { a }` or `Foo bar ((2, 3){a})`.

actual:
```scala
Term.Block(
  stats = List(
    Term.ApplyInfix(
      lhs = Term.Name(value = "Foo"),
      op = Term.Name(value = "bar"),
      targClause = Type.ArgClause(values = List()),
      argClause = Term.ArgClause(values = List(Lit.Int(value = 2), Lit.Int(value = 3)), mod = None)
    ),
    Term.Block(stats = List(Term.Name(value = "a")))
  )
)
```

expected:
```scala
Term.ApplyInfix(
  lhs = Term.Name(value = "Foo"),
  op = Term.Name(value = "bar"),
  targClause = Type.ArgClause(values = List()),
  argClause = Term.ArgClause(
    values = List(
      Term.Apply(
        fun = Term.Tuple(args = List(Lit.Int(value = 2), Lit.Int(value = 3))),
        argClause = Term.ArgClause(values = List(Term.Block(stats = List(Term.Name(value = "a")))), mod = None)
      )
    ),
    mod = None
  )
)
```

---

Here is a small Scala 2.13 example that uses this syntax:
```scala
object Foo {
  def bar(tuple: (Int, Int)): List[Int] = List(tuple._1, tuple._2)
}
implicit class Extension(tuple: (Int, Int)) {
  def apply(n: Int): (Int, Int) = (tuple._1 + n, tuple._2 + n)
}
val a = 5
println(
  Foo bar (2, 3)
  { a }
)
// prints
// List(7, 8)
```

Pull request:
Fixes #3138.

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 11448711ad..3204cdea13 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
@@ -2306,10 +2306,13 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
       val args =
         if (isBrace) checkNoTripleDot(blockExpr(allowRepeated = true)) :: Nil
         else inParensOnOpenOr(argumentExprsInParens(location))(Nil)
+      def getRest() = {
+        findRep(args).foreach(x => syntaxError("repeated argument not allowed here", at = x))
+        simpleExprRest(makeTupleTerm(lpPos, args), canApply = true, startPos = lpPos)
+      }
       token match {
-        case Dot() | LeftBracket() | LeftParen() | LeftBrace() | Underscore() =>
-          findRep(args).foreach(x => syntaxError("repeated argument not allowed here", at = x))
-          simpleExprRest(makeTupleTerm(lpPos, args), canApply = true, startPos = lpPos)
+        case _: Dot | _: OpenDelim | _: Underscore => getRest()
+        case _: LF if peekToken.is[LeftBrace] => next(); getRest()
         case _ =>
           makeTupleTerm { arg =>
             val res = maybeAnonymousFunction(arg)


Test patch:
diff --git a/tests/shared/src/test/scala/scala/meta/tests/parsers/TermSuite.scala b/tests/shared/src/test/scala/scala/meta/tests/parsers/TermSuite.scala
index 69dfbb5e31..6339dfaac9 100644
--- a/tests/shared/src/test/scala/scala/meta/tests/parsers/TermSuite.scala
+++ b/tests/shared/src/test/scala/scala/meta/tests/parsers/TermSuite.scala
@@ -1579,4 +1579,67 @@ class TermSuite extends ParseSuite {
     )
   }
 
+  test("#3138: infix with a following block") {
+    val codeOnNextLine =
+      """|{
+         |  Foo bar (2, 3)
+         |  { a }
+         |}
+         |""".stripMargin
+    val codeOnSameLine =
+      """|{
+         |  Foo bar (2, 3) { a }
+         |}
+         |""".stripMargin
+    val syntaxOnSameLine =
+      """|{
+         |  Foo bar (2, 3) {
+         |    a
+         |  }
+         |}
+         |""".stripMargin
+    val treeOnSameLine = Term.Block(
+      Term.ApplyInfix(
+        Term.Name("Foo"),
+        Term.Name("bar"),
+        Nil,
+        Term.Apply(
+          Term.Tuple(List(Lit.Int(2), Lit.Int(3))),
+          List(Term.Block(List(Term.Name("a"))))
+        ) :: Nil
+      ) :: Nil
+    )
+    runTestAssert[Stat](codeOnNextLine, Some(syntaxOnSameLine))(treeOnSameLine)
+    runTestAssert[Stat](codeOnSameLine, Some(syntaxOnSameLine))(treeOnSameLine)
+  }
+
+  test("#3138: apply with a following block") {
+    val codeOnNextLine =
+      """|{
+         |  Foo.bar (2, 3)
+         |  { a }
+         |}
+         |""".stripMargin
+    val codeOnSameLine =
+      """|{
+         |  Foo.bar (2, 3) { a }
+         |}
+         |""".stripMargin
+    val syntaxOnSameLine =
+      """|{
+         |  Foo.bar(2, 3) {
+         |    a
+         |  }
+         |}
+         |""".stripMargin
+    val treeOnSameLine = Term.Block(
+      Term.Apply(
+        Term.Apply(Term.Select(Term.Name("Foo"), Term.Name("bar")), List(Lit.Int(2), Lit.Int(3))),
+        List(Term.Block(List(Term.Name("a"))))
+      ) :: Nil
+    )
+    runTestAssert[Stat](codeOnNextLine, Some(syntaxOnSameLine))(treeOnSameLine)
+    runTestAssert[Stat](codeOnSameLine, Some(syntaxOnSameLine))(treeOnSameLine)
+  }
+
 }