Description
In 1.3.1, Magnolia usages in Tapir generate warnings like:
[warn] /xxx/src/core/impl.scala: the type test for Option[com.softwaremill.Endpoints.UserId]
cannot be checked at runtime because its type arguments can't be determined from Any
I have added a workaround with suppression in #473, but a proper fix may be needed in the future.
Detailed problem description
When deriving default values for case class members, Magnolia checks if there's a user-defined default, otherwise it falls back to a default based on field's type. For example, for case class Example(field: Int)
it would derive a default of Example(0)
. However, there's an issue when dealing with generic classes with generic field types, where user defined default exists:
case class Example[A](field: Option[A] = Some("A"))
val defaultVal = HasDefault.derived[Example[Int]].defaultValue.right.get
// defaultVal == Example(Some("A")), wrong! Should be Example(None)
val fieldVal: Int = defaultVal.field.get // compiles, but throws a ClassCastException
Consequences
The Example(Some("A"))
default value is incorrect, because object's type is Example[Int]
. This would throw a ClassCastException
on resolving the field's wrapped value. Using generic types with default values like this doesn't seem like a common scenario, so severity of this issue is very low.
Cause
In core.paramsFromMaps default values for case class fields are passed in an argument of type defaults: Map[String, Option[() => Any]]
. These Any
values are unsafely casted to type parameters in the safeCast
method, which may cause casting for example an Option[String]
to an Option[Int]
. Instead, for a default param that doesn't match the type, a None
should be returned, causing a fallback type's proper default value.
Possible solution
A proper resolution might require enriching defaults: Map[String, Option[() => Any]]
with type information, which, compared to actual type (in the method called p
) would allow to detect a mismatch, causing a fallback to None
. This requires nontrivial changes to the macro which generates defaults
.