How to annotate expected compiler fails

I often do small experiments with scala and type acrobatics. And I need to check if I implemented expected behaviour precisely. So I write several examples that should either compile or fail.

Those that fail give me a little inconvenience, it stops entire file from compiling. So I commonly use the same pattern: write failing example, check that it really fails, comment it. Then I encounter some example that works unexpectedly, fix bug in the code. After that I need to uncomment failed examples and make sure that they would fail again.

Not so much trouble really from expended time perspective, but a programmer is always irritated from doing monotonous manual work.

So I’m searching for a method to annotate block of code as intended to fail. Either some compiler switch or macros that sends block content to the compiler and check for failing. The goal is simple: compiler should swallow all errors inside the block and emit error there were none.


Sounds like what you want is invoke the Scala compiler from your own
Scala app. It’s called
Check here
how to use it.

 Best, Oliver

I’d like to just to execute some blocks in the current scope, not parsing some string in external scope.

case class Single[T](unbox : T)

val legal = Single(8)

   val illegal = Singe(false, "name")

The example above should successfully compile.

As I deduced from the provided links, there is no reference implementation for hiding compiler errors, so people invents their own macroses. I tried to write macros too. And I get quite a wonder that I need to get scala-reflect.jar for for using internal language feature.

Ordinary macro methods turned out to be no good: by the time they are called arguments become typechecked so it could not intercept intended error in their arguments.

So I tried to switch to macroparadise annotations, that transforms code before it get typechecked.

final class Fail extends StaticAnnotation {
  def macroTransform(annottees : Any*) : Any = macro ShouldFailImpl.impl

object ShouldFailImpl {
  def impl(ctx : Context)(annottees : ctx.Expr[Any]*) : ctx.Expr[Any] = {
    var failed = false
    try {
      for (code <- annottees) {
        val tree = code.tree.duplicate
    } catch {
      case e: TypecheckException => failed = true
    if (! failed)
      ctx.error(ctx.enclosingPosition, "expected to fail but typechecks succesfully")

But suddenly it get me Illegal cyclic reference error. I tried to get clean copy of tree to avoid cycles, but failed nevertheless.

I find writing macroses very painful and obstructed task: I could find almost zero information on how they should interact with compiler context and how it works.

Then I tried to write simple compiler plugin. If I has already macroparadise plugin, why not to insert another one? Information blockade towards compiler plugins is even denser than for macroses. I found pair of introduction videos from which I could easily get that awesome scala compiler is divided on phases and holds bunch of mutable information in Tree and Symbol. And example of simple tree transformation between the phases. Nice. But zero details are revealed about API and inner structures, hooks and so on. What should I do if I need to alternate existing phase behaviour? Just go somewhere and see other plugin examples is what I told. I went and studied macroparadise sources. All I could say it is complicated and presumably uses heavily compiler inside machinery designed for mundane macroses.

So as I get from compiler presentation, I need to add two phases. Before the Typer I should hide all marked tree. And I should typecheck them afterward. So, how could I transform information between phases? Should I encode it in a custom expression type and unpack it later? How could I define such extension to the Tree structure? How could I get proper context to typecheck swallowed info later?

Why not use one of the macros that someone else wrote? Most testing frameworks already contain methods for checking that compilation fails.

They works with strings, not with code. If I need to employ macroses, it is worth trying to get best service possible.

They work with strings cause it’s probably not feasible with code.