diff --git a/build.sbt b/build.sbt index 254b5332..b1f86b5b 100644 --- a/build.sbt +++ b/build.sbt @@ -2,7 +2,7 @@ import com.softwaremill.UpdateVersionInDocs import com.softwaremill.SbtSoftwareMillCommon.commonSmlBuildSettings import com.softwaremill.Publish.{updateDocs, ossPublishSettings} -val scala3 = "3.2.1" +val scala3 = "3.3.4" ThisBuild / dynverTagPrefix := "scala3-v" // a custom prefix is needed to differentiate tags between scala2 & scala3 versions @@ -75,7 +75,7 @@ lazy val test = (projectMatrix in file(".test")) name := "magnolia-test", scalacOptions += "-Yretain-trees", projectDependencies ++= Seq( - "org.scalameta" %%% "munit" % "1.0.0-M6" + "org.scalameta" %%% "munit" % "1.0.0-M12" ), testFrameworks += new TestFramework("munit.Framework"), Test / scalaSource := baseDirectory.value / ".." / ".." / ".." / "src" / "test", diff --git a/project/build.properties b/project/build.properties index 8b9a0b0a..ee4c672c 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.8.0 +sbt.version=1.10.1 diff --git a/project/plugins.sbt b/project/plugins.sbt index 5f14d51c..59a00b63 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,4 +1,4 @@ -addSbtPlugin("com.eed3si9n" % "sbt-projectmatrix" % "0.9.0") +addSbtPlugin("com.eed3si9n" % "sbt-projectmatrix" % "0.10.0") val sbtSoftwareMillVersion = "2.0.12" addSbtPlugin( @@ -8,6 +8,6 @@ addSbtPlugin( "com.softwaremill.sbt-softwaremill" % "sbt-softwaremill-publish" % sbtSoftwareMillVersion ) -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.12.0") -addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.9") -addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "1.1.1") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.16.0") +addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.4") +addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "1.1.3") diff --git a/src/core/impl.scala b/src/core/impl.scala index 85d344dd..4b1f0d9b 100644 --- a/src/core/impl.scala +++ b/src/core/impl.scala @@ -1,5 +1,6 @@ package magnolia2 +import scala.annotation.Annotation import scala.compiletime.* import scala.deriving.Mirror import scala.reflect.* @@ -85,7 +86,7 @@ object CaseClassDerivation: parameters, IArray(anns[A]*), IArray(inheritedAnns[A]*), - IArray[Any](typeAnns[A]*) + IArray[Annotation](typeAnns[A]*) ): def construct[PType: ClassTag](makeParam: Param => PType): A = product.fromProduct(Tuple.fromArray(params.map(makeParam).to(Array))) @@ -120,9 +121,9 @@ object CaseClassDerivation: } inline def paramsFromMaps[Typeclass[_], A, Labels <: Tuple, Params <: Tuple]( - annotations: Map[String, List[Any]], - inheritedAnnotations: Map[String, List[Any]], - typeAnnotations: Map[String, List[Any]], + annotations: Map[String, List[Annotation]], + inheritedAnnotations: Map[String, List[Annotation]], + typeAnnotations: Map[String, List[Annotation]], repeated: Map[String, Boolean], defaults: Map[String, Option[() => Any]], idx: Int = 0 @@ -165,7 +166,7 @@ trait SealedTraitDerivation: typeInfo[A], IArray(subtypesFromMirror[A, m.MirroredElemTypes](m)*), IArray.from(anns[A]), - IArray(paramTypeAnns[A]*), + IArray.from(paramTypeAnns[A]), isEnum[A], IArray.from(inheritedAnns[A]) ) diff --git a/src/core/interface.scala b/src/core/interface.scala index db67cd35..dccb3ceb 100644 --- a/src/core/interface.scala +++ b/src/core/interface.scala @@ -1,6 +1,6 @@ package magnolia2 -import scala.annotation.tailrec +import scala.annotation.{Annotation, tailrec} import scala.reflect.* case class TypeInfo( @@ -15,8 +15,8 @@ object CaseClass: val label: String, val index: Int, val repeated: Boolean, - val annotations: IArray[Any], - val typeAnnotations: IArray[Any] + val annotations: IArray[Annotation], + val typeAnnotations: IArray[Annotation] ) extends Serializable: type PType @@ -42,7 +42,7 @@ object CaseClass: * default argument value, if any */ def default: Option[PType] - def inheritedAnnotations: IArray[Any] = IArray.empty[Any] + def inheritedAnnotations: IArray[Annotation] = IArray.empty[Annotation] override def toString: String = s"Param($label)" object Param: @@ -53,9 +53,9 @@ object CaseClass: repeated: Boolean, cbn: CallByNeed[F[P]], defaultVal: CallByNeed[Option[P]], - annotations: IArray[Any], - inheritedAnns: IArray[Any], - typeAnnotations: IArray[Any] + annotations: IArray[Annotation], + inheritedAnns: IArray[Annotation], + typeAnnotations: IArray[Annotation] ): Param[F, T] = new CaseClass.Param[F, T]( name, @@ -89,9 +89,9 @@ abstract class CaseClass[Typeclass[_], Type]( val isObject: Boolean, val isValueClass: Boolean, val params: IArray[CaseClass.Param[Typeclass, Type]], - val annotations: IArray[Any], - val inheritedAnnotations: IArray[Any] = IArray.empty[Any], - val typeAnnotations: IArray[Any] + val annotations: IArray[Annotation], + val inheritedAnnotations: IArray[Annotation] = IArray.empty[Annotation], + val typeAnnotations: IArray[Annotation] ) extends Serializable: type Param = CaseClass.Param[Typeclass, Type] @@ -113,9 +113,9 @@ abstract class CaseClass[Typeclass[_], Type]( repeated: Boolean, cbn: CallByNeed[Typeclass[P]], defaultVal: CallByNeed[Option[P]], - annotations: IArray[Any], - inheritedAnns: IArray[Any], - typeAnnotations: IArray[Any] + annotations: IArray[Annotation], + inheritedAnns: IArray[Annotation], + typeAnnotations: IArray[Annotation] ): Param = new CaseClass.Param[Typeclass, Type]( name, @@ -141,10 +141,10 @@ end CaseClass case class SealedTrait[Typeclass[_], Type]( typeInfo: TypeInfo, subtypes: IArray[SealedTrait.Subtype[Typeclass, Type, _]], - annotations: IArray[Any], - typeAnnotations: IArray[Any], + annotations: IArray[Annotation], + typeAnnotations: IArray[(String, List[Annotation])], isEnum: Boolean, - inheritedAnnotations: IArray[Any] + inheritedAnnotations: IArray[Annotation] ) extends Serializable: type Subtype[S] = SealedTrait.SubtypeValue[Typeclass, Type, S] @@ -191,9 +191,9 @@ object SealedTrait: */ class Subtype[Typeclass[_], Type, SType]( val typeInfo: TypeInfo, - val annotations: IArray[Any], - val inheritedAnnotations: IArray[Any], - val typeAnnotations: IArray[Any], + val annotations: IArray[Annotation], + val inheritedAnnotations: IArray[Annotation], + val typeAnnotations: IArray[(String, List[Annotation])], val isObject: Boolean, callByNeed: CallByNeed[Typeclass[SType]], isType: Type => Boolean, diff --git a/src/core/macro.scala b/src/core/macro.scala index 66537d24..35092884 100644 --- a/src/core/macro.scala +++ b/src/core/macro.scala @@ -3,6 +3,7 @@ package magnolia2 import scala.quoted.* import scala.annotation.meta.field import scala.annotation.Annotation +import scala.reflect.ClassTag object Macro: @@ -15,19 +16,19 @@ object Macro: inline def isEnum[T]: Boolean = ${ isEnum[T] } - inline def anns[T]: List[Any] = + inline def anns[T]: List[Annotation] = ${ anns[T] } - inline def inheritedAnns[T]: List[Any] = + inline def inheritedAnns[T]: List[Annotation] = ${ inheritedAnns[T] } - inline def typeAnns[T]: List[Any] = + inline def typeAnns[T]: List[Annotation] = ${ typeAnns[T] } - inline def paramAnns[T]: List[(String, List[Any])] = + inline def paramAnns[T]: List[(String, List[Annotation])] = ${ paramAnns[T] } - inline def inheritedParamAnns[T]: List[(String, List[Any])] = + inline def inheritedParamAnns[T]: List[(String, List[Annotation])] = ${ inheritedParamAnns[T] } inline def isValueClass[T]: Boolean = @@ -36,7 +37,7 @@ object Macro: inline def defaultValue[T]: List[(String, Option[() => Any])] = ${ defaultValue[T] } - inline def paramTypeAnns[T]: List[(String, List[Any])] = + inline def paramTypeAnns[T]: List[(String, List[Annotation])] = ${ paramTypeAnns[T] } inline def repeated[T]: List[(String, Boolean)] = @@ -60,26 +61,26 @@ object Macro: def paramTypeAnns[T: Type](using q: Quotes - ): Expr[List[(String, List[Any])]] = + ): Expr[List[(String, List[Annotation])]] = new CollectAnnotations[q.type, T].paramTypeAnns - def anns[T: Type](using q: Quotes): Expr[List[Any]] = + def anns[T: Type](using q: Quotes): Expr[List[Annotation]] = new CollectAnnotations[q.type, T].anns - def inheritedAnns[T: Type](using q: Quotes): Expr[List[Any]] = + def inheritedAnns[T: Type](using q: Quotes): Expr[List[Annotation]] = new CollectAnnotations[q.type, T].inheritedAnns - def typeAnns[T: Type](using q: Quotes): Expr[List[Any]] = + def typeAnns[T: Type](using q: Quotes): Expr[List[Annotation]] = new CollectAnnotations[q.type, T].typeAnns def paramAnns[T: Type](using q: Quotes - ): Expr[List[(String, List[Any])]] = + ): Expr[List[(String, List[Annotation])]] = new CollectAnnotations[q.type, T].paramAnns def inheritedParamAnns[T: Type](using q: Quotes - ): Expr[List[(String, List[Any])]] = + ): Expr[List[(String, List[Annotation])]] = new CollectAnnotations[q.type, T].inheritedParamAnns def isValueClass[T: Type](using Quotes): Expr[Boolean] = @@ -192,14 +193,14 @@ object Macro: val tpe: TypeRepr = TypeRepr.of[T] - def anns: Expr[List[Any]] = + def anns: Expr[List[Annotation]] = Expr.ofList { tpe.typeSymbol.annotations .filter(filterAnnotation) - .map(_.asExpr.asInstanceOf[Expr[Any]]) + .map(_.asExpr.asInstanceOf[Expr[Annotation]]) } - def inheritedAnns: Expr[List[Any]] = + def inheritedAnns: Expr[List[Annotation]] = Expr.ofList { tpe.baseClasses .filterNot(isObjectOrScala) @@ -208,10 +209,10 @@ object Macro: } // skip self .flatten .filter(filterAnnotation) - .map(_.asExpr.asInstanceOf[Expr[Any]]) + .map(_.asExpr.asInstanceOf[Expr[Annotation]]) } - def typeAnns: Expr[List[Any]] = + def typeAnns: Expr[List[Annotation]] = val symbol: Option[Symbol] = if tpe.typeSymbol.isNoSymbol then None else Some(tpe.typeSymbol) @@ -231,7 +232,7 @@ object Macro: } .flatMap(loopForAnnotations) .filter(filterAnnotation) - .map { _.asExpr.asInstanceOf[Expr[Any]] } + .map { _.asExpr.asInstanceOf[Expr[Annotation]] } case _ => List.empty } @@ -248,8 +249,8 @@ object Macro: } .filter(_._2.nonEmpty) - def paramTypeAnns: Expr[List[(String, List[Any])]] = - liftTermsDict(paramTypeAnnsOnTerms) + def paramTypeAnns: Expr[List[(String, List[Annotation])]] = + liftTermsDict(paramTypeAnnsOnTerms).asInstanceOf[Expr[List[(String, List[Annotation])]]] def paramAnnsOnTerms: List[(String, List[Term])] = val terms = (annotationsFromConstructorOnTerms( @@ -259,8 +260,8 @@ object Macro: groupByNameOnTerms(terms) - def paramAnns: Expr[List[(String, List[Any])]] = - liftTermsDict(paramAnnsOnTerms) + def paramAnns: Expr[List[(String, List[Annotation])]] = + liftTermsDict(paramAnnsOnTerms).asInstanceOf[Expr[List[(String, List[Annotation])]]] def inheritedParamAnnsOnTerms: List[(String, List[Term])] = val annTerms: List[(String, List[Term])] = @@ -279,8 +280,8 @@ object Macro: groupByNameOnTerms(annTerms) - def inheritedParamAnns: Expr[List[(String, List[Any])]] = - liftTermsDict(inheritedParamAnnsOnTerms) + def inheritedParamAnns: Expr[List[(String, List[Annotation])]] = + liftTermsDict(inheritedParamAnnsOnTerms).asInstanceOf[Expr[List[(String, List[Annotation])]]] private def loopForAnnotations(t: TypeRepr): List[Term] = t match @@ -310,10 +311,10 @@ object Macro: private def annotationsfromConstructor( from: Symbol - ): List[(String, List[Expr[Any]])] = + ): List[(String, List[Expr[Annotation]])] = annotationsFromConstructorOnTerms(from) .map { p => - p._1 -> p._2.map(_.asExpr.asInstanceOf[Expr[Any]]) + p._1 -> p._2.map(_.asExpr.asInstanceOf[Expr[Annotation]]) } private def annotationsFromDeclarationsOnTerms( @@ -327,10 +328,10 @@ object Macro: private def annotationsFromDeclarations( from: Symbol - ): List[(String, List[Expr[Any]])] = + ): List[(String, List[Expr[Annotation]])] = annotationsFromDeclarationsOnTerms(from) .map { p => - p._1 -> p._2.map(_.asExpr.asInstanceOf[Expr[Any]]) + p._1 -> p._2.map(_.asExpr.asInstanceOf[Expr[Annotation]]) } private def groupByNameOnTerms( @@ -346,8 +347,9 @@ object Macro: bc.fullName.startsWith("scala.") private def filterAnnotation(a: Term): Boolean = - a.tpe.typeSymbol.maybeOwner.isNoSymbol || - a.tpe.typeSymbol.owner.fullName != "scala.annotation.internal" + (a.tpe <:< TypeRepr.of[Annotation]) && + (a.tpe.typeSymbol.maybeOwner.isNoSymbol || + a.tpe.typeSymbol.owner.fullName != "scala.annotation.internal") object ValueClassDerivation: @@ -383,7 +385,7 @@ object Macro: case Some(expr) => '{ new CallByNeed(() => Some(($expr).asInstanceOf[P])) } - def selectFromDict( + def selectFromDict[T: Type]( dict: List[(String, List[Term])], name: String ) = @@ -393,11 +395,11 @@ object Macro: dict, name ) - .map(_.asExpr) + .map(_.asExprOf[T]) } - def toIArray(es: Expr[List[Any]]): Expr[IArray[Any]] = - '{ IArray($es: _*) } + def toAnnIArray(es: Expr[List[Annotation]]): Expr[IArray[Annotation]] = + '{ IArray[Annotation]($es: _*) } extension [B: Type](e: Expr[B]) def asCallByNeedExpr: Expr[CallByNeed[B]] = @@ -437,24 +439,24 @@ object Macro: selectDefault[p](defaultValueOnTerms[a], paramSymbol.name) val annotations = - toIArray( - selectFromDict( + toAnnIArray( + selectFromDict[Annotation]( new CollectAnnotations[q.type, A].paramAnnsOnTerms, paramSymbol.name ) ) val inheritedAnnotations = - toIArray( - selectFromDict( + toAnnIArray( + selectFromDict[Annotation]( new CollectAnnotations[q.type, A].inheritedParamAnnsOnTerms, paramSymbol.name ) ) val typeAnnotations = - toIArray( - selectFromDict( + toAnnIArray( + selectFromDict[Annotation]( new CollectAnnotations[q.type, A].paramTypeAnnsOnTerms, paramSymbol.name ) diff --git a/src/core/magnolia.scala b/src/core/magnolia.scala index 02384683..bdff5244 100644 --- a/src/core/magnolia.scala +++ b/src/core/magnolia.scala @@ -1,5 +1,6 @@ package magnolia2 +import scala.annotation.Annotation import scala.compiletime.* import scala.deriving.Mirror import scala.reflect.* @@ -30,9 +31,9 @@ trait CommonDerivation[TypeClass[_]]: ): Typeclass[A] = join(CaseClassDerivation.fromMirror(product)) inline def getParams__[T, Labels <: Tuple, Params <: Tuple]( - annotations: Map[String, List[Any]], - inheritedAnnotations: Map[String, List[Any]], - typeAnnotations: Map[String, List[Any]], + annotations: Map[String, List[Annotation]], + inheritedAnnotations: Map[String, List[Annotation]], + typeAnnotations: Map[String, List[Annotation]], repeated: Map[String, Boolean], defaults: Map[String, Option[() => Any]], idx: Int = 0 diff --git a/src/test/AnnotationsTests.scala b/src/test/AnnotationsTests.scala index da617857..5b552cde 100644 --- a/src/test/AnnotationsTests.scala +++ b/src/test/AnnotationsTests.scala @@ -74,17 +74,17 @@ class AnnotationsTests extends munit.FunSuite: assertEquals(subtypeAnnotations(1).mkString, "MyAnnotation(2)") } - // TODO - not compiling - // test("serialize case class with Java annotations by skipping them") { - // val res = Show.derived[MyDto].show(MyDto("foo", 42)) - // assertEquals(res, "MyDto{MyAnnotation(0)}(foo=foo,bar=42)") - // } - - // TODO - not compiling - // test("serialize case class with Java annotations which comes from external module by skipping them") { - // val res = Show.derived[JavaAnnotatedCase].show(JavaAnnotatedCase(1)) - // assertEquals(res, "JavaAnnotatedCase(v=1)") - // } + test("serialize case class with Java annotations by skipping them") { + val res = Show.derived[MyDto].show(MyDto("foo", 42)) + assertEquals(res, "MyDto{MyAnnotation(0)}(foo=foo,bar=42)") + } + + test( + "serialize case class with Java annotations which comes from external module by skipping them" + ) { + val res = Show.derived[JavaAnnotatedCase].show(JavaAnnotatedCase(1)) + assertEquals(res, "JavaAnnotatedCase(v=1)") + } object AnnotationsTests: diff --git a/src/test/RecursiveTypesTests.scala b/src/test/RecursiveTypesTests.scala index 84fb060b..004e01e7 100644 --- a/src/test/RecursiveTypesTests.scala +++ b/src/test/RecursiveTypesTests.scala @@ -95,9 +95,7 @@ class RecursiveTypesTests extends munit.FunSuite: val error = compileErrors("ExportedTypeclass.derived[Recursive]") val expectedError = """|No given instance of type magnolia2.examples.ExportedTypeclass[ - | Seq[magnolia2.tests.RecursiveTypesTests.Recursive] - |] was found. - |""".stripMargin + | Seq[magnolia2.tests.RecursiveTypesTests.Recursive]""".stripMargin assert(error contains expectedError) }