From c3de0caa0a8ddf7bcdc31649def550eb4c6d5558 Mon Sep 17 00:00:00 2001
From: Albert Meltzer <7529386+kitbellew@users.noreply.github.com>
Date: Wed, 6 Mar 2024 09:17:05 -0800
Subject: [PATCH 1/4] Dialect: add `allowQuotedTypeVariables`
---
.../shared/src/main/scala/scala/meta/Dialect.scala | 12 ++++++++++++
.../src/main/scala/scala/meta/dialects/package.scala | 1 +
.../meta/tests/prettyprinters/PublicSuite.scala | 2 +-
3 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/scalameta/dialects/shared/src/main/scala/scala/meta/Dialect.scala b/scalameta/dialects/shared/src/main/scala/scala/meta/Dialect.scala
index 854c6fcd6f..a439938212 100644
--- a/scalameta/dialects/shared/src/main/scala/scala/meta/Dialect.scala
+++ b/scalameta/dialects/shared/src/main/scala/scala/meta/Dialect.scala
@@ -110,6 +110,8 @@ final class Dialect private[meta] (
val allowInfixMods: Boolean,
// Scala 3 splices/quotes
val allowSpliceAndQuote: Boolean,
+ // https://docs.scala-lang.org/sips/quote-pattern-type-variable-syntax.html
+ val allowQuotedTypeVariables: Boolean,
// Scala 3 disallowed symbol literals
val allowSymbolLiterals: Boolean,
// Scala 3 disallowed symbol literals
@@ -235,6 +237,7 @@ final class Dialect private[meta] (
allowTypeMatch = false,
allowInfixMods = false,
allowSpliceAndQuote = false,
+ allowQuotedTypeVariables = false,
allowSymbolLiterals = true,
allowDependentFunctionTypes = false,
allowPostfixStarVarargSplices = false,
@@ -409,6 +412,9 @@ final class Dialect private[meta] (
def withAllowSpliceAndQuote(newValue: Boolean): Dialect = {
privateCopy(allowSpliceAndQuote = newValue)
}
+ def withAllowQuotedTypeVariables(newValue: Boolean): Dialect = {
+ privateCopy(allowQuotedTypeVariables = newValue)
+ }
def withAllowSymbolLiterals(newValue: Boolean): Dialect = {
privateCopy(allowSymbolLiterals = newValue)
}
@@ -524,6 +530,7 @@ final class Dialect private[meta] (
allowTypeMatch: Boolean = this.allowTypeMatch,
allowInfixMods: Boolean = this.allowInfixMods,
allowSpliceAndQuote: Boolean = this.allowSpliceAndQuote,
+ allowQuotedTypeVariables: Boolean = this.allowQuotedTypeVariables,
allowSymbolLiterals: Boolean = this.allowSymbolLiterals,
allowDependentFunctionTypes: Boolean = this.allowDependentFunctionTypes,
allowPostfixStarVarargSplices: Boolean = this.allowPostfixStarVarargSplices,
@@ -585,6 +592,7 @@ final class Dialect private[meta] (
allowTypeMatch = allowTypeMatch,
allowInfixMods = allowInfixMods,
allowSpliceAndQuote = allowSpliceAndQuote,
+ allowQuotedTypeVariables = allowQuotedTypeVariables,
allowSymbolLiterals = allowSymbolLiterals,
allowDependentFunctionTypes = allowDependentFunctionTypes,
allowPostfixStarVarargSplices = allowPostfixStarVarargSplices,
@@ -648,6 +656,7 @@ final class Dialect private[meta] (
allowTypeMatch = allowTypeMatch,
allowInfixMods = allowInfixMods,
allowSpliceAndQuote = allowSpliceAndQuote,
+ allowQuotedTypeVariables = allowQuotedTypeVariables,
allowSymbolLiterals = allowSymbolLiterals,
allowDependentFunctionTypes = allowDependentFunctionTypes,
allowPostfixStarVarargSplices = allowPostfixStarVarargSplices,
@@ -736,6 +745,7 @@ final class Dialect private[meta] (
allowTypeMatch: Boolean,
allowInfixMods: Boolean,
allowSpliceAndQuote: Boolean,
+ allowQuotedTypeVariables: Boolean,
allowSymbolLiterals: Boolean,
allowDependentFunctionTypes: Boolean,
allowPostfixStarVarargSplices: Boolean,
@@ -795,6 +805,7 @@ final class Dialect private[meta] (
&& this.allowTypeMatch == allowTypeMatch
&& this.allowInfixMods == allowInfixMods
&& this.allowSpliceAndQuote == allowSpliceAndQuote
+ && this.allowQuotedTypeVariables == allowQuotedTypeVariables
&& this.allowSymbolLiterals == allowSymbolLiterals
&& this.allowDependentFunctionTypes == allowDependentFunctionTypes
&& this.allowPostfixStarVarargSplices == allowPostfixStarVarargSplices
@@ -857,6 +868,7 @@ final class Dialect private[meta] (
allowTypeMatch = that.allowTypeMatch,
allowInfixMods = that.allowInfixMods,
allowSpliceAndQuote = that.allowSpliceAndQuote,
+ allowQuotedTypeVariables = that.allowQuotedTypeVariables,
allowSymbolLiterals = that.allowSymbolLiterals,
allowDependentFunctionTypes = that.allowDependentFunctionTypes,
allowPostfixStarVarargSplices = that.allowPostfixStarVarargSplices,
diff --git a/scalameta/dialects/shared/src/main/scala/scala/meta/dialects/package.scala b/scalameta/dialects/shared/src/main/scala/scala/meta/dialects/package.scala
index 6418b7bc98..b9c3025962 100644
--- a/scalameta/dialects/shared/src/main/scala/scala/meta/dialects/package.scala
+++ b/scalameta/dialects/shared/src/main/scala/scala/meta/dialects/package.scala
@@ -153,6 +153,7 @@ package object dialects {
.withAllowParamClauseInterleaving(true)
implicit val Scala34: Dialect = Scala33
+ .withAllowQuotedTypeVariables(true)
implicit val Scala3: Dialect = Scala34
diff --git a/tests/shared/src/test/scala/scala/meta/tests/prettyprinters/PublicSuite.scala b/tests/shared/src/test/scala/scala/meta/tests/prettyprinters/PublicSuite.scala
index c8587f4239..77a41aa060 100644
--- a/tests/shared/src/test/scala/scala/meta/tests/prettyprinters/PublicSuite.scala
+++ b/tests/shared/src/test/scala/scala/meta/tests/prettyprinters/PublicSuite.scala
@@ -109,7 +109,7 @@ class PublicSuite extends TreeSuiteBase {
}
test("scala.meta.dialects.Scala33.toString") {
- assertNoDiff(scala.meta.dialects.Scala33.toString, "Scala34")
+ assertNoDiff(scala.meta.dialects.Scala33.toString, "Scala33")
}
test("scala.meta.dialects.Scala34.toString") {
From a5ffaa253eee88066d1366af8902ab11dc7a61e5 Mon Sep 17 00:00:00 2001
From: Albert Meltzer <7529386+kitbellew@users.noreply.github.com>
Date: Wed, 6 Mar 2024 07:58:59 -0800
Subject: [PATCH 2/4] MacroSuite: add test with quoted type blocks
---
.../meta/tests/parsers/dotty/MacroSuite.scala | 24 +++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/MacroSuite.scala b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/MacroSuite.scala
index eaeef3c3f5..58ca540d6e 100644
--- a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/MacroSuite.scala
+++ b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/MacroSuite.scala
@@ -440,4 +440,28 @@ class MacroSuite extends BaseDottySuite {
)
)
}
+
+ test("#3610 1") {
+ val code = "'[ List[Int] ]"
+ val tree = Term.QuotedMacroType(Type.Apply(pname("List"), List(pname("Int"))))
+ runTestAssert[Stat](code)(tree)
+ }
+
+ test("#3610 2") {
+ val code = "'[ type t = Int; List[t] ]"
+ val error =
+ """|:1: error: identifier expected but type found
+ |'[ type t = Int; List[t] ]
+ | ^""".stripMargin
+ runTestError[Stat](code, error)
+ }
+
+ test("#3610 3") {
+ val code = "'[ type tail <: Tuple; *:[Int, tail] ]"
+ val error =
+ """|:1: error: identifier expected but type found
+ |'[ type tail <: Tuple; *:[Int, tail] ]
+ | ^""".stripMargin
+ runTestError[Stat](code, error)
+ }
}
From ef07d6312f29036a3e0d90c060a543fc6b60275e Mon Sep 17 00:00:00 2001
From: Albert Meltzer <7529386+kitbellew@users.noreply.github.com>
Date: Wed, 6 Mar 2024 10:18:28 -0800
Subject: [PATCH 3/4] Trees: add `Type.Block`, containing TypeDefs
To support SIP-53, it's been defined as `TypeBlock`
under https://dotty.epfl.ch/docs/internals/syntax.html.
---
.../shared/src/main/scala/scala/meta/Trees.scala | 3 +++
.../meta/internal/prettyprinters/TreeSyntax.scala | 1 +
.../scala/meta/tests/api/SurfaceSuite.scala | 1 +
.../scala/meta/tests/trees/ReflectionSuite.scala | 3 ++-
.../tests/prettyprinters/SyntacticSuite.scala | 15 +++++++++++++++
5 files changed, 22 insertions(+), 1 deletion(-)
diff --git a/scalameta/trees/shared/src/main/scala/scala/meta/Trees.scala b/scalameta/trees/shared/src/main/scala/scala/meta/Trees.scala
index bd9e1013fc..42c836f1bd 100644
--- a/scalameta/trees/shared/src/main/scala/scala/meta/Trees.scala
+++ b/scalameta/trees/shared/src/main/scala/scala/meta/Trees.scala
@@ -424,6 +424,9 @@ object Type {
@ast class ParamClause(values: List[Param]) extends Member.ParamClause
@ast class Match(tpe: Type, cases: List[TypeCase] @nonEmpty) extends Type with Tree.WithCases
+
+ @ast class Block(typeDefs: List[Stat.TypeDef], tpe: Type) extends Type
+
def fresh(): Type.Name = fresh("fresh")
def fresh(prefix: String): Type.Name = Type.Name(prefix + Fresh.nextId())
}
diff --git a/scalameta/trees/shared/src/main/scala/scala/meta/internal/prettyprinters/TreeSyntax.scala b/scalameta/trees/shared/src/main/scala/scala/meta/internal/prettyprinters/TreeSyntax.scala
index 0cd1399926..a533237411 100644
--- a/scalameta/trees/shared/src/main/scala/scala/meta/internal/prettyprinters/TreeSyntax.scala
+++ b/scalameta/trees/shared/src/main/scala/scala/meta/internal/prettyprinters/TreeSyntax.scala
@@ -705,6 +705,7 @@ object TreeSyntax {
}
val cbounds = r(t.cbounds.map { s(kw(":"), " ", _) })
s(w(mods, " "), variance, t.name, t.tparamClause, tbounds, vbounds, cbounds)
+ case t: Type.Block => s(w(r(t.typeDefs, "; "), "; "), t.tpe)
// Pat
case t: Pat.Var =>
diff --git a/tests/jvm/src/test/scala-2.13/scala/meta/tests/api/SurfaceSuite.scala b/tests/jvm/src/test/scala-2.13/scala/meta/tests/api/SurfaceSuite.scala
index 6777f437c8..f70143c101 100644
--- a/tests/jvm/src/test/scala-2.13/scala/meta/tests/api/SurfaceSuite.scala
+++ b/tests/jvm/src/test/scala-2.13/scala/meta/tests/api/SurfaceSuite.scala
@@ -469,6 +469,7 @@ class SurfaceSuite extends FunSuite {
|scala.meta.Type.Apply
|scala.meta.Type.ApplyInfix
|scala.meta.Type.ArgClause
+ |scala.meta.Type.Block
|scala.meta.Type.Bounds
|scala.meta.Type.ByName
|scala.meta.Type.ContextFunction
diff --git a/tests/jvm/src/test/scala-2.13/scala/meta/tests/trees/ReflectionSuite.scala b/tests/jvm/src/test/scala-2.13/scala/meta/tests/trees/ReflectionSuite.scala
index 717e41595b..c20297fa2e 100644
--- a/tests/jvm/src/test/scala-2.13/scala/meta/tests/trees/ReflectionSuite.scala
+++ b/tests/jvm/src/test/scala-2.13/scala/meta/tests/trees/ReflectionSuite.scala
@@ -24,7 +24,7 @@ class ReflectionSuite extends FunSuite {
val sym = symbolOf[scala.meta.Tree]
assert(sym.isRoot)
val root = sym.asRoot
- assertEquals((root.allBranches.length, root.allLeafs.length), (50, 408))
+ assertEquals((root.allBranches.length, root.allLeafs.length), (50, 410))
}
test("If") {
@@ -98,6 +98,7 @@ class ReflectionSuite extends FunSuite {
|List[scala.meta.Mod]
|List[scala.meta.Pat]
|List[scala.meta.Source]
+ |List[scala.meta.Stat.TypeDef]
|List[scala.meta.Stat]
|List[scala.meta.Term.Name]
|List[scala.meta.Term.Param]
diff --git a/tests/shared/src/test/scala/scala/meta/tests/prettyprinters/SyntacticSuite.scala b/tests/shared/src/test/scala/scala/meta/tests/prettyprinters/SyntacticSuite.scala
index 13e434ce9e..7107f6ce56 100644
--- a/tests/shared/src/test/scala/scala/meta/tests/prettyprinters/SyntacticSuite.scala
+++ b/tests/shared/src/test/scala/scala/meta/tests/prettyprinters/SyntacticSuite.scala
@@ -2064,4 +2064,19 @@ class SyntacticSuite extends scala.meta.tests.parsers.ParseSuite {
)
}
+ test("#3610 Type.Block 1") {
+ val tree = Type.Block(Nil, pname("Int"))
+ assertEquals(tree.syntax, "Int")
+ }
+
+ test("#3610 Type.Block 2") {
+ val tree = Type.Block(List(Decl.Type(Nil, pname("t"), Nil, noBounds)), pname("t"))
+ assertEquals(tree.syntax, "type t; t")
+ }
+
+ test("#3610 Type.Block 3") {
+ val tree = Type.Block(List(Defn.Type(Nil, pname("t"), Nil, pname("Int"))), pname("t"))
+ assertEquals(tree.syntax, "type t = Int; t")
+ }
+
}
From 3e44535f2c2ac5db8778cee53d0f8d9db0959198 Mon Sep 17 00:00:00 2001
From: Albert Meltzer <7529386+kitbellew@users.noreply.github.com>
Date: Wed, 6 Mar 2024 10:59:38 -0800
Subject: [PATCH 4/4] ScalametaParser: parse quoted type blocks
---
.../internal/parsers/ScalametaParser.scala | 20 +++++++++++++++-
.../meta/tests/parsers/dotty/MacroSuite.scala | 24 +++++++++++--------
2 files changed, 33 insertions(+), 11 deletions(-)
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 d9ed4b115d..81fee45e10 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
@@ -849,6 +849,23 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
}).getOrElse(t)
}
+ def typeBlock(): Type =
+ // TypeBlock, https://dotty.epfl.ch/docs/internals/syntax.html#expressions-3
+ if (dialect.allowQuotedTypeVariables && token.is[KwType]) autoPos {
+ val typeDefs = listBy[Stat.TypeDef] { buf =>
+ @tailrec def iter(): Unit = {
+ buf += typeDefOrDcl(Nil)
+ if (StatSep(token)) {
+ next()
+ if (token.is[KwType]) iter()
+ }
+ }
+ iter()
+ }
+ Type.Block(typeDefs, typ())
+ }
+ else typ()
+
private def typeFuncOnArrow(
paramPos: Int,
params: List[Type],
@@ -2297,7 +2314,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
next()
token match {
case _: LeftBrace => Term.QuotedMacroExpr(autoPos(inBracesOnOpen(blockWithinDelims())))
- case _: LeftBracket => Term.QuotedMacroType(inBracketsOnOpen(typ()))
+ case _: LeftBracket => Term.QuotedMacroType(inBracketsOnOpen(typeBlock()))
case t => syntaxError("Quotation only works for expressions and types", at = t)
}
})
@@ -2982,6 +2999,7 @@ class ScalametaParser(input: Input)(implicit dialect: Dialect) { parser =>
* initiated from non-pattern context.
*/
def typ() = outPattern.typ()
+ private def typeBlock() = outPattern.typeBlock()
def typeIndentedOpt() = outPattern.typeIndentedOpt()
def quasiquoteType() = outPattern.quasiquoteType()
def entrypointType() = outPattern.entrypointType()
diff --git a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/MacroSuite.scala b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/MacroSuite.scala
index 58ca540d6e..a9eddb30db 100644
--- a/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/MacroSuite.scala
+++ b/tests/shared/src/test/scala/scala/meta/tests/parsers/dotty/MacroSuite.scala
@@ -449,19 +449,23 @@ class MacroSuite extends BaseDottySuite {
test("#3610 2") {
val code = "'[ type t = Int; List[t] ]"
- val error =
- """|:1: error: identifier expected but type found
- |'[ type t = Int; List[t] ]
- | ^""".stripMargin
- runTestError[Stat](code, error)
+ val tree = Term.QuotedMacroType(
+ Type.Block(
+ List(Defn.Type(Nil, pname("t"), Nil, pname("Int"), noBounds)),
+ Type.Apply(pname("List"), List(pname("t")))
+ )
+ )
+ runTestAssert[Stat](code)(tree)
}
test("#3610 3") {
val code = "'[ type tail <: Tuple; *:[Int, tail] ]"
- val error =
- """|:1: error: identifier expected but type found
- |'[ type tail <: Tuple; *:[Int, tail] ]
- | ^""".stripMargin
- runTestError[Stat](code, error)
+ val tree = Term.QuotedMacroType(
+ Type.Block(
+ List(Decl.Type(Nil, pname("tail"), Nil, bounds(hi = "Tuple"))),
+ Type.Apply(pname("*:"), List(pname("Int"), pname("tail")))
+ )
+ )
+ runTestAssert[Stat](code)(tree)
}
}