编译时不会显示通过Scala宏注入的方法

编译时不会显示通过Scala宏注入的方法,scala,methods,macros,annotations,inject,Scala,Methods,Macros,Annotations,Inject,我的目标是拥有一个注释宏,将方法和变量注入到trait中。虽然这似乎为trait生成了正确的代码,但我无法让编译器认识到这些方法现在已经存在。仅供参考,在尝试访问值时,实际实现确实有效 我希望用它来清理一些代码,并更好地理解以下情况下的宏: 我遵循Scala宏文档中指向的示例项目中的代码 下面是使用Scala 2.10.4和宏天堂插件2.1.0-M5编译宏的实际代码: import scala.reflect.internal.annotations.compileTimeOnly import

我的目标是拥有一个注释宏,将方法和变量注入到trait中。虽然这似乎为trait生成了正确的代码,但我无法让编译器认识到这些方法现在已经存在。仅供参考,在尝试访问值时,实际实现确实有效

我希望用它来清理一些代码,并更好地理解以下情况下的宏:

我遵循Scala宏文档中指向的示例项目中的代码

下面是使用Scala 2.10.4和宏天堂插件2.1.0-M5编译宏的实际代码:

import scala.reflect.internal.annotations.compileTimeOnly
import scala.reflect.macros.Context
import scala.language.experimental.macros
import scala.annotation.StaticAnnotation

/**
 * Represents a macro that injects appropriate getters and setters into the
 * annotated trait such that it can be treated as a dependency.
 *
 * @param variableName The name of the variable to provide getter/setter
 * @param className The class name of the variable
 */
@compileTimeOnly("Enable macro paradise to expand macro annotations")
class Dependency(
  variableName: String,
  className: String
) extends StaticAnnotation {
  def macroTransform(annottees: Any*): Any = macro Dependency.impl
}

object Dependency {
  def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
    import c.universe._
    println("Starting macro")
    println("Prefix: " + c.prefix)

    val q"new Dependency(..$params)" = c.prefix.tree
    println("Params: " + params)

    val (variableName: String, className: String) = params match {
      case p: List[_] =>
        val stringParams = p.map(_.toString.replace("\"", ""))
        (stringParams(0), stringParams(1))
      case _ => c.abort(
        c.enclosingPosition,
        "Required arguments are (variable name: String) (class name: String)"
      )
    }

    println("Variable name: " + variableName)
    println("Class name: " + className)

    def modifiedTrait(traitDecl: ClassDef) = {
      val (name) = try {
        val q"trait $name" = traitDecl
        (name)
      } catch {
        case _: MatchError => c.abort(
          c.enclosingPosition,
          "Annotation is only supported on trait"
        )
      }
      println("Trait name: " + name)

      val actualVariableName = variableName.toLowerCase
      println("Actual variable name: " + actualVariableName)
      val actualVariableClass = newTypeName(className)
      println("Actual class name: " + actualVariableClass)

      val internalVariableName = newTermName("_" + actualVariableName)

      println("Internal variable name: " + internalVariableName)

      val getterName = newTermName(actualVariableName)
      println("Getter name: " + getterName)
      val setterName = newTermName(actualVariableName + "_=")
      println("Setter name: " + setterName)
      val setterVariableName = newTermName("new" + actualVariableName.capitalize)

      println("Setter variable name: " + setterVariableName)

      val generatedTrait = q"""
        trait $name {
          private var $internalVariableName: $actualVariableClass = _

          def $setterName($setterVariableName: $actualVariableClass) =
            $internalVariableName = $setterVariableName

          def $getterName: $actualVariableClass = $internalVariableName
        }
      """

      println("Generated trait: " + generatedTrait)

      c.Expr[Any](generatedTrait)
    }

    annottees.map(_.tree) match {
      case (traitDecl: ClassDef) :: Nil => modifiedTrait(traitDecl)
      case _ => c.abort(c.enclosingPosition, "Invalid annottee")
    }
  }
}
下面是我在ScalaTest中使用注释的尝试:

@Dependency("someFakeField", "Int") trait TestTrait

class A extends TestTrait
val a = new A

println("TestTrait")
classOf[TestTrait].getDeclaredMethods.foreach(println)

println("A")
classOf[A].getDeclaredMethods.foreach(println)

// This line fails
//a.someFakeField = 3
以下是运行“我的宏”的输出:

