Defining val from ValDef #13042
-
I have a macro which works in Scala 2 which I'm trying to rewrite in Scala 3. The original macro looks like this: def decorateVals[A](output: c.Expr[ValDefInspection => Unit])(block: c.Expr[A]): c.Expr[A] = {
val loggedStats = block.tree.children.flatMap {
case valdef @ ValDef(_, termName, _, _) =>
List(
valdef,
q"$output(com.tersesystems.blindsight.inspection.ValDefInspection(${termName.encodedName.toString}, $termName))"
)
case stat =>
List(stat)
}
val outputExpr: c.Expr[A] = c.Expr[A](c.untypecheck(q"..$loggedStats"))
outputExpr
} But I'm having problems translating this to Scala 3 as I'm not sure how to untypecheck the original list using quotes and splices, and I don't know how to get the encoded name out of the term in Scala 3. case class ValDefInspection(name: String, value: Any)
object InspectionMacros {
import scala.quoted.*
inline def decorateVals[A](output: ValDefInspection => Unit)(block: => A): A =
${ Impl.decorateValsImpl('output, 'block) }
def decorateValsImpl[A: Type](output: Expr[ValDefInspection => Unit], block: Expr[A])(using Quotes): Expr[A] = {
import quotes.reflect.*
val blockTransformer = new TreeMap {
override def transformStatement(tree: Statement)(owner: Symbol): Statement = {
tree match {
case valdef@ValDef(termName, tpt: TypeTree, rhs) =>
// println(s"statement = ${tree.show}")
// By-name reference to the val
val valName = Expr(termName)
val termRef = TermRef(tpt.tpe, termName)
val identExpr = Ident(termRef).asExpr
// XXX Isn't there a way to convert the ValDef to an expression?
val definedVal: Expr[Any] = tpt.tpe.asType match
case '[t] => '{ val valName: t = ${rhs.get.asExprOf[t]} }
//val valExpr = Expr(valdef)
val rewrite = '{ ${definedVal}; $output(ValDefInspection($valName, $identExpr)); }
//rewriting as = {
// val $needThisToBeTheOriginalValDefName: scala.Int = 15
// output$proxy3.apply(example.ValDefInspection.apply("b", scala.Int.b))
//}
//println(s"rewriting as = ${rewrite.show}")
rewrite.asTerm
case other =>
super.transformStatement(tree)(owner)
}
}
}
block.asTerm match {
case inlined@Inlined(_,_,ident) =>
val defDef = ident.symbol.tree
// We're only interested in trees representing a block
val transformed: Tree = blockTransformer.transformTree(defDef)(Symbol.spliceOwner)
println(s"original = ${defDef.show}")
println(s"transformed = ${transformed.show}")
block
case other =>
println(s"other = ${other.show(using Printer.TreeStructure)}")
block
}
}
}
} When I run this with InspectionMacros.decorateVals(dval => logger.debug(s"${dval.name} = ${dval.value}")) {
val a = 5
val b = 15
a + b
} This returns:
which doesn't look right at all -- but I'm eyeballing the Dotty source code and tests and I don't see what I'm doing wrong in the Ident or how to convert from a ValDef back to an expression... |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 2 replies
-
So there are two parts to this answer -- use an
and then define the macro with:
Wrote up general experience of macros here. |
Beta Was this translation helpful? Give feedback.
So there are two parts to this answer -- use an
inline block
to remove a layer of indirection:and then define the macro with: