0

I am trying to create an implicit encoder using Circe. However this encoder will be created using an annotation hence I am using Scalameta. Here is my code. However, the compiler complains about having an override statement within quasiquotes.

class HalResource extends StaticAnnotation {
  inline def apply(defn: Any): Any = meta {
    val q"..$mods class $tName (..$params) extends $template {..$stats}" = defn

    q"object $tName {${createApply(tName)}}"
  }


  private def createApply(className: Type.Name): Defn.Def = {
    q"""
       import _root_.io.circe.Json
       import _root_.io.circe.syntax._
       import _root_.io.circe.Encoder


       implicit def encoder = Encoder[$className] {
          override def apply(a: $className): Json = {
           val (simpleFields: Seq[Term.Param], nonSimpleFields: Seq[Term.Param]) =
             params.partition(field => field.decltpe.fold(false) {
               case _: Type.Name => true
               case _ => false
             })

           val embedded: Seq[(String, Json)] = nonSimpleFields.map(field => field.name.syntax -> field.name.value.asJson)
           val simpleJsonFields: Seq[(String, Json)] = simpleFields.map(field => field.name.syntax -> field.name.value.asJson)

           val baseSeq: Seq[(String, Json)] = Seq(
             "_links" -> Json.obj(
               "href" -> Json.obj(
                 "self" -> Json.fromString("self_reference")
               )
             ),
             "_embedded" -> Json.fromFields(embedded),
           ) ++ simpleJsonFields

           val result: Seq[(String, Json)] = baseSeq ++ simpleJsonFields
           Json.fromFields(result)
          }
       }
     """
  }
}

The build file is as follows:

import sbt.Keys.{scalaVersion, scalacOptions}

val circeVersion = "0.8.0"

lazy val circeDependencies = Seq(
  "io.circe" %% "circe-core",
  "io.circe" %% "circe-generic",
  "io.circe" %% "circe-parser"
).map(_ % circeVersion)

lazy val commonSettings = Seq(
  name := "Annotation",
  version := "1.0",
  scalaVersion := "2.12.2",
  scalacOptions ++= Seq("-unchecked", "-deprecation", "-feature"),
  resolvers += Resolver.sonatypeRepo("releases")
)

lazy val macroAnnotationSettings = Seq(
  addCompilerPlugin("org.scalameta" % "paradise" % "3.0.0-M9" cross CrossVersion.full),
  scalacOptions += "-Xplugin-require:macroparadise",
  scalacOptions in (Compile, console) ~= (_ filterNot (_ contains "paradise"))
)

lazy val projectThatDefinesMacroAnnotations = project.in(file("annotation-definition"))
  .settings(commonSettings)
  .settings(
    name := "HalResource",
    libraryDependencies += "org.scalameta" %% "scalameta" % "1.8.0" % Provided,
    macroAnnotationSettings)

lazy val annotation = project.in(file("."))
  .settings(commonSettings)
  .settings(macroAnnotationSettings)
  .settings(
    libraryDependencies ++= circeDependencies
  ).dependsOn(projectThatDefinesMacroAnnotations)

As a result I still get: macro annotation could not be expanded (the most common reason for that is that you need to enable the macro paradise plugin; another possibility is that you try to use macro annotation in the same compilation run that defines it)

igalbenardete
  • 207
  • 2
  • 12

1 Answers1

1

You are just missing new before Encoder[$className] { (there may be other errors, but that's the immediate one).

Because of this, the compiler thinks you are trying to call a generic method Encoder with the block

{
  override def apply(a: $className): Json = ...
  ...
}

as the argument, and local methods can't be override.

Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
  • Thank you @AlexeyRomanov , it was a silly mistake. However I am still having problems when I try to use that annotation on a case class getting the following error: `[error] /Users/ibenardetelevis/Personal/annotation/src/main/scala/Mdsol.scala:25: macro annotation could not be expanded (the most common reason for that is that you need to enable the macro paradise plugin; another possibility is that you try to use macro annotation in the same compilation run that defines it) ` – igalbenardete Jul 20 '17 at 12:46
  • Judging by the path, `Mdsol.scala` is in the same project. If so, this is exactly the second reason described in the error message. If you are just trying to test it, move `Mdsol` to `src/test/scala`, if you need it elsewhere, split your project into two modules. – Alexey Romanov Jul 20 '17 at 13:03
  • Just in case you also have reason 1, see http://scalameta.org/tutorial/#Setupbuild. – Alexey Romanov Jul 20 '17 at 13:04
  • I have followed the same link when I started the task and here is an example of my build.sbt: ` lazy val macroAnnotationSettings = Seq( addCompilerPlugin("org.scalameta" % "paradise" % "3.0.0-M9" cross CrossVersion.full), scalacOptions += "-Xplugin-require:macroparadise", scalacOptions in (Compile, console) ~= (_ filterNot (_ contains "paradise")) )` – igalbenardete Jul 20 '17 at 14:19
  • I have followed the same link when I started the task and here is an example of my build.sbt file as a part of the original question. I have split them into different submodules already. @AlexeyRomanov – igalbenardete Jul 20 '17 at 14:25
  • Your proposed edit should go into your question, not into the answer. But the difference I've noticed from that page is that your `annotation` project doesn't set `macroAnnotationSettings`, it uses the non-Scalameta Macro Paradise plugin instead. – Alexey Romanov Jul 20 '17 at 14:36
  • I've added it there as well, but I'm still getting the same error. Also I have removed the scalamacros dependency otherwise it gives a conflict with scalameta. @AlexRomanov – igalbenardete Jul 20 '17 at 14:52
  • and my bad for trying to edit it in the answer rather than the question. @AlexeyRomanov – igalbenardete Jul 20 '17 at 15:04