Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,10 @@ object QuicklensMacros {
def toPathModifyFromFocus[S: Type, A: Type](obj: Expr[S], focus: Expr[S => A])(using Quotes): Expr[PathModify[S, A]] =
toPathModify(obj, modifyImpl(obj, Seq(focus)))

private def modifyImpl[S: Type, A: Type](obj: Expr[S], focuses: Seq[Expr[S => A]])(using
Quotes
): Expr[(A => A) => S] = {
private def modifyImpl[S: Type, A: Type](
obj: Expr[S],
focuses: Seq[Expr[S => A]]
)(using Quotes): Expr[(A => A) => S] = {
import quotes.reflect.*

def unsupportedShapeInfo(tree: Tree) =
Expand Down Expand Up @@ -209,8 +210,9 @@ object QuicklensMacros {
case AppliedType(_, typeParams) => Some(typeParams)
case _ => None
}

val fieldsIdxs = 1.to(objSymbol.primaryConstructor.paramSymss.flatten.filter(_.isTerm).length)
val constructorTree: DefDef = objSymbol.primaryConstructor.tree.asInstanceOf[DefDef]
val firstParamListLength: Int = constructorTree.termParamss.headOption.map(_.params).toList.flatten.length
val fieldsIdxs = 1.to(firstParamListLength)
val args = fieldsIdxs.map { i =>
val defaultMethod = obj.select(symbolMethodByNameUnsafe(objSymbol, "copy$default$" + i.toString))
argsMap.getOrElse(
Expand All @@ -219,6 +221,11 @@ object QuicklensMacros {
)
}.toList

if constructorTree.termParamss.drop(1).exists(_.params.exists(!_.symbol.flags.is(Flags.Implicit))) then
report.errorAndAbort(
s"Implementation limitation: Only the first parameter list of the modified case classes can be non-implicit."
)

typeParams match {
// if the object's type is parametrised, we need to call .copy with the same type parameters
case Some(typeParams) => Apply(TypeApply(Select(obj, copy), typeParams.map(Inferred(_))), args)
Expand Down Expand Up @@ -335,6 +342,7 @@ object QuicklensMacros {

val res: (Expr[A => A] => Expr[S]) = (mod: Expr[A => A]) =>
Typed(mapToCopy(Symbol.spliceOwner, mod, obj.asTerm, pathTree), TypeTree.of[S]).asExpr.asInstanceOf[Expr[S]]

to(res)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.softwaremill.quicklens

import com.softwaremill.quicklens.TestData._
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

class SecondParamListTest extends AnyFlatSpec with Matchers {
it should "modify an object with second implicit param list" in {
import com.softwaremill.quicklens._

case class State(inside: Boolean)(implicit d: Double)

val d: Double = 1.0

val state1 = State(true)(d)

implicit val dd: Double = d
val state2 = state1.modify(_.inside).setTo(true)

state1 should be(state2)
}

it should "should give a meaningful error for an object with more than one non-implicit param list" in {
import com.softwaremill.quicklens._

case class State(inside: Boolean)(d: Double)

val d: Double = 1.0

val state1 = State(true)(d)

implicit val dd: Double = d

assertDoesNotCompile("state1.modify(_.inside).setTo(true)")
}
}