Skip to content

Commit

Permalink
Add missing implicit not found messages (#471)
Browse files Browse the repository at this point in the history
Unfortunately the experiece is subpar, because it doesn't work well
with type aliases which don't support type argument interpolation.
  • Loading branch information
joroKr21 authored May 22, 2022
1 parent 5dc1983 commit 8f76c24
Show file tree
Hide file tree
Showing 23 changed files with 112 additions and 50 deletions.
5 changes: 1 addition & 4 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,7 @@ lazy val commonSettings = Seq(
),
libraryDependencies ++= (CrossVersion.partialVersion(scalaVersion.value) match {
case Some((3, _)) =>
Seq(
"org.typelevel" %%% "shapeless3-deriving" % shapeless3Version,
"org.typelevel" %%% "shapeless3-test" % shapeless3Version % Test
)
Seq("org.typelevel" %%% "shapeless3-deriving" % shapeless3Version)
case _ =>
Seq(
"com.chuusai" %%% "shapeless" % shapeless2Version,
Expand Down
1 change: 1 addition & 0 deletions core/src/main/scala-3/cats/derived/Derived.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import shapeless3.deriving.*
import scala.annotation.*
import scala.compiletime.*

@implicitNotFound("Could not derive an instance of ${A}")
opaque type Derived[A] = A
object Derived:
def apply[A](instance: A): Derived[A] = instance
Expand Down
8 changes: 3 additions & 5 deletions core/src/main/scala-3/cats/derived/DerivedApplicative.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,9 @@ import scala.deriving.Mirror

@implicitNotFound("""Could not derive an instance of Applicative[F] where F = ${F}.
Make sure that F[_] satisfies one of the following conditions:
* it is a constant type λ[x => T] where T: Monoid
* it is a nested type λ[x => G[H[x]]] where G: Applicative and H: Applicative
* it is a generic case class where all fields have an Applicative instance
Note: using kind-projector notation - https://github.com/typelevel/kind-projector""")
* it is a constant type [x] =>> T where T: Monoid
* it is a nested type [x] =>> G[H[x]] where G: Applicative and H: Applicative
* it is a generic case class where all fields have an Applicative instance""")
type DerivedApplicative[F[_]] = Derived[Applicative[F]]
object DerivedApplicative:
type Or[F[_]] = Derived.Or[Applicative[F]]
Expand Down
8 changes: 3 additions & 5 deletions core/src/main/scala-3/cats/derived/DerivedApply.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,9 @@ import scala.deriving.Mirror

@implicitNotFound("""Could not derive an instance of Apply[F] where F = ${F}.
Make sure that F[_] satisfies one of the following conditions:
* it is a constant type λ[x => T] where T: Semigroup
* it is a nested type λ[x => G[H[x]]] where G: Apply and H: Apply
* it is a generic case class where all fields have an Apply instance
Note: using kind-projector notation - https://github.com/typelevel/kind-projector""")
* it is a constant type [x] =>> T where T: Semigroup
* it is a nested type [x] =>> G[H[x]] where G: Apply and H: Apply
* it is a generic case class where all fields have an Apply instance""")
type DerivedApply[F[_]] = Derived[Apply[F]]
object DerivedApply:
type Or[F[_]] = Derived.Or[Apply[F]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ package cats.derived
import cats.kernel.CommutativeMonoid
import shapeless3.deriving.K0

import scala.annotation.implicitNotFound
import scala.compiletime.*

@implicitNotFound("""Could not derive an instance of CommutativeMonoid[A] where A = ${A}.
Make sure that A is a case class where all fields have a CommutativeMonoid instance.""")
type DerivedCommutativeMonoid[A] = Derived[CommutativeMonoid[A]]
object DerivedCommutativeMonoid:
type Or[A] = Derived.Or[CommutativeMonoid[A]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ package cats.derived
import cats.kernel.CommutativeSemigroup
import shapeless3.deriving.K0

import scala.annotation.implicitNotFound
import scala.compiletime.*

@implicitNotFound("""Could not derive an instance of CommutativeSemigroup[A] where A = ${A}.
Make sure that A is a case class where all fields have a CommutativeSemigroup instance.""")
type DerivedCommutativeSemigroup[A] = Derived[CommutativeSemigroup[A]]
object DerivedCommutativeSemigroup:
type Or[A] = Derived.Or[CommutativeSemigroup[A]]
Expand Down
5 changes: 5 additions & 0 deletions core/src/main/scala-3/cats/derived/DerivedEmpty.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,13 @@ import alleycats.Empty
import cats.derived.util.Kinds
import shapeless3.deriving.K0

import scala.annotation.implicitNotFound
import scala.compiletime.*

@implicitNotFound("""Could not derive an instance of Empty[A] where A = ${A}.
Make sure that A satisfies one of the following conditions:
* it is a case class where all fields have an Empty instance
* it is a sealed trait where exactly one subclass has an Empty instance""")
type DerivedEmpty[A] = Derived[Empty[A]]
object DerivedEmpty:
type Or[A] = Derived.Or[Empty[A]]
Expand Down
10 changes: 4 additions & 6 deletions core/src/main/scala-3/cats/derived/DerivedEmptyK.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,11 @@ import scala.util.NotGiven

@implicitNotFound("""Could not derive an instance of EmptyK[F] where F = ${F}.
Make sure that F[_] satisfies one of the following conditions:
* it is a constant type λ[x => T] where T: Empty
* it is a nested type λ[x => G[H[x]]] where G: EmptyK
* it is a nested type λ[x => G[H[x]]] where G: Pure and H: EmptyK
* it is a constant type [x] =>> T where T: Empty
* it is a nested type [x] =>> G[H[x]] where G: EmptyK
* it is a nested type [x] =>> G[H[x]] where G: Pure and H: EmptyK
* it is a generic case class where all fields have an EmptyK instance
* it is a generic sealed trait where exactly one subtype has an EmptyK instance
Note: using kind-projector notation - https://github.com/typelevel/kind-projector""")
* it is a generic sealed trait where exactly one subclass has an EmptyK instance""")
type DerivedEmptyK[F[_]] = Derived[EmptyK[F]]
object DerivedEmptyK:
type Or[F[_]] = Derived.Or[EmptyK[F]]
Expand Down
5 changes: 5 additions & 0 deletions core/src/main/scala-3/cats/derived/DerivedEq.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@ package cats.derived
import cats.Eq
import shapeless3.deriving.{Complete, K0}

import scala.annotation.implicitNotFound
import scala.compiletime.*

@implicitNotFound("""Could not derive an instance of Eq[A] where A = ${A}.
Make sure that A satisfies one of the following conditions:
* it is a case class where all fields have an Eq instance
* it is a sealed trait where all subclasses have an Eq instance""")
type DerivedEq[A] = Derived[Eq[A]]
object DerivedEq:
type Or[A] = Derived.Or[Eq[A]]
Expand Down
8 changes: 8 additions & 0 deletions core/src/main/scala-3/cats/derived/DerivedFoldable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,16 @@ package cats.derived

import cats.{Eval, Foldable}
import shapeless3.deriving.{Const, Continue, K1}

import scala.annotation.implicitNotFound
import scala.compiletime.*

@implicitNotFound("""Could not derive an instance of Foldable[F] where F = ${F}.
Make sure that F[_] satisfies one of the following conditions:
* it is a constant type [x] =>> T
* it is a nested type [x] =>> G[H[x]] where G: Foldable and H: Foldable
* it is a generic case class where all fields have a Foldable instance
* it is a generic sealed trait where all subclasses have a Foldable instance""")
type DerivedFoldable[F[_]] = Derived[Foldable[F]]
object DerivedFoldable:
type Or[F[_]] = Derived.Or[Foldable[F]]
Expand Down
8 changes: 8 additions & 0 deletions core/src/main/scala-3/cats/derived/DerivedFunctor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,16 @@ package cats.derived
import cats.{Contravariant, Functor}
import shapeless3.deriving.{Const, K1}

import scala.annotation.implicitNotFound
import scala.compiletime.*

@implicitNotFound("""Could not derive an instance of Functor[F] where F = ${F}.
Make sure that F[_] satisfies one of the following conditions:
* it is a constant type [x] =>> T
* it is a nested type [x] =>> G[H[x]] where G: Functor and H: Functor
* it is a nested type [x] =>> G[H[x]] where G: Contravariant and H: Contravariant
* it is a generic case class where all fields have a Functor instance
* it is a generic sealed trait where all subclasses have a Functor instance""")
type DerivedFunctor[F[_]] = Derived[Functor[F]]
object DerivedFunctor:
type Or[F[_]] = Derived.Or[Functor[F]]
Expand Down
5 changes: 5 additions & 0 deletions core/src/main/scala-3/cats/derived/DerivedHash.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,14 @@ package cats.derived
import cats.Hash
import shapeless3.deriving.{K0, Continue}

import scala.annotation.implicitNotFound
import scala.compiletime.*
import scala.util.hashing.MurmurHash3

@implicitNotFound("""Could not derive an instance of Hash[A] where A = ${A}.
Make sure that A satisfies one of the following conditions:
* it is a case class where all fields have a Hash instance
* it is a sealed trait where all subclasses have a Hash instance""")
type DerivedHash[A] = Derived[Hash[A]]
object DerivedHash:
type Or[A] = Derived.Or[Hash[A]]
Expand Down
3 changes: 3 additions & 0 deletions core/src/main/scala-3/cats/derived/DerivedMonoid.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ package cats.derived
import cats.Monoid
import shapeless3.deriving.K0

import scala.annotation.implicitNotFound
import scala.compiletime.*

@implicitNotFound("""Could not derive an instance of Monoid[A] where A = ${A}.
Make sure that A is a case class where all fields have a Monoid instance.""")
type DerivedMonoid[A] = Derived[Monoid[A]]
object DerivedMonoid:
type Or[A] = Derived.Or[Monoid[A]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,14 @@ package cats.derived
import cats.{Applicative, Apply, NonEmptyTraverse, Traverse}
import shapeless3.deriving.{Const, K1}

import scala.annotation.implicitNotFound
import scala.compiletime.*

@implicitNotFound("""Could not derive an instance of NonEmptyTraverse[F] where F = ${F}.
Make sure that F[_] satisfies one of the following conditions:
* it is a nested type [x] =>> G[H[x]] where G: NonEmptyTraverse and H: NonEmptyTraverse
* it is a generic case class where at least one field has a NonEmptyTraverse and the rest Traverse instances
* it is a generic sealed trait where all subclasses have a NonEmptyTraverse instance""")
type DerivedNonEmptyTraverse[F[_]] = Derived[NonEmptyTraverse[F]]
object DerivedNonEmptyTraverse:
type Or[F[_]] = Derived.Or[NonEmptyTraverse[F]]
Expand Down
9 changes: 6 additions & 3 deletions core/src/main/scala-3/cats/derived/DerivedOrder.scala
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package cats.derived

import cats.{Order, Show}

import scala.compiletime.*
import shapeless3.deriving.{Complete, Continue, K0, Labelling}

import scala.annotation.targetName
import scala.annotation.implicitNotFound
import scala.compiletime.*
import scala.deriving.Mirror

@implicitNotFound("""Could not derive an instance of Order[A] where A = ${A}.
Make sure that A satisfies one of the following conditions:
* it is a case class where all fields have an Order instance
* it is a sealed trait where all subclasses have an Order instance""")
type DerivedOrder[A] = Derived[Order[A]]
object DerivedOrder:
type Or[A] = Derived.Or[Order[A]]
Expand Down
7 changes: 7 additions & 0 deletions core/src/main/scala-3/cats/derived/DerivedReducible.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,15 @@ package cats.derived

import cats.{Eval, Foldable, Reducible}
import shapeless3.deriving.{Continue, Const, K1}

import scala.annotation.implicitNotFound
import scala.compiletime.*

@implicitNotFound("""Could not derive an instance of Reducible[F] where F = ${F}.
Make sure that F[_] satisfies one of the following conditions:
* it is a nested type [x] =>> G[H[x]] where G: Reducible and H: Reducible
* it is a generic case class where at least one field has a Reducible and the rest Foldable instances
* it is a generic sealed trait where all subclasses have a Reducible instance""")
type DerivedReducible[F[_]] = Derived[Reducible[F]]
object DerivedReducible:
type Or[F[_]] = Derived.Or[Reducible[F]]
Expand Down
3 changes: 3 additions & 0 deletions core/src/main/scala-3/cats/derived/DerivedSemigroup.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ package cats.derived
import cats.Semigroup
import shapeless3.deriving.K0

import scala.annotation.implicitNotFound
import scala.compiletime.*

@implicitNotFound("""Could not derive an instance of Semigroup[A] where A = ${A}.
Make sure that A is a case class where all fields have a Semigroup instance.""")
type DerivedSemigroup[A] = Derived[Semigroup[A]]
object DerivedSemigroup:
type Or[A] = Derived.Or[Semigroup[A]]
Expand Down
8 changes: 7 additions & 1 deletion core/src/main/scala-3/cats/derived/DerivedShow.scala
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
package cats.derived

import cats.Show
import scala.compiletime.*
import shapeless3.deriving.{Continue, K0, Labelling}

import scala.annotation.implicitNotFound
import scala.compiletime.*
import scala.deriving.Mirror

@implicitNotFound("""Could not derive an instance of Show[A] where A = ${A}.
Make sure that A satisfies one of the following conditions:
* it is a case class where all fields have a Show instance
* it is a sealed trait where all subclasses have a Show instance""")
type DerivedShow[A] = Derived[Show[A]]
object DerivedShow:
type Or[A] = Derived.Or[Show[A]]
Expand Down
7 changes: 7 additions & 0 deletions core/src/main/scala-3/cats/derived/DerivedTraverse.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,15 @@ package cats.derived
import cats.{Applicative, Eval, Traverse}
import shapeless3.deriving.{Const, K1}

import scala.annotation.implicitNotFound
import scala.compiletime.*

@implicitNotFound("""Could not derive an instance of Traverse[F] where F = ${F}.
Make sure that F[_] satisfies one of the following conditions:
* it is a constant type [x] =>> T
* it is a nested type [x] =>> G[H[x]] where G: Traverse and H: Traverse
* it is a generic case class where all fields have a Traverse instance
* it is a generic sealed trait where all subclasses have a Traverse instance""")
type DerivedTraverse[F[_]] = Derived[Traverse[F]]
object DerivedTraverse:
type Or[F[_]] = Derived.Or[Traverse[F]]
Expand Down
2 changes: 0 additions & 2 deletions core/src/test/scala-3/cats/derived/EmptyKSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ import alleycats.{EmptyK, Pure}
import alleycats.std.all.*
import cats.data.NonEmptyList
import cats.laws.discipline.SerializableTests
import cats.syntax.all.*
import shapeless3.test.illTyped

import scala.compiletime.summonInline

Expand Down
19 changes: 6 additions & 13 deletions core/src/test/scala-3/cats/derived/EmptySuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import alleycats.Empty
import cats.laws.discipline.SerializableTests

import scala.compiletime.*
import scala.util.NotGiven

class EmptySuite extends KittensSuite:
import EmptySuite.given
Expand All @@ -40,26 +39,20 @@ class EmptySuite extends KittensSuite:
test(s"$context.Empty respects existing instances")(assert(empty[Box[Mask]] == Box(Mask(0xffffffff))))
checkAll(s"$context.Empty is Serializable", SerializableTests.serializable(summonInline[Empty[Foo]]))

inline def testNoAuto[A]: Unit =
testNoInstance("Empty[" + nameOf[A] + "]", "Could not find an instance of Empty")

inline def testNoSemi[A]: Unit =
testNoInstance("semiauto.empty[" + nameOf[A] + "]", "no implicit argument of type cats.derived.DerivedEmpty")

locally {
import auto.empty.given
testEmpty("auto")
testNoAuto[IList[Int]]
testNoAuto[Snoc[Int]]
testNoAuto[Rgb]
testNoAuto("Empty", "IList[Int]")
testNoAuto("Empty", "Snoc[Int]")
testNoAuto("Empty", "Rgb")
}

locally {
import semiInstances.given
testEmpty("semiauto")
testNoSemi[IList[Int]]
testNoSemi[Snoc[Int]]
testNoSemi[Rgb]
testNoSemi("Empty", "IList[Int]")
testNoSemi("Empty", "Snoc[Int]")
testNoSemi("Empty", "Rgb")
}

end EmptySuite
Expand Down
20 changes: 14 additions & 6 deletions core/src/test/scala-3/cats/derived/KittensSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,12 @@ import scala.quoted.*
*/
abstract class KittensSuite extends KittensSuite.WithoutEq, TestEqInstances
object KittensSuite:
def nameOfMacro[A: Type](using Quotes) =
Expr(Type.show[A])
def deCapitalizeMacro(str: Expr[String])(using Quotes) =
val value = str.valueOrAbort
Expr(if (value.isEmpty) "" else value.head.toLower +: value.tail)

inline def deCapitalize(inline str: String): String =
${ deCapitalizeMacro('str) }

/** Used to test `Eq` derivation. */
abstract class WithoutEq extends DisciplineSuite, AllSyntax:
Expand All @@ -44,8 +48,12 @@ object KittensSuite:
given [A: Arbitrary]: Arbitrary[List[A]] =
Arbitrary.arbContainer

inline def nameOf[A]: String =
${ KittensSuite.nameOfMacro[A] }
inline def testNoInstance(inline tc: String, target: String, message: String): Unit =
val errors = compileErrors(tc + "[" + target + "]")
test(s"No $tc for $target")(assert(errors.contains(message), s"$errors did not contain $message"))

inline def testNoAuto(inline tc: String, target: String): Unit =
testNoInstance(tc, target, "Could not find an instance of " + tc)

inline def testNoInstance(inline code: String, message: String): Unit =
test(s"No $code")(assert(compileErrors(code).contains(message)))
inline def testNoSemi(inline tc: String, target: String): Unit =
testNoInstance("semiauto." + deCapitalize(tc), target, "Could not derive an instance of " + tc)
9 changes: 4 additions & 5 deletions core/src/test/scala-3/cats/derived/PureSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ package cats.derived
import alleycats.Pure
import cats.data.NonEmptyList
import cats.laws.discipline.SerializableTests
import shapeless3.test.illTyped

import scala.compiletime.summonInline

Expand All @@ -43,15 +42,15 @@ class PureSuite extends KittensSuite:
locally {
import auto.pure.given
testPure("auto")
illTyped("Pure[IList]")
illTyped("Pure[Snoc]")
testNoAuto("Pure", "IList")
testNoAuto("Pure", "Snoc")
}

locally {
import semiInstances.given
testPure("semiauto")
illTyped("semiauto.pure[IList]")
illTyped("semiauto.pure[Snoc]")
testNoSemi("Pure", "IList")
testNoSemi("Pure", "Snoc")
}

end PureSuite
Expand Down

0 comments on commit 8f76c24

Please sign in to comment.