0

I'm writing a macro which generate some code like this:

q"_root_.ru.lmars.macropack.TagsAndTags2.$tagName(..$tagParams)"

but I want to generate this code only if $tagName is defined and have some "marker" (like annotation or some special return type). How to get a Symbol of $tagName for this?

It's easy if $tagName is defined inside TagsAndTags2 object:

object TagsAndTags2
{
    def dialog(caption: String): String = ???
}

you can write something like this to get Symbol of dialog:

val tagParentAccess = q"_root_.ru.lmars.macropack.TagsAndTags2"
val tagParent = c.typecheck(tagParentAccess, silent = true)
val tagSymbol = tagParent.tpe.member(tagName)

But how to do the same if $tagName is available via an implicit conversion?

implicit final class UserTags(x: TagsAndTags2.type)
{
    def dialog(caption: String): String = ???
}
lmars
  • 302
  • 1
  • 6
  • 1
    Sorry you example is not clear to me at all. Could you please extend the context? Where the `tagName` is supposed to come from? What it represents? And what is the marker in your "easy" example? – SergGr Feb 02 '19 at 01:09
  • @SergGr my macro translates Scala XML literals to Scala code (for example `` will be translated to `_root_.ru.lmars.macropack.TagsAndTags2.dialog()`). `tagName` is name of current translated XML tag. With an implicit conversation from `TagsAndTags2` object an user of my macro can extend a list of supported tags. – lmars Feb 02 '19 at 08:38
  • @SergGr there is no sample of "marker" in my "easy" example. I suppose, if I'll have a `Symbol` for `dialog` member (from the same example) I can able use any meta information as a "marker", for example a special annotation . – lmars Feb 02 '19 at 08:52

1 Answers1

1

Here is a quick & dirty example (I've tried it in Scala 2.11):

temp/Foo.scala:

package temp

import scala.language.experimental.macros

object Foo {
  def printSymbol(name: String): Unit = macro FooMacro.printSymbol
}

object FooTarget

private class FooMacro(val c: scala.reflect.macros.blackbox.Context) {

  import c.universe._

  def printSymbol(name: Tree): Tree = {
    name match {
      case Literal(Constant(lv)) =>
        val a = q"_root_.temp.FooTarget.${TermName(lv.toString)}"
        val ca = c.typecheck(a)
        println("checked apply symbol", ca.symbol)
    }

    q"()"
  }

}

temp/Bar.scala:

package temp

object Implicits {

  implicit class BarObjContainer(f: FooTarget.type) {

    object bar

  }

}

object UseMacro {

  import Implicits._

  val v = Foo.printSymbol("bar")

}

Is ca.symbol what you want?

=== UPDATE ===

Here is the quick & dirty demo for function with param:

temp/Foo.scala:

package temp

import scala.language.experimental.macros

object Foo {
  def printSymbol(name: String): Unit = macro FooMacro.printSymbol
}

object FooTarget

private class FooMacro(val c: scala.reflect.macros.blackbox.Context) {

  import c.universe._

  def printSymbol(name: Tree): Tree = {
    name match {
      case Literal(Constant(lv)) =>
        val nameStr = lv.toString
        val f = q"_root_.temp.FooTarget.${TermName(nameStr)}(_)"
        c.typecheck(f) match {
          case Function(_, Apply(s@Select(_, TermName(`nameStr`)), _)) =>
            println(s.symbol)
        }
    }

    q"()"
  }

}

temp/Bar.scala:

package temp

object Implicits {

  implicit class BarObjContainer(f: FooTarget.type) {

    def bar(baz: String): Unit = ()

  }

}

object UseMacro {

  import Implicits._

  val v = Foo.printSymbol("bar")

}

s is the method symbol for "bar".

lxohi
  • 350
  • 2
  • 11
  • Not exactly :( It works good if `tag` doesn't have the arguments (like `object bar` in your example), but doesn't compile if it has (like `def dialog(caption: String)` in my). Compilation error is: scala.reflect.macros.TypecheckException: missing argument list for method dialog in class UserTags Unapplied methods are only converted to functions when a function type is expected. You can make this conversion explicit by writing `dialog _` or `dialog(_)` instead of `dialog`. – lmars Feb 04 '19 at 12:29
  • oops, it seems my code pieces not the same as in the question. I have ran into this exception when I was trying to construct an ```Apply(f, _)``` and only do typecheck on ```f```. So I think probably there is a way to tweak around and get that typecheck done in an reverse fashion. Will get back later – lxohi Feb 04 '19 at 18:20
  • Thank you. I should use the different ways to get a `Symbol` of method in case of `tagName` is val/var, method with arguments or without arguments. But solution recommended by you is the best ;) for now! – lmars Feb 11 '19 at 06:39