There are compile time and runtime of macros. And there are compile time and runtime of main code. The runtime of macros is the compile time of main code.
def getPropsImpl... =
'{ Symbol.classSymbol($className).fieldMembers.map(_.name) }
...
is incorrect because what Scala 3 macros do is transforming trees into trees (i.e. Expr
s into Expr
s, Expr
is a wrapper over a tree) (*). The tree
Symbol.classSymbol($className).fieldMembers.map(_.name)
will make no sense inside the scope of application site. Symbol
, Symbol.classSymbol
etc. make sense here, inside the scope of macro.
def getPropsImpl... =
Symbol.classSymbol(className).fieldMembers.map(_.name)
...
would be also incorrect because className
as a value doesn't exist yet, it's just a tree now.
I guess correct is with .valueOrAbort
import scala.quoted.*
inline def getProps(inline className: String): Iterable[String] = ${getPropsImpl('className)}
def getPropsImpl(className: Expr[String])(using Quotes): Expr[Iterable[String]] = {
import quotes.reflect.*
Expr.ofSeq(
Symbol.classSymbol(className.valueOrAbort).fieldMembers.map(s =>
Literal(StringConstant(s.name)).asExprOf[String]
)
)
}
Usage:
// in other file
getProps("mypackage.App.A") //ArraySeq(s, i)
// in other subproject
package mypackage
object App {
case class A(i: Int, s: String)
}
(*) Scala 2 macros can do more with c.eval
. In Scala 3 there is similar thing staging.run
but it's forbidden in macros.
Actually, c.eval
(or forbidden staging.run
) can be emulated in Scala 3 too
get annotations from class in scala 3 macros