Repository: scalameta/scalameta
Pull request #4233 (2025-04-23 23:53:33)

ScalametaParser: allow indented infix type args
Follow-on to #4227.


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 d51b41ee7..ea49af000 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
@@ -1998,7 +1998,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) {
         // Infix chain has ended with a postfix expression.
         // This never happens in the running example.
         if (targs.nonEmpty)
-          syntaxError("type application is not allowed for postfix operators", at = currToken)
+          syntaxError("type application is not allowed for postfix operators", at = targs)
         val finQual = getPrevLhs(op)
         val term: Term = atPos(getLhsStartPos(finQual), op)(Term.SelectPostfix(finQual, op))
         Left(term)
@@ -2009,7 +2009,9 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) {
       def getPostfixOrNextRhs(op: Term.Name): Either[Term, Term] = {
         // Infix chain continues.
         // In the running example, we're at `a [+] b`.
-        val targs = if (at[LeftBracket]) exprTypeArgs() else emptyTypeArgs
+        val hasTypeArgs = at[LeftBracket] ||
+          isIndentingOrEOL(nonOptBracesOK = false) && tryAhead[LeftBracket]
+        val targs = if (hasTypeArgs) exprTypeArgs() else emptyTypeArgs
 
         // Check whether we're still infix or already postfix by testing the current token.
         // In the running example, we're at `a + [b]` (infix).
@@ -2191,7 +2193,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) {
         }
         if (isOk(t)) {
           var app: Term = t
-          while (at[LeftBracket]) app = addPos(Term.ApplyType(app, exprTypeArgs()))
+          doWhile { app = addPos(Term.ApplyType(app, exprTypeArgs())) }(at[LeftBracket])
           simpleExprRest(app, canApply = true, startPos = startPos)
         } else addPos(t)
       case tok @ (_: LeftParen | _: LeftBrace) if canApply =>


Test patch:
diff --git a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/InfixSuite.scala b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/InfixSuite.scala
index 7727eaedf..e00b16c2c 100644
--- a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/InfixSuite.scala
+++ b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/InfixSuite.scala
@@ -826,4 +826,48 @@ class InfixSuite extends BaseDottySuite {
     runTestAssert[Stat](code, layout)(tree)
   }
 
+  test("infix with inline type args") {
+    val code =
+      """|a op [T]
+         |  b
+         |""".stripMargin
+    val layout = "a op[T] b"
+    val tree = tinfix("a", "op", List("T"), "b")
+    runTestAssert[Stat](code, layout)(tree)
+  }
+
+  test("infix with indented type args") {
+    val code =
+      """|a op
+         |  [T]
+         |  b
+         |""".stripMargin
+    val layout = "a op[T] b"
+    val tree = tinfix("a", "op", List("T"), "b")
+    runTestAssert[Stat](code, layout)(tree)
+  }
+
+  test("postfix with illegal inline type args") {
+    val code =
+      """|a op [T]
+         |""".stripMargin
+    val error =
+      """|<input>:1: error: type application is not allowed for postfix operators
+         |a op [T]
+         |     ^""".stripMargin
+    runTestError[Stat](code, error)
+  }
+
+  test("postfix with illegal indented type args") {
+    val code =
+      """|a op
+         |  [T]
+         |""".stripMargin
+    val error =
+      """|<input>:2: error: type application is not allowed for postfix operators
+         |  [T]
+         |  ^""".stripMargin
+    runTestError[Stat](code, error)
+  }
+
 }