如:
import scala.reflect.macros.blackbox
import scala.language.experimental.macros
object Hello {
def hello(msg:String): Unit = macro helloImpl
def helloImpl(c: blackbox.Context)(msg: c.Expr[String]): c.Expr[Unit] = {
import c.universe._
c.Expr(q"""println("hello!")""")
}
}
-Ymacro-annotations
。字节码生成+Intellij支持
在这里custom-scala-plugin指的是自己编写的Scala-Macro-Tools插件。
步骤
/*
* Copyright (c) 2021 jxnu-liguobin && contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package io.github.dreamylost.macros
import scala.reflect.macros.whitebox
/**
*
* @author 梦境迷离
* @since 2021/7/7
* @version 1.0
*/
object builderMacro {
class BuilderProcessor(override val c: whitebox.Context) extends AbstractMacroProcessor(c) {
import c.universe._
private def getBuilderClassName(classTree: TypeName): TypeName = {
TypeName(classTree.toTermName.decodedName.toString + "Builder")
}
private def getFieldDefinition(field: Tree): Tree = {
val ValDef(_, name, tpt, rhs) = field
q"private var $name: $tpt = $rhs"
}
private def getFieldSetMethod(typeName: TypeName, field: Tree, classTypeParams: List[Tree]): Tree = {
val builderClassName = getBuilderClassName(typeName)
val returnTypeParams = extractClassTypeParamsTypeName(classTypeParams)
lazy val valDefMapTo = (v: ValDef) => {
q"""
def ${v.name}(${v.name}: ${v.tpt}): $builderClassName[..$returnTypeParams] = {
this.${v.name} = ${v.name}
this
}
"""
}
valDefMapTo(field.asInstanceOf[ValDef])
}
private def getBuilderClassAndMethod(typeName: TypeName, fieldss: List[List[Tree]], classTypeParams: List[Tree], isCase: Boolean): List[Tree] = {
val fields = fieldss.flatten
val builderClassName = getBuilderClassName(typeName)
val builderFieldMethods = fields.map(f => getFieldSetMethod(typeName, f, classTypeParams))
val builderFieldDefinitions = fields.map(f => getFieldDefinition(f))
val returnTypeParams = extractClassTypeParamsTypeName(classTypeParams)
val builderMethod = q"def builder[..$classTypeParams](): $builderClassName[..$returnTypeParams] = new $builderClassName()"
val buulderClass =
q"""
class $builderClassName[..$classTypeParams] {
..$builderFieldDefinitions
..$builderFieldMethods
def build(): $typeName[..$returnTypeParams] = ${getConstructorWithCurrying(typeName, fieldss, isCase)}
}
"""
List(builderMethod, buulderClass)
}
override def createCustomExpr(classDecl: ClassDef, compDeclOpt: Option[ModuleDef] = None): Any = {
val classDefinition = mapToClassDeclInfo(classDecl)
val builder = getBuilderClassAndMethod(classDefinition.className, classDefinition.classParamss,
classDefinition.classTypeParams, isCaseClass(classDecl))
val compDecl = appendModuleBody(compDeclOpt, builder, classDefinition.className)
// Return both the class and companion object declarations
c.Expr(
q"""
$classDecl
$compDecl
""")
}
}
}
完整代码 https://github.com/jxnu-liguobin/scala-macro-tools/blob/master/src/main/scala/io/github/dreamylost/macros/builderMacro.scala
步骤
/*
* Copyright (c) 2021 jxnu-liguobin && contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package io.github.dreamylost.macros
import io.github.dreamylost.logs.LogType._
import io.github.dreamylost.logs.{
LogTransferArgument, LogType }
import io.github.dreamylost.{
PACKAGE, logs }
import scala.reflect.macros.whitebox
/**
*
* @author 梦境迷离
* @since 2021/7/7
* @version 1.0
*/
object logMacro {
class LogProcessor(override val c: whitebox.Context) extends AbstractMacroProcessor(c) {
import c.universe._
private val extractArgumentsDetail: (Boolean, logs.LogType.Value) = extractArgumentsTuple2 {
case q"new log(logType=$logType)" =>
val tpe = getLogType(logType.asInstanceOf[Tree])
(false, tpe)
case q"new log(verbose=$verbose)" => (evalTree(verbose.asInstanceOf[Tree]), LogType.JLog)
case q"new log(verbose=$verbose, logType=$logType)" =>
val tpe = getLogType(logType.asInstanceOf[Tree])
(evalTree(verbose.asInstanceOf[Tree]), tpe)
case q"new log()" => (false, LogType.JLog)
case _ => c.abort(c.enclosingPosition, ErrorMessage.UNEXPECTED_PATTERN)
}
private def getLogType(logType: Tree): LogType = {
if (logType.children.exists(t => t.toString().contains(PACKAGE))) {
evalTree(logType)
} else {
LogType.getLogType(logType.toString())
}
}
private def logTree(annottees: Seq[c.universe.Expr[Any]]): c.universe.Tree = {
val buildArg = (name: Name) => LogTransferArgument(name.toTermName.decodedName.toString, isClass = true)
(annottees.map(_.tree) match {
case (classDef: ClassDef) :: Nil =>
LogType.getLogImpl(extractArgumentsDetail._2).getTemplate(c)(buildArg(classDef.name))
case (moduleDef: ModuleDef) :: Nil =>
LogType.getLogImpl(extractArgumentsDetail._2).getTemplate(c)(buildArg(moduleDef.name).copy(isClass = false))
case (classDef: ClassDef) :: (_: ModuleDef) :: Nil =>
LogType.getLogImpl(extractArgumentsDetail._2).getTemplate(c)(buildArg(classDef.name))
case _ => c.abort(c.enclosingPosition, ErrorMessage.ONLY_OBJECT_CLASS)
}).asInstanceOf[Tree]
}
override def impl(annottees: c.universe.Expr[Any]*): c.universe.Expr[Any] = {
val resTree = annottees.map(_.tree) match {
case (classDef: ClassDef) :: _ =>
if (classDef.mods.hasFlag(Flag.CASE)) {
c.abort(c.enclosingPosition, ErrorMessage.ONLY_OBJECT_CLASS)
}
val newClass = extractArgumentsDetail._2 match {
case ScalaLoggingLazy | ScalaLoggingStrict =>
appendImplDefSuper(checkGetClassDef(annottees), _ => List(logTree(annottees)))
case _ =>
prependImplDefBody(checkGetClassDef(annottees), _ => List(logTree(annottees)))
}
val moduleDef = getModuleDefOption(annottees)
q"""
${if (moduleDef.isEmpty) EmptyTree else moduleDef.get}
$newClass
"""
case (_: ModuleDef) :: _ =>
extractArgumentsDetail._2 match {
case ScalaLoggingLazy | ScalaLoggingStrict => appendImplDefSuper(getModuleDefOption(annottees).get, _ => List(logTree(annottees)))
case _ => prependImplDefBody(getModuleDefOption(annottees).get, _ => List(logTree(annottees)))
}
// Note: If a class is annotated and it has a companion, then both are passed into the macro.
// (But not vice versa - if an object is annotated and it has a companion class, only the object itself is expanded).
// see https://docs.scala-lang.org/overviews/macros/annotations.html
}
printTree(force = extractArgumentsDetail._1, resTree)
c.Expr[Any](resTree)
}
}
}
完整代码 https://github.com/jxnu-liguobin/scala-macro-tools/blob/master/src/main/scala/io/github/dreamylost/macros/logMacro.scala
步骤
private final val log: java.util.logging.Logger= ???