From f73d8b6f5e96a1707ab703a7a4b1f82ef263f13a Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Wed, 3 May 2023 04:40:45 +0200 Subject: [PATCH] Move the second type argument to the modify/modifyAll method in Scala 3 This allows manually specifying type arguments of the focused field Also suppress some warnings in Scala 3 macros related to softwaremill#157 --- .../quicklens/QuicklensMacros.scala | 6 ++-- .../com/softwaremill/quicklens/package.scala | 6 ++-- .../quicklens/test/LiteralTypeTest.scala | 34 +++++++++++++++++++ 3 files changed, 41 insertions(+), 5 deletions(-) create mode 100644 quicklens/src/test/scala-3/com/softwaremill/quicklens/test/LiteralTypeTest.scala 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 00aa09a..7fbd08c 100644 --- a/quicklens/src/main/scala-3/com/softwaremill/quicklens/QuicklensMacros.scala +++ b/quicklens/src/main/scala-3/com/softwaremill/quicklens/QuicklensMacros.scala @@ -67,7 +67,7 @@ object QuicklensMacros { case Empty case Node(children: Seq[(PathSymbol, Seq[PathTree])]) - def <>(symbols: Seq[PathSymbol]): PathTree = (this, symbols) match + def <>(symbols: Seq[PathSymbol]): PathTree = ((this, symbols): @unchecked) match case (PathTree.Empty, _) => symbols.toPathTree case (PathTree.Node(children), (symbol :: Nil)) => @@ -283,7 +283,9 @@ object QuicklensMacros { ) val defdefStatements = DefDef( defdefSymbol, - { case List(List(x)) => Some(mapToCopy(defdefSymbol, mod, x.asExpr.asTerm, tree)) } + ((_: @unchecked) match + case List(List(x)) => Some(mapToCopy(defdefSymbol, mod, x.asExpr.asTerm, tree)) + ) ) val closure = Closure(Ref(defdefSymbol), None) val block = Block(List(defdefStatements), closure) diff --git a/quicklens/src/main/scala-3/com/softwaremill/quicklens/package.scala b/quicklens/src/main/scala-3/com/softwaremill/quicklens/package.scala index 12e3e9d..54d5b20 100644 --- a/quicklens/src/main/scala-3/com/softwaremill/quicklens/package.scala +++ b/quicklens/src/main/scala-3/com/softwaremill/quicklens/package.scala @@ -9,7 +9,7 @@ package object quicklens { // #114: obj shouldn't be inline since we want to reference the parameter by-name, rather then embedding the whole // expression whenever obj is used; this is especially important for chained .modify invocations - extension [S, A](obj: S) + extension [S](obj: S) /** Create an object allowing modifying the given (deeply nested) field accessible in a `case class` hierarchy via * `path` on the given `obj`. * @@ -17,7 +17,7 @@ package object quicklens { * * You can use `.each` to traverse options, lists, etc. */ - inline def modify(inline path: S => A): PathModify[S, A] = ${ toPathModifyFromFocus('obj, 'path) } + inline def modify[A](inline path: S => A): PathModify[S, A] = ${ toPathModifyFromFocus('obj, 'path) } /** Create an object allowing modifying the given (deeply nested) fields accessible in a `case class` hierarchy via * `paths` on the given `obj`. @@ -26,7 +26,7 @@ package object quicklens { * * You can use `.each` to traverse options, lists, etc. */ - inline def modifyAll(inline path: S => A, inline paths: (S => A)*): PathModify[S, A] = ${ + inline def modifyAll[A](inline path: S => A, inline paths: (S => A)*): PathModify[S, A] = ${ modifyAllImpl('obj, 'path, 'paths) } diff --git a/quicklens/src/test/scala-3/com/softwaremill/quicklens/test/LiteralTypeTest.scala b/quicklens/src/test/scala-3/com/softwaremill/quicklens/test/LiteralTypeTest.scala new file mode 100644 index 0000000..8e8cab3 --- /dev/null +++ b/quicklens/src/test/scala-3/com/softwaremill/quicklens/test/LiteralTypeTest.scala @@ -0,0 +1,34 @@ +package com.softwaremill.quicklens.test + +import com.softwaremill.quicklens.TestData.duplicate +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +import com.softwaremill.quicklens._ + +object LiteralTypeTestData { + case class Test(f: "foo") + case class Test1[A](f: A) +} + +class LiteralTypeTest extends AnyFlatSpec with Matchers { + import LiteralTypeTestData._ + + it should "modify a literal type field with an explicit parameter" in { + Test("foo").modify["foo"](_.f).setTo("foo") should be(Test("foo")) + } + + it should "modify a literal type field as a type parameter with an explicit parameter" in { + Test1["foo"]("foo").modify["foo"](_.f).setTo("foo") should be(Test1("foo")) + } + + it should "not compile for a wrong literal type" in { + assertDoesNotCompile(""" + import com.softwaremill.quicklens.* + + case class Test1[A](f: A) + + Test1["foo"]("foo").modify["foo"](_.f).setTo("bar") + """) + } +} \ No newline at end of file