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 blockto remove a layer of indirection:and then define the macro with: