diff --git a/core/src/main/scala/com/avsystem/commons/meta/MetadataCompanion.scala b/core/src/main/scala/com/avsystem/commons/meta/MetadataCompanion.scala index 61b962bd0..103f93c6d 100644 --- a/core/src/main/scala/com/avsystem/commons/meta/MetadataCompanion.scala +++ b/core/src/main/scala/com/avsystem/commons/meta/MetadataCompanion.scala @@ -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 @@ -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() } @@ -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() } diff --git a/core/src/main/scala/com/avsystem/commons/misc/Implicits.scala b/core/src/main/scala/com/avsystem/commons/misc/Implicits.scala index 6e8f3140f..3b014c8d3 100644 --- a/core/src/main/scala/com/avsystem/commons/misc/Implicits.scala +++ b/core/src/main/scala/com/avsystem/commons/misc/Implicits.scala @@ -1,6 +1,8 @@ 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 @@ -8,8 +10,11 @@ object Implicits { * `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] } @@ -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() } diff --git a/core/src/main/scala/com/avsystem/commons/rpc/AsRawReal.scala b/core/src/main/scala/com/avsystem/commons/rpc/AsRawReal.scala index 84de8fe04..dc1819880 100644 --- a/core/src/main/scala/com/avsystem/commons/rpc/AsRawReal.scala +++ b/core/src/main/scala/com/avsystem/commons/rpc/AsRawReal.scala @@ -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] { @@ -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() @@ -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() diff --git a/core/src/test/scala/com/avsystem/commons/misc/ImplicitNotFoundTest.scala b/core/src/test/scala/com/avsystem/commons/misc/ImplicitNotFoundTest.scala index dea8dfd2a..ecd3ec9f4 100644 --- a/core/src/test/scala/com/avsystem/commons/misc/ImplicitNotFoundTest.scala +++ b/core/src/test/scala/com/avsystem/commons/misc/ImplicitNotFoundTest.scala @@ -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") - } } diff --git a/macros/src/main/scala/com/avsystem/commons/macros/MacroCommons.scala b/macros/src/main/scala/com/avsystem/commons/macros/MacroCommons.scala index bd8ad6463..2e20f4eff 100644 --- a/macros/src/main/scala/com/avsystem/commons/macros/MacroCommons.scala +++ b/macros/src/main/scala/com/avsystem/commons/macros/MacroCommons.scala @@ -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 @@ -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") @@ -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) @@ -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 = {