0

Type scala.quoted.Quotes.reflectModule.SymbolMethods has a method called annotations which returns annotations attached to the input symbol.

Type scala.quoted.Quotes.reflectModule.SymbolModule has two methods called newMethod that generate "a new method symbol with the given parent, name and type."

How can I attach annotations to the methods I create with newMethod. In particular, I am interested in adding @targetName annotation.

Koosha
  • 1,492
  • 7
  • 19

1 Answers1

0

Current Quotes API is not perfect. To add annotation try compiler internals

extension (symb: Symbol)
  def addAnnotation(annotation: Term): Symbol =
    // libraryDependencies += scalaOrganization.value %% "scala3-compiler" % scalaVersion.value
    given dotty.tools.dotc.core.Contexts.Context =
      quotes.asInstanceOf[scala.quoted.runtime.impl.QuotesImpl].ctx
    symb.asInstanceOf[dotty.tools.dotc.core.Symbols.Symbol]
      .denot
      .addAnnotation(
        dotty.tools.dotc.core.Annotations.ConcreteAnnotation(
          annotation.asInstanceOf[dotty.tools.dotc.ast.tpd.Tree]
        )
      )
    symb

Symbol.newMethod(...)
  .addAnnotation('{new scala.annotation.targetName("foo")}.asTerm)

For example

import scala.annotation.experimental
import scala.quoted.*

inline def newClass[A]: A = ${newClassImpl[A]}

@experimental
def newClassImpl[A: Type](using Quotes): Expr[A] =
  import quotes.reflect.*

  extension (symb: Symbol)
    def addAnnotation(annotation: Term): Symbol = ...

  val name: String = TypeRepr.of[A].typeSymbol.name + "Impl"
  val parents = List(TypeTree.of[A])

  def decls(cls: Symbol): List[Symbol] =
    List(
      Symbol.newMethod(cls, "func", MethodType(List("s"))(_ => List(TypeRepr.of[String]), _ => TypeRepr.of[String]), Flags.Override, Symbol.noSymbol)
        .addAnnotation('{new scala.annotation.targetName("foo")}.asTerm)
    )

  val cls = Symbol.newClass(Symbol.spliceOwner, name, parents = parents.map(_.tpe), decls, selfType = None)
  val funcSym = cls.declaredMethod("func").head

  val funcDef = DefDef(funcSym, argss => Some('{ "override" }.asTerm))
  val clsDef = ClassDef(cls, parents, body = List(funcDef))
  val newCls = Typed(Apply(Select(New(TypeIdent(cls)), cls.primaryConstructor), Nil), TypeTree.of[A])

  Block(List(clsDef), newCls).asExprOf[A]
class TestClass:
  def func(s: String) = "base"

val res: TestClass = newClass[TestClass]

   // scalacOptions += "-Xprint:pickleQuotes"
// {
//   class TestClassImpl extends TestClass {
//     @targetName override def func(s: String): String = "override"
//   }
//   new TestClassImpl():TestClass
// }:TestClass

Scala 3 macros: create a new polynmorphic function using the reflect api

Method Override with Scala 3 Macros

`tq` equivalent in Scala 3 macros

Scala3: Crafting Types Through Metaprogramming?

Not sure whether this @targetName works as expected.

Also we can define

extension (symb: Symbol)
  def addTargetName(name: String): Symbol =
    given dotty.tools.dotc.core.Contexts.Context =
      quotes.asInstanceOf[scala.quoted.runtime.impl.QuotesImpl].ctx
    import dotty.tools.dotc.core.Decorators.toTermName
    symb.asInstanceOf[dotty.tools.dotc.core.Symbols.Symbol]
      .denot
      .setTargetName(name.toTermName)
    symb

It's possible that @targetName is handled before macros are expanded. In such case it's too late to add it with macros.

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66