使用Scala宏获取具体化函数的名称

使用Scala宏获取具体化函数的名称,scala,macros,scala-macros,Scala,Macros,Scala Macros,我希望在遍历树的宏中对某些函数和其他变量引用进行模式匹配。目前,我正在根据符号进行匹配。全名如下: 树匹配{ 如果f.symbol!=null,则为案例“$f($x)” &&f.symbol.fullName==“\u root\uu.mypackage.MyClass.myfunc”=> ... 如果f.symbol!=null,则为案例“$f($x)” &&f.symbol.fullName=“\u root\uu.mypackage.MyClass2.myfunc2”=> ... 案例 }

我希望在遍历
树的宏中对某些函数和其他变量引用进行模式匹配。目前,我正在根据
符号进行匹配。全名如下:

树匹配{
如果f.symbol!=null,则为案例“$f($x)”
&&f.symbol.fullName==“\u root\uu.mypackage.MyClass.myfunc”=>
...
如果f.symbol!=null,则为案例“$f($x)”
&&f.symbol.fullName=“\u root\uu.mypackage.MyClass2.myfunc2”=>
...
案例
}
但是,我想在编译这个宏的过程中另外检查这些函数是否确实存在,例如,我不想使用字符串,而是直接使用引用,这样IDE就可以告诉我“myfunc”名称是否有输入错误。我曾考虑使用
具体化
,但我不确定这是否有效

现在让我们假设我正在寻找
println
,而不是
MyClass.myfunc
。我遇到的第一个问题是,您不能直接
reify(println)
或任何其他函数引用,因为Scala中没有函数引用,所以您必须编写
reify(println)
,或者更具体地说,在本例中
reify(println(:String))
来选择我们要调用的println函数。通过以下代码,我收集了表达式中的所有符号,但遗憾的是,只找到了Predef(来自Predef.println),而没有找到println本身:

println(具体化(println(:字符串)).tree
.collect{如果x.symbol!=null=>x.symbol.fullName},则为案例x)
//列表(、scala.Predef、、scala.Predef、、scala.Predef、、scala.Predef)
在scala(目前使用的是2.12)中获取某物名称有什么想法吗?

reify(println)
编译

您如何想象函数的符号
println(uux0:String)
功能定义在哪里?在
Predef
中定义了两种方法

def println(): Unit
def println(x: Any): Unit
试一试

对于
MyClass#myfunc

definitions.PredefModule.typeSignature.decl(TermName("println")).alternatives
// List(method println, method println)
对于两个
println

definitions.PredefModule.typeSignature.decl(TermName("println")).alternatives
  .filter(_.asMethod.paramLists.map(_.map(_.typeSignature)) == List(List(definitions.AnyTpe)))
// List(method println)
对于
println(任何):单位

比如说

def foo(x: Any): Unit = macro impl

def impl(c: blackbox.Context)(x: c.Tree): c.Tree = {
  import c.universe._

  val printlnSymb = definitions.PredefModule.typeSignature.decl(TermName("println")).alternatives
    .filter(_.asMethod.paramLists.map(_.map(_.typeSignature)) == List(List(definitions.AnyTpe)))
    .head

  x match {
    case q"$f($x)" if f.symbol == printlnSymb =>
      println("test")
  }

  q"()"
}

foo(println(1)) //Warning:scalac: test
reify(println(:String)).tree.collect{…
只生成
列表(,,scala.Predef,,scala.Predef,,scala.Predef)
,因为tree
reify(println(:String)).tree
未进行类型检查(对于它生成的类型检查过的树
List($anonfun,x$1,java.lang.String,scala.Predef.println,scala.Predef.println,scala.Predef,x$1,java.lang.String)

因此,另一种选择是执行
c.类型检查

def impl(c: blackbox.Context)(x: c.Tree): c.Tree = {
  import c.universe._

  val printlnSymb = (c.typecheck(q"println(_:Any)", silent = false) match {
    case q"($_) => $p($_)" => p
  }).symbol

//val printlnSymb = (c.typecheck(reify { println(_:Any) }.tree, silent = false) match {
//  case q"($_) => $p($_)" => p
//}).symbol

  x match {
    case q"$f($x)" if f.symbol == printlnSymb =>
      println("test")
  }

  q"()"
}
具体化(println)
编译

你如何想象函数的符号
println(u:String)
在哪里定义了函数?在
Predef
中定义了两种方法:

def println(): Unit
def println(x: Any): Unit
试一试

对于
MyClass#myfunc

definitions.PredefModule.typeSignature.decl(TermName("println")).alternatives
// List(method println, method println)
对于两个
println

definitions.PredefModule.typeSignature.decl(TermName("println")).alternatives
  .filter(_.asMethod.paramLists.map(_.map(_.typeSignature)) == List(List(definitions.AnyTpe)))
// List(method println)
对于
println(任何):单位

比如说

def foo(x: Any): Unit = macro impl

def impl(c: blackbox.Context)(x: c.Tree): c.Tree = {
  import c.universe._

  val printlnSymb = definitions.PredefModule.typeSignature.decl(TermName("println")).alternatives
    .filter(_.asMethod.paramLists.map(_.map(_.typeSignature)) == List(List(definitions.AnyTpe)))
    .head

  x match {
    case q"$f($x)" if f.symbol == printlnSymb =>
      println("test")
  }

  q"()"
}

foo(println(1)) //Warning:scalac: test
reify(println(:String)).tree.collect{…
只生成
列表(,,scala.Predef,,scala.Predef,,scala.Predef)
,因为tree
reify(println(:String)).tree
未进行类型检查(对于它生成的类型检查过的树
List($anonfun,x$1,java.lang.String,scala.Predef.println,scala.Predef.println,scala.Predef,x$1,java.lang.String)

因此,另一种选择是执行
c.类型检查

def impl(c: blackbox.Context)(x: c.Tree): c.Tree = {
  import c.universe._

  val printlnSymb = (c.typecheck(q"println(_:Any)", silent = false) match {
    case q"($_) => $p($_)" => p
  }).symbol

//val printlnSymb = (c.typecheck(reify { println(_:Any) }.tree, silent = false) match {
//  case q"($_) => $p($_)" => p
//}).symbol

  x match {
    case q"$f($x)" if f.symbol == printlnSymb =>
      println("test")
  }

  q"()"
}

要获取具体化表达式的符号,表达式树需要另一个显式通过类型检查,因此以下代码可以工作:

val mySym1=c.typecheck(具体化(mypackage.myfunction1)).tree)匹配{
案例q“{(..$\}=>f(..$\}”=>f.symbol
}
val mySym2=c.typecheck(具体化(mypackage.myfunction2_).tree)匹配{
案例q“{(..$\}=>f(..$\}”=>f.symbol
}
println(mySym.fullName)
//\u根目录\u.mypackage.myfunction1
然后可以根据符号进行匹配:

tree match {
  case "$f($x)" if f.symbol != null => f.symbol match {
    case x if x == mySym1 => ...
    case x if x == mySym2 => ...
    case ...
  }
  case ...
}

要获取具体化表达式的符号,表达式树需要另一个显式通过类型检查,因此以下代码可以工作:

val mySym1=c.typecheck(具体化(mypackage.myfunction1)).tree)匹配{
案例q“{(..$\}=>f(..$\}”=>f.symbol
}
val mySym2=c.typecheck(具体化(mypackage.myfunction2_).tree)匹配{
案例q“{(..$\}=>f(..$\}”=>f.symbol
}
println(mySym.fullName)
//\u根目录\u.mypackage.myfunction1
然后可以根据符号进行匹配:

tree match {
  case "$f($x)" if f.symbol != null => f.symbol match {
    case x if x == mySym1 => ...
    case x if x == mySym2 => ...
    case ...
  }
  case ...
}

实际上,有一个名为
def symbolOf[X]:TypeSymbol
的函数,可用于从类型中获取TypeSymbol。但这只适用于TypeSymbol,而不适用于TermSymbol,因此我们无法对函数执行此操作

但是,如果这是我们自己的函数,我们可以将
def myfunc():Int=…
中的定义替换为
object myfunc{def apply():Int=…}

package main
object myfunc  { def apply(): Int = 1 }
object myfunc2 { def apply(): Int = 1 }

...

tree match {
  case "$f.apply($x)" if f.symbol == symbolOf[myfunc.type]  => ...
  case "$f.apply($x)" if f.symbol == symbolOf[myfunc2.type] => ...
  case ...
}

实际上,有一个名为
def symbolOf[X]:TypeSymbol
的函数,可用于从类型中获取TypeSymbol。但这只适用于TypeSymbol,而不适用于TermSymbol,因此我们无法对函数执行此操作

但是,如果这是我们自己的函数,我们可以将
def myfunc():Int=…
中的定义替换为
object myfunc{def apply():Int=…}

package main
object myfunc  { def apply(): Int = 1 }
object myfunc2 { def apply(): Int = 1 }

...

tree match {
  case "$f.apply($x)" if f.symbol == symbolOf[myfunc.type]  => ...
  case "$f.apply($x)" if f.symbol == symbolOf[myfunc2.type] => ...
  case ...
}
Thx!顺便说一句,
reify(prinln)
起作用,但只有bc它变成了
reify(println())
,所以它是一个调用而不是引用。另外,这个问题的目的在于println不是一个字符串/准注释(没有用引号括起来),所以我不明白为什么你给出了很多例子,其中println用引号括起来……另一方面,你似乎是对的,对typecheck的调用丢失了。我仍然不明白为什么reify返回一个非类型树,因为它显然是这样的