Repository: scalameta/scalameta (scala)

Issue:
Bad 2.x invalid parse error w/ extra newline before generic param of super type
Consider the following scala code:
```scala
trait F[A]
trait B extends F
  [Int]
```
Although miserably formatted, it should parse as `trait F` and `trait B` in at least Scala 2.12, 2.13, 3.6 (I think all Scala versions, but I'm not sure). However, Scalameta doesn't like it:

```
scala> "trait F[A]\ntrait B extends F\n  [Int]".parse[Source].get
scala.meta.parsers.ParseException: <input>:3: error: illegal start of definition `[`
  [Int]
  ^
  at scala.meta.internal.parsers.Reporter.syntaxError(Reporter.scala:16)
  at scala.meta.internal.parsers.Reporter.syntaxError$(Reporter.scala:16)
  at scala.meta.internal.parsers.Reporter$$anon$1.syntaxError(Reporter.scala:22)
  at scala.meta.internal.parsers.Reporter.syntaxError(Reporter.scala:17)
  at scala.meta.internal.parsers.Reporter.syntaxError$(Reporter.scala:17)
  at scala.meta.internal.parsers.Reporter$$anon$1.syntaxError(Reporter.scala:22)
  at scala.meta.internal.parsers.ScalametaParser.statSeqBuf(ScalametaParser.scala:4236)
  at scala.meta.internal.parsers.ScalametaParser.$anonfun$statSeq$1(ScalametaParser.scala:4224)
  at scala.meta.internal.parsers.ScalametaParser.$anonfun$statSeq$1$adapted(ScalametaParser.scala:4224)
  at scala.meta.internal.parsers.ScalametaParser.scala$meta$internal$parsers$ScalametaParser$$listBy(ScalametaParser.scala:555)
  at scala.meta.internal.parsers.ScalametaParser.statSeq(ScalametaParser.scala:4224)
  at scala.meta.internal.parsers.ScalametaParser.bracelessPackageStats$1(ScalametaParser.scala:4394)
  at scala.meta.internal.parsers.ScalametaParser.$anonfun$source$1(ScalametaParser.scala:4397)
  at scala.meta.internal.parsers.ScalametaParser.atPos(ScalametaParser.scala:324)
  at scala.meta.internal.parsers.ScalametaParser.autoPos(ScalametaParser.scala:379)
  at scala.meta.internal.parsers.ScalametaParser.source(ScalametaParser.scala:4362)
  at scala.meta.internal.parsers.ScalametaParser.entrypointSource(ScalametaParser.scala:4400)
  at scala.meta.internal.parsers.ScalametaParser.parseSourceImpl(ScalametaParser.scala:114)
  at scala.meta.internal.parsers.ScalametaParser.$anonfun$parseSource$1(ScalametaParser.scala:111)
  at scala.meta.internal.parsers.ScalametaParser.parseRuleAfterBOF(ScalametaParser.scala:60)
  at scala.meta.internal.parsers.ScalametaParser.parseRule(ScalametaParser.scala:55)
  at scala.meta.internal.parsers.ScalametaParser.parseSource(ScalametaParser.scala:111)
  at scala.meta.parsers.Parse$.$anonfun$parseSource$1(Parse.scala:30)
  at scala.meta.parsers.Parse$$anon$1.apply(Parse.scala:37)
  at scala.meta.parsers.Api$XtensionParseDialectInput.parse(Api.scala:22)
  at scala.meta.parsers.Api$XtensionParseInputLike.parse(Api.scala:13)
  ... 59 elided
```

Pull request:
Fixes https://github.com/scalameta/scalameta/issues/4305

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 8f7364fec4..d5cf574e4c 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
@@ -1125,13 +1125,17 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) {
     }
 
     @tailrec
-    private final def simpleTypeRest(t: Type, startPos: Int): Type = currToken match {
-      case _: Hash =>
-        next()
-        simpleTypeRest(autoEndPos(startPos)(Type.Project(t, typeName())), startPos)
-      case _: LeftBracket =>
-        simpleTypeRest(autoEndPos(startPos)(Type.Apply(t, typeArgsInBrackets())), startPos)
-      case _ => t
+    private final def simpleTypeRest(t: Type, startPos: Int): Type = {
+      def onLeftBracket() = autoEndPos(startPos)(Type.Apply(t, typeArgsInBrackets()))
+      currToken match {
+        case _: Hash =>
+          next()
+          simpleTypeRest(autoEndPos(startPos)(Type.Project(t, typeName())), startPos)
+        case _: LeftBracket => simpleTypeRest(onLeftBracket(), startPos)
+        case _: AtEOL if isIndentingOrEOL(nonOptBracesOK = true) && tryAhead[LeftBracket] =>
+          simpleTypeRest(onLeftBracket(), startPos)
+        case _ => t
+      }
     }
 
     def typesInParens(): List[Type] =


Test patch:
diff --git a/tests/shared/src/test/scala/scala/meta/tests/parsers/TemplateSuite.scala b/tests/shared/src/test/scala/scala/meta/tests/parsers/TemplateSuite.scala
index a96f384588..c85d737df0 100644
--- a/tests/shared/src/test/scala/scala/meta/tests/parsers/TemplateSuite.scala
+++ b/tests/shared/src/test/scala/scala/meta/tests/parsers/TemplateSuite.scala
@@ -189,6 +189,45 @@ class TemplateSuite extends ParseSuite {
     checkStat(code, code)(tree)
   }
 
+  test("blank-after-template") {
+    val code =
+      """|
+         |trait B extends F
+         |  [Int]
+         |""".stripMargin
+    val code2 =
+      """|
+         |trait B extends F
+         |  [
+         |    Int]
+         |""".stripMargin
+    val code3 =
+      """|
+         |trait B extends F
+         |[Int]
+         |""".stripMargin
+
+    val tree = Defn.Trait(
+      Nil,
+      pname("B"),
+      Type.ParamClause(Nil),
+      EmptyCtor(),
+      Template(
+        None,
+        List(Init(
+          Type.Apply(Type.Name("F"), Type.ArgClause(List(Type.Name("Int")))),
+          Name.Anonymous(),
+          Seq.empty[Term.ArgClause]
+        )),
+        Template.Body(None, Nil),
+        Nil
+      )
+    )
+    checkStat(code, "trait B extends F[Int]")(tree)
+    checkStat(code2, "trait B extends F[Int]")(tree)
+    checkStat(code3, "trait B extends F[Int]")(tree)
+  }
+
   test("blank after template 1") {
     val code =
       """|class DerivationSpec {
diff --git a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/NewFunctionsSuite.scala b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/NewFunctionsSuite.scala
index 0329675c5f..bc678fada0 100644
--- a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/NewFunctionsSuite.scala
+++ b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/NewFunctionsSuite.scala
@@ -742,4 +742,42 @@ class NewFunctionsSuite extends BaseDottySuite {
     runTestAssert[Stat](code, layout)(tree)
   }
 
+  test("polymorphic-no-indent") {
+    val code =
+      """|def foo =
+         |  trait B extends F
+         |  [X, Y] => Z
+         |
+         |""".stripMargin
+    val layout =
+      """|def foo = {
+         |  trait B extends F
+         |  [X, Y] => Z
+         |}
+         |""".stripMargin
+
+    val tree = Defn.Def(
+      Nil,
+      Term.Name("foo"),
+      Nil,
+      None,
+      Term.Block(List(
+        Defn.Trait(
+          Nil,
+          Type.Name("B"),
+          Type.ParamClause(Nil),
+          EmptyCtor(),
+          Template(
+            None,
+            List(Init(Type.Name("F"), Name.Anonymous(), Seq.empty[Term.ArgClause])),
+            Template.Body(None, Nil),
+            Nil
+          )
+        ),
+        Term.PolyFunction(Type.ParamClause(List(pparam("X"), pparam("Y"))), Term.Name("Z"))
+      ))
+    )
+    runTestAssert[Stat](code, layout)(tree)
+  }
+
 }
diff --git a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/TemplateSuite.scala b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/TemplateSuite.scala
new file mode 100644
index 0000000000..54ef4de5ef
--- /dev/null
+++ b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/TemplateSuite.scala
@@ -0,0 +1,45 @@
+package scala.meta.tests.parsers.dotty
+
+import scala.meta._
+
+class TemplateSuite extends BaseDottySuite {
+
+  test("blank-after-template") {
+    val code =
+      """|
+         |trait B extends F
+         |  [Int]
+         |""".stripMargin
+    val code2 =
+      """|
+         |trait B extends F
+         |  [
+         |    Int]
+         |""".stripMargin
+    val code3 =
+      """|
+         |trait B extends F
+         |[Int]
+         |""".stripMargin
+
+    val tree = Defn.Trait(
+      Nil,
+      pname("B"),
+      Type.ParamClause(Nil),
+      EmptyCtor(),
+      Template(
+        None,
+        List(Init(
+          Type.Apply(Type.Name("F"), Type.ArgClause(List(Type.Name("Int")))),
+          Name.Anonymous(),
+          Seq.empty[Term.ArgClause]
+        )),
+        Template.Body(None, Nil),
+        Nil
+      )
+    )
+    checkStat(code, "trait B extends F[Int]")(tree)
+    checkStat(code2, "trait B extends F[Int]")(tree)
+    runTestError[Source](code3, "error: illegal start of definition `[`")
+  }
+}