MUnit implements compileErrors on Scala 3 with inline def delegating to typeCheckErrors. In the source of typeCheckErrors we can read:
/** Whether the code type checks in the current context? If not,
* returns a list of errors encountered on compilation.
* IMPORTANT: No stability guarantees are provided on the format of these
* errors. This means the format and the API may change from
* version to version. This API is to be used for testing purposes
* only.
*
* An inline definition with a call to `typeCheckErrors` should be transparent.
*
* @param code The code to be type checked
*
* @return a list of errors encountered during parsing and typechecking.
*
* The code should be a sequence of expressions or statements that may appear in a block.
*/
transparent inline def typeCheckErrors(inline code: String): List[Error] = ...
This lack of transparent is a source of unwanted behavior that I noticed in my project and, from what I heard, also other people in their projects. However, it is quite difficult to create a reproduction that is completely independent of external libraries, which is why nobody reported it yet.
I also failed to came up with a simple reproduction, but I can demonstrate the issue using existing libraries:
//> using scala 3.3.1
//> using dep io.scalaland::chimney::0.8.0
//> using dep org.scalameta::munit::1.0.0-M10
import io.scalaland.chimney.dsl.*
import munit.internal.MacroCompat
object IOnlyNeedErrors extends MacroCompat.CompileErrorMacro {
/* Copy/Paste from munit, with transparent keyword added. */
transparent inline def compileErrorsFixed(inline code: String): String = {
val errors = scala.compiletime.testing.typeCheckErrors(code)
errors
.map { error =>
val indent = " " * (error.column - 1)
val trimMessage = error.message.linesIterator
.map { line =>
if line.matches(" +") then ""
else line
}
.mkString("\n")
val separator = if error.message.contains('\n') then "\n" else " "
s"error:$separator$trimMessage\n${error.lineContent}\n$indent^"
}
.mkString("\n")
}
}
case class Source(a: Int)
case class Target(b: String)
// If uncommented:
//Source(1).transformInto[Target]
// results in:
//Chimney can't derive transformation from Playground.Source to Playground.Target
//
//Playground.Target
// b: java.lang.String - no accessor named b in source type Playground.Source
//
//
//Consult https://chimney.readthedocs.io for usage examples.
println("munit, not fixed (inline def):")
println(IOnlyNeedErrors.compileErrors("Source(1).transformInto[Target]"))
println("munit, fixed (transparent inline def):")
println(IOnlyNeedErrors.compileErrorsFixed("Source(1).transformInto[Target]"))
As we can see on this example, expected error, the one we would see if compiling the code outside compileErrors would be:
Chimney can't derive transformation from Playground.Source to Playground.Target
Playground.Target
b: java.lang.String - no accessor named b in source type Playground.Source
Consult https://chimney.readthedocs.io for usage examples.
meanwhile, MUnit produces:
No given instance of type io.scalaland.chimney.Transformer.AutoDerived[Playground.Source,
Playground.Target] was found for parameter transformer of method transformInto in package io.scalaland.chimney.dsl.
I found:
io.scalaland.chimney.Transformer.AutoDerived.deriveAutomatic[Playground.Source,
Playground.Target]
But method deriveAutomatic in trait TransformerAutoDerivedCompanionPlatform does not match type io.scalaland.chimney.Transformer.AutoDerived[Playground.Source,
Playground.Target].
The following import might make progress towards fixing the problem:
import io.scalaland.chimney.auto.deriveAutomaticTransformer
The behavior works as expected on Scala 2. Fixing it is as easy as adding transparent before inline.
Unfortunately, as I said, I cannot provide better reproduction which would have no external dependencies.
MUnit implements
compileErrors
on Scala 3 withinline def
delegating totypeCheckErrors
. In the source oftypeCheckErrors
we can read:This lack of
transparent
is a source of unwanted behavior that I noticed in my project and, from what I heard, also other people in their projects. However, it is quite difficult to create a reproduction that is completely independent of external libraries, which is why nobody reported it yet.I also failed to came up with a simple reproduction, but I can demonstrate the issue using existing libraries:
(see Scastie)
As we can see on this example, expected error, the one we would see if compiling the code outside
compileErrors
would be:meanwhile, MUnit produces:
The behavior works as expected on Scala 2. Fixing it is as easy as adding
transparent
beforeinline
.Unfortunately, as I said, I cannot provide better reproduction which would have no external dependencies.