Warning:scalac: Starting macro
Warning:scalac:
Warning:scalac: Prefix: Expr[Nothing](new Dependency("someFakeField", "Int"))
Warning:scalac: Params: List("someFakeField", "Int")
Warning:scalac: Variable name: someFakeField
Warning:scalac: Class name: Int
Warning:scalac: Trait name: TestTrait
Warning:scalac: Actual variable name: somefakefield
Warning:scalac: Actual class name: Int
Warning:scalac: Internal variable name: _somefakefield
Warning:scalac: Getter name: somefakefield
Warning:scalac: Setter name: somefakefield_=
Warning:scalac: Setter variable name: newSomefakefield
Warning:scalac: Generated trait: abstract trait TestTrait extends scala.AnyRef {
  def $init$() = {
    ()
  };
  private var _somefakefield: Int = _;
  def somefakefield_=(newSomefakefield: Int) = _somefakefield = newSomefakefield;
  def somefakefield: Int = _somefakefield
}
下面是运行反射以使用trait访问trait和类的方法的输出,注意方法的外观:

TestTrait
public abstract void com.ibm.spark.annotations.DependencySpec$$anonfun$1$$anonfun$apply$mcV$sp$1$TestTrait$1.somefakefield_=(int)
public abstract com.ibm.spark.annotations.DependencySpec$$anonfun$1$$anonfun$apply$mcV$sp$1 com.ibm.spark.annotations.DependencySpec$$anonfun$1$$anonfun$apply$mcV$sp$1$TestTrait$1.com$ibm$spark$annotations$DependencySpec$$anonfun$$anonfun$TestTrait$$$outer()
public abstract int com.ibm.spark.annotations.DependencySpec$$anonfun$1$$anonfun$apply$mcV$sp$1$TestTrait$1.com$ibm$spark$annotations$DependencySpec$$anonfun$$anonfun$TestTrait$$_somefakefield()
public abstract void com.ibm.spark.annotations.DependencySpec$$anonfun$1$$anonfun$apply$mcV$sp$1$TestTrait$1.com$ibm$spark$annotations$DependencySpec$$anonfun$$anonfun$TestTrait$$_somefakefield_$eq(int)
public abstract int com.ibm.spark.annotations.DependencySpec$$anonfun$1$$anonfun$apply$mcV$sp$1$TestTrait$1.somefakefield()
A
public void com.ibm.spark.annotations.DependencySpec$$anonfun$1$$anonfun$apply$mcV$sp$1$A$1.somefakefield_=(int)
public com.ibm.spark.annotations.DependencySpec$$anonfun$1$$anonfun$apply$mcV$sp$1 com.ibm.spark.annotations.DependencySpec$$anonfun$1$$anonfun$apply$mcV$sp$1$A$1.com$ibm$spark$annotations$DependencySpec$$anonfun$$anonfun$A$$$outer()
public com.ibm.spark.annotations.DependencySpec$$anonfun$1$$anonfun$apply$mcV$sp$1 com.ibm.spark.annotations.DependencySpec$$anonfun$1$$anonfun$apply$mcV$sp$1$A$1.com$ibm$spark$annotations$DependencySpec$$anonfun$$anonfun$TestTrait$$$outer()
public int com.ibm.spark.annotations.DependencySpec$$anonfun$1$$anonfun$apply$mcV$sp$1$A$1.com$ibm$spark$annotations$DependencySpec$$anonfun$$anonfun$TestTrait$$_somefakefield()
public void com.ibm.spark.annotations.DependencySpec$$anonfun$1$$anonfun$apply$mcV$sp$1$A$1.com$ibm$spark$annotations$DependencySpec$$anonfun$$anonfun$TestTrait$$_somefakefield_$eq(int)
public int com.ibm.spark.annotations.DependencySpec$$anonfun$1$$anonfun$apply$mcV$sp$1$A$1.somefakefield()
这里有一个手写的特征确实有效:

trait IncludeInterpreter {

  private var _interpreter: Interpreter = _
  def interpreter: Interpreter = _interpreter
  def interpreter_=(newInterpreter: Interpreter) =
    _interpreter = newInterpreter
}
有没有想过我做错了什么?如果我不能用宏来做这样的事情,那么我想我需要继续用手写。仅供参考,我还尝试在一个模块中编译宏,在另一个模块中使用宏为trait生成方法,并在第三个模块中实际使用trait。不确定哪些需要分开,哪些不需要分开


当我运行宏,然后尝试从ScalaTest中没有的另一个模块访问方法时,我甚至看不到使用反射的方法,尽管宏的输出仍然显示。

看起来应该可以工作。你能把你的项目发布到公共的地方吗?这个代码实际上在这个公共项目的一个分支上可用:我知道代码有点重。您可以通过运行sbt kernel/run进行测试。实际相关文件如下:1。依赖项注释的规范:2。依赖项注释:3。在独立模块中编译的特征,该模块依赖于宏模块:4。依赖于编译trait的模块的模块中trait的用法:5。多模块项目的生成设置: