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

calac-mill-moduledefs-plugin_2.13.6.0.11.3-M3.source-code.AutoOverridePlugin.scala Maven / Gradle / Ivy

The newest version!
package mill.moduledefs

import scala.collection.mutable.ListBuffer
import scala.reflect.internal.Flags
import scala.tools.nsc.doc.ScaladocSyntaxAnalyzer
import scala.tools.nsc.plugins.{Plugin, PluginComponent}
import scala.tools.nsc.transform.Transform
import scala.tools.nsc.{Global, Phase}

class AutoOverridePlugin(val global: Global) extends Plugin {

  override def init(options: List[String], error: String => Unit): Boolean = true

  val name = "auto-override-plugin"
  val description = "automatically inserts `override` keywords for you"

  val components = List[PluginComponent](EnableScaladocAnnotation, AutoOverride)

  private object EnableScaladocAnnotation extends PluginComponent with Transform {
    type GT = AutoOverridePlugin.this.global.type

    override val global: GT = AutoOverridePlugin.this.global

    override val phaseName: String = "EmbedScaladocAnnotation"

    override val runsAfter: List[String] = List("parser")

    override def newTransformer(unit: global.CompilationUnit): global.Transformer = {
      new ScaladocTransformer
    }
    import global._

    class ScaladocTransformer extends global.Transformer {

      val comments = new Comments()

      override def transformUnit(unit: CompilationUnit) = {
        if (
          unit.source.file.name.endsWith(".scala") ||
          unit.source.file.name.endsWith(".sc") ||
          unit.source.file.name.endsWith(".mill")
        ) {
          comments.parseComments(unit)
          super.transformUnit(unit)
        }
      }

      override def transform(tree: global.Tree): global.Tree = {
        super.transform(tree match {
          case x: global.ClassDef =>
            comments.getComment(x.pos) match {
              case Some(comment) =>
                global.treeCopy.ClassDef(
                  tree,
                  newMods(x.mods, comment),
                  x.name,
                  x.tparams,
                  x.impl
                )
              case None => x
            }

          case x: global.ModuleDef =>
            comments.getComment(x.pos) match {
              case Some(comment) =>
                global.treeCopy.ModuleDef(tree, newMods(x.mods, comment), x.name, x.impl)
              case None => x
            }

          case x: global.DefDef =>
            comments.getComment(x.pos) match {
              case Some(comment) =>
                global.treeCopy.DefDef(
                  tree,
                  newMods(x.mods, comment),
                  x.name,
                  x.tparams,
                  x.vparamss,
                  x.tpt,
                  x.rhs
                )
              case None => x
            }

          case x: global.ValDef =>
            comments.getComment(x.pos) match {
              case Some(comment) =>
                global.treeCopy.ValDef(tree, newMods(x.mods, comment), x.name, x.tpt, x.rhs)
              case None => x
            }

          case x => x
        })
      }

      def newMods(old: global.Modifiers, comment: String) = {
        old.copy(
          annotations = createAnnotation(comment) :: old.annotations
        )
      }

      private def createAnnotation(comment: String): global.Tree =
        global.Apply(
          global.Select(
            global.New(
              AutoOverridePlugin.scaladocAnnotationClassNameTree(global)
            ),
            global.nme.CONSTRUCTOR
          ),
          List(Literal(Constant(comment)))
        )

    }

    class Comments extends ScaladocSyntaxAnalyzer[global.type](global) {
      val comments = ListBuffer[(Position, String)]()

      def getComment(pos: Position): Option[String] = {
        val tookComments = comments.takeWhile { case (x, _) => x.end < pos.start }
        comments --= (tookComments)
        tookComments.lastOption.map(_._2)
      }

      def parseComments(unit: CompilationUnit): Unit = {
        comments.clear()

        new ScaladocUnitParser(unit, Nil) {
          override def newScanner = new ScaladocUnitScanner(unit, Nil) {
            override def registerDocComment(str: String, pos: Position) = {
              comments += ((pos, str))
            }
          }
        }.parse()
      }

      override val runsAfter: List[String] = Nil
      override val runsRightAfter: Option[String] = None
    }
  }

  private object AutoOverride extends PluginComponent {

    val global = AutoOverridePlugin.this.global
    import global._

    override val runsAfter = List("typer")
    override val runsBefore = List("patmat")

    val phaseName = "auto-override"

    override def newPhase(prev: Phase): Phase = new GlobalPhase(prev) {

      def name: String = phaseName

      def isCacher(owner: Symbol) = {
        val baseClasses =
          if (owner.isClass) Some(owner.asClass.baseClasses)
          else if (owner.isModule) Some(owner.asModule.baseClasses)
          else None
        baseClasses.exists(_.exists(_.fullName == AutoOverridePlugin.cacherClassName))
      }

      def apply(unit: global.CompilationUnit): Unit = {
        object AutoOverrider extends global.Transformer {
          override def transform(tree: global.Tree) = tree match {
            case d: DefDef
                if d.symbol.overrideChain.count(!_.isAbstract) > 1
                  && !d.mods.isOverride
                  && isCacher(d.symbol.owner) =>
              d.symbol.flags = d.symbol.flags | Flags.OVERRIDE
              copyDefDef(d)(mods = d.mods | Flags.OVERRIDE)
            case _ => super.transform(tree)

          }
        }

        unit.body = AutoOverrider.transform(unit.body)
      }
    }
  }

}

object AutoOverridePlugin {

  private val cacherClassName = "mill.moduledefs.Cacher"

  private def scaladocAnnotationClassNameTree(global: Global) =
    global.Select(
      global.Select(
        global.Ident(
          global.newTermName("mill")
        ),
        global.newTermName("moduledefs")
      ),
      global.newTypeName("Scaladoc")
    )
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy