巨集中的錯誤
巨集可以通過使用 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.???"
}