You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+23-1Lines changed: 23 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -40,8 +40,30 @@ The library has independent developers, release cycle and versioning from core m
40
40
## Partial unification
41
41
If you're in Scala 2.12 you'll probably want to add the compiler flag `-Ypartial-unification`, if you don't you risk some compile errors when trying to stub complex types using the idiomatic syntax
42
42
43
+
## Notes for 2.1.0
44
+
45
+
### Scala 3: `mock[T]` wrapper methods must be `inline`
46
+
47
+
In Scala 3, `mock[T]` uses a compile-time macro to register by-name/vararg parameter metadata
48
+
and value-class return types for `T`. This macro only works when `T` is concrete at the call site,
49
+
which requires every method in the call chain to be `inline`.
50
+
51
+
If you wrap `mock[T]` in a helper, declare it `inline`:
Copy file name to clipboardExpand all lines: common/src/main/scala-3/org/mockito/MockCreator.scala
+17Lines changed: 17 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -18,6 +18,23 @@ import scala.reflect.ClassTag
18
18
* - `inline` keeps the concrete `T` at call sites, allowing compile-time metadata extraction.
19
19
* - `createMock` registers per-method metadata via [[org.mockito.internal.MockMethodMetadata.registerByNameAndVarArgInfo]] before delegating to runtime creation; this feeds
20
20
* `ReflectionUtils`/`ScalaMockHandler` with by-name, vararg and return metadata.
21
+
*
22
+
* ==Wrapper methods must be `inline` (Scala 3)==
23
+
*
24
+
* All `mock[T]` overloads are `inline` so that `T` is concrete at the macro expansion point. If you wrap `mock[T]` in your own helper, that helper must also be `inline`:
25
+
*
26
+
* {{{
27
+
* // CORRECT – T is known at every call site
28
+
* inline def lenientMock[T <: AnyRef: ClassTag]: T =
* Without `inline`, the compile-time macro [[org.mockito.internal.MockMethodMetadata.registerByNameAndVarArgInfo]] sees `T` as an abstract type variable and produces no output. At
37
+
* runtime the `ScalaMockHandler` will not find the method in its cache, so vararg arrays won't be expanded and stubs on vararg methods will silently fail to match.
* 3. `false` — if the method is not in the cache it means the macro did not classify it as a value-class return, so it is a plain reference type.
45
43
*
46
-
* This mirrors Scala 2 intent ("identify returns that require value-like handling") while replacing runtime Scala reflection with compile-time metadata plus conservative runtime
47
-
* checks.
44
+
* NOTE: `classOf[AnyVal].isAssignableFrom(returnType)` cannot be used as a fallback in Scala 3 because `classOf[AnyVal]` compiles to `java.lang.Object` on the JVM, making the
45
+
* check return `true` for every reference type and causing null stubs to be replaced by smart-null proxies.
* - `byName`: keyed by declaring `Class[?]`, stores `Seq[(Method, Set[Int])]`:
11
-
* - outer `Seq`: one entry per method on that class with metadata
12
-
* - inner `Set[Int]`: zero-based parameter indices that are by-name or vararg for that method
13
-
* (example: for `def foo(x: => Int, ys: String*, z: () => String)`, indices are `Set(0, 1)`; `z` is plain `Function0`, so excluded)
11
+
* - outer `Seq`: one entry per method on that class with metadata
12
+
* - inner `Set[Int]`: zero-based parameter indices that are by-name or vararg for that method (example: for `def foo(x: => Int, ys: String*, z: () => String)`, indices are
13
+
* `Set(0, 1)`; `z` is plain `Function0`, so excluded)
14
14
* - `returnsValueClass`: keyed by `Method`, stores whether the declared Scala return type extends `AnyVal`.
15
15
* - `returnType`: keyed by `Method`, stores the concrete declared return class when JVM erasure gives `Object` but the Scala source type is more specific (e.g. abstract type
Copy file name to clipboardExpand all lines: macro-common/src/main/scala-3/org/mockito/internal/MockMethodMetadata.scala
+46-17Lines changed: 46 additions & 17 deletions
Original file line number
Diff line number
Diff line change
@@ -16,7 +16,13 @@ import scala.reflect.ClassTag
16
16
*/
17
17
objectMockMethodMetadata {
18
18
19
-
/** Entry point called from Scala 3 `MockCreator` during mock creation. */
19
+
/**
20
+
* Entry point called from Scala 3 `MockCreator` during mock creation.
21
+
*
22
+
* `T` '''must be a concrete type''' at the call site — the macro inspects `T`'s methods at compile time. This is guaranteed when every method in the call chain from user code
23
+
* down to this call is `inline`. If any intermediate method is not `inline`, `T` will be an abstract type variable and the macro will produce no output, leaving the runtime
0 commit comments