diff --git a/shared/src/main/scala-3/org/scalamock/clazz/MockMaker.scala b/shared/src/main/scala-3/org/scalamock/clazz/MockMaker.scala index 95db97a5..76930da7 100644 --- a/shared/src/main/scala-3/org/scalamock/clazz/MockMaker.scala +++ b/shared/src/main/scala-3/org/scalamock/clazz/MockMaker.scala @@ -40,14 +40,28 @@ private[clazz] object MockMaker: def asParent(tree: TypeTree): TypeTree | Term = val constructorFieldsFilledWithNulls: List[List[Term]] = + // we don't care about any of the parameters, and just want to pass null, since all interesting parts will be mocked. + // that's simpler said than done, because if type of the argument is a primitive + // then null needs to be cast to that primitive type. + // the type could be a generic type though, which could resolve to a primitive, so we need to find the type the param + // should resolve to, and cast it if needed. + val paramsMap = { + val abstractParams = tree.tpe.dealias.typeSymbol.primaryConstructor.paramSymss.filter(_.exists(_.isType)).flatten + val resolvedParams = tpe.widen match + case AppliedType(_, params) => params + case _ => Nil + abstractParams.zip(resolvedParams).toMap + } + tree.tpe.dealias.typeSymbol.primaryConstructor.paramSymss .filterNot(_.exists(_.isType)) - .map(_.map(_.info.widen match { - case t@AppliedType(inner, applied) => - Select.unique('{null}.asTerm, "asInstanceOf").appliedToTypes(List(inner.appliedTo(tpe.typeArgs))) - case other => - Select.unique('{null}.asTerm, "asInstanceOf").appliedToTypes(List(other)) - })) + .map(_.map{ sym => + val target = paramsMap.getOrElse(sym.info.typeSymbol, sym.info).widen.dealias + if target <:< TypeRepr.of[AnyVal] then + Select.unique('{null}.asTerm, "asInstanceOf").appliedToType(target) + else + '{null}.asTerm + }) if constructorFieldsFilledWithNulls.forall(_.isEmpty) then tree diff --git a/shared/src/test/scala-3/com/paulbutcher/test/MockTestScala3.scala b/shared/src/test/scala-3/com/paulbutcher/test/MockTestScala3.scala new file mode 100644 index 00000000..7b1a8b4b --- /dev/null +++ b/shared/src/test/scala-3/com/paulbutcher/test/MockTestScala3.scala @@ -0,0 +1,62 @@ +package com.paulbutcher.test + +// Copyright (c) 2011-2015 ScalaMock Contributors (https://github.com/paulbutcher/ScalaMock/graphs/contributors) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +package com.paulbutcher.test.mock + +import org.scalamock.scalatest.MockFactory + +import com.paulbutcher.test._ +import org.scalatest.freespec.AnyFreeSpec +import org.scalatest.matchers.should.Matchers + +class MockTestScala3 extends AnyFreeSpec with MockFactory with Matchers { + + autoVerify = false + + "In Scala 3, Mocks should" - { + + "mock type constructor arguments" in { + class WithTC[TC[_]](tc: TC[Int]) + type ID[A] = A + "mock[WithTC[List]]" should compile + pendingUntilFixed { + "mock[WithTC[ID]]" should compile + } + } + + "mock generic arguments" in { + class WithGeneric[T](t: T) + "mock[WithGeneric[String]]" should compile + "mock[WithGeneric[Int]]" should compile + } + + "mock type constructor context bounds" in { + trait Async[F[_]] + class A[F[_]: Async](val b: B[F]) + class B[F[_]: Async](val c: C[F]) + trait C[F[_]] + "mock[A[List]]" should compile + "mock[B[List]]" should compile + "mock[C[List]]" should compile + } + } +} \ No newline at end of file diff --git a/shared/src/test/scala/com/paulbutcher/test/mock/MockTest.scala b/shared/src/test/scala/com/paulbutcher/test/mock/MockTest.scala index cdd5f31a..efdfd9c7 100644 --- a/shared/src/test/scala/com/paulbutcher/test/mock/MockTest.scala +++ b/shared/src/test/scala/com/paulbutcher/test/mock/MockTest.scala @@ -488,5 +488,14 @@ class MockTest extends AnyFreeSpec with MockFactory with Matchers { "stub[HasNotifyMethod]" should compile } + + "mock constructor arguments" in { + class WithOption(opt: Option[String]) + class WithInt(i: Int) + class WithString(s: String) + "mock[WithOption]" should compile + "mock[WithInt]" should compile + "mock[WithString]" should compile + } } }