宏中的错误
宏可以通过使用 Context
来触发编译器警告和错误。
假设我们在糟糕的代码方面特别过分热心,我们希望用编译器信息消息标记技术债务的每个实例(让我们不要考虑这个想法有多糟糕)。我们可以使用除了发出这样的消息之外什么都不做的宏。
import reflect.macros.blackbox.Context
def debtMark(message: String): Unit = macro debtMark_impl
def debtMarkImpl(c: Context)(message: c.Tree): c.Tree = {
message match {
case Literal(Constant(msg: String)) => c.info(c.enclosingPosition, msg, false)
// false above means "do not force this message to be shown unless -verbose"
case _ => c.abort(c.enclosingPosition, "Message must be a string literal.")
// Abort causes the compilation to completely fail. It's not even a compile error, where
// multiple can stack up; this just kills everything.
}
q"()" // At runtime this method does nothing, so we return ()
}
此外,我们可以创建两个宏 !!!
和 ?!?
,而不是使用 ???
标记未实现的代码,它们用于相同的目的,但会发出编译器警告。?!?
将发出警告,!!!
将导致彻底错误。
import reflect.macros.blackbox.Context
def ?!? : Nothing = macro impl_?!?
def !!! : Nothing = macro impl_!!!
def impl_?!?(c: Context): c.Tree = {
import c.universe._
c.warning(c.enclosingPosition, "Unimplemented!")
q"${termNames.ROOTPKG}.scala.Predef.???"
// If someone were to shadow the scala package, scala.Predef.??? would not work, as it
// would end up referring to the scala that shadows and not the actual scala.
// ROOTPKG is the very root of the tree, and acts like it is imported anew in every
// expression. It is actually named _root_, but if someone were to shadow it, every
// reference to it would be an error. It allows us to safely access ??? and know that
// it is the one we want.
}
def impl_!!!(c: Context): c.Tree = {
import c.universe._
c.error(c.enclosingPosition, "Unimplemented!")
q"${termNames.ROOTPKG}.scala.Predef.???"
}