diff --git a/build.sbt b/build.sbt index 9490d53..30e7e9f 100644 --- a/build.sbt +++ b/build.sbt @@ -14,7 +14,14 @@ excludeLintKeys in Global ++= Set(ideSkipProject) val commonSettings = commonSmlBuildSettings ++ ossPublishSettings ++ Seq( organization := "com.softwaremill.quicklens", updateDocs := UpdateVersionInDocs(sLog.value, organization.value, version.value, List(file("README.md"))), - scalacOptions ++= Seq("-deprecation", "-feature", "-unchecked"), // useful for debugging macros: "-Ycheck:all", "-Xcheck-macros" + scalacOptions ++= Seq("-deprecation", "-feature", "-unchecked"), + scalacOptions ++= { + val sv = (Compile / scalaVersion).value + CrossVersion.partialVersion(sv) match { + case Some((3, _)) => List("-Xcheck-macros") + case _ => Nil + } + }, ideSkipProject := (scalaVersion.value != scalaIdeaVersion) ) diff --git a/quicklens/src/main/scala-3/com/softwaremill/quicklens/QuicklensMacros.scala b/quicklens/src/main/scala-3/com/softwaremill/quicklens/QuicklensMacros.scala index 6215ed3..21aa38a 100644 --- a/quicklens/src/main/scala-3/com/softwaremill/quicklens/QuicklensMacros.scala +++ b/quicklens/src/main/scala-3/com/softwaremill/quicklens/QuicklensMacros.scala @@ -157,6 +157,8 @@ object QuicklensMacros { def poorMansLUB: TypeRepr = tpe match { case AndType(l, r) if l <:< r => l case AndType(l, r) if r <:< l => r + case AndType(_, _) => + report.errorAndAbort(s"Implementation limitation: Cannot modify an & type with unrelated types.") case _ => tpe } @@ -176,6 +178,10 @@ object QuicklensMacros { extension (term: Term) def appliedToIfNeeded(args: List[Term]): Term = if args.isEmpty then term else term.appliedToArgs(args) + def appliedToTypesIfNeeded(args: List[TypeRepr]): Term = + if term.symbol.paramSymss.headOption.toList.flatten.filter(_.isType).nonEmpty then + term.appliedToTypes(args) + else term def symbolAccessorByNameOrError(obj: Term, name: String): Term = { val objTpe = obj.tpe.widenAll @@ -251,7 +257,7 @@ object QuicklensMacros { val methodSymbol = methodSymbolByNameOrError(objSymbol, copy.name + "$default$" + i.toString) // default values in extension methods take the extension receiver as the first parameter val defaultMethodArgs = argsMap.dropRight(1).flatMap(_.values) - obj.select(methodSymbol).appliedToIfNeeded(defaultMethodArgs) + obj.select(methodSymbol).appliedToTypesIfNeeded(typeParams).appliedToIfNeeded(defaultMethodArgs) n -> v.getOrElse(defaultMethod) }.toMap diff --git a/quicklens/src/test/scala-3/com/softwaremill/quicklens/test/ModifyAndTypeTest.scala b/quicklens/src/test/scala-3/com/softwaremill/quicklens/test/ModifyAndTypeTest.scala index 5d97a9f..8102b5a 100644 --- a/quicklens/src/test/scala-3/com/softwaremill/quicklens/test/ModifyAndTypeTest.scala +++ b/quicklens/src/test/scala-3/com/softwaremill/quicklens/test/ModifyAndTypeTest.scala @@ -9,6 +9,7 @@ object ModifyAndTypeTest { case class A(a: Int) extends B trait B { def a: Int + def b: Int = 0 } case class A1(a: Int) @@ -26,6 +27,7 @@ class ModifyAndTypeTest extends AnyFlatSpec with Matchers { val modified = ab.modify(_.a).setTo(1) + modified.b shouldBe 0 modified.a shouldBe 1 } @@ -34,22 +36,7 @@ class ModifyAndTypeTest extends AnyFlatSpec with Matchers { val modified = ab.modify(_.a).setTo(1) - modified.a shouldBe 1 - } - - it should "modify an & type object 2" in { - val ab: B & A1 = new A1(0) with B - - val modified = ab.modify(_.a).setTo(1) - - modified.a shouldBe 1 - } - - it should "modify an & type object 3" in { - val ab: A1 & B = new A1(0) with B - - val modified = ab.modify(_.a).setTo(1) - + modified.b shouldBe 0 modified.a shouldBe 1 }