问题描述
考虑以下简单的宏片段:
val lstConstructor = weakTypeTag[List[String]].tpe.typeConstructor
println(c.typecheck(tq"$lstConstructor[String]",mode = c.TYPEmode))
这似乎是一段非常简单的代码失败,并出现以下信息异常:
[error] scala.reflect.macros.TypecheckException: Any does not take type parameters
[error] at scala.reflect.macros.contexts.Typers.$anonfun$typecheck$3(Typers.scala:44)
[error] at scala.reflect.macros.contexts.Typers.$anonfun$typecheck$2(Typers.scala:38)
[error] at scala.reflect.macros.contexts.Typers.doTypecheck$1(Typers.scala:37)
[error] at scala.reflect.macros.contexts.Typers.typecheck(Typers.scala:51)
[error] at scala.reflect.macros.contexts.Typers.typecheck$(Typers.scala:32)
[error] at scala.reflect.macros.contexts.Context.typecheck(Context.scala:18)
[error] at scala.reflect.macros.contexts.Context.typecheck(Context.scala:18)
UPD:
println(c.typecheck(tq"List[String]",mode = c.TYPEmode))
工作正常
解决方法
欢迎来到无类型编译器的无证之地。
拟引用 tq"List[String]"
生成以下树:
AppliedTypeTree(Ident(TypeName("List")),List(Ident(TypeName("String"))))
请注意,第一个参数不是 Type
,而是 Ident
。因此,您的准引用 tq"$lstConstructor[String]"
会悄悄地用空的 TypeTree()
替换不适当类型的树:
AppliedTypeTree(TypeTree(),List(Ident(TypeName("String"))))
我很确定它等价于 Any
,所以你会得到错误。
现在,要从 Ident
中获取 Type
,您可以使用 .typeSymbol
方法(尽管 idents 没有类型参数信息,但您是否这样做并不重要.typeConstructor
之前与否)。这应该有效:
c.typecheck(tq"${lstConstructor.typeSymbol}[String]",mode = c.TYPEmode)
Scastie 上的代码(使用运行时 Universe)here。