问题描述
我正在尝试使用宏来构建简单的类型类IsEnum[T]
。
如果kNownDirectSubclasses
,我使用T
获取所有直接子类,确保T
是密封特征,并且所有子类都是case对象(使用subSymbol.asClass.isModuleClass && subSymbol.asClass.isCaseClass
)
现在,我正在尝试使用子类引用的case对象构建一个Seq
。
正在使用一种解决方法:
Ident(subSymbol.asInstanceOf[scala.reflect.internal.Symbols#Symbol].sourceModule.asInstanceOf[Symbol])
但是我从其他question那里复制了它,但看起来似乎很错误。为什么行得通?有没有更清洁的方法来实现这一目标?
解决方法
在2.13中,您可以实现scala.ValueOf
val instanceTree = c.inferImplicitValue(appliedType(typeOf[ValueOf[_]].typeConstructor,subSymbol.asClass.toType))
q"$instanceTree.value"
树会有所不同
sealed trait A
object A {
case object B extends A
case object C extends A
}
//scalac: Seq(new scala.ValueOf(A.this.B).value,new scala.ValueOf(A.this.C).value)
但在运行时仍为Seq(B,C)
。
在2.12版本中,可以使用shapeless.Witness
代替ValueOf
val instanceTree = c.inferImplicitValue(appliedType(typeOf[Witness.Aux[_]].typeConstructor,subSymbol.asClass.toType))
q"$instanceTree.value"
//scalac: Seq(Witness.mkWitness[App.A.B.type](A.this.B.asInstanceOf[App.A.B.type]).value,Witness.mkWitness[App.A.C.type](A.this.C.asInstanceOf[App.A.C.type]).value)
libraryDependencies += "com.chuusai" %% "shapeless" % "2.4.0-M1" // in 2.3.3 it doesn't work
在Shapeless中,他们使用了
subSymbol.asClass.toType match {
case ref @ TypeRef(_,sym,_) if sym.isModuleClass => mkAttributedQualifier(ref)
}
或者在我们的情况下简单
mkAttributedQualifier(subSymbol.asClass.toType)
但是他们的mkAttributedQualifier
也使用向下转换来编译器内部,并且获得的树就像Seq(A.this.B,A.this.C)
。
也
Ident(subSymbol.companionSymbol)
似乎可以正常工作(树为Seq(B,C)
),但不建议使用.companionSymbol
(在scaladocs中,其写为“可能为模块类返回意外结果”,即对象)。
与 @MateuszKubuszok 在其图书馆enumz中使用的方法类似,您也可以尝试
val objectName = symbol.fullName
c.typecheck(c.parse(s"$objectName"))
树是Seq(App.A.B,App.A.C)
。
最后,如果您对树Seq(B,C)
(而不是更复杂的树)感兴趣,似乎可以替换
Ident(subSymbol.asInstanceOf[scala.reflect.internal.Symbols#Symbol].sourceModule.asInstanceOf[Symbol])
更常规
Ident(subSymbol.owner.info.decl(subSymbol.name.toTermName))