From deb5dbbe8a48f76b8c6a8091139a79fdf4a672dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Kozak?= Date: Mon, 22 Sep 2025 15:28:06 +0200 Subject: [PATCH 1/3] fix: enhance transientDefault handling to warn when no default value is provided --- .../NotUsedTransientDefault.scala | 26 +++++++++++++++++++ .../macros/serialization/GenCodecMacros.scala | 16 +++++++----- 2 files changed, 36 insertions(+), 6 deletions(-) create mode 100644 core/src/test/scala/com/avsystem/commons/serialization/NotUsedTransientDefault.scala diff --git a/core/src/test/scala/com/avsystem/commons/serialization/NotUsedTransientDefault.scala b/core/src/test/scala/com/avsystem/commons/serialization/NotUsedTransientDefault.scala new file mode 100644 index 000000000..2d0577341 --- /dev/null +++ b/core/src/test/scala/com/avsystem/commons/serialization/NotUsedTransientDefault.scala @@ -0,0 +1,26 @@ +package com.avsystem.commons.serialization + +import org.scalatest.funsuite.AnyFunSuite + +final class NotUsedTransientDefault extends AnyFunSuite { + + test("no warnings when @transientDefault is used properly") { + assertCompiles( + //language=Scala + s""" + |case class X(@transientDefault a: String = "default") + |val codec = GenCodec.materialize[X] + |""".stripMargin + ) + } + + test("warnings when missing default value") { + assertDoesNotCompile( + //language=Scala + s""" + |case class X(@transientDefault a: String) + |val codec = GenCodec.materialize[X] + |""".stripMargin + ) + } +} diff --git a/macros/src/main/scala/com/avsystem/commons/macros/serialization/GenCodecMacros.scala b/macros/src/main/scala/com/avsystem/commons/macros/serialization/GenCodecMacros.scala index 36a92e5bb..c60ddcbcd 100644 --- a/macros/src/main/scala/com/avsystem/commons/macros/serialization/GenCodecMacros.scala +++ b/macros/src/main/scala/com/avsystem/commons/macros/serialization/GenCodecMacros.scala @@ -131,9 +131,13 @@ class GenCodecMacros(ctx: blackbox.Context) extends CodecMacroCommons(ctx) with } } - def isTransientDefault(param: ApplyParam): Boolean = - param.defaultValue.nonEmpty && hasAnnotation(param.sym, TransientDefaultAnnotType) - + def isTransientDefault(param: ApplyParam, warnIfDefaultNotProvided: Boolean = false): Boolean = + (hasAnnotation(param.sym, TransientDefaultAnnotType), param.defaultValue.nonEmpty, warnIfDefaultNotProvided) match { + case (true, false, true) => + c.warning(param.sym.pos, s"@transientDefault has no effect on parameter ${param.sym.name} because it has no default value") + false + case (hasAnnotation, noDefaultValue, _) => hasAnnotation && noDefaultValue + } def isOptimizedPrimitive(param: ApplyParam): Boolean = { val vt = param.valueType vt =:= typeOf[Boolean] || vt =:= typeOf[Int] || vt =:= typeOf[Long] || vt =:= typeOf[Double] @@ -251,13 +255,13 @@ class GenCodecMacros(ctx: blackbox.Context) extends CodecMacroCommons(ctx) with } def anyParamHasTransientDefault: Boolean = - params.exists(isTransientDefault) + params.exists(isTransientDefault(_)) def isOptionLike(p: ApplyParam): Boolean = p.optionLike.nonEmpty def mayBeTransient(p: ApplyParam): Boolean = - isOptionLike(p) || isTransientDefault(p) + isOptionLike(p) || isTransientDefault(p, warnIfDefaultNotProvided = true) def transientValue(p: ApplyParam): Tree = p.optionLike match { case Some(optionLike) => q"${optionLike.reference(Nil)}.none" @@ -614,7 +618,7 @@ class GenCodecMacros(ctx: blackbox.Context) extends CodecMacroCommons(ctx) with val deps = new mutable.ListBuffer[Tree] ttpe.members.iterator.foreach { getter => - if(getter.isMethod && isJavaGetter(getter.asMethod)) { + if (getter.isMethod && isJavaGetter(getter.asMethod)) { val propType = getter.typeSignatureIn(ttpe).finalResultType val getterName = getter.name.decodedName.toString val setterName = getterName.replaceFirst("^(get|is)", "set") From 63c8b4c86bc3218917e83adce508c67f9e7c6c03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Kozak?= Date: Mon, 22 Sep 2025 16:09:05 +0200 Subject: [PATCH 2/3] fix: update warning message for transientDefault when no default value is provided --- .../avsystem/commons/macros/serialization/GenCodecMacros.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/macros/src/main/scala/com/avsystem/commons/macros/serialization/GenCodecMacros.scala b/macros/src/main/scala/com/avsystem/commons/macros/serialization/GenCodecMacros.scala index c60ddcbcd..a958e2185 100644 --- a/macros/src/main/scala/com/avsystem/commons/macros/serialization/GenCodecMacros.scala +++ b/macros/src/main/scala/com/avsystem/commons/macros/serialization/GenCodecMacros.scala @@ -134,7 +134,7 @@ class GenCodecMacros(ctx: blackbox.Context) extends CodecMacroCommons(ctx) with def isTransientDefault(param: ApplyParam, warnIfDefaultNotProvided: Boolean = false): Boolean = (hasAnnotation(param.sym, TransientDefaultAnnotType), param.defaultValue.nonEmpty, warnIfDefaultNotProvided) match { case (true, false, true) => - c.warning(param.sym.pos, s"@transientDefault has no effect on parameter ${param.sym.name} because it has no default value") + warning(s"@transientDefault has no effect on parameter ${param.sym.name} because it has no default value") false case (hasAnnotation, noDefaultValue, _) => hasAnnotation && noDefaultValue } From b345e5a5bf2650bb9bb145574c3aafc75b4da102 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Kozak?= Date: Tue, 23 Sep 2025 09:29:09 +0200 Subject: [PATCH 3/3] replace warning with error --- .../commons/serialization/NotUsedTransientDefault.scala | 8 ++++---- .../commons/macros/serialization/GenCodecMacros.scala | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/src/test/scala/com/avsystem/commons/serialization/NotUsedTransientDefault.scala b/core/src/test/scala/com/avsystem/commons/serialization/NotUsedTransientDefault.scala index 2d0577341..b32966a24 100644 --- a/core/src/test/scala/com/avsystem/commons/serialization/NotUsedTransientDefault.scala +++ b/core/src/test/scala/com/avsystem/commons/serialization/NotUsedTransientDefault.scala @@ -3,13 +3,14 @@ package com.avsystem.commons.serialization import org.scalatest.funsuite.AnyFunSuite final class NotUsedTransientDefault extends AnyFunSuite { + case class Valid(@transientDefault a: String = "default") + case class Invalid(@transientDefault a: String) test("no warnings when @transientDefault is used properly") { assertCompiles( //language=Scala s""" - |case class X(@transientDefault a: String = "default") - |val codec = GenCodec.materialize[X] + |GenCodec.materialize[Valid] |""".stripMargin ) } @@ -18,8 +19,7 @@ final class NotUsedTransientDefault extends AnyFunSuite { assertDoesNotCompile( //language=Scala s""" - |case class X(@transientDefault a: String) - |val codec = GenCodec.materialize[X] + |GenCodec.materialize[Invalid] |""".stripMargin ) } diff --git a/macros/src/main/scala/com/avsystem/commons/macros/serialization/GenCodecMacros.scala b/macros/src/main/scala/com/avsystem/commons/macros/serialization/GenCodecMacros.scala index a958e2185..7a1eb4f19 100644 --- a/macros/src/main/scala/com/avsystem/commons/macros/serialization/GenCodecMacros.scala +++ b/macros/src/main/scala/com/avsystem/commons/macros/serialization/GenCodecMacros.scala @@ -134,7 +134,7 @@ class GenCodecMacros(ctx: blackbox.Context) extends CodecMacroCommons(ctx) with def isTransientDefault(param: ApplyParam, warnIfDefaultNotProvided: Boolean = false): Boolean = (hasAnnotation(param.sym, TransientDefaultAnnotType), param.defaultValue.nonEmpty, warnIfDefaultNotProvided) match { case (true, false, true) => - warning(s"@transientDefault has no effect on parameter ${param.sym.name} because it has no default value") + error(s"@transientDefault has no effect on parameter ${param.sym.name} because it has no default value") false case (hasAnnotation, noDefaultValue, _) => hasAnnotation && noDefaultValue }