Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package meta
import com.avsystem.commons.macros.misc.MiscMacros
import com.avsystem.commons.misc.ImplicitNotFound

import scala.annotation.implicitNotFound
import scala.annotation.nowarn

/** Base trait for companion objects of _metadata classes_. A metadata class is a generic class that captures metadata
* for some Scala type, typically an RPC interface ([[com.avsystem.commons.rpc.RpcMetadataCompanion
Expand All @@ -27,7 +27,8 @@ trait MetadataCompanion[M[_]] {
// macro effectively turns `metadata` param into by-name param (implicit params by themselves cannot be by-name)
implicit def lazyMetadata[Real](implicit metadata: M[Real]): Lazy[Real] = macro MiscMacros.lazyMetadata

@implicitNotFound("#{forNotLazy}")
@deprecated("Use native implicitNotFound instead", "2.29.0")
@nowarn("msg=deprecated")
implicit def notFound[T](implicit forNotLazy: ImplicitNotFound[M[T]]): ImplicitNotFound[Lazy[T]] =
ImplicitNotFound()
}
Expand Down Expand Up @@ -57,7 +58,8 @@ trait BoundedMetadataCompanion[Hi, Lo <: Hi, M[_ >: Lo <: Hi]] {
// macro effectively turns `metadata` param into by-name param (implicit params by themselves cannot be by-name)
implicit def lazyMetadata[Real >: Lo <: Hi](implicit metadata: M[Real]): Lazy[Real] = macro MiscMacros.lazyMetadata

@implicitNotFound("#{forNotLazy}")
@deprecated("Use native implicitNotFound instead", "2.29.0")
@nowarn("msg=deprecated")
implicit def notFound[T >: Lo <: Hi](implicit forNotLazy: ImplicitNotFound[M[T]]): ImplicitNotFound[Lazy[T]] =
ImplicitNotFound()
}
Expand Down
10 changes: 10 additions & 0 deletions core/src/main/scala/com/avsystem/commons/misc/Implicits.scala
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
package com.avsystem.commons
package misc

import scala.annotation.nowarn

object Implicits {

/** Similar to `implicitly` from standard library but implemented as a macro which materializes directly into the
* implicit value (without being wrapped as implicit parameter of a dummy method like `implicitly`). Also, using
* `infer` lets you have more detailed control over implicit-not-found compilation error messages through
* [[ImplicitNotFound]].
*/
@deprecated("Use native implicitly instead", "2.29.0")
def infer[T]: T = macro macros.misc.MiscMacros.infer[T]
@deprecated("Use native implicitly instead", "2.29.0")
def infer[T](clue: String): T = macro macros.misc.MiscMacros.clueInfer[T]
@deprecated("Use native implicitly instead", "2.29.0")
def inferNonMacro[T](clue: String): T = macro macros.misc.MiscMacros.inferNonMacro[T]
}

Expand Down Expand Up @@ -40,9 +45,14 @@ object Implicits {
* ): ImplicitNotFound[GenCodec[Box[T]]] = ImplicitNotFound()
* }}}
*/
@deprecated("Use native implicitNotFound instead", "2.29.0")
sealed trait ImplicitNotFound[T]

@nowarn("msg=deprecated")
object ImplicitNotFound {
@deprecated("Use native implicitNotFound instead", "2.29.0")
def apply[T](): ImplicitNotFound[T] = throw new NotImplementedError("ImplicitNotFound.apply")

@deprecated("Use native implicitNotFound instead", "2.29.0")
implicit def dummy[T]: ImplicitNotFound[T] = apply()
}
8 changes: 5 additions & 3 deletions core/src/main/scala/com/avsystem/commons/rpc/AsRawReal.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import com.avsystem.commons.meta.Fallback
import com.avsystem.commons.misc.ImplicitNotFound
import com.avsystem.commons.serialization.TransparentWrapping

import scala.annotation.implicitNotFound
import scala.annotation.{implicitNotFound, nowarn}

@implicitNotFound("Cannot serialize ${Real} into ${Raw}, appropriate AsRaw instance not found")
trait AsRaw[Raw, Real] {
Expand Down Expand Up @@ -35,7 +35,8 @@ object AsRaw extends FallbackAsRaw {
implicit def forTry[Raw, Real](implicit asRaw: AsRaw[Raw, Real]): AsRaw[Try[Raw], Try[Real]] =
_.map(asRaw.asRaw)

@implicitNotFound("#{forPlain}")
@deprecated("Use native implicitNotFound instead", "2.29.0")
@nowarn("msg=deprecated")
implicit def notFoundForTry[Raw, Real](
implicit forPlain: ImplicitNotFound[AsRaw[Raw, Real]]
): ImplicitNotFound[AsRaw[Try[Raw], Try[Real]]] = ImplicitNotFound()
Expand Down Expand Up @@ -67,7 +68,8 @@ object AsReal extends FallbackAsReal {
implicit def forTry[Raw, Real](implicit asReal: AsReal[Raw, Real]): AsReal[Try[Raw], Try[Real]] =
_.map(asReal.asReal)

@implicitNotFound("#{forPlain}")
@deprecated("Use native implicitNotFound instead", "2.29.0")
@nowarn("msg=deprecated")
implicit def notFoundForTry[Raw, Real](
implicit forPlain: ImplicitNotFound[AsReal[Raw, Real]]
): ImplicitNotFound[AsReal[Try[Raw], Try[Real]]] = ImplicitNotFound()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,11 @@ import org.scalatest.funsuite.AnyFunSuite

import scala.annotation.implicitNotFound

@implicitNotFound("no stuff available")
sealed trait Stuff
object Stuff {
@implicitNotFound("no stuff available")
implicit def inf: ImplicitNotFound[Stuff] = ImplicitNotFound()
}

sealed trait OtherStuff
object OtherStuff {
@implicitNotFound("no other stuff available because: #{forStuff}")
implicit def inf(implicit forStuff: ImplicitNotFound[Stuff]): ImplicitNotFound[OtherStuff] = ImplicitNotFound()
}

class ImplicitNotFoundTest extends AnyFunSuite with CompilationErrorAssertions {
test("simple") {
test("native implicitNotFound message is used by infer") {
assert(typeErrorFor("Implicits.infer[Stuff]") == "no stuff available")
}

test("with dependencies") {
assert(typeErrorFor("Implicits.infer[OtherStuff]") == "no other stuff available because: no stuff available")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ trait MacroCommons extends CompatMacroCommons { bundle =>
final def TryCls: Tree = tq"$ScalaPkg.util.Try"
final def TryObj: Tree = q"$ScalaPkg.util.Try"
final def ImplicitsObj: Tree = q"$MiscPkg.Implicits"
final def ImplicitNotFoundCls: Tree = tq"$MiscPkg.ImplicitNotFound"
final def ConcurrentPkg: Tree = q"$ScalaPkg.concurrent"
final lazy val MapSym = typeOf[scala.collection.immutable.Map[_, _]].typeSymbol
final lazy val FutureSym = typeOf[scala.concurrent.Future[_]].typeSymbol
Expand All @@ -60,7 +59,6 @@ trait MacroCommons extends CompatMacroCommons { bundle =>
final lazy val SeqCompanionSym = typeOf[scala.collection.Seq.type].termSymbol
final lazy val PositionedAT = staticType(tq"$CommonsPkg.annotation.positioned")
final lazy val ImplicitNotFoundAT = staticType(tq"$ScalaPkg.annotation.implicitNotFound")
final lazy val ImplicitNotFoundSym = staticType(tq"$MiscPkg.ImplicitNotFound[_]").typeSymbol
final lazy val AggregatedMethodSym =
staticType(tq"$CommonsPkg.annotation.AnnotationAggregate").member(TermName("aggregated"))
final lazy val OptionalParamAT = staticType(tq"$CommonsPkg.serialization.optionalParam")
Expand Down Expand Up @@ -639,10 +637,9 @@ trait MacroCommons extends CompatMacroCommons { bundle =>
else
cachedImplicit.recreateDef(Modifiers(Flag.PRIVATE))

private def standardImplicitNotFoundMsg(tpe: Type): String =
symbolImplicitNotFoundMsg(tpe, tpe.typeSymbol, tpe.typeSymbol.typeSignature.typeParams, tpe.typeArgs)

private def symbolImplicitNotFoundMsg(tpe: Type, sym: Symbol, tparams: List[Symbol], typeArgs: List[Type]): String =
// reads the native `@implicitNotFound` annotation off the type's symbol, substituting `${TypeParam}` placeholders
def implicitNotFoundMsg(tpe: Type): String = {
val sym = tpe.typeSymbol
rawAnnotations(sym)
.find(_.tree.tpe <:< ImplicitNotFoundAT)
.map(_.tree.children.tail.head)
Expand All @@ -651,42 +648,13 @@ trait MacroCommons extends CompatMacroCommons { bundle =>
case NamedArgTree(_, StringLiteral(error)) => error
}
.map { error =>
val tpNames = tparams.map(_.name.decodedName.toString)
(tpNames zip typeArgs).foldLeft(error) { case (err, (tpName, tpArg)) =>
val tpNames = sym.typeSignature.typeParams.map(_.name.decodedName.toString)
(tpNames zip tpe.typeArgs).foldLeft(error) { case (err, (tpName, tpArg)) =>
err.replace(s"$${$tpName}", tpArg.toString)
}
}
.getOrElse {
if (sym != tpe.typeSymbol) standardImplicitNotFoundMsg(tpe)
else s"no implicit value of type $tpe found"
}

private def replaceArgs(stack: List[Type], error: String, params: List[Symbol], args: List[Tree]): String =
(params zip args)
.flatMap { case (param, arg) =>
arg.tpe.baseType(ImplicitNotFoundSym).typeArgs.headOption.map { delTpe =>
param.name.decodedName.toString -> implicitNotFoundMsg(stack, delTpe, arg)
}
}
.foldLeft(error) { case (err, (paramName, replacement)) =>
err.replace(s"#{$paramName}", replacement)
}

private def implicitNotFoundMsg(stack: List[Type], tpe: Type, tree: Tree): String =
if (stack.exists(_ =:= tpe))
standardImplicitNotFoundMsg(tpe)
else
tree match {
case MaybeApply(MaybeTypeApply(fun, typeArgs), args) =>
val sym = Option(fun.symbol).getOrElse(NoSymbol)
val sig = sym.typeSignature
val targs = typeArgs.map(_.tpe)
val baseMsg = symbolImplicitNotFoundMsg(tpe, sym, sig.typeParams, targs)
replaceArgs(tpe :: stack, baseMsg, sig.paramLists.headOption.getOrElse(Nil), args)
}

def implicitNotFoundMsg(tpe: Type): String =
implicitNotFoundMsg(Nil, tpe, inferImplicitValue(getType(tq"$ImplicitNotFoundCls[$tpe]")))
.getOrElse(s"no implicit value of type $tpe found")
}

class treeOps[T <: Tree](t: T) {
def debug: T = {
Expand Down
Loading