Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions core-tests/shared/src/test/scala-2/zio/HasNoScopeSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package zio

import zio.test._

object HasNoScopeSpec extends ZIOSpecDefault {
def isScopeError(e: Either[String, Unit], typeString: String): Boolean = e match {
case Left(err) =>
err.startsWith(
s"""Can not prove that $typeString does not contain Scope.
|If $typeString contains a zio.Scope, please handle it explicitly. If it contains a generic type, add a context bound""".stripMargin
)
case _ => false
}

def noScope[R: HasNoScope]: String = "noScope"

def genericWithImplicit[R: HasNoScope]: String =
noScope[R]

override def spec: Spec[TestEnvironment with Scope, Any] =
suiteAll("HasNoScope") {
test("no scope") {
noScope[Any]
assertTrue(true)
}
test("with implicit") {
genericWithImplicit[Any]
assertTrue(true)
}
test("with scope") {
typeCheck(
// language=Scala
"""noScope[Scope]"""
).map(e => assertTrue(e == Left("The type Scope contains a zio.Scope. This is not allowed.")))
}
test("generic") {
typeCheck(
// language=Scala
"""
def genericNoImplicit[R]: String = noScope[R]
"""
).map(e =>
assertTrue(
e == Left("Can not prove that R does not contain a zio.Scope. Please add a context bound R: HasNoScope.")
)
)
}
test("generic with R") {
typeCheck(
// language=Scala
"""
def genericNoImplicitWithR[R]: ZIO[Int & R, Nothing, Unit] =
noScope[Int & R]
"""
).map(e =>
assertTrue(
e == Left("Can not prove that R does not contain a zio.Scope. Please add a context bound R: HasNoScope.")
)
)
}
}
}
55 changes: 55 additions & 0 deletions core-tests/shared/src/test/scala-3/zio/HasNoScopeSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package zio.managed

import zio._
import zio.test._

object HasNoScopeSpec extends ZIOSpecDefault {
def isScopeError(e: Either[String, Unit], typeString: String): Boolean = e match {
case Left(err) =>
err.startsWith(
s"""Can not prove that $typeString does not contain Scope.
|If $typeString contains a zio.Scope, please handle it explicitly. If it contains a generic type, add a context bound""".stripMargin
)
case _ => false
}

def noScope[R: HasNoScope]: String = "noScope"

def genericWithImplicit[R: HasNoScope]: String =
noScope[R]

override def spec: Spec[TestEnvironment with Scope, Any] =
suiteAll("HasNoScope") {
test("no scope") {
noScope[Any]
assertTrue(true)
}
test("with implicit") {
genericWithImplicit[Any]
assertTrue(true)
}
test("with scope") {
typeCheck(
// language=Scala
"""noScope[Scope]"""
).map(e => assertTrue(isScopeError(e, "zio.Scope")))
}
test("generic") {
typeCheck(
// language=Scala
"""
def genericNoImplicit[R]: String = noScope[R]
"""
).map(e => assertTrue(isScopeError(e, "R")))
}
test("generic with R") {
typeCheck(
// language=Scala
"""
def genericNoImplicitWithR[R]: ZIO[Int & R, Nothing, Unit] =
noScope[Int & R]
"""
).map(e => assertTrue(isScopeError(e, "Int & R")))
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package zio

import scala.language.experimental.macros
import scala.reflect.macros.blackbox

private[zio] abstract class HasNoScopeCompanionVersionSpecific {

val instance: HasNoScope[Any]

implicit def noScope[R]: HasNoScope[R] =
macro HasNoScopeMacro.impl[R]
}

private[zio] class HasNoScopeMacro(val c: blackbox.Context) {
import c.universe._

/**
* Given a type `A with B with C` You'll get back List[A,B,C]
*/
def intersectionTypes(self: Type): List[Type] =
self.dealias match {
case t: RefinedType =>
t.parents.flatMap(intersectionTypes)
case TypeRef(_, sym, _) if sym.info.isInstanceOf[RefinedTypeApi] =>
intersectionTypes(sym.info)
case other =>
List(other)
}

def impl[R: c.WeakTypeTag]: c.Expr[HasNoScope[R]] = {
import c._

val rType = weakTypeOf[R]
val rTypes = intersectionTypes(rType.dealias.map(_.dealias))
val scopeType = weakTypeOf[zio.Scope]
if (rTypes.contains(scopeType)) {
val rName = rType.dealias.typeSymbol.name
c.abort(
c.enclosingPosition,
s"The type $rName contains a zio.Scope. This is not allowed."
)
} else if (rType.typeSymbol.isParameter) {
val rName = rType.dealias.typeSymbol.name
c.abort(
c.enclosingPosition,
s"Can not prove that $rName does not contain a zio.Scope. Please add a context bound $rName: HasNoScope."
)
} else if (rTypes.exists(_.typeSymbol.isParameter)) {
val rName = rTypes.find(_.typeSymbol.isParameter).get.typeSymbol.name
c.abort(
c.enclosingPosition,
s"Can not prove that $rName does not contain a zio.Scope. Please add a context bound $rName: HasNoScope."
)
} else {
reify(HasNoScope.instance.asInstanceOf[HasNoScope[R]])
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package zio

import scala.quoted._

private[zio] abstract class HasNoScopeCompanionVersionSpecific {

val instance: HasNoScope[Any]

final transparent inline given hasNoScope[R]: HasNoScope[R] =
${ HasNoScopeMacro.noScope[R] }

}

private[zio] object HasNoScopeMacro {

def noScope[R: Type](using Quotes): Expr[HasNoScope[R]] = {
import quotes.reflect._

def intersectionTypes(tpe: TypeRepr): List[Symbol] = tpe.dealias match {
case AndType(left, right) => intersectionTypes(left) ++ intersectionTypes(right)
case other => List(other.typeSymbol)
}

val rTypes = intersectionTypes(TypeRepr.of[R])
val scopeType = TypeRepr.of[zio.Scope].typeSymbol

if (rTypes.contains(scopeType)) {
val rName = TypeRepr.of[R].typeSymbol.name
report.errorAndAbort(s"The type $rName contains a zio.Scope. This is not allowed.")
} else if (rTypes.exists(_.isTypeParam)) {
val rName = rTypes.find(_.isTypeParam).get.name
report.errorAndAbort(
s"Can not prove that $rName does not contain a zio.Scope. Please add a context bound ${rName}: HasNoScope."
)
} else {
'{ HasNoScope.instance.asInstanceOf[HasNoScope[R]] }
}
}
}
12 changes: 12 additions & 0 deletions core/shared/src/main/scala/zio/HasNoScope.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package zio

@scala.annotation.implicitNotFound(
"""Can not prove that ${R} does not contain Scope.
If ${R} contains a zio.Scope, please handle it explicitly. If it contains a generic type, add a context bound
like def myMethod[R: HasNoScope](...) = ..."""
)
sealed trait HasNoScope[R]

object HasNoScope extends HasNoScopeCompanionVersionSpecific {
override val instance: HasNoScope[Any] = new HasNoScope[Any] {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we change this to package-private?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be called by the inlined code of the macro. If it is not public, the compiler will fail

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

package-private methods / vals are public when compiled. Have you tried changing it to package-private and it didn't work?

}
Loading