Skip to content

Commit

Permalink
Fix #1648 forbid implicit trait construction, add explicit `makeTrait…
Browse files Browse the repository at this point in the history
…` and `fromTrait` methods
  • Loading branch information
neko-kai committed Oct 23, 2023
1 parent 501bd65 commit 5e4ee5a
Show file tree
Hide file tree
Showing 22 changed files with 144 additions and 119 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ import scala.language.experimental.macros as enableMacros
* An implicitly summonable constructor for a type `T`, can generate constructors for:
*
* - concrete classes (using [[ClassConstructor]])
* - traits and abstract classes ([[https://izumi.7mind.io/distage/basics.html#auto-traits Auto-Traits feature]], using [[TraitConstructor]])
*
* Since version `1.1.0`, does not generate constructors "factory-like" traits and abstract classes, instead use [[FactoryConstructor]].
* Since version `1.2.0`, does not generate constructors for traits and abstract classes, or for "factory-like" traits
* and abstract classes, instead use [[TraitConstructor]] ([[https://izumi.7mind.io/distage/basics.html#auto-traits Auto-Traits]])
* and [[FactoryConstructor]] respectively. ([[https://izumi.7mind.io/distage/basics#auto-factories Auto-Factories]])
*
* Use [[ZEnvConstructor]] to generate constructors for `zio.ZEnvironment` values.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,14 @@ object AnyConstructorMacro {
if (reflectionProvider.isConcrete(targetType)) {
ClassConstructorMacro.mkClassConstructor[T](c)
} else if (reflectionProvider.isWireableAbstract(targetType)) {
TraitConstructorMacro.mkTraitConstructor[T](c)
c.abort(
c.enclosingPosition,
s"AnyConstructor failure: $targetType is a trait or an abstract class, use makeTrait or fromTrait to wire traits.",
)
} else if (reflectionProvider.isFactory(targetType)) {
c.abort(
c.enclosingPosition,
s"""AnyConstructor failure: $targetType is a Factory, use makeFactory or fromFactory to wire factories.""".stripMargin,
s"AnyConstructor failure: $targetType is a Factory, use makeFactory or fromFactory to wire factories.",
)
} else {
c.abort(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,14 @@ object AnyConstructorMacro {
tpeDeref match { case _: ConstantType | _: TermRef => true; case _ => false }
}) {
ClassConstructorMacro.makeImpl[R](util)
} else if ({
context.isWireableTrait
}) {
} else if (context.isWireableTrait) {
TraitConstructorMacro.makeImpl[R](util, context)
report.errorAndAbort(
s"AnyConstructor failure: ${Type.show[R]} is a trait or an abstract class, use makeTrait or fromTrait to wire traits."
)
} else if (context.isFactoryOrTrait) {
report.errorAndAbort(
s"""AnyConstructor failure: ${Type.show[R]} is a Factory, use makeFactory or fromFactory to wire factories.""".stripMargin
s"AnyConstructor failure: ${Type.show[R]} is a Factory, use makeFactory or fromFactory to wire factories."
)
} else {
report.errorAndAbort(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ import zio.ZEnvironment
* An implicitly summonable constructor for a type `T`, can generate constructors for:
*
* - concrete classes (using [[ClassConstructor]])
* - traits and abstract classes ([[https://izumi.7mind.io/distage/basics.html#auto-traits Auto-Traits feature]], using [[TraitConstructor]])
*
* Since version `1.1.0`, does not generate constructors "factory-like" traits and abstract classes, instead use [[FactoryConstructor]].
* Since version `1.2.0`, does not generate constructors for traits and abstract classes, or for "factory-like" traits
* and abstract classes, instead use [[TraitConstructor]] ([[https://izumi.7mind.io/distage/basics.html#auto-traits Auto-Traits]])
* and [[FactoryConstructor]] respectively. ([[https://izumi.7mind.io/distage/basics#auto-factories Auto-Factories]])
*
* Use [[ZEnvConstructor]] to generate constructors for `zio.ZEnvironment` values.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ import izumi.distage.model.definition.dsl.ModuleDefDSL
*
* Singleton bindings:
* - `make[X]` = create X using its constructor
* - `makeTrait[X]` = create an abstract class or a trait `X` using [[izumi.distage.constructors.TraitConstructor]] ([[https://izumi.7mind.io/distage/basics.html#auto-traits Auto-Traits feature]])
* - `makeFactory[X]` = create a "factory-like" abstract class or a trait `X` using [[izumi.distage.constructors.FactoryConstructor]] ([[https://izumi.7mind.io/distage/basics.html#auto-factories Auto-Factories feature]])
* - `make[X].from[XImpl]` = bind X to its subtype XImpl using XImpl's constructor
* - `make[X].fromTrait[XImpl]` = bind X to its abstract class or a trait subtype XImpl, deriving constructor using [[izumi.distage.constructors.TraitConstructor]] ([[https://izumi.7mind.io/distage/basics.html#auto-traits Auto-Traits feature]])
* - `make[X].fromFactory[XImpl]` = bind X to its "factory-like" abstract class or a trait subtype XImpl, deriving constructor using [[izumi.distage.constructors.FactoryConstructor]] ([[https://izumi.7mind.io/distage/basics.html#auto-factories Auto-Factories feature]])
* - `make[X].from(myX)` = bind X to an already existing instance `myX`
* - `make[X].from { y: Y => new X(y) }` = bind X to an instance of X constructed by a given [[izumi.distage.model.providers.Functoid Functoid]] requesting an Y parameter
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package izumi.distage.model.definition.dsl

import izumi.distage.constructors.FactoryConstructor
import izumi.distage.constructors.{FactoryConstructor, TraitConstructor}
import izumi.distage.model.definition.*
import izumi.distage.model.definition.Binding.{EmptySetBinding, ImplBinding, SetElementBinding, SingletonBinding}
import izumi.distage.model.definition.dsl.AbstractBindingDefDSL.SetElementInstruction.ElementAddTags
Expand Down Expand Up @@ -149,6 +149,12 @@ trait AbstractBindingDefDSL[BindDSL[_], BindDSLAfterFrom[_], SetDSL[_]] extends
_bindDSL[T](ref)
}

/** @see [[https://izumi.7mind.io/distage/basics.html#auto-traits Auto-Traits feature]] */
final protected[this] def makeTrait[T: Tag: TraitConstructor]: BindDSLAfterFrom[T] = {
val ref = _registered(new SingletonRef(Bindings.provider[T](TraitConstructor[T])))
_bindDSLAfterFrom[T](ref)
}

/** @see [[https://izumi.7mind.io/distage/basics.html#auto-factories Auto-Factories feature]] */
final protected[this] def makeFactory[T: Tag: FactoryConstructor]: BindDSLAfterFrom[T] = {
val ref = _registered(new SingletonRef(Bindings.provider[T](FactoryConstructor[T])))
Expand Down Expand Up @@ -194,6 +200,7 @@ trait AbstractBindingDefDSL[BindDSL[_], BindDSLAfterFrom[_], SetDSL[_]] extends
final protected[this] def _make[T: Tag](provider: Functoid[T])(implicit pos: CodePositionMaterializer): BindDSL[T] = self._make[T](provider)

final protected[this] def makeFactory[T: Tag: FactoryConstructor]: BindDSLAfterFrom[T] = self.makeFactory[T]
final protected[this] def makeTrait[T: Tag: TraitConstructor]: BindDSLAfterFrom[T] = self.makeTrait[T]

/**
* Avoid `discarded non-Unit value` warning.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package izumi.distage.model.definition.dsl

import izumi.distage.LocalContext
import izumi.distage.constructors.{AnyConstructor, FactoryConstructor, ZEnvConstructor}
import izumi.distage.constructors.{AnyConstructor, FactoryConstructor, TraitConstructor, ZEnvConstructor}
import izumi.distage.model.definition.*
import izumi.distage.model.definition.dsl.AbstractBindingDefDSL.*
import izumi.distage.model.definition.dsl.AbstractBindingDefDSL.MultiSetElementInstruction.MultiAddTags
Expand Down Expand Up @@ -41,8 +41,10 @@ import scala.collection.immutable.HashSet
*
* Singleton bindings:
* - `make[X]` = create X using its constructor
* - `makeTrait[X]` = create an abstract class or a trait `X` using [[izumi.distage.constructors.TraitConstructor]] ([[https://izumi.7mind.io/distage/basics.html#auto-traits Auto-Traits feature]])
* - `makeFactory[X]` = create a "factory-like" abstract class or a trait `X` using [[izumi.distage.constructors.FactoryConstructor]] ([[https://izumi.7mind.io/distage/basics.html#auto-factories Auto-Factories feature]])
* - `make[X].from[XImpl]` = bind X to its subtype XImpl using XImpl's constructor
* - `make[X].fromTrait[XImpl]` = bind X to its abstract class or a trait subtype XImpl, deriving constructor using [[izumi.distage.constructors.TraitConstructor]] ([[https://izumi.7mind.io/distage/basics.html#auto-traits Auto-Traits feature]])
* - `make[X].fromFactory[XImpl]` = bind X to its "factory-like" abstract class or a trait subtype XImpl, deriving constructor using [[izumi.distage.constructors.FactoryConstructor]] ([[https://izumi.7mind.io/distage/basics.html#auto-factories Auto-Factories feature]])
* - `make[X].from(myX)` = bind X to an already existing instance `myX`
* - `make[X].from { y: Y => new X(y) }` = bind X to an instance of X constructed by a given [[izumi.distage.model.providers.Functoid Functoid]] requesting an Y parameter
Expand Down Expand Up @@ -116,24 +118,6 @@ object ModuleDefDSL {
final def fromValue[I <: T: Tag](instance: I): AfterBind =
bind(ImplDef.InstanceImpl(SafeType.get[I], instance))

/**
* Defines local context with empty local module and local keys
*/
def fromLocalContext[F[_], R](defn: LocalContextDef[F[R]])(implicit @unused ev: T =:= LocalContext[F, R]): LocalContextDSL[F, R, AfterBind] = {
new LocalContextDSL[F, R, AfterBind](defn.module, Set.empty, bind, defn.function).bound()
}

/**
* Defines local context with empty local module and local keys, specialised for Identity
*/
def fromLocalContext[R](
defn: LocalContextDef[R]
)(implicit ev: T =:= LocalContext[Identity, R],
dummyImplicit: DummyImplicit,
): LocalContextDSL[Identity, R, AfterBind] = {
fromLocalContext[Identity, R](defn)
}

/**
* A function that receives its arguments from DI object graph, including named instances via [[izumi.distage.model.definition.Id]] annotation.
*
Expand Down Expand Up @@ -194,9 +178,30 @@ object ModuleDefDSL {
final def from[I <: T](function: Functoid[I]): AfterBind =
bind(ImplDef.ProviderImpl(function.get.ret, function.get))

/** @see [[https://izumi.7mind.io/distage/basics.html#auto-traits Auto-Traits feature]] */
final def fromTrait[I <: T: TraitConstructor]: AfterBind =
from[I](TraitConstructor[I])

/** @see [[https://izumi.7mind.io/distage/basics.html#auto-factories Auto-Factories feature]] */
final def fromFactory[I <: T: FactoryConstructor]: AfterBind = {
final def fromFactory[I <: T: FactoryConstructor]: AfterBind =
from[I](FactoryConstructor[I])

/**
* Defines local context with empty local module and local keys
*/
def fromLocalContext[F[_], R](defn: LocalContextDef[F[R]])(implicit @unused ev: T =:= LocalContext[F, R]): LocalContextDSL[F, R, AfterBind] = {
new LocalContextDSL[F, R, AfterBind](defn.module, Set.empty, bind, defn.function).bound()
}

/**
* Defines local context with empty local module and local keys, specialised for Identity
*/
def fromLocalContext[R](
defn: LocalContextDef[R]
)(implicit ev: T =:= LocalContext[Identity, R],
dummyImplicit: DummyImplicit,
): LocalContextDSL[Identity, R, AfterBind] = {
fromLocalContext[Identity, R](defn)
}

/**
Expand Down Expand Up @@ -360,6 +365,10 @@ object ModuleDefDSL {
final def addValue[I <: T: Tag](instance: I)(implicit pos: CodePositionMaterializer): AfterAdd =
appendElement(ImplDef.InstanceImpl(SafeType.get[I], instance), pos)

/** @see [[https://izumi.7mind.io/distage/basics.html#auto-traits Auto-Traits feature]] */
final def addTrait[I <: T: Tag: TraitConstructor](implicit pos: CodePositionMaterializer): AfterAdd =
add[I](TraitConstructor[I])

/** @see [[https://izumi.7mind.io/distage/basics.html#auto-factories Auto-Factories feature]] */
final def addFactory[I <: T: Tag: FactoryConstructor](implicit pos: CodePositionMaterializer): AfterAdd =
add[I](FactoryConstructor[I])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class CglibProxiesTestJvm extends AnyWordSpec with MkInjector with ScalatestGuar

val definition = PlannerInput.everything(new ModuleDef {
make[Circular2]
make[Circular1]
makeTrait[Circular1]
})

val injector = mkInjector()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ class CompactPlanFormatterTest extends AnyWordSpec with MkInjector {
make[JustTrait].from[Impl1]
make[OptionT[scala.Either[Nothing, _], Unit]].from(OptionT[Either[Nothing, _], Unit](Right(None)))
make[K1[T1]].from(new K1[T1] {})
make[W1.T2]
make[W2.T2]
makeTrait[W1.T2]
makeTrait[W2.T2]
}))

val formatted = plan.render().replaceAll("\u001B\\[[;\\d]*m", "")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class StaticDSLTest extends AnyWordSpec {
.from[TestClass]
make[TestDependency0]
.named("named.test.dependency.0")
.from[TestDependency0]
.fromTrait[TestDependency0]
make[TestInstanceBinding]
.named("named.test")
.from(TestInstanceBinding())
Expand All @@ -29,7 +29,7 @@ class StaticDSLTest extends AnyWordSpec {
many[JustTrait]
.add[Impl0]
.add(new Impl1)
.add[JustTrait]
.addTrait[JustTrait]
many[JustTrait]
.named("named.set")
.add(new Impl2())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package izumi.distage.compat

import cats.syntax.all._
import izumi.distage.fixtures.BasicCases._
import izumi.distage.model.definition.Bindings.binding
import izumi.distage.model.definition._
import cats.syntax.all.*
import izumi.distage.fixtures.BasicCases.*
import izumi.distage.model.definition.Bindings.{binding, bindingTrait}
import izumi.distage.model.definition.*
import org.scalatest.wordspec.AnyWordSpec

final class ModuleBaseInstancesTest extends AnyWordSpec {
Expand All @@ -20,12 +20,12 @@ final class ModuleBaseInstancesTest extends AnyWordSpec {
}

val mod3_1: Module = new ModuleDef {
make[TestDependency1]
makeTrait[TestDependency1]
}

val mod3_2 = Module.empty

val mod3 = (mod3_1 |+| mod3_2) :+ binding[NotInContext]
val mod3 = (mod3_1 |+| mod3_2) :+ bindingTrait[NotInContext]

val mod4 = Module.make(
Set(
Expand Down
34 changes: 17 additions & 17 deletions distage/distage-core/src/test/scala/izumi/distage/dsl/DSLTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class DSLTest extends AnyWordSpec with MkInjector with should.Matchers {

make[TestClass]
.named("named.test.class")
make[TestDependency0]
makeTrait[TestDependency0]
.named("named.test.dependency.0")
make[TestInstanceBinding]
.named("named.test")
Expand All @@ -47,7 +47,7 @@ class DSLTest extends AnyWordSpec with MkInjector with should.Matchers {
.add[Impl3]

make[TestDependency0].namedByImpl.from[TestImpl0]
make[TestDependency0].namedByImpl
makeTrait[TestDependency0].namedByImpl
}

assert(definition != null)
Expand Down Expand Up @@ -161,12 +161,12 @@ class DSLTest extends AnyWordSpec with MkInjector with should.Matchers {
}

val mod3_1 = new ModuleDef {
make[TestDependency1]
makeTrait[TestDependency1]
}

val mod3_2 = Module.empty

val mod3 = (mod3_1 ++ mod3_2) :+ Bindings.binding[NotInContext]
val mod3 = (mod3_1 ++ mod3_2) :+ Bindings.bindingTrait[NotInContext]

val mod4: ModuleBase = Module.make {
Set(
Expand Down Expand Up @@ -201,7 +201,7 @@ class DSLTest extends AnyWordSpec with MkInjector with should.Matchers {
make[TestClass]
}
object mod2 extends ModuleDef {
make[TestDependency0]
makeTrait[TestDependency0]
}

mod1 ++ mod2
Expand All @@ -214,7 +214,7 @@ class DSLTest extends AnyWordSpec with MkInjector with should.Matchers {
make[TestClass]
}
class mod2 extends ModuleDef {
make[TestDependency0]
makeTrait[TestDependency0]
}

val _: Module = new mod1 ++ new mod2
Expand All @@ -226,11 +226,11 @@ class DSLTest extends AnyWordSpec with MkInjector with should.Matchers {
val definition: ModuleBase = new ModuleDef {
tag("tag1")
make[TestClass]
make[TestDependency0].tagged("sniv")
makeTrait[TestDependency0].tagged("sniv")
tag("tag2")
}

assert(definition.bindings == Set(Bindings.binding[TestClass].addTags(Set("tag1", "tag2")), Bindings.binding[TestDependency0].addTags(Set("tag1", "tag2", "sniv"))))
assert(definition.bindings == Set(Bindings.binding[TestClass].addTags(Set("tag1", "tag2")), Bindings.bindingTrait[TestDependency0].addTags(Set("tag1", "tag2", "sniv"))))
}

"ModuleBuilder supports tags; same bindings with different tags are NOT merged (tag merging removed in 0.11.0)" in {
Expand Down Expand Up @@ -299,16 +299,16 @@ class DSLTest extends AnyWordSpec with MkInjector with should.Matchers {
import BasicCase1._

val def1 = new ModuleDef {
make[TestDependency0].tagged("a")
make[TestDependency0].tagged("b")
makeTrait[TestDependency0].tagged("a")
makeTrait[TestDependency0].tagged("b")

tag("1")
}

val def2 = new ModuleDef {
tag("2")

make[TestDependency0].tagged("x").tagged("y")
makeTrait[TestDependency0].tagged("x").tagged("y")
}

val definition = def1 ++ def2
Expand All @@ -324,15 +324,15 @@ class DSLTest extends AnyWordSpec with MkInjector with should.Matchers {
val tags12: Seq[BindingTag] = Seq("1", "2")

val def1 = new ModuleDef {
make[TestDependency0].tagged("a").tagged("b")
makeTrait[TestDependency0].tagged("a").tagged("b")

tag(tags12: _*)
}

val def2 = new ModuleDef {
tag("2", "3")

make[TestDependency0].tagged("x").tagged("y")
makeTrait[TestDependency0].tagged("x").tagged("y")
}

val definition = def1 overriddenBy def2
Expand All @@ -344,14 +344,14 @@ class DSLTest extends AnyWordSpec with MkInjector with should.Matchers {
"support zero element" in {
import BasicCase1._
val def1 = new ModuleDef {
make[TestDependency0]
makeTrait[TestDependency0]
}

val def2 = new ModuleDef {
make[TestDependency0]
makeTrait[TestDependency0]
}
val def3 = new ModuleDef {
make[TestDependency1]
makeTrait[TestDependency1]
}

assert((def1 overriddenBy Module.empty) == def1)
Expand All @@ -363,7 +363,7 @@ class DSLTest extends AnyWordSpec with MkInjector with should.Matchers {
import BasicCase1._

trait Def1 extends ModuleDef {
make[TestDependency0]
makeTrait[TestDependency0]
tag("tag2")
}

Expand Down
Loading

0 comments on commit 5e4ee5a

Please sign in to comment.