1
votes

I'm trying to define a macro annotation on a case class that expands to a class with a companion object, but I'm running in some problems.

I can create an object with the same name as the class, and define methods on it. But when I try to use the typename of the class as a return or argument type of a method, I get an "not found: type "

class term extends StaticAnnotation {
def macroTransform(annottees: Any*) = macro termMacro.impl
}

object termMacro {

def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
    import c.universe._
    val inputs = annottees.map(_.tree).toList

    val (cls, comp) = annottees.map(_.tree) match {

        case cd@q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$_ } with ..$_ { $self => ..$stats }" :: tail =>

            val paramnames = paramss.head.map {
                case q"$mods val $name: $tpt = $default" => name
            }

            val ctorparams = List(paramss.head ++ Seq(
                q"val position: SourcePosition = (0,0)"
            ))

            val ctorname = TermName(tpname.decodedName.toString)
            //val clstype  =
            (
                q"""
                        import ast.Term
                        case class $tpname(...$ctorparams) extends Term { $self =>
                            def children() = {
                                List(..$paramnames)
                            }

                            ..$stats
                        }
                 """,
                q"""
                        object $ctorname {
                            def unapply(t: $tpname): Option[(Int, Int)] = {
                                Some(Tuple2(3, 4))
                            }
                    }
                """
            )

        case head :: tail =>
            c.abort(c.enclosingPosition, s"The @Term annotation is for case classes, found $head")
    }

    c.error(c.enclosingPosition, showRaw(cls) + "\n" + showRaw(comp))
    c.Expr[Any](Block(List(cls, comp), Literal(Constant(()))))
}
}

e.g. usage would be: @term case class A(x: Term) extends Term, and would give me the compiler error "not found: type A' at location @term in that definition.

I've narrowed the location down to the unapply method in the object definition.

Any help is appreciated. I'm very much new to scala macros, so any further advice is also appreciated.

Side question: any advice on debugging macros in intellij / gradle projects?

1
For me it says "method unapply is defined twice". Could you post the entire project somewhere, e.g. at github?Eugene Burmako
Thanks for taking a look at this, Eugene. If possible I would like to keep the project private for now, but since I would appreciate your further help I've given you access to the repo on github. The branch term-macro contains the macro definition as seen here.A.J.Rouvoet
I have seen a similar error earlier btw. It seems that somehow the case class' own companion objects still gets created by the compiler.A.J.Rouvoet
Cool! I'll take a look. Do I just clone and compile?Eugene Burmako
Just running ./gradlew says "BUILD SUCCESSFUL".Eugene Burmako

1 Answers

1
votes

q"import foo; class Bar" creates a block, so the macro annotation will inadvertently replace class Bar with { import foo; class Bar }, which makes Bar a local class, which is invisible from the outside of the block.