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/5] 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/5] 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/5] 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 } From 0b1b03528ae687fad8f2ac6372793a3db7efe51e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Kozak?= Date: Mon, 5 Jan 2026 10:02:26 +0100 Subject: [PATCH 4/5] refactor: overload isTransientDefault to separate default parameter handling --- .../avsystem/commons/macros/serialization/GenCodecMacros.scala | 3 ++- 1 file changed, 2 insertions(+), 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 7a1eb4f19..11feb3dea 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,7 +131,8 @@ class GenCodecMacros(ctx: blackbox.Context) extends CodecMacroCommons(ctx) with } } - def isTransientDefault(param: ApplyParam, warnIfDefaultNotProvided: Boolean = false): Boolean = + def isTransientDefault(param: ApplyParam): Boolean = isTransientDefault(param, warnIfDefaultNotProvided = false) + def isTransientDefault(param: ApplyParam, warnIfDefaultNotProvided: Boolean): Boolean = (hasAnnotation(param.sym, TransientDefaultAnnotType), param.defaultValue.nonEmpty, warnIfDefaultNotProvided) match { case (true, false, true) => error(s"@transientDefault has no effect on parameter ${param.sym.name} because it has no default value") From 0c2d70a9afbda836c47fc9d7492786254de17ef4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Kozak?= Date: Mon, 5 Jan 2026 15:48:32 +0100 Subject: [PATCH 5/5] fix: correct transientDefault handling and update test description --- .../commons/serialization/NotUsedTransientDefault.scala | 2 +- .../avsystem/commons/macros/serialization/GenCodecMacros.scala | 3 ++- 2 files changed, 3 insertions(+), 2 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 b32966a24..c92824537 100644 --- a/core/src/test/scala/com/avsystem/commons/serialization/NotUsedTransientDefault.scala +++ b/core/src/test/scala/com/avsystem/commons/serialization/NotUsedTransientDefault.scala @@ -15,7 +15,7 @@ final class NotUsedTransientDefault extends AnyFunSuite { ) } - test("warnings when missing default value") { + test("fails to compile when missing default value") { assertDoesNotCompile( //language=Scala s""" 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 11feb3dea..cd252d7de 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 @@ -137,8 +137,9 @@ class GenCodecMacros(ctx: blackbox.Context) extends CodecMacroCommons(ctx) with case (true, false, true) => error(s"@transientDefault has no effect on parameter ${param.sym.name} because it has no default value") false - case (hasAnnotation, noDefaultValue, _) => hasAnnotation && noDefaultValue + case (hasAnnotation, hasDefaultValue, _) => hasAnnotation && hasDefaultValue } + def isOptimizedPrimitive(param: ApplyParam): Boolean = { val vt = param.valueType vt =:= typeOf[Boolean] || vt =:= typeOf[Int] || vt =:= typeOf[Long] || vt =:= typeOf[Double]