Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Broken compileErrors behavior on Scala 3 #711

Closed
MateuszKubuszok opened this issue Oct 18, 2023 · 1 comment
Closed

Broken compileErrors behavior on Scala 3 #711

MateuszKubuszok opened this issue Oct 18, 2023 · 1 comment
Milestone

Comments

@MateuszKubuszok
Copy link

MateuszKubuszok commented Oct 18, 2023

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]"))

(see Scastie)

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.

@valencik valencik added this to the MUnit v1.0 milestone Feb 2, 2024
tgodzik added a commit to tgodzik/munit that referenced this issue Apr 15, 2024
@tgodzik
Copy link
Contributor

tgodzik commented Apr 15, 2024

Testing if it breaks anything in #759

tgodzik added a commit that referenced this issue Apr 15, 2024
@tgodzik tgodzik closed this as completed Apr 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants