diff options
author | Li Haoyi <haoyi.sg@gmail.com> | 2017-12-15 17:41:32 -0800 |
---|---|---|
committer | Li Haoyi <haoyi.sg@gmail.com> | 2017-12-15 17:42:22 -0800 |
commit | 4ec6dca8c3432339620bbc6298fdf75a4b930fb4 (patch) | |
tree | 1c17bb9bb3477be1b498c72ff2c74b9d235dd4bd /plugin | |
parent | 3a40842d7b3aeedddb9ab5e8261dd48ea4e024b6 (diff) | |
download | mill-4ec6dca8c3432339620bbc6298fdf75a4b930fb4.tar.gz mill-4ec6dca8c3432339620bbc6298fdf75a4b930fb4.tar.bz2 mill-4ec6dca8c3432339620bbc6298fdf75a4b930fb4.zip |
First pass at using a compiler plugin to remove the need for the `override` keyword when overriding a field within a `mill.Module`
This only applies to `mill.Module`s, not overrides elsewhere which still require the keyword. `mill.Module`s tend to have lots and lots of overriding, so the keyword is basically noise.
Also includes the necessary build changes to enable the locally-built Scalac plugin when compiling the test suite. Note that no changes are necessary for the executable assembly, because the `scalac-plugin.xml` will be included in the assembly and get picked up by the Ammonite scalac plugin classloader automatically
Diffstat (limited to 'plugin')
-rw-r--r-- | plugin/src/main/resources/scalac-plugin.xml | 4 | ||||
-rw-r--r-- | plugin/src/main/scala/mill/plugin/AutoOverridePlugin.scala | 58 | ||||
-rw-r--r-- | plugin/src/main/scala/mill/plugin/Cacher.scala | 33 |
3 files changed, 95 insertions, 0 deletions
diff --git a/plugin/src/main/resources/scalac-plugin.xml b/plugin/src/main/resources/scalac-plugin.xml new file mode 100644 index 00000000..fb9a4404 --- /dev/null +++ b/plugin/src/main/resources/scalac-plugin.xml @@ -0,0 +1,4 @@ +<plugin> + <name>demo-plugin</name> + <classname>mill.plugin.AutoOverridePlugin</classname> +</plugin>
\ No newline at end of file diff --git a/plugin/src/main/scala/mill/plugin/AutoOverridePlugin.scala b/plugin/src/main/scala/mill/plugin/AutoOverridePlugin.scala new file mode 100644 index 00000000..ab9d8db0 --- /dev/null +++ b/plugin/src/main/scala/mill/plugin/AutoOverridePlugin.scala @@ -0,0 +1,58 @@ +package mill.plugin + +import scala.reflect.internal.Flags +import scala.tools.nsc.io.VirtualFile +import scala.tools.nsc.util.BatchSourceFile +import scala.tools.nsc.{Global, Phase} +import scala.tools.nsc.plugins.{Plugin, PluginComponent} + +class AutoOverridePlugin(val global: Global) extends Plugin { + import global._ + 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]( + new 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) = 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 == "mill.plugin.Cacher")) + } + + 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) + } + } + } + ) +}
\ No newline at end of file diff --git a/plugin/src/main/scala/mill/plugin/Cacher.scala b/plugin/src/main/scala/mill/plugin/Cacher.scala new file mode 100644 index 00000000..4c854df7 --- /dev/null +++ b/plugin/src/main/scala/mill/plugin/Cacher.scala @@ -0,0 +1,33 @@ +package mill.plugin + +import scala.collection.mutable +import scala.reflect.macros.blackbox.Context + + +trait Cacher[C[_]]{ + private[this] val cacherLazyMap = mutable.Map.empty[sourcecode.Enclosing, C[_]] + def wrapCached[T](in: C[T], enclosing: String): C[T] + protected[this] def cachedTarget[T](t: => C[T]) + (implicit c: sourcecode.Enclosing): C[T] = synchronized{ + cacherLazyMap.getOrElseUpdate(c, wrapCached(t, c.value)).asInstanceOf[C[T]] + } +} +object Cacher{ + def impl0[M[_], T: c.WeakTypeTag](c: Context)(t: c.Expr[M[T]]): c.Expr[M[T]] = { + c.Expr[M[T]](wrapCached(c)(t.tree)) + } + def wrapCached(c: Context)(t: c.Tree) = { + + import c.universe._ + val owner = c.internal.enclosingOwner + val ownerIsCacherClass = + owner.owner.isClass && + owner.owner.asClass.baseClasses.exists(_.fullName == "mill.plugin.Cacher") + + if (ownerIsCacherClass && owner.isMethod) q"this.cachedTarget($t)" + else c.abort( + c.enclosingPosition, + "T{} members must be defs defined in a Cacher class/trait/object body" + ) + } +}
\ No newline at end of file |