Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support quoted type variables in Dialect and ScalametaParser #3614

Merged
merged 4 commits into from Mar 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 12 additions & 0 deletions scalameta/dialects/shared/src/main/scala/scala/meta/Dialect.scala
Expand Up @@ -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
Expand Down Expand Up @@ -235,6 +237,7 @@ final class Dialect private[meta] (
allowTypeMatch = false,
allowInfixMods = false,
allowSpliceAndQuote = false,
allowQuotedTypeVariables = false,
allowSymbolLiterals = true,
allowDependentFunctionTypes = false,
allowPostfixStarVarargSplices = false,
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -585,6 +592,7 @@ final class Dialect private[meta] (
allowTypeMatch = allowTypeMatch,
allowInfixMods = allowInfixMods,
allowSpliceAndQuote = allowSpliceAndQuote,
allowQuotedTypeVariables = allowQuotedTypeVariables,
allowSymbolLiterals = allowSymbolLiterals,
allowDependentFunctionTypes = allowDependentFunctionTypes,
allowPostfixStarVarargSplices = allowPostfixStarVarargSplices,
Expand Down Expand Up @@ -648,6 +656,7 @@ final class Dialect private[meta] (
allowTypeMatch = allowTypeMatch,
allowInfixMods = allowInfixMods,
allowSpliceAndQuote = allowSpliceAndQuote,
allowQuotedTypeVariables = allowQuotedTypeVariables,
allowSymbolLiterals = allowSymbolLiterals,
allowDependentFunctionTypes = allowDependentFunctionTypes,
allowPostfixStarVarargSplices = allowPostfixStarVarargSplices,
Expand Down Expand Up @@ -736,6 +745,7 @@ final class Dialect private[meta] (
allowTypeMatch: Boolean,
allowInfixMods: Boolean,
allowSpliceAndQuote: Boolean,
allowQuotedTypeVariables: Boolean,
allowSymbolLiterals: Boolean,
allowDependentFunctionTypes: Boolean,
allowPostfixStarVarargSplices: Boolean,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down
Expand Up @@ -153,6 +153,7 @@ package object dialects {
.withAllowParamClauseInterleaving(true)

implicit val Scala34: Dialect = Scala33
.withAllowQuotedTypeVariables(true)

implicit val Scala3: Dialect = Scala34

Expand Down
Expand Up @@ -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],
Expand Down Expand Up @@ -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)
}
})
Expand Down Expand Up @@ -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()
Expand Down
3 changes: 3 additions & 0 deletions scalameta/trees/shared/src/main/scala/scala/meta/Trees.scala
Expand Up @@ -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())
}
Expand Down
Expand Up @@ -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 =>
Expand Down
Expand Up @@ -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
Expand Down
Expand Up @@ -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") {
Expand Down Expand Up @@ -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]
Expand Down
Expand Up @@ -440,4 +440,32 @@ 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 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 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)
}
}
Expand Up @@ -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") {
Expand Down
Expand Up @@ -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")
}

}