All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.scalajs.junit.plugin.ScalaJUnitMixinPlugin.scala Maven / Gradle / Ivy

package org.scalajs.junit.plugin

import scala.tools.nsc._
import scala.tools.nsc.plugins.{
  Plugin => NscPlugin, PluginComponent => NscPluginComponent
}

class ScalaJUnitMixinPlugin(val global: Global) extends NscPlugin {

  // Disable the warnings emitted during mixin
  global.settings.nowarnDefaultJunitMethods.tryToSet(List("true"))

  val name: String = "Scala 2.12 JUnit mixin plugin"

  val components: List[NscPluginComponent] =
    List(ScalaJUnitMixinPluginComponent)

  val description: String = "Makes JUnit tests in traits invokable in Scala 2.12."

  object ScalaJUnitMixinPluginComponent
      extends plugins.PluginComponent with transform.Transform {

    val global: Global = ScalaJUnitMixinPlugin.this.global
    import global._

    val phaseName: String = "junit-make-mixin-bridges"
    val runsAfter: List[String] = List("mixin")
    override val runsBefore: List[String] = List("cleanup", "junit-inject")

    protected def newTransformer(unit: CompilationUnit): Transformer =
      new ScalaJUnitMixinPluginTransformer

    class ScalaJUnitMixinPluginTransformer extends Transformer {

      import rootMirror.getRequiredClass

      private val junitMethodAnnotations = List(
          getRequiredClass("org.junit.Test"),
          getRequiredClass("org.junit.Before"),
          getRequiredClass("org.junit.After"),
          getRequiredClass("org.junit.BeforeClass"),
          getRequiredClass("org.junit.AfterClass"),
          getRequiredClass("org.junit.Ignore")
      )

      override def transform(tree: Tree): Tree = tree match {
        case tree: ClassDef =>
          val classSym = tree.symbol.asClass
          val missingMethods = classSym.info.members.filter { member =>
            member.isMethod &&
            !member.isAbstract &&
            classSym != member.owner &&
            junitMethodAnnotations.exists(member.hasAnnotation)
          }
          if (missingMethods.nonEmpty) {
            val impl = tree.impl
            val bridges =
              missingMethods.map(sym => mkBridge(sym.asMethod, classSym))
            val newBody = tree.impl.body ++ bridges
            val newImpl =
              treeCopy.Template(impl, impl.parents, impl.self, newBody)
            treeCopy.ClassDef(tree, tree.mods, tree.name, tree.tparams, newImpl)
          } else {
            tree
          }

        case _ =>
          super.transform(tree)
      }

      private def mkBridge(sym: MethodSymbol, classSym: ClassSymbol): DefDef = {
        val name = sym.name.toTermName

        val ddefSym = classSym.newMethod(name)
        val ddefInfo = MethodType(sym.info.params.map(_.cloneSymbol(ddefSym)),
            sym.info.resultType)
        ddefSym.setInfo(ddefInfo)
        ddefSym.flags = sym.flags
        ddefSym.flags += Flag.OVERRIDE
        sym.annotations.foreach(ddefSym.addAnnotation)

        val select = Select(Super(This(classSym), typeNames.EMPTY), name)
        select.setSymbol(sym)
        val rhs = Apply(select, ddefSym.info.params.map(gen.mkAttributedIdent))
        rhs.setSymbol(sym)

        typer.typedDefDef(newDefDef(ddefSym, rhs)())
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